How to segmentation object c# - c#

I used this code for segmentation, I'm trying to detect pixels one by one because my object is a binary, not a grayscale. when i run the program, it draws 2 object. The first object is successfully drawn (object still has a black color and a red rectangle), but the second object fails get drawn. Screenshot is here. Please help me, why does this happen?
#region Edge Detection
private void btnSegmentasi_Click(object sender, EventArgs e)
{
Segments = new List<ImageSegment>();
Bitmap bmp = (Bitmap)pb2.Image;
imageArea = new Rectangle(0, 0, pb2.Image.Width - 1, pb2.Image.Height - 1);
for (int y = 0; y < pb2.Image.Height; y++)
{
for (int x = 0; x < pb2.Image.Width; x++)
{
bool skip = false;
foreach (ImageSegment segment in Segments)
{
if (pointIsInRect(x, y, segment.Rect))
{
skip = true;
break;
}
}
if (skip) continue;
Color warna = bmp.GetPixel(x, y);
if (warna.G == 0)
startEdgeDetection(x, y, ref bmp);
}
}
DGVProses.DataSource = Segments;
if (Segments.Count > 0)
{
Graphics g = pb2.CreateGraphics();
Rectangle[] rects = (from theSegment in Segments select theSegment.Rect).ToArray();
g.DrawRectangles(new Pen(Brushes.Red), rects);
g.Dispose();
}
}
private void startEdgeDetection(int x, int y, ref Bitmap bmp)
{
Point startPoint = new Point(x, y);
Point currPoint = new Point(x, y);
int sudut = 180;
int xMin = x, yMin = y, xMax = x, yMax = y;
do
{
sudut -= 45;
Point offset = angleToPoint(ref sudut);
Point trialPoint = new Point(currPoint.X + offset.X, currPoint.Y + offset.Y);
if (!pointIsInRect(trialPoint.X, trialPoint.Y, imageArea))
continue;
Color theColor = bmp.GetPixel(trialPoint.X, trialPoint.Y);
if (theColor.G == 0)
{
currPoint = trialPoint;
sudut -= 180;
if (currPoint.X > xMax)
xMax = currPoint.X;
else if (currPoint.X < xMin)
xMin = currPoint.X;
if (currPoint.Y > yMax)
yMax = currPoint.Y;
else if (currPoint.Y < yMin)
yMin = currPoint.Y;
if (sudut < 0)
sudut += 360;
if (currPoint == startPoint && sudut == 180)
break;
}
}
while (!(currPoint == startPoint && sudut == 180));
Rectangle r = new Rectangle(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1);
Bitmap newImage = new Bitmap(r.Width + 2, r.Height + 2);
using (Graphics g = Graphics.FromImage(newImage))
{
g.FillRectangle(Brushes.White, 0, 0, newImage.Width, newImage.Height);
g.DrawImage(bmp, new Rectangle(1, 1, r.Width, r.Height), r, GraphicsUnit.Pixel);
g.Dispose();
}
Segments.Add(new ImageSegment(r, newImage));
}
private Point angleToPoint(ref int sudut)
{
if (sudut < 0)
sudut += 360;
switch (sudut)
{
case 135: return new Point(-1, -1);
case 90: return new Point(0, -1);
case 45: return new Point(1, -1);
case 0: return new Point(1, 0);
case 315: return new Point(1, 1);
case 270: return new Point(0, 1);
case 225: return new Point(-1, 1);
default: return new Point(-1, 0);
}
}
private bool pointIsInRect(int x, int y, Rectangle rect)
{
if (x < rect.X)
return false;
if (x > rect.X + rect.Width)
return false;
if (x < rect.Y)
return false;
if (x > rect.Y + rect.Height)
return false;
return true;
}
#endregion

Okay, I think I've now got a clue of how your algorithm is supposed to work. I'd guess you are running around in circles within the object. I do not really know why it does not happen for the first object, but this is another story.
When you enter startEdgeDetection you start at some point, check if it's black, move by an angle and repeat the whole procedure. You stop when the current point reaches the starting point. The crux is, that this algorithm does not guarantee to walk the whole object, but may just do the following (I do not know it is exactly like this, but pretty much):
OOOOOO
O####O
O####O
OOOOOO
OOOOOO
O*###O
O####O
OOOOOO
OOOOOO
O**##O
O####O
OOOOOO
OOOOOO
O**##O
O#*##O
OOOOOO
OOOOOO
O**##O
O**##O
OOOOOO
O = pixels filled with white
# = pixels filled with black
* = pixels you stepped through
You've reached your starting point again and the algorithm stops, but the bounding box does not contain the whole object, but just a part. If all of your objects bounding boxes have either a width or a height of 1 you fill up your whole object with bounding boxes, hence it appears red.
You'll have to fix the startEdgeDetection to avoid the described case and make sure that you really detect the edge.

I made up a simple class that finds the bounding box of an object. It should be easy to apply it to your problem.
public class BoundingBoxCalculator
{
Bitmap bitmapToCalculateBoundingBoxFor;
Point startingPoint;
Point[] neighborOffsets =
{new Point(-1,-1),
new Point(0,-1),
new Point(1,-1),
new Point(-1, 0),
new Point(1, 0),
new Point(-1,1),
new Point(0,1),
new Point(1,1)};
public BoundingBoxCalculator(Bitmap bitmapContainingObject, Point borderPoint)
{
this.bitmapToCalculateBoundingBoxFor = bitmapContainingObject;
this.startingPoint = borderPoint;
}
public Rectangle CalculateBoundingBox()
{
List<Point> edgePoints = CalculateEdge();
int minX = edgePoints.Min(p => p.X);
int maxX = edgePoints.Max(p => p.X);
int minY = edgePoints.Min(p => p.Y);
int maxY = edgePoints.Max(p => p.Y);
return new Rectangle(minX, minY, maxX - minX, maxY - minY);
}
List<Point> CalculateEdge()
{
List<Point> edgePoints = new List<Point>();
Point currentPoint = startingPoint;
do
{
IEnumerable<Point> neighboringEdgePoints = GetNeighboringEdgePoints(currentPoint);
IEnumerable<Point> neighboringEdgePointsNotVisited = from p in neighboringEdgePoints where !edgePoints.Contains(p) select p;
edgePoints.Add(currentPoint);
if(neighboringEdgePointsNotVisited.Count() == 0
&& neighboringEdgePoints.Contains(startingPoint))
{
currentPoint = startingPoint;
}
else if(neighboringEdgePointsNotVisited.Count() == 1)
{
Point nextPoint = neighboringEdgePointsNotVisited.First();
currentPoint = nextPoint;
}
else if(neighboringEdgePointsNotVisited.Count() > 1)
{
Point nextPoint = GetPointWithMinDistance(currentPoint, neighboringEdgePointsNotVisited);
currentPoint = nextPoint;
}
else
{
throw new Exception();
}
} while(currentPoint != startingPoint);
return edgePoints;
}
Point GetPointWithMinDistance(Point origin, IEnumerable<Point> pointsToTest)
{
double minDistance = double.MaxValue;
Point pointWithMinDistance = new Point(0,0);
foreach(Point pointToTest in pointsToTest)
{
double currentDistance = GetPointsDistance(origin, pointToTest);
if(currentDistance < minDistance)
{
minDistance = currentDistance;
pointWithMinDistance = pointToTest;
}
}
return pointWithMinDistance;
}
double GetPointsDistance(Point p1, Point p2)
{
return Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y));
}
IEnumerable<Point> GetNeighboringEdgePoints(Point currentPoint)
{
IEnumerable<Point> neighboringPoints = GetNeighboringPoints(currentPoint);
List<Point> neighboringEdgePoints = new List<Point>();
foreach(Point pointToTest in neighboringPoints)
{
if(GetNeighboringPoints(pointToTest).Count() < 8)
{
neighboringEdgePoints.Add(pointToTest);
}
}
return neighboringEdgePoints;
}
IEnumerable<Point> GetNeighboringPoints(Point currentPoint)
{
List<Point> neighbors = new List<Point>();
for(int offsetsCount = 0; offsetsCount < neighborOffsets.Length; offsetsCount++)
{
Point currentPointWithOffset = AddPointOffset(currentPoint, neighborOffsets[offsetsCount]);
if(IsInImage(currentPointWithOffset) &&
IsInObject(currentPointWithOffset))
{
neighbors.Add(currentPointWithOffset);
}
}
return neighbors;
}
bool IsInImage(Point pointToTest)
{
return pointToTest.X >= 0
&& pointToTest.X < bitmapToCalculateBoundingBoxFor.Width
&& pointToTest.Y >= 0
&& pointToTest.Y < bitmapToCalculateBoundingBoxFor.Height;
}
bool IsInObject(Point pointToTest)
{
Color colorInPointPosition = bitmapToCalculateBoundingBoxFor.GetPixel(pointToTest.X, pointToTest.Y);
//assume object is color is not white
return colorInPointPosition.R != 255
|| colorInPointPosition.G != 255
|| colorInPointPosition.B != 255;
}
Point AddPointOffset(Point point, Point offset)
{
return new Point(point.X + offset.X, point.Y + offset.Y);
}
}
Find an example at:
https://dotnetfiddle.net/49bnTV
I just tested it with a rectangle, but I guess it should work with any shape. Just give it a try.

Related

I want to implement a maze game function that prevents players from moving when they encounter a wall

bluepicturebox = PlayerPictureBox
NewgameButton = btnCreate
First, the shape of the maze was implemented in gdi+.
How do I implement the function of the player moving, but if I encounter a wall, how do I implement the function of not moving?
private void btnCreate_Click(object sender, EventArgs e)
{
int wid = 15;
int hgt = 15;
CellWidth = picMaze.ClientSize.Width / (wid+2);
CellHeight = picMaze.ClientSize.Height / (hgt+2);
Xmin = (picMaze.ClientSize.Width - wid * CellWidth) / 2;
Ymin = (picMaze.ClientSize.Height - hgt * CellHeight) / 2;
MazeNode[,] nodes = MakeNodes(wid, hgt);
MakeRandomMaze(nodes[0, 0]);
DisplayMaze(nodes);
PlayerPictureBox.Visible = true;
arrivePicturBox.Visible = true;
PlayerPictureBox.Location = new Point(70,51);
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
switch (keyData)
{
case Keys.Left:
PlayerPictureBox.Left -= 5;
break;
case Keys.Right:
PlayerPictureBox.Left += 5;
break;
case Keys.Up:
PlayerPictureBox.Top -= 5;
break;
case Keys.Down:
PlayerPictureBox.Top += 5;
break;
default: return base.ProcessCmdKey(ref msg, keyData);
}
return true;
}
You need to implement a collision algorithm between a rectangle (in your case, it is a square) and a line segment. Even though all the shapes are axis-aligned, the algorithm is pretty complex. It involves a lot of vector math and computational geometry tricks. I don't think that you may want to learn the math behind it. Therefore, I don't explain the algorithm. If you want, you can ask another question about how it works.
The implementation is my own implementation. It is not based on any paper on the subject or external example source code. It may not be optimized well. Keep that in mind.
I have created a LineSegment class to hold the line information and if you would use my algorithm, I suggest you to use the class or extend it according to your needs.
PS: I must admit that I supposed that the collision algorithm would be simple but it has evolved a far complex algorithm especially, for a beginner to the collision math.
Here is the example Winforms App. You can tinker it as you want.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace LineRectangleCollision
{
public partial class Form1 : Form
{
public Form1()
{
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
InitializeComponent();
lineSegments = new List<LineSegment>();
lineSegments.Add(new LineSegment(new PointF(100, 100), new PointF(200, 100)));
lineSegments.Add(new LineSegment(new PointF(100, 100), new PointF(100, 300)));
lineSegments.Add(new LineSegment(new PointF(400, 100), new PointF(400, 300)));
points = new List<PointF>();
points.Add(new PointF(10, 10));
points.Add(new PointF(256, 485));
points.Add(new PointF(110, 50));
rectangle = new RectangleF(0, 0, 30, 30);
}
private List<LineSegment> lineSegments;
private List<PointF> points;
private RectangleF rectangle;
private float velocity = 5f;
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
g.Clear(Color.White);
for (int i = 0; i < points.Count; i++)
g.FillEllipse(Brushes.Blue, points[i].X - 2, points[i].Y - 2, 4, 4);
for (int i = 0; i < lineSegments.Count; i++)
lineSegments[i].Draw(g);
g.FillRectangle(Brushes.Red, rectangle);
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
switch (e.KeyCode)
{
case Keys.Left:
rectangle.Offset(-velocity, 0);
break;
case Keys.Up:
rectangle.Offset(0, -velocity);
break;
case Keys.Right:
rectangle.Offset(velocity, 0);
break;
case Keys.Down:
rectangle.Offset(0, velocity);
break;
}
bool result = false;
for (int i = 0; i < lineSegments.Count; i++)
{
PointF translationVector = CheckCollision(rectangle, lineSegments[i], out result);
if (result)
{
rectangle.Offset(translationVector.X, translationVector.Y);
continue;
}
}
for (int i = 0; i < points.Count; i++)
{
PointF translationVector = CheckCollision(rectangle, points[i], out result);
if (result)
{
rectangle.Offset(translationVector.X, translationVector.Y);
continue;
}
}
Invalidate();
}
private PointF CheckCollision(RectangleF rect, LineSegment line, out bool result)
{
PointF lineParallel = line.Unit;
PointF rectCenter = new PointF(rect.X + rect.Width / 2.0f, rect.Y + rect.Height / 2.0f);
PointF collisionVector = VectorMath.Subtract(rectCenter, line.Start);
float minHalfLength = line.IsVertical ? rect.Width : rect.Height;
minHalfLength /= 2.0f;
float length = VectorMath.Length(VectorMath.Subtract(line.End, line.Start))
float t = VectorMath.Dot(collisionVector, lineParallel);
t = t < 0 ? t + minHalfLength : t - minHalfLength;
if (t > 0 && t <= length)
{
PointF translationVector = CheckCollision(rect, line.Start, out result);
if (result)
return translationVector;
translationVector = CheckCollision(rect, line.End, out result);
if (result)
return translationVector;
PointF collisionNormal = VectorMath.RightPerp(lineParallel);
float d = VectorMath.Dot(collisionNormal, collisionVector);
if (d < 0)
collisionNormal = new PointF(-collisionNormal.X, -collisionNormal.Y);
d = Math.Abs(d);
if (d > minHalfLength)
{
result = false;
return PointF.Empty;
}
result = true;
float penetration = minHalfLength - d + 0.5f;
return new PointF(penetration * collisionNormal.X, penetration * collisionNormal.Y);
}
result = false;
return PointF.Empty;
}
private PointF CheckCollision(RectangleF rect, PointF point, out bool result)
{
if(rect.Contains(point))
{
LineSegment[] sides = new LineSegment[4];
sides[0] = new LineSegment(rect.X, rect.Y, rect.X + rect.Width, rect.Y);
sides[1] = new LineSegment(rect.X + rect.Width, rect.Y, rect.X + rect.Width, rect.Y + rect.Height);
sides[2] = new LineSegment(rect.X + rect.Width, rect.Y + rect.Height, rect.X, rect.Y + rect.Height);
sides[3] = new LineSegment(rect.X, rect.Y + rect.Height, rect.X, rect.Y);
result = true;
float minPen = float.MaxValue;
int index = 0;
for (int i = 0; i < 4; i++)
{
float d = VectorMath.GetDistanceBetweenPointLine(point, sides[i]);
if (d < minPen)
{
minPen = d;
index = i;
}
}
return VectorMath.Multiply(VectorMath.RightPerp(sides[index].Unit), minPen);
}
result = false;
return PointF.Empty;
}
private class LineSegment
{
public PointF Start;
public PointF End;
public LineSegment(float x0, float y0, float x1, float y1) : this(new PointF(x0, y0), new PointF(x1, y1))
{
}
public LineSegment(PointF start, PointF end)
{
Start = start;
End = end;
}
public bool IsVertical
{
get
{
return Start.X == End.X;
}
}
public PointF Unit
{
get
{
PointF unit = new PointF(End.X - Start.X, End.Y - Start.Y);
float length = VectorMath.Length(unit);
unit.X /= length;
unit.Y /= length;
return unit;
}
}
public void Draw(Graphics g)
{
using (Pen pen = new Pen(Color.Black, 2.0f))
g.DrawLine(pen, Start, End);
}
}
private class VectorMath
{
public static float Dot(PointF v0, PointF v1)
{
return v0.X * v1.X + v0.Y * v1.Y;
}
public static float Length(PointF vector)
{
return (float)Math.Sqrt(vector.X * vector.X + vector.Y * vector.Y);
}
public static PointF LeftPerp(PointF vector)
{
return new PointF(vector.Y, vector.X);
}
public static PointF RightPerp(PointF vector)
{
return new PointF(-vector.Y, vector.X);
}
public static PointF Add(PointF v0, PointF v1)
{
return new PointF(v0.X + v1.X, v0.Y + v1.Y);
}
public static PointF Subtract(PointF v0, PointF v1)
{
return new PointF(v0.X - v1.X, v0.Y - v1.Y);
}
public static PointF Multiply(PointF vector, float scaler)
{
return new PointF(vector.X * scaler, vector.Y * scaler);
}
public static PointF Negate(PointF vector)
{
return Multiply(vector, -1.0f);
}
public static float GetDistanceBetweenPointLine(PointF point, LineSegment line)
{
PointF unitParallel = line.Unit;
PointF normal = RightPerp(unitParallel);
float d = Dot(normal, Subtract(point, line.Start));
return Math.Abs(d);
}
}
}
}

How to find the bounding rectangle(s) of a given color, withing a bitmap?

I have an Bitmap with various color patterns and I need to find the bounding rectangles of one given color (For example: Red) within the Bitmap. I found some code to process images but unable to figure out how to achieve this.
Any help would be highly appreciated.
This is my code.
private void LockUnlockBitsExample(PaintEventArgs e)
{
// Create a new bitmap.
Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg");
// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
// Set every third value to 255. A 24bpp bitmap will look red.
for (int counter = 2; counter < rgbValues.Length; counter += 3)
rgbValues[counter] = 255;
// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
// Unlock the bits.
bmp.UnlockBits(bmpData);
// Draw the modified image.
e.Graphics.DrawImage(bmp, 0, 150);
}
Edit: The Bitmap contains solid color shapes, multiple shapes with same color can appear. I need to find the bounding rectangle of each shape.
Just like the paint fills color with bucket tool, I need the bounding rectangle of the filled area.
I can provide x, y coordinates of point on Bitmap to find the bound rectangle of color.
You would do this just like any other code where you want to find the min or max value in a list. With the difference that you want to find both min and max in both X and Y dimensions. Ex:
public static Rectangle GetBounds(this Bitmap bmp, Color color)
{
int minX = int.MaxValue;
int minY = int.MaxValue;
int maxX = int.MinValue;
int maxY = int.MinValue;
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmp.Width; x++)
{
var c = bmp.GetPixel(x, y);
if (color == c)
{
if (x < minX) minX = x;
if (x > maxX) maxX = x;
if (y < minY) minY = y;
if (y > maxY) maxY = y;
}
}
}
var width = maxX - minX;
var height = maxY - minY;
if (width <= 0 || height <= 0)
{
// Handle case where no color was found, or if color is a single row/column
return default;
}
return new Rectangle(minX, minY, width, height);
}
There are plenty of resources on how to use LockBits/pointers. So converting the code to use this instead of GetPixel is left as an exercise.
If you are not concerned with the performance, and an exact color match is enough for you, then just scan the bitmap:
var l = bmp.Width; var t = bmp.Height; var r = 0; var b = 0;
for (var i = 0; i<rgbValues.Length, i++)
{
if(rgbValues[i] == 255) // rgb representation of red;
{
l = Math.Min(l, i % bmpData.Stride); r = Math.Max(r, i % bmpData.Stride);
t = Math.Min(l, i / bmpData.Stride); b = Math.Max(b, i / bmpData.Stride);
}
}
if(l>=r) // at least one point is found
return new Rectangle(l, t, r-l+1, b-t+1);
else
return new Rectangle(0, 0, 0, 0); // nothing found
You can search for the first point of each shape that fills a different area on the Bitmap, read a single horizontal row to get the points of the given color, then loop vertically within the horizontal range to get the adjacent points.
Once you get all the points of each, you can calculate the bounding rectangle through the first and last points.
public static IEnumerable<Rectangle> GetColorRectangles(Bitmap src, Color color)
{
var rects = new List<Rectangle>();
var points = new List<Point>();
var srcRec = new Rectangle(0, 0, src.Width, src.Height);
var srcData = src.LockBits(srcRec, ImageLockMode.ReadOnly, src.PixelFormat);
var srcBuff = new byte[srcData.Stride * srcData.Height];
var pixSize = Image.GetPixelFormatSize(src.PixelFormat) / 8;
Marshal.Copy(srcData.Scan0, srcBuff, 0, srcBuff.Length);
src.UnlockBits(srcData);
Rectangle GetColorRectangle()
{
var curX = points.First().X;
var curY = points.First().Y + 1;
var maxX = points.Max(p => p.X);
for(var y = curY; y < src.Height; y++)
for(var x = curX; x <= maxX; x++)
{
var pos = (y * srcData.Stride) + (x * pixSize);
var blue = srcBuff[pos];
var green = srcBuff[pos + 1];
var red = srcBuff[pos + 2];
if (Color.FromArgb(red, green, blue).ToArgb().Equals(color.ToArgb()))
points.Add(new Point(x, y));
else
break;
}
var p1 = points.First();
var p2 = points.Last();
return new Rectangle(p1.X, p1.Y, p2.X - p1.X, p2.Y - p1.Y);
}
for (var y = 0; y < src.Height; y++)
{
for (var x = 0; x < src.Width; x++)
{
var pos = (y * srcData.Stride) + (x * pixSize);
var blue = srcBuff[pos];
var green = srcBuff[pos + 1];
var red = srcBuff[pos + 2];
if (Color.FromArgb(red, green, blue).ToArgb().Equals(color.ToArgb()))
{
var p = new Point(x, y);
if (!rects.Any(r => new Rectangle(r.X - 2, r.Y - 2,
r.Width + 4, r.Height + 4).Contains(p)))
points.Add(p);
}
}
if (points.Any())
{
var rect = GetColorRectangle();
rects.Add(rect);
points.Clear();
}
}
return rects;
}
Demo
private IEnumerable<Rectangle> shapesRects = Enumerable.Empty<Rectangle>();
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
var sx = 1f * pictureBox1.Width / pictureBox1.ClientSize.Width;
var sy = 1f * pictureBox1.Height / pictureBox1.ClientSize.Height;
var p = Point.Round(new PointF(e.X * sx, e.Y * sy));
var c = (pictureBox1.Image as Bitmap).GetPixel(p.X, p.Y);
shapesRects = GetColorRectangles(pictureBox1.Image as Bitmap, c);
pictureBox1.Invalidate();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (shapesRects.Any())
using (var pen = new Pen(Color.Black, 2))
e.Graphics.DrawRectangles(pen, shapesRects.ToArray());
}

Pad or Crop Image to achieve square size in C#

I have Bitmap Images that are usually smaller than 500x500 pixels.
But sometimes one or both dimensions can exceed 500 pixels.
If the height is >500 I want to crop the image at the bottom and if the width is >500 I want to crop it on both sides equally.
If the Image is <500 in any dimension I want to pad it with white pixels on each side equally to make it 500x500.
I'm not familliar with .NET but I understand there's a lot that'S already been done for you (I'm a C++ developer).
I appreciate any help! Thanks!
This is what I have so far, which puts the image in the center of a white 500x500 rectangular image. It's just that I cant wrap my head around the cases where one dimension of the original image exceeds 500 pixels. (See the two lines with ??)
public static System.Drawing.Bitmap PadImage(System.Drawing.Bitmap originalImage)
{
if (originalImage.Height > 500)
??
if (originalImage.Width > 500)
??
Size squareSize = new Size(500, 500);
System.Drawing.Bitmap squareImage = new System.Drawing.Bitmap(squareSize.Width, squareSize.Height);
using (Graphics graphics = Graphics.FromImage(squareImage))
{
graphics.FillRectangle(System.Drawing.Brushes.White, 0, 0, squareSize.Width, squareSize.Height);
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
graphics.DrawImage(originalImage, (squareSize.Width / 2) - (originalImage.Width / 2), (squareSize.Height / 2) - (originalImage.Height / 2), originalImage.Width, originalImage.Height);
}
return squareImage;
}
Bitmap PadCropImage(Bitmap original)
{
if (original.Width == 500 && original.Height == 500)
return original;
if (original.Width > 500 && original.Height > 500)
{
int x = (original.Width - 500) / 2;
int y = (original.Height - 500) / 2;
return original.Clone(new Rectangle(x, y, 500, 500), original.PixelFormat);
}
Bitmap square = new Bitmap(500, 500);
var g = Graphics.FromImage(square);
if (original.Width > 500)
{
int x = (original.Width - 500) / 2;
int y = (500 - original.Height) / 2;
g.DrawImageUnscaled(original, -x, y);
}
else if (original.Height > 500)
{
int x = (500 - original.Width) / 2;
int y = (original.Height - 500) / 2;
g.DrawImageUnscaled(original, x, -y);
}
else
{
int x = (500 - original.Width) / 2;
int y = (500 - original.Height) / 2;
g.DrawImageUnscaled(original, x, y);
}
return square;
}
Here is an old method I have used many times
public static Image ThumbnailImage(Image sourceImage, int imageSize, bool maintainAspectRatio, bool maintainImageSize, Color backgroundColor)
{
try
{
int thumbnailWidth = imageSize;
int thumbnailHeight = imageSize;
if (maintainAspectRatio)
{
float aspectRatio = (float) sourceImage.Width/sourceImage.Height;
float targetAspectRatio = (float) thumbnailWidth/thumbnailHeight;
if (aspectRatio < targetAspectRatio)
{
thumbnailWidth = (int) (thumbnailHeight*aspectRatio);
}
else if (aspectRatio > targetAspectRatio)
{
thumbnailHeight = (int) (thumbnailWidth/aspectRatio);
}
}
Image thumbnail = sourceImage.GetThumbnailImage(thumbnailWidth, thumbnailHeight, null, new IntPtr());
if (maintainImageSize)
{
var offset = new Point(0, 0);
if (thumbnailWidth != imageSize)
{
offset.X = ((imageSize - thumbnailWidth)/2);
}
if (thumbnailHeight != imageSize)
{
offset.Y = ((imageSize - thumbnailHeight)/2);
}
var bmpImage = new Bitmap(imageSize, imageSize, PixelFormat.Format32bppArgb);
using (Graphics graphics = Graphics.FromImage(bmpImage))
{
graphics.Clear(backgroundColor);
graphics.DrawImage(thumbnail, new Rectangle(offset.X, offset.Y, thumbnailWidth, thumbnailHeight), new Rectangle(0, 0, thumbnailWidth, thumbnailHeight), GraphicsUnit.Pixel);
}
thumbnail.Dispose();
return Image.FromHbitmap(bmpImage.GetHbitmap());
}
return thumbnail;
}
catch (Exception exception)
{
const string strExMsg = "Error Creating Thumbnail";
throw new Exception(Assembly.GetExecutingAssembly().GetName().Name + " - " + strExMsg + " Msg : " + exception.Message);
}
}

How can I keep detecting clouds(points) but making the image background to be black?

The class is a bit long and working.
The cone is rotating 360 degrees and any clouds inside the cone area are painting in yellow.
But what I want to do now is that the background image will be in black and only when the cone is rotating over a cloud/s then light them in yellow only when the cone is passing over them one he moved on close the detected clouds. I mean like display the detected clouds only when the cone is passing over them.
What I'm getting today is the original image and when the cone is moving over clouds it's making them yellow:
This screenshot showing what happen when the cone is over clouds.
And this screenshot showing when the cone is not over any clouds:
Now what I want to do is instead displaying the original image in the class I called the variable: bmpWithClouds
I need the bmpWithClouds to scan the clouds but after it I want to use empty black image 512,512 resolution and to make that only when the cone is passing over a cloud/s then show them in yellow.
Then later I want to add my own points(pixels = clouds) make them move random in the black image and when the cone will pass over them to show them.
This is how I'm using the DopplerRadar class in form1 constructor:
DopplerRadar dr = new DopplerRadar();
dr.pb1 = pictureBox1;
dr.Init();
And this is the DopplerRadar class a bit long:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.IO;
using System.Drawing;
using System.Diagnostics;
namespace mws
{
class DopplerRadar
{
[DllImport("msvcrt.dll", EntryPoint = "memset", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
public static extern IntPtr MemSet(IntPtr dest, int c, int count);
private System.Windows.Forms.Timer timer1;
private Stream mymem;
private Bitmap ConvertedBmp;
private Device D3Ddev = null;
private PresentParameters D3Dpp = null;
private DisplayMode DispMode;
private Sprite D3Dsprite = null;
private Texture backTexture = null;
private Texture scannedCloudsTexture = null;
private byte[] argbValuesOfTransparentTexture;
private float distanceFromCenterPixels;
private float distanceFromCenterKm = 200F;
private List<Point> detectedPoints;
private float[] angleArray, distanceArray;
private double angleCalculation, distance;
private Bitmap bmpnew = new Bitmap(512, 512);
private int bytes = 2048 * 512;
public System.Windows.Forms.PictureBox pb1;
public void Init()
{
timer1 = new System.Windows.Forms.Timer();
timer1.Interval = 10;
timer1.Enabled = false;
timer1.Tick += Timer1_Tick;
ConvertedBmp = ConvertTo24(#"c:\temp\anim3.gif");
mymem = ToStream(ConvertedBmp, ImageFormat.Bmp);
distanceFromCenterPixels = (float)(/*183d*/ ((double)200 / 1.09289617486) * (double)distanceFromCenterKm / 200d);
argbValuesOfTransparentTexture = new byte[bytes];
InitializeDirectX(pb1);
FindPoints();
//initialize angleArray
angleArray = new float[detectedPoints.Count];
distanceArray = new float[detectedPoints.Count];
for (int i = 0; i < detectedPoints.Count; i++)
{
CalculateAngleAndDistance(detectedPoints[i].X, detectedPoints[i].Y, out angleCalculation, out distance);
angleArray[i] = (float)angleCalculation;
distanceArray[i] = (float)distance;
}
timer1.Enabled = true;
}
static float angleF_ = 0.0F;
private void Timer1_Tick(object sender, EventArgs e)
{
if (angleF_ > 360F)
{
angleF_ -= 360F;
}
ReturnTexture(scannedCloudsTexture, detectedPoints, angleArray, angleF_, bmpnew);
DisplayOnScreen(angleF_);
// To change direction to change += to -=
// To change speed to raise the value 1.0d
angleF_ += 1.0F;
}
private Bitmap ConvertTo24(string inputFileName)
{
Stopwatch sw = new Stopwatch();
sw = Stopwatch.StartNew();
Bitmap bmpIn = (Bitmap)Bitmap.FromFile(inputFileName);
Bitmap converted = new Bitmap(bmpIn.Width, bmpIn.Height, PixelFormat.Format24bppRgb);
using (Graphics g = Graphics.FromImage(converted))
{
// Prevent DPI conversion
g.PageUnit = GraphicsUnit.Pixel;
// Draw the image
g.DrawImageUnscaled(bmpIn, 0, 0);
}
//converted.Save(outputFileName, ImageFormat.Bmp);
sw.Stop();
return converted;
}
public static Stream ToStream(Image image, ImageFormat formaw)
{
var stream = new MemoryStream();
image.Save(stream, formaw);
stream.Position = 0;
return stream;
}
public Boolean InitializeDirectX(System.Windows.Forms.PictureBox pb1)
{
DispMode = Manager.Adapters[Manager.Adapters.Default.Adapter].CurrentDisplayMode;
D3Dpp = new PresentParameters();
D3Dpp.BackBufferFormat = DispMode.Format;
D3Dpp.PresentFlag = PresentFlag.LockableBackBuffer;
D3Dpp.SwapEffect = SwapEffect.Discard;
D3Dpp.PresentationInterval = PresentInterval.One; //wait for vertical sync. Synchronizes the painting with
//monitor refresh rate for smoooth animation
D3Dpp.Windowed = true; //the application has borders
try
{
D3Ddev = new Device(Manager.Adapters.Default.Adapter, DeviceType.Hardware, pb1.Handle,
CreateFlags.SoftwareVertexProcessing, D3Dpp);
//D3Ddev.VertexFormat = CustomVertex.PositionColored.Format;
D3Ddev.RenderState.Lighting = false;
D3Ddev.RenderState.CullMode = Cull.CounterClockwise;
//load imagesBmp to panelTexture
//panelTexture = Texture.FromBitmap(D3Ddev, imagesBmp, Usage.Dynamic, Pool.Default)
backTexture = TextureLoader.FromStream(D3Ddev, mymem);
//scannerTexture = TextureLoader.FromFile(D3Ddev, #"D:\Buttons\Radar\radar.png");
scannedCloudsTexture = new Texture(D3Ddev, 512, 512, 1, Usage.Dynamic, Format.A8R8G8B8, Pool.Default);
//sprite is used to draw the texture
D3Dsprite = new Sprite(D3Ddev);
return true;
}
catch
{
return false;
}
}
Bitmap bmpn;
float angle = 0;
private void DisplayOnScreen(float angleF)
{
if (angle < 360)
{
bmpn = new Bitmap(512, 512);
angle++;
}
else
{
angle = 361;
}
Surface backbuffer;
Brush myBrush = new SolidBrush(Color.FromArgb(110, 0, 255, 0)); //semi transparent color to draw the rotating cone
Graphics g;
//clear the backbuffer with Color.FromArgb(56, 56, 56). This is the double buffer mechanism. Drawing to offscreen
//backbuffer and in the end flipping it to main one which is our panelContainer
D3Ddev.Clear(ClearFlags.Target, Color.FromArgb(56, 56, 56), 1, 0);
D3Ddev.BeginScene();
//Draw Sprites
//////////////////////////////////////////////////////
D3Dsprite.Begin(SpriteFlags.AlphaBlend);
// bitmap with clouds
D3Dsprite.Draw2D(backTexture, new PointF(0, 0), 0F, new PointF(0F, 0F), Color.White);
//the part of clouds that are inside the cone
D3Dsprite.Draw2D(scannedCloudsTexture, new PointF(0F, 0F), 0F, new PointF(0F, 0F), Color.White);
//rotate cone
//D3Dsprite.Draw2D(scannerTexture, new PointF(104.5F, 0F), angle, new PointF(256F, 255F), Color.White);
D3Dsprite.Flush();
D3Dsprite.End();
//////////////////////////////////////////////////////
//Draw the cone.
using (backbuffer = D3Ddev.GetBackBuffer(0, 0, BackBufferType.Mono))
{
using (g = backbuffer.GetGraphics())
{
g.SmoothingMode = SmoothingMode.AntiAlias;
g.FillPie(myBrush, 256F - distanceFromCenterPixels, 255F - distanceFromCenterPixels,
distanceFromCenterPixels * 2F, distanceFromCenterPixels * 2F, angleF - 23F, 46F);
if (angle <= 360)
{
}
}
}
D3Ddev.EndScene();
D3Ddev.Present(); //performs the flipping
}
private void CalculateAngleAndDistance(int x, int y, out double angle, out double distance)
{
Double dbl = -1.0d;
Point center = new Point(256, 255);
distance = Math.Sqrt((double)((center.Y - y) * (center.Y - y) + (center.X - x) * (center.X - x)));
if (y == center.Y && x > center.X)
{
dbl = 0d;
angle = dbl;
return;
}
else if (x == center.X && y > center.Y)
{
dbl = 90d;
angle = dbl;
return;
}
else if (y == center.Y && x < center.X)
{
dbl = 180d;
angle = dbl;
return;
}
else if (x == center.X && y < center.Y)
{
dbl = 279d;
angle = dbl;
return;
}
else if (x == center.X && y == center.Y)
{
angle = dbl;
return;
}
if (x > center.X && y > center.Y) //1
{
dbl = Math.Atan(((double)y - (double)center.Y) / ((double)x - (double)center.X));
dbl = 180d * dbl / Math.PI;
}
else if (x < center.X && y > center.Y) //2
{
dbl = Math.Atan(((double)y - (double)center.Y) / ((double)center.X - (double)x));
dbl = 180d * dbl / Math.PI;
dbl = 180d - dbl;
}
else if (x < center.X && y < center.Y) //3
{
dbl = Math.Atan(((double)center.Y - (double)y) / ((double)center.X - (double)x));
dbl = 180d * dbl / Math.PI;
dbl += 180d;
}
else //4
{
dbl = Math.Atan(((double)center.Y - (double)y) / ((double)x - (double)center.X));
dbl = 180d * dbl / Math.PI;
dbl = 360d - dbl;
}
angle = dbl;
}
private void ReturnTexture(Texture texture_take, List<Point> lstPnt, float[] anglArr, float angle, Bitmap bmpNew)
{
int i, j, stride = 2048;
float angleBefore, angleAfter;
GraphicsStream textureStream;
Boolean bl = false;
if (bmpNew.Width != 512 && bmpNew.Height != 512)
throw new Exception("Bitmaps must be of same size.");
//sets texture to complete transparent
unsafe
{
fixed (byte* p = argbValuesOfTransparentTexture)
{
MemSet((IntPtr)p, 0x0, argbValuesOfTransparentTexture.Length);
}
}
angleAfter = angle + 23F;
if (angleAfter >= 360F)
{
angleAfter -= 360F;
}
angleBefore = angleAfter - 46;
if (angleBefore < 0F)
{
angleBefore += 360F;
bl = true;
}
BitmapData bmD = bmpNew.LockBits(new Rectangle(0, 0, bmpNew.Width, bmpNew.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
unsafe
{
byte* p = (byte*)bmD.Scan0.ToPointer();
//checks all points and draws yellow only those who are inside the cone
for (i = 0; i < lstPnt.Count - 1; i++)
{
if (anglArr[i] == -1F)
{
continue;
}
if (bl == true)
{
if (anglArr[i] <= angleAfter || anglArr[i] >= angleBefore) //if point angle is inside cone. Cone angle is 46 degrees
{
if (distanceArray[i] <= distanceFromCenterPixels)
{
j = lstPnt[i].Y * stride + lstPnt[i].X * 4;
//yellow
argbValuesOfTransparentTexture[j + 0] = (byte)0;
argbValuesOfTransparentTexture[j + 1] = (byte)255;
argbValuesOfTransparentTexture[j + 2] = (byte)255;
argbValuesOfTransparentTexture[j + 3] = (byte)255;
p[j] = (byte)0;
p[j + 1] = (byte)0;
p[j + 2] = (byte)255;
p[j + 3] = (byte)255;
}
}
}
else
{
if (anglArr[i] <= angleAfter && anglArr[i] >= angleBefore) //if point angle is inside cone. Cone angle is 46 degrees
{
if (distanceArray[i] <= distanceFromCenterPixels)
{
j = lstPnt[i].Y * stride + lstPnt[i].X * 4;
//yellow
argbValuesOfTransparentTexture[j + 0] = (byte)0;
argbValuesOfTransparentTexture[j + 1] = (byte)255;
argbValuesOfTransparentTexture[j + 2] = (byte)255;
argbValuesOfTransparentTexture[j + 3] = (byte)255;
p[j] = (byte)0;
p[j + 1] = (byte)0;
p[j + 2] = (byte)255;
p[j + 3] = (byte)255;
}
}
}
}
}
//if (angle <= 360)
// pictureBox1.Image.Save(#"c:\coneimages\" + angle + ".gif");
bmpNew.UnlockBits(bmD);
{
using (textureStream = texture_take.LockRectangle(0, LockFlags.None))
{
textureStream.Write(argbValuesOfTransparentTexture);
texture_take.UnlockRectangle(0);
}
//if (angle <= 360)
// pictureBox1.Image.Save(#"c:\coneimages\" + angle + ".gif");
}
}
private void FindPoints()
{
//Bitmap bmptest;
GraphicsPath gp = new GraphicsPath();
int x, y, p, j, wdthHght;
int bytes;
//byte error_ = 5;
byte[] rgbValuesWithClouds;
byte[] rgbValuesWithoutClouds;
IntPtr ptr;
Rectangle rect;
BitmapData bitmap_Data;
Bitmap bmpWithClouds; //No memory is allocated
Bitmap bmpWithoutClouds; //No memory is allocated
gp.AddEllipse(new RectangleF(73, 72, 367, 367));
//gp.CloseFigure();
//using the using statement, bmpWithClouds bitmap is automatically disposed at the end of statement. No memory leaks :)
using (bmpWithClouds = new Bitmap(mymem))//#"D:\MyWeatherStation-Images-And-Icons\radartobmp.bmp")) //24 bit bitmap
{
rect = new Rectangle(0, 0, bmpWithClouds.Width, bmpWithClouds.Height);
wdthHght = bmpWithClouds.Width;
//Lock bitmap to copy its color information fast
bitmap_Data = bmpWithClouds.LockBits(rect, ImageLockMode.ReadWrite, bmpWithClouds.PixelFormat);
ptr = bitmap_Data.Scan0;
bytes = bitmap_Data.Stride * bmpWithClouds.Height;
rgbValuesWithClouds = new byte[bytes];
//copy color information to rgbValues array
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValuesWithClouds, 0, bytes);
//we are done copying so unlock bitmap. We dont need it anymore
bmpWithClouds.UnlockBits(bitmap_Data);
}
//using the using statement, bmpWithClouds bitmap is automatically disposed at the end of statement. No memory leaks :)
using (bmpWithoutClouds = new Bitmap(
#"D:\C-Sharp\Download File\Downloading-File-Project-Version-012\Downloading File\bin\x86\Release\WithoutClouds.bmp"))//su + "\\WithoutClouds.bmp")) //24 bit bitmap
{
rect = new Rectangle(0, 0, bmpWithoutClouds.Width, bmpWithoutClouds.Height);
//Lock bitmap to copy its color information fast
bitmap_Data = bmpWithoutClouds.LockBits(rect, ImageLockMode.ReadWrite, bmpWithoutClouds.PixelFormat);
ptr = bitmap_Data.Scan0;
bytes = bitmap_Data.Stride * bmpWithoutClouds.Height;
rgbValuesWithoutClouds = new byte[bytes];
//copy color information to rgbValues array
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValuesWithoutClouds, 0, bytes);
//we are done copying so unlock bitmap. We dont need it anymore
bmpWithoutClouds.UnlockBits(bitmap_Data);
}
// Each position in these arrays, rgbValuesWithoutClouds and rgbValuesWithClouds, corresponds a color. eg
// First pixel Second pixel Third pixel Forth pixel .... // bitmaps
// B G R B G R B G R B G R .... // rgbValues arrays
//bmptest = new Bitmap(512, 512);
detectedPoints = new List<Point>();
for (y = 0; y < wdthHght; y++)
{
j = 0;
for (x = 0; x < wdthHght; x++)
{
p = y * wdthHght * 3 + j;
if (rgbValuesWithClouds[p] != rgbValuesWithoutClouds[p])
{
detectedPoints.Add(new Point(x, y));
//bmptest.SetPixel(x, y, Color.Red);
}
j += 3;
}
}
}
}
}

Snapping drawn rectangles to each other

I have a WPF application, wherein I draw rectangles in a canvas.I need to add a functionality in which when i draw a rectangle if there is a rectangle next to it (for eg: suppose first rectangle x coordinate is 236 and second rectangle coordinate is 235) i need to snap the second rectangle x coordinate to 236 as shown in the image.
The snap would be done only if the distance difference is 10.
I have written the following code to do this.
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
startPos = e.GetPosition(Canvas);
System.Windows.Point curPosition = e.GetPosition(SectionCanvas);
rect = new System.Windows.Shapes.Rectangle
{
Stroke = brushColor,
StrokeDashArray = new DoubleCollection { 2, 2 },
Tag = "rectangle"
};
Canvas.SetLeft(rect, startPos.X);
Canvas.SetTop(rect, startPos.X);
SectionCanvas.Children.Add(rect);
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
currentPos = e.GetPosition(SectionCanvas);
var x = Math.Min(currentPos.X, startPos.X);
var y = Math.Min(currentPos.Y, startPos.Y);
var w = Math.Max(currentPos.X, startPos.X) - x;
var h = Math.Max(currentPos.Y, startPos.Y) - y;
rect.Width = w;
rect.Height = h;
Canvas.SetLeft(rect, x);
Canvas.SetTop(rect, y);
}
private void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
{
if(rect == null)
{
MessageBox.Show("Could not capture section, Please try again");
return;
}
endPos = e.GetPosition(SectionCanvas);
IEnumerable<Rect> coordinates = rectCollection.Select(r => new Rect(Canvas.GetLeft(r), Canvas.GetTop(r), r.Width, r.Height));
Rect newCordinates = new Rect(Canvas.GetLeft(rect), Canvas.GetTop(rect), rect.Width, rect.Height);
if (coordinates.Any(c => c.IntersectsWith(newCordinates)))))
{
MessageBox.Show("New Rectangle intersects with existing rectangle");
Canvas.Children.Remove(rect);
return;
}
rectCollection.Add(rect);
rect = null;
foreach(Point p in tempCollection)
{
if((startPos.X <= (p.X + 10) && startPos.X >= (p.X -10)))
{
startPos.X = p.X;
}
if(endPos.X <= (p.X + 10) && endPos.X >= (p.X - 10))
{
var x1 = Math.Max(endPos.X,p.X) - Math.Min(endPos.X, p.X);
var w1 = startPos.X - x1;
endPos.X = p.X;
startPos.X = w1;
}
if ((startPos.Y <= (p.Y + 10) && startPos.Y >= (p.Y - 10)))
{
startPos.Y = p.Y;
}
if (endPos.Y <= (p.Y + 10) && endPos.Y >= (p.Y - 10))
{
var x1 = Math.Max(endPos.Y, p.Y) - Math.Min(endPos.Y, p.Y);
var w1 = startPos.Y - x1;
endPos.Y = p.Y;
}
}
var x = Math.Min(currentPos.X, startPos.X);
var y = Math.Min(currentPos.Y, startPos.Y);
var w = Math.Max(currentPos.X, startPos.X) - x;
var h = Math.Max(currentPos.Y, startPos.Y) - y;
rect.Width = w;
rect.Height = h;
rect.Stroke = Brushes.Coral;
Canvas.SetLeft(rect, x);
Canvas.SetTop(rect, y);
rect = null;
tempCollection.Add(startPos);
tempCollection.Add(endPos);
}
The above code doesnt work when I am changing the endpoints values. While debugging I can see that the end point value changes but the rectangle drawn doesn't change. I am not able to find out what I am doing wrong.
I figured out the answer which was a dumb mistake from my end.
While calculating the width and height after snapping, I am using currentPos, instead I should use endPos.
var x = Math.Min(endPos.X, startPos.X);
var y = Math.Min(endPos.Y, startPos.Y);
var w = Math.Max(endPos.X, startPos.X) - x;
var h = Math.Max(endPos.Y, startPos.Y) - y;

Categories

Resources