Convert an extent to rect - c#

I need to convert an extents BottomLeft (-180, -90), TopRight (180, 90) to System.Windows.Rect. Rect is created using top left position and size of rect so, I tried to create it using position as (-180, 90) and size as width & height of above extents but which is wrong.
So, I am unable to create Rect for the above extents. Please help in creating it.
Thanks!

Rectangle can be represented as:
Rectangle d = new Rectangle(xCoordinate, yCoordinate, width, height);
where coordinates relevant here is that of the top-left corner. So you should calculate these parameters from the coordinates available to you. In your case:
Rectangle d = new Rectangle(-90, 180, 180, 360);
where
width = right - left
height = top - bottom

Related

Calculate coordinates rectangle for picture cropping, when picture bigger than rectangle

I'm developing my own picture viewer and in the process of creating an image cropping method. It does work with my current code. However, the application is dynamically resizing the image to fit the user's screen. So when it is resized, the calculated X.Y coordinates of the image are incorrect. I'm not very good at math, so I don't know how to calculate that.
This is the code that I am using
internal static Int32Rect GetCrop()
{
var cropArea = cropppingTool.CropTool.CropService.GetCroppedArea();
var x = Convert.ToInt32(cropArea.CroppedRectAbsolute.X);
var y = Convert.ToInt32(cropArea.CroppedRectAbsolute.Y);
var width = Convert.ToInt32(cropArea.CroppedRectAbsolute.Width);
var height = Convert.ToInt32(cropArea.CroppedRectAbsolute.Height);
return new Int32Rect(x, y, width, height);
}
The cropArea variable is from my own modified version of https://github.com/dmitryshelamov/UI-Cropping-Image. It is a Rect that returns X and Y coordinates and width and height from the user drawn square, used to select cropping area of image.
I have the variables for resized image width and height, and the original pixel width and pixel height of the images. The cropping UI uses the resized variables, to fit on the user's screen.
For clarity, the image size is calculated as so, with image control set to Stretch.Fill
double width = sourceBitmap.PixelWidth;
double height = sourceBitmap.PixelHeight;
double maxWidth = Math.Min(SystemParameters.PrimaryScreenWidth - 300, width);
double maxHeight = Math.Min(SystemParameters.PrimaryScreenHeight - 300, height);
var aspectRatio = Math.Min(maxWidth / width, maxHeight / height);
width *= aspectRatio;
height *= aspectRatio;
image.Width = width;
image.Height = height;
So the question is, how do I calculate the offset between rendered size and actual pixel size?
If I understand this: you've calculated a ratio named aspectRatio to scale the image from it's actual size to the size of the screen. You have a cropping tool that gives you coordinates based on the scaled size image, and you want to convert those coordinates so they can be applied to the image's original size.
Assuming the above is right, this should be simple.
If the scaled height and width are calculated by:
scaledWidth = originalWidth * ratio
scaledHeigth = originalHeigth * ratio
Then you can reverse the multiplication by dividing instead:
originalWidth = scaledWidth / ratio
originalHeight = scaledHeight / ratio
This also applies to any coordinates within the image. You can take coordinates from the scaled image, and convert them into coordinates for the original image like so:
originalX = scaledRect.X / ratio
originalY = scaledRect.Y / ratio
originalWidth = scaledRect.Width / ratio
originalHeight = scaledRect.Height / ratio
You'll have to be careful to make sure that none of the values of scaledRect are 0, since division and 0 don't mix. A value of 0 in the scaled coordinate will also translate to 0 in the original coordinate space, so 0 should just stay 0. You can do this with if statements.

How to visually connect 2 circles?

We know 2 circle's x and y center position, and the radius is the same. I want to visually connect the circles without looping the draw ellipse for each point on the line what connects the 2 circle's center.
From this:
To this:
Code:
int radius = 75;
int x1 = 100;
int y1 = 200;
int x2 = 300;
int y2 = 100;
g.FillEllipse(Brushes.Blue, new Rectangle(x1 - radius / 2, y1 - radius / 2, radius, radius));
g.FillEllipse(Brushes.Blue, new Rectangle(x2 - radius / 2, y2 - radius / 2, radius, radius));
A solution for when the Circles don't have the same Diameter.
The first information needed is the distance between the Centers of two Circles.
To calculate it, we use the Euclidean distance applied to a Cartesian plane:
Where (x1, y1) and (x2, y2) are the coordinates of the Centers of two Circles.
We also need to know the Direction (expressed as a positive or negative value): the calculated [Distance] will always be positive.
in C# it, it can be coded as:
float Direction = (Circle1Center.X > Circle2Center.X) ? -1 : 1;
float Distance = (float)Math.Sqrt(Math.Pow(Circle1Center.X - Circle2Center.X, 2) +
Math.Pow(Circle1Center.Y - Circle2Center.Y, 2));
Distance *= Direction;
Now, we have the Distance between the Centers of two Circles, which also expresses a direction.
We also need to know how this virtual line - connecting the two Centers - is rotated in relation to our drawing plane. In the figure below, the Distance can be viewed as the hypotenuse of a right triangle h = (A, B). The C angle is determined by the intersection of the straight lines, parallel to the axis, that cross the Centers of the Circles.
We need to calculate the angle Theta (θ).
Using the Pythagorean theorem, we can derive that the Sine of the angle Theta is Sinθ = b/h (as in the figure)
Using the Circles' Centers coordinates, this can be coded in C# as:
(Distance is the triangle's hypotenuse)
float SinTheta = (Math.Max(Circle1Center.Y, Circle2Center.Y) -
Math.Min(Circle1Center.Y, Circle2Center.Y)) / Distance;
SinTheta expresses an angle in Radians. We need the angle expressed in Degrees: the Graphics object uses this measure for its world transformation functions.
float RotationAngle = (float)(Math.Asin(SinTheta) * (180 / Math.PI));
Now, we need to build a Connector, a shape that links the 2 Circles. We need a Polygon; a Rectangle can't have different pairs of sides (we are considering Circles with different Diameters).
This Polygon will have the longer sides = to the Distance between the Circles Centers, the shorter sides = to the Circles Diameters.
To build a Polygon, we can use both Graphics.DrawPolygon and GraphicsPath.AddPolygon. I'm choosing the GraphicsPath method, because a GraphicsPath can hold more that one shape and these shapes can interact, in a way.
To connect the 2 considered Circles with a Polygon, we need to rotate the Polygon using the RotationAngle previously calculated.
A simple way to perform the rotation, is to move the world coordinates to the Center of one of the Circles, using the Graphics.TranslateTransform method, then rotate the new coordinates, using Graphics.RotateTransform.
We need to draw our Polygon positioning one of the short sides - corresponding to the Diameter of the Circle which is the center of the coordinates transformation - in the center of the Cirle. Hence, when the rotation will be applied, it's short side it will be in the middle of this transformation, anchored to the Center.
Here, figure 3 shows the positioning of the Polygon (yellow shape) (ok, it looks like a rectangle, never mind);in figure 4 the same Polygon after the rotation.
Notes:
As TaW pointed out, this drawing needs to be performed using a SolidBrush with a non-transparent Color, which is kind of disappointing.
Well, a semi-transparent Brush is not forbidden, but the overlapping shapes will have a different color, the sum of the transparent colors of the intersections.
It is however possible to draw the shapes using a semi-transparent Brush without a Color change, using the GraphicsPath ability to fill its shapes using a color that is applied to all the overlapping parts. We just need to change the default FillMode (see the example in the Docs), setting it to FillMode.Winding.
Sample code:
In this example, two couples of Circles are drawn on a Graphics context. They are then connected with a Polygon shape, created using GraphicsPath.AddPolygon().
(Of course, we need to use the Paint event of a drawable Control, a Form here)
The overloaded helper function accepts both the Circles' centers position, expressed as a PointF and a RectangleF structure, representing the Circles bounds.
This is the visual result, with full Colors and using a semi-transparent brush:
using System.Drawing;
using System.Drawing.Drawing2D;
private float Radius1 = 30f;
private float Radius2 = 50f;
private PointF Circle1Center = new PointF(220, 47);
private PointF Circle2Center = new PointF(72, 254);
private PointF Circle3Center = new PointF(52, 58);
private PointF Circle4Center = new PointF(217, 232);
private void form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.CompositingQuality = CompositingQuality.GammaCorrected;
e.Graphics.PixelOffsetMode = PixelOffsetMode.Half;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
DrawLinkedCircles(Circle1Center, Circle2Center, Radius1, Radius2, Color.FromArgb(200, Color.YellowGreen), e.Graphics);
DrawLinkedCircles(Circle3Center, Circle4Center, Radius1, Radius2, Color.FromArgb(200, Color.SteelBlue), e.Graphics);
//OR, passing a RectangleF structure
//RectangleF Circle1 = new RectangleF(Circle1Center.X - Radius1, Circle1Center.Y - Radius1, Radius1 * 2, Radius1 * 2);
//RectangleF Circle2 = new RectangleF(Circle2Center.X - Radius2, Circle2Center.Y - Radius2, Radius2 * 2, Radius2 * 2);
//DrawLinkedCircles(Circle1, Circle2, Color.FromArgb(200, Color.YellowGreen), e.Graphics);
}
Helper function:
public void DrawLinkedCircles(RectangleF Circle1, RectangleF Circle2, Color FillColor, Graphics g)
{
PointF Circle1Center = new PointF(Circle1.X + (Circle1.Width / 2), Circle1.Y + (Circle1.Height / 2));
PointF Circle2Center = new PointF(Circle2.X + (Circle2.Width / 2), Circle2.Y + (Circle2.Height / 2));
DrawLinkedCircles(Circle1Center, Circle2Center, Circle1.Width / 2, Circle2.Width / 2, FillColor, g);
}
public void DrawLinkedCircles(PointF Circle1Center, PointF Circle2Center, float Circle1Radius, float Circle2Radius, Color FillColor, Graphics g)
{
float Direction = (Circle1Center.X > Circle2Center.X) ? -1 : 1;
float Distance = (float)Math.Sqrt(Math.Pow(Circle1Center.X - Circle2Center.X, 2) +
Math.Pow(Circle1Center.Y - Circle2Center.Y, 2));
Distance *= Direction;
float SinTheta = (Math.Max(Circle1Center.Y, Circle2Center.Y) -
Math.Min(Circle1Center.Y, Circle2Center.Y)) / Distance;
float RotationDirection = (Circle1Center.Y > Circle2Center.Y) ? -1 : 1;
float RotationAngle = (float)(Math.Asin(SinTheta) * (180 / Math.PI)) * RotationDirection;
using (GraphicsPath path = new GraphicsPath(FillMode.Winding))
{
path.AddEllipse(new RectangleF(-Circle1Radius, -Circle1Radius, 2 * Circle1Radius, 2 * Circle1Radius));
path.AddEllipse(new RectangleF(-Circle2Radius + (Math.Abs(Distance) * Direction),
-Circle2Radius, 2 * Circle2Radius, 2 * Circle2Radius));
path.AddPolygon(new[] {
new PointF(0, -Circle1Radius),
new PointF(0, Circle1Radius),
new PointF(Distance, Circle2Radius),
new PointF(Distance, -Circle2Radius),
});
path.AddEllipse(new RectangleF(-Circle1Radius, -Circle1Radius, 2 * Circle1Radius, 2 * Circle1Radius));
path.AddEllipse(new RectangleF(-Circle2Radius + (Math.Abs(Distance) * Direction),
-Circle2Radius, 2 * Circle2Radius, 2 * Circle2Radius));
path.CloseAllFigures();
g.TranslateTransform(Circle1Center.X, Circle1Center.Y);
g.RotateTransform(RotationAngle);
using (SolidBrush FillBrush = new SolidBrush(FillColor)) {
g.FillPath(FillBrush, path);
}
g.ResetTransform();
}
}
As the other answers so far slightly miss the correct solution, here is one that connects two circles of equal size:
using (Pen pen = new Pen(Color.Blue, radius)
{ EndCap = LineCap.Round, StartCap = LineCap.Round } )
g.DrawLine(pen, x1, y1, x2, y2);
Notes:
Usually is is good idea to set the smoothing mode of the graphics object to anti-alias..
To connect two circles of different sizes will take some math to calculate the four outer tangent points. From these one can get a polygon to fill or, if necessary one could create a GraphicsPath to fill, in case the color has an alpha < 1.
Jimi's comments point to a different solution that make use of GDI+ transformation capabilities.
Some of the answers or comments refer to the desired shape as an oval. While this ok in common speech, here, especially when geometry books are mentioned, this is wrong, as an oval will not have any straight lines.
As Jimi noted, what you call radius is really the diameter of the circles. I left the wrong term in the code but you should not!
Pseudo style:
circle1x;
circle1y;
circle2x;
circle2y;
midx=circle1x-circle2x;
midy=circle2x-circle2x;
draw circle at midx midy;
repeat for midx midy, in both directions. add another circle. honestly man, this isnt worth it,in order to make it smooth, you will need several circles. you need to draw an oval using the center of both circles as the two centers of your oval

Cut rectangle area from given image

I have the following problem:
I have an image saved as: Image X; and a list of Point.
point is defined as following:
public struct Point
{
public int X;
public int Y;
}
on the list (which isn't sorted) there are 2 Points. the Points represent cords on the image. these cords define a rectangle shape. for example if cords are: (0,0) and (1,1) then the rectangle edges are: (0,0) - (0,1) - (1,1) - (1,0).
I am suppose to write a method that returns a cropped image where the rectangle lays. in the above example the cropped image will be everything within the boundary of (0,0) - (0,1) - (1,1) - (1,0).
any ideas for simple way to implement it?
note that i can't know where the rectangle lays in the image X. rectangles can have same area or even the exact same shape but in different places on the image.
assume it can be anywhere yet rectangle can not be outside of the image in any way (whole nor partly)
First of all, you need to get the min corner and the max corner, an easy way is:
//Having p1 and p2
Point min = new Point(Math.Min(p1.X, p2.X), Math.Min(p1.Y, p2.Y));
Point max = new Point(Math.Max(p1.X, p2.X), Math.Max(p1.Y, p2.Y));
Once you have max and min you can construct a rectangle for source:
Rectangle srcRect = new Rectangle(min.X, min.Y, max.X - min.X, max.Y - min.Y);
Then you create a Bitmap with the rect size:
Bitmap cropped= new Bitmap(srcRect.Width, srcRect.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
Create a Graphics object from the image:
Graphics g = Graphics.FromImage(bmp);
And draw the cropped area:
//Having source image SrcImg
g.DrawImage(SrcImage, new Rectangle(Point.Empty, srcRect.Size), srcRect, GraphicsUnit.Pixel);
Now you have your cropped image at "cropped"
Don't forget to dispose graphics!!

Oddly drawn GraphicsPath with Graphics.FillPath

I have written some code which creates a rounded rectangle GraphicsPath, based on a custom structure, BorderRadius (which allows me to define the top left, top right, bottom left and bottom right radius of the rectangle), and the initial Rectangle itself:
public static GraphicsPath CreateRoundRectanglePath(BorderRadius radius, Rectangle rectangle)
{
GraphicsPath result = new GraphicsPath();
if (radius.TopLeft > 0)
{
result.AddArc(rectangle.X, rectangle.Y, radius.TopLeft, radius.TopLeft, 180, 90);
}
else
{
result.AddLine(new System.Drawing.Point(rectangle.X, rectangle.Y), new System.Drawing.Point(rectangle.X, rectangle.Y));
}
if (radius.TopRight > 0)
{
result.AddArc(rectangle.X + rectangle.Width - radius.TopRight, rectangle.Y, radius.TopRight, radius.TopRight, 270, 90);
}
else
{
result.AddLine(new System.Drawing.Point(rectangle.X + rectangle.Width, rectangle.Y), new System.Drawing.Point(rectangle.X + rectangle.Width, rectangle.Y));
}
if (radius.BottomRight > 0)
{
result.AddArc(rectangle.X + rectangle.Width - radius.BottomRight, rectangle.Y + rectangle.Height - radius.BottomRight, radius.BottomRight, radius.BottomRight, 0, 90);
}
else
{
result.AddLine(new System.Drawing.Point(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height), new System.Drawing.Point(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height));
}
if (radius.BottomLeft > 0)
{
result.AddArc(rectangle.X, rectangle.Y + rectangle.Height - radius.BottomLeft, radius.BottomLeft, radius.BottomLeft, 90, 90);
}
else
{
result.AddLine(new System.Drawing.Point(rectangle.X, rectangle.Y + rectangle.Height), new System.Drawing.Point(rectangle.X, rectangle.Y + rectangle.Height));
}
return result;
}
Now if I use this along with FillPath and DrawPath, I notice some odd results:
GraphicsPath path = CreateRoundRectanglePath(new BorderRadius(8), new Rectangle(10, 10, 100, 100));
e.Graphics.DrawPath(new Pen(Color.Black, 1), path);
e.Graphics.FillPath(new SolidBrush(Color.Black), path);
I've zoomed into each resulting Rectangle (right hand side) so you can see clearly, the problem:
What I would like to know is: Why are all of the arcs on the drawn rectangle equal, and all of the arcs on the filled rectangle, odd?
Better still, can it be fixed, so that the filled rectangle draws correctly?
EDIT: Is it possible to fill the inside of a GraphicsPath without using FillPath?
EDIT: As per comments....here is an example of the BorderRadius struct
public struct BorderRadius
{
public Int32 TopLeft { get; set; }
public Int32 TopRight { get; set; }
public Int32 BottomLeft { get; set; }
public Int32 BottomRight { get; set; }
public BorderRadius(int all) : this()
{
this.TopLeft = this.TopRight = this.BottomLeft = this.BottomRight = all;
}
}
I experienced the same problem and found a solution. Might be too late for you #seriesOne but it can be useful to other people if they have this problem.
Basically when using the fill methods (and also when setting the rounded rectangle as the clipping path with Graphics.SetClip) we have to move by one pixel the right and bottom lines. So I came up with a method that accepts a parameter to fix the rectangle is using the fill or not. Here it is:
private static GraphicsPath CreateRoundedRectangle(Rectangle b, int r, bool fill = false)
{
var path = new GraphicsPath();
var r2 = (int)r / 2;
var fix = fill ? 1 : 0;
b.Location = new Point(b.X - 1, b.Y - 1);
if (!fill)
b.Size = new Size(b.Width - 1, b.Height - 1);
path.AddArc(b.Left, b.Top, r, r, 180, 90);
path.AddLine(b.Left + r2, b.Top, b.Right - r2 - fix, b.Top);
path.AddArc(b.Right - r - fix, b.Top, r, r, 270, 90);
path.AddLine(b.Right, b.Top + r2, b.Right, b.Bottom - r2);
path.AddArc(b.Right - r - fix, b.Bottom - r - fix, r, r, 0, 90);
path.AddLine(b.Right - r2, b.Bottom, b.Left + r2, b.Bottom);
path.AddArc(b.Left, b.Bottom - r - fix, r, r, 90, 90);
path.AddLine(b.Left, b.Bottom - r2, b.Left, b.Top + r2);
return path;
}
So this is how you use it:
g.DrawPath(new Pen(Color.Red), CreateRoundedRectangle(rect, 24, false));
g.FillPath(new SolidBrush(Color.Red), CreateRoundedRectangle(rect, 24, true));
I'd suggest explicitly adding a line from the end of each arc to the beginning of the next one.
You could also try using the Flatten method to approximate all curves in your path with lines. That should remove any ambiguity.
The result you're getting from FillPath looks similar to an issue I had where the points on a Path were interpreted incorrectly, essentially leading to a quadratic instead of a cubic bezier spline.
You can examine the points on your path using the GetPathData function: http://msdn.microsoft.com/en-us/library/ms535534%28v=vs.85%29.aspx
Bezier curves (which GDI+ uses to approximate arcs) are represented by 4 points. The first is an end point and can be any type. The second and third are control points and have type PathPointBezier. The last is the other end point and has type PathPointBezier. In other words, when GDI+ sees PathPointBezier, it uses the path's current position and the 3 Bezier points to draw the curve. Bezier curves can be strung together but the number of bezier points should always be divisible by 3.
What you're doing is a bit strange, in that you are drawing curves in different places without explicit lines to join them. I'd guess it creates a pattern like this:
PathPointStart - end point of first arc
PathPointBezier - control point of first arc
PathPointBezier - control point of first arc
PathPointBezier - end point of first arc
PathPointLine - end point of second arc
PathPointBezier - control point of second arc
PathPointBezier - control point of second arc
PathPointBezier - end point of second arc
PathPointLine - end point of third arc
PathPointBezier - control point of third arc
PathPointBezier - control point of third arc
PathPointBezier - end point of third arc
That looks reasonable. GDI+ should be drawing a line from the last endpoint of each curve to the first endpoint of the next one. DrawPath clearly does this, but I think FillPath is interpreting the points differently. Some end points are being treated as control points and vice versa.
The real reason for the behavior is explained at Pixel behaviour of FillRectangle and DrawRectangle.
It has to do with the default pixel rounding and the fact that FillRectangle/FillPath with integer coordinates end up drawing in the middle of a pixel and get rounded (according to Graphics.PixelOffsetMode).
On the other hand, DrawRectangle/DrawPath draw with a 1px pen that gets perfectly rounded on the pixel boundaries.
Depending on your usage the solution could be to inflate/deflate the rectangle for FillRectangle/FillPath by .5px.
"Is it possible to fill the inside of a GraphicsPath without using FillPath?"
Yes...but I think this is more of a parlor trick (and it might not work as you expect for more complex shapes). You can clip the graphics to the path, and then just fill the entire encompassing rectangle:
Rectangle rc = new Rectangle(10, 10, 100, 100);
GraphicsPath path = CreateRoundRectanglePath(new BorderRadius(8), rc);
e.Graphics.SetClip(path);
e.Graphics.FillRectangle(Brushes.Black, rc);
e.Graphics.ResetClip();

WinForms - how much % of the area one rectangle covers another

Given two System.Drawing.Rectangle's - how to determine what % of the first rectangle's area the second rectangle covers?
For example, if the second rectangle is half-way positioned to the first one, the result should be 50%.
You can use the Rectangle.Intersect method to get the intersection rectangle:
Rectangle rect = new Rectangle(firstRect.Location, firstRect.Size);
rect.Intersect(secondRectangle);
var percentage = (rect.Width * rect.Height) * 100f/(firstRect.Width * firstRect.Height);

Categories

Resources