Rotation of vector graphics - c#

I have an application for drawing and editing vector graphics in WinForms
I have images, rectangles, ellipses, regions etc. and I know how to resize them by mouse move. But I don't know how to rotate them by mouse move.
I draw objects into Graphics.
I've tried this, but it didn't work.
g.TranslateTransform((float)(this.Rectangle.X + this.Rectangle.Width / 2), (float)(this.Rectangle.Y + this.Rectangle.Height / 2));
g.RotateTransform(this.Rotation);
g.TranslateTransform(-(float)(this.Rectangle.X + this.Rectangle.Width / 2), -(float)(this.Rectangle.Y + this.Rectangle.Height / 2));
//g.TranslateTransform(-(float)(rect.X + rect.Width / 2), -(float)(rect.Y + rect.Height / 2));
g.DrawImage(img, rect);
g.ResetTransform();
This didn't work, because I don't know how to find corners of objects in new (rotated) position, so I'm not able to resize that...

You need to apply high school trigonometry. There are lots of articles if you google "graphics.drawimage rotation"
But to start with, you should NOT be transforming the Graphics object itself. You are just looking to get the new bounding box of your image. To do this:
Take the bounding box of the image centered on the origin. Remember this is defined as three points for the benefit of DrawImage(Image, Point[])
Point[] boundingBox = { new Point(-width /2, -height/2),
new Point(width/2, -height/2),
new Point(-width/2, height/2) };
Use trig to rotate it. Feed each point through the following function:
Point rotatePointAroundOrigin(Point point, float angleInDegrees) {
float angle = angleInDegrees * Math.PI/180; // get angle in radians
return new Point( point.X * Math.Cos(angle) - point.Y * Math.Sin(angle),
point.X * Math.Sin(angle) + point.Y * Math.Cos(angle));
}
Translate the boundind box to where it has to go. Add the width/2 and height/2 to each of its points, plus whatever extra amount you want.
Call DrawImage(image, boundingBox)

Related

Finding the End Coordinates of 2 DrawArc instances and drawing a line to "Close" it

So, I am not very proficient in C#/.Net/PDFSharp functions and I cannot seem to find any suitable answer to solve my issue.
Basically, I have a simple program that has to draw contour of an object(it can be curved and etc.) from user input.
I have Radius and Degrees of an angle to draw 2 arc lines.
In 360 degrees one circle radius is smaller by X amount of thickness that user inputs so the "inside" of the two circles is the same thickness as the whole draft.
Finally, the thing I need the program to do is to draw two lines, on the 2 sides of the arc lines to "connect them" to make it a proper contour, I can manage the starting Line easily enough since it is not dynamic however the End line is dependent on Radius and degrees of an angle.
How do I properly find the End coordinates I suppose, of the Arcs, so that it successfully draws the End line at the end regardless of the Users inputted Radius/Thickness/Angle.
Here is some code for how I drawn the Arcs, the Start line and my failure try of finding the End line (which just draws the line too far from the whole draft).
Input is User Control where User can input the variables (needed multiple but ended up with one, so it sounds waste to use User Control + Form window).
mmradius, thickness, mmangle are all user inputs, Innerradius is the innercircle radius.
var innerradius = Input.mmradius- Input.thickness;
gfx.DrawArc(pen, start_x, start_y, mmradius*2, mmradius*2, 0, mmangle);
gfx.DrawLine(pen, start_x + mmradius+ innerradius, start_y + mmradius,
(start_x + mmradius) + mmradius, start_y + mmradius);
gfx.DrawArc(pen, (start_x + mmradius) - innerradius,
(start_y + mmradius) - innerradius, innerradius*2, innerradius*2, 0, mmangle);
var CenterX = start_x + mmradius;
var CenterY = start_y + mmradius;
double degrees = mmangle * (Math.PI / 180);
var end_x = mmradius + CenterX * Math.Cos(degrees);
var end_y = mmradius + CenterY * Math.Sin(degrees);
gfx.DrawLine(pen, end_x, end_y, end_x - innerradius, end_y - 2);
Hope it's understandable of what I want if not I'll try my best to clarify!
Maybe it works better like this:
var end_x = CenterX + mmradius * Math.Cos(degrees);
var end_y = CenterY + mmradius * Math.Sin(degrees);
Cannot try to run it, so maybe some more changes are needed.

Align elements on border of ellipse

I have a canvas with objects (product images, e.g. an apple) and I want elements to circle around them (icons, e.g. warnings about allegenes).
I've tried to add an Ellipse and now I want to align the icons on the border of the Ellipse. Is that possible? Hope you can help - thanks a lot!
You don't need an ellipse, you just need to do the maths yourself.
You know how many elements you want to order so you calculate the positions using trigonometry and then set the position with:
element.SetValue(Canvas.TopProperty, pos.Y);
element.SetValue(Canvas.LeftProperty, pos.X);
To calculate the positions, first calculate the angle between each object in the circle.
double radius = 100; // Or what ever your radius is
double angle = 360.0 / numItems * Math.PI / 180.0;
var centre = //position of product image
pos.X = centre.X + Math.Cos(angle * itemNumber) * radius;
pos.Y = centre.Y + Math.Sin(angle * itemNumber) * radius;
so you'll need a list or array of your elements and loop over that.

Deformed object after 2d rotation

I have certain objects in a 2D plane.I'm using a drawing techonology(drawing visuals) which draws the elements like it pushes them on a stack,first element is on the bottom second - on top of it and so on.Now,the problem is that I need all objects, except one of them(the background), on the same Z level because in the current state that my program is it happens to rotate in a sort of 3D way everything and it is supposed to rotate it in a 2D way.I understand that this explanation is NOT good that's why please refer to the images below.
Before rotating by theta angle :
After rotating by a theta angle :
You can see how the two lines start to overlap and this mustn't happen.They do get closer to each other as I rotate the figure and there's a certain angle where the two lines become fully overlapped and they look like one line.I want to avoid that.
The forumlae that I use for the rotation:
foreach(var item in Visuals){
var p = new Point(item.Position.X - center.X, item.Position.Y - center.Y);
var xnew = p.X * cos - p.Y * sin;
var ynew = p.X * sin + p.Y * cos;
p.X = xnew + center.X;
p.Y = ynew + center.Y;
item.Update(p.X, p.Y);
}
This is how I get the sin and cos of the angle
var pos = new Point(position.Y - center.Y, position.X - center.X);
var rad = Math.Atan2(pos.Y, pos.X);
var deg = rad.ToDegrees();
var diff = RotationLastAngle - deg;//The last angle that we rotated to.
RotationLastAngle = deg;
var ans = diff.ToRadians();
Host.Representation.Rotate(Math.Cos(ans), Math.Sin(ans), center);
Update() basically sets the coordinates of item in a single line.
What I think is causing the issue is that the DrawingVisual renders items on layers and thus one of the lines is higher than the other one(Correct me if I'm wrong).I need to find a way to avoid this.
This is how I draw the lines :
var dx = FromAtom.Atom.X - ToAtom.Atom.X;
var dy = FromAtom.Atom.Y - ToAtom.Atom.Y;
var slope = dy / dx;
if (slope > 0)
{
context.DrawLine(new Pen(Brushes.Black, Thickness), new Point(FromAtom.Position.X + 3, FromAtom.Position.Y + 3), new Point(ToAtom.Position.X + 3, ToAtom.Position.Y + 3));
context.DrawLine(new Pen(Brushes.Black, Thickness), new Point(FromAtom.Position.X - 3, FromAtom.Position.Y - 3), new Point(ToAtom.Position.X - 3, ToAtom.Position.Y - 3));
}
else
{
context.DrawLine(new Pen(Brushes.Black, Thickness), new Point(FromAtom.Position.X + 3, FromAtom.Position.Y - 3), new Point(ToAtom.Position.X + 3, ToAtom.Position.Y - 3));
context.DrawLine(new Pen(Brushes.Black, Thickness), new Point(FromAtom.Position.X - 3, FromAtom.Position.Y + 3), new Point(ToAtom.Position.X - 3, ToAtom.Position.Y + 3));
}
Taken from Adam Nathan's WPF 4.5 Unleashed says : "
Later drawings are placed on top of earlier drawings,so they preserve proper Z ordering
which refers to GeometryDrawing but I think this holds for drawing lines too.
In your case the result fails because you are offsetting the lines by 3 pixels vertically or horizontally with no consideration of the actual orientation of the line. The offset vector needs to be rotated with the line in order to achieve this effect. The problem then is that the lines will not meet together and have gaps or overlaps. To solve that problem you will need some math.
Drawing a parallel lines (or polygon) is not a trivial problem. See this answer for how to do it, but it your case you might be able to use compound lines.
Parallel Line Example

Calculate top-left and bottom right of a Rectangle GIven Origin, Direction & Size

I did a search on this, but didn't find a question that quite matched what I was after. I want the user to be able to define a textured plane. The parameters I have are:
Size (A Vector2)
Direction (A Vector3)
Origin (A Vector3)
So, I want to be able to calculate the 4 vertices of the rectangle given the above information. So, if I wanted a plane facing up, with a width and height of 1000:
Size = (1000, 1000)
Direction = 0, 1, 0 (Up)
Origin = 0, 0, 0
So this would define a plane on the X & Z axis, facing upwards. What I do not understand is how to calculate the 4 corners in 3D space given this information. Do I need extra information, or is there a better way to arbitrarily specify a plane?
Edit : Current Code
In the following code:
Size = 10000, 10000
Center = 0, 0, 0
Normal = 0, 1, 0
Vector3 arb = new Vector3(1, 1, 1);
Vector3 planeY = Vector3.Normalize(Vector3.Cross(Normal, arb));
Vector3 planeX = Vector3.Normalize(Vector3.Cross(Normal, planeY));
planeX *= Size.X / 2;
planeY *= Size.Y / 2;
Vector3[] ret = new Vector3[4]
{
(Center - planeX - planeY),
(Center - planeX + planeY),
(Center + planeX - planeY),
(Center + planeX + planeY)
};
Your plane isn't fully defined yet. You need another vector going along the plane, the so called 'tangent' vector. In your above example, where should the Y-axis of the texture be pointing? Along the X-axis, along the Z-axis? Or maybe a completely different user defined axis? Your tangent vector is a vector that should point in the general direction where the X-axis of the plane should go.
Let's say we have a tangent vector as well, it doesn't neccesarilly need to point along the plane. You can construct the plane as follows:
Vector3[] vertices(Vector2 size, Vector3 center, Vector3 normal, Vector3 tangent)
{
Vector3 planeY = Vector3.Normalize(Vector3.Cross(normal, tangent));
Vector3 planeX = Vector3.Normalize(Vector3.Cross(normal, planeY));
planeX *= size.X / 2;
planeY *= size.Y / 2;
vertices = new Vector3[]
{
(center - planeX - planeY),
(center - planeX + planeY),
(center + planeX - planeY),
(center + planeX + planeY),
};
return vertices;
}
planeX and planeY are normalized vectors which point along X and Y axes of the plane itself. By multiplying these by size / 2, we get 2 vectors that span from the center to the edge of the plane in both X and Y directions. By adding these two together in different ways, we get the four corners.
Here's a diagram so you can get a better picture in your head. The tangent vector T is "flattened" onto the X-axis.
This is fine as a definition of a plane: you have a point and the normal vector. You need to get two vectors (A & B) on the plane and add one (A * one of the size values) to the origin to get the second corner. Add the second vector (B * the other size value) to get the third corner and add both vectors * their corresponding size values to the origin to get the forth corner.
To get the first vector, calculate the cross product of the normal vector (Direction) with an arbitrary vector (not equal to the direction). This will give you vector A. To get vector B calculate the cross product of A and the Direction.

Updating four points to stay in a Rectangle shape when one is moved

I am trying to draw a rectangular object that allows the user to click on a corner-point to resize and also rotate the rectangle in a 2D space.
Therefore I am using an array of four points ordered A, B, C, D (or 0, 1, 2, 3) from top-left to bottom-left in clockwise order.
The rotation works fine, I calculate the center point and rotate each point around it by an angle.
The resizing is done by determining which point was pressed down, and then setting its new position to the position of the mouse on each MouseMove event. The two adjacent points then need to be updated to stay in a rectangular shape. The resizing is intermittently not working. I have tried many ways to error-check, but all leave me with the same problem where if I move the mouse back and forth over the opposing point while moving a point, the points get distorted and are no longer a rectangular shape.
SOURCE CODE HERE
https://www.assembla.com/code/moozhe-testing/subversion/nodes/rotateRectangle
EXERPT OF PROBLEM CODE
private void MovePoint(int id, PointF newPoint)
{
PointF oldPoint = points[id];
PointF delta = newPoint.Substract(oldPoint);
PointF pointPrevious = points[(id + 3) % 4];
PointF pointNext = points[(id + 1) % 4];
PointF sidePrevious = pointPrevious.Substract(oldPoint);
PointF sideNext = pointNext.Substract(oldPoint);
PointF previousProjection = Projection(delta, sidePrevious);
PointF nextProjection = Projection(delta, sideNext);
pointNext = pointNext.AddPoints(previousProjection);
pointPrevious = pointPrevious.AddPoints(nextProjection);
points[(id + 3) % 4] = pointPrevious;
points[(id + 1) % 4] = pointNext;
points[id] = newPoint;
}
private PointF Projection(PointF vectorA, PointF vectorB)
{
PointF vectorBUnit = new PointF(vectorB.X, vectorB.Y);
vectorBUnit = vectorBUnit.Normalize();
float dotProduct = vectorA.X * vectorBUnit.X + vectorA.Y * vectorBUnit.Y;
return vectorBUnit.MultiplyByDecimal(dotProduct);
}
It sounds like you might want to be using a transformation matrix, instead of updating X/Y coordinates manually. Please check out this link:
Comparing GDI mapping modes with GDI+ transforms
Here's the MSDN reference:
https://learn.microsoft.com/en-us/dotnet/api/system.drawing.graphics.transform

Categories