Draw arc with angles ranging from -360 to 360 degrees - c#

I am having some trouble trying to implement drawing an arc. Basically it's supposed to be drawn exactly like how you can draw a 3 point arc in AutoCAD. I can get the arc to draw with start and end angles from from -180 degrees to 360, but if I try to go more than -180, the arc flips around to +180 degrees. My problem is that I need the arc to continue when the angle passes -180. I have everything right in the code (I think) except for calculating the start and end angles. Thanks in advance for any help. Here is my code:
private void DrawArc()
{
//fyi linept is of type List<Point3D>();
Point3D currentPoint = (Point3D)GetPoints(e);
double rad;
Point3D center = GetCenterOfCircle(linept.ElementAt(0), linept.ElementAt(1), currentPoint, out rad);
PieSliceVisual3D circle = new PieSliceVisual3D();
circle.Center = center;
circle.OuterRadius = rad;
circle.InnerRadius = circle.OuterRadius + 3;
circle.StartAngle = (Math.Atan2(linept.ElementAt(0).Y - center.Y, linept.ElementAt(0).X - center.X) * 180 / Math.PI);
circle.EndAngle = (Math.Atan2(currentPoint.Y - center.Y, currentPoint.X - center.X) * 180 / Math.PI);
//I've also tried these next 4 lines to no avail
double startAngle = (Math.Atan2(linept.ElementAt(0).Y - center.Y, linept.ElementAt(0).X - center.X) * 180 / Math.PI);
circle.StartAngle = (startAngle > 0.0 ? startAngle : (360.0 + startAngle));
double endAngle = (Math.Atan2(currentPoint.Y - center.Y, currentPoint.X - center.X) * 180 / Math.PI);
circle.EndAngle = (endAngle > 0.0 ? endAngle : (360.0 + endAngle));
}

Related

Calculate a simple angle

I'm calculating the angle using 2 points of a square.
The angle works well but it gives me values from -180 to 180 and it's hard for me to code the direction of my robot. I wanted the angle only with position values ex: 0 - 360;
var deltay = pontos_quadrado[0].Y - pontos_quadrado[1].Y;
var deltax = pontos_quadrado[1].X - pontos_quadrado[0].X;
angulo = Math.Atan2(deltay, deltax) * 180 / Math.PI;
angulo = Math.Round(angulo, 0);
You can force the angle to be in the range of 0 to 360 by adding 360 and taking the remainder modulo 360.
var deltay = pontos_quadrado[0].Y - pontos_quadrado[1].Y;
var deltax = pontos_quadrado[1].X - pontos_quadrado[0].X;
angulo = Math.Atan2(deltay, deltax) * 180 / Math.PI;
angulo = (angulo + 360) % 360; // note this line
angulo = Math.Round(angulo, 0);

Calculate new point offset based on angle of rotation?

I am working on an application for the past few weeks which involves some trigonometry and am currently stuck. As shown in the diagram below, I have a circular item (green circle at position #1) which I know the center point (let's call that X1,Y1). The circle has another point (orange circle) that is off-centered a bit - midway between two other marks (blue circles). These marks can move around. The coordinates of the orange point are calculated (let's call it X2, Y2) and the angle of the blue line is calculated (call it Angle) in relation to the horizontal of the circle.
I can calculate the difference between the center of the circle and the point by:
deltaX = X2-X1
deltaY = Y2-Y1
I need to move and rotate the green circle (either CW or CCW - whichever is shorter) from it's start location (position 1) over to position 2. This means the angle could be negative or positive. The blue line must end up vertical and the orange dot at the center of position 2 (red square). I know the coordinates for the center of position 2 (let's call this point X3,Y3). Position #1 and position #2 are exactly 90 degrees from each other.
I thought I could use some trig identity formulas that calculate the rotation of a point, as such:
offsetX = deltaX * cos(90-Angle) - deltaY * sin(90-Angle)
offsetY = deltaX * sin(90-Angle) + deltaY * cos(90-Angle)
I was hoping these offsets would be what I need to adjust the circle to it's new center when it moves/rotates over to position 2.
X3 = X3 + offsetX
Y3 = Y3 + offsetY
However, when I try use this math, it's not placing the orange mark of the circle in the center of the square. Not sure if my equations and calculations are correct based on the angle of rotation (positive or negative, CW or CCW) or if I'm using the angle correctly (where I subtract the known angle from 90 degrees). How do I correctly calculate the final point/position? Any help and examples would be greatly appreciated!
Thank you very much for your time!
So you need to rotate your circle by 90 - Angle and then move orange point to (X3, Y3)?
First you need to find orange point coordinate after rotation:
newX = X2 * cos(90 - Angle) - Y2 * sin(90 - Angle);
newY = X2 * sin(90 - Angle) + Y2 * cos(90 - Angle);
newX and newY are orange point coordinates after rotation. To find move transformation simply substract:
moveX = X3 - newX;
moveY = Y3 - newY;
Now if you rotate circle by 90 - Angle and move it by (moveX, moveY) orange point will move to (X3, Y3). That is if you rotate circle around (0, 0) point. If you rotating around some (X, Y) point, you first need to substract X from X2, Y from Y2 and then add X to newX, Y to newY. That substraction 'moves' your rotation base point to (0, 0), so after rotation you need to move it back:
newX = (X2 - X) * cos(90 - Angle) - (Y2 - Y) * sin(90 - Angle) + X;
newY = (X2 - X) * sin(90 - Angle) + (Y2 - Y) * cos(90 - Angle) + Y;
Be aware that your code is using a counter clockwise rotation, (conventionally angles are measured counter clockwise) which may be why you're not getting the results you expect. if you want a clockwise rotation try:
offsetX = deltaX * cos(angle) + deltaY * sin(angle)
offsetY = -deltaX * sin(angle) + deltaY * cos(angle)
Ensure your angles are in radians not degrees.
Drawing some lines may help you debug things too.

How to find the point on ellipse given the angle

I have a ellipse with center point is at origin(0,0)
double dHalfwidthEllipse = 10;
double dHalfheightEllipse = 20;
double dAngle = 30;//Its in degree
PointF ptfPoint = new PointF();//To be found
PointF ptfOrigin = new PointF(0, 0);//Origin
Angle of point with respect to origin = 30 degree;
How to get the point now given the above values using C#?
See http://www.mathopenref.com/coordparamellipse.html
The parametric equation for an ellipse with center point at the origin, half width a and half height b is
x(t) = a cos t,
y(t) = b sin t
If you simply wish to draw an ellipse, given
double dHalfwidthEllipse = 10; // a
double dHalfheightEllipse = 20; // b
PointF ptfOrigin = new PointF(0, 0); // Origin
all you need is
PointF ptfPoint =
new PointF(ptfOrigin.X + dHalfwidthEllipse * Math.Cos(t * Math.Pi/180.0),
ptfOrigin.Y + dHalfheightEllipse * Math.Sin(t * Math.Pi/180.0) );
with t varying between -180 and 180 degrees.
However, as #Sebastian points out, if you wish to compute the exact intersection with a line through the center with angle theta, it gets a bit more complicated, since we need to find a t that corresponds to theta:
y(t)/x(t) = tan θ
b sin t / (a cos t) = tan θ
b/a tan t = tan θ
t= arctan(a tan θ / b) + n * π
So if we add
double dAngle = 30; // theta, between -90 and 90 degrees
We can compute t and ptfPoint:
double t = Math.Atan( dHalfwidthEllipse * Math.Tan( dAngle * Math.Pi/180.0 )
/ dHalfheightEllipse);
PointF ptfPoint =
new PointF(ptfOrigin.X + dHalfwidthEllipse * Math.Cos(t),
ptfOrigin.Y + dHalfheightEllipse * Math.Sin(t) );
This works fine for the area around the positive x axis.
For theta between 90 and 180 degrees, add π:
double t = Math.Atan( dHalfwidthEllipse * Math.Tan( dAngle * Math.Pi/180.0 )
/ dHalfheightEllipse) + Math.Pi;
For theta between -180 and -90 degrees, subtract π:
double t = Math.Atan( dHalfwidthEllipse * Math.Tan( dAngle * Math.Pi/180.0 )
/ dHalfheightEllipse) - Math.Pi;
As you get close to the y axis, x(t) approaches zero and the above calculation divides by zero, but there you can use the opposite:
x(t)/y(t) = tan (90 - θ)
a cos t / (b sin t) = tan (90 - θ)
a/b tan t = tan (90 - θ)
t = arctan ( b tan (90 - θ) / a ) + n * π

Inconsistency with Math.Round()

I have two functions that are intended to contain angles between (-180,180] and (-π,π]. The intent is that given any angle from -inf to +inf it will retain the equivalent angle in the intervals specified. For example the angle for 1550° is 110°.
public double WrapBetween180(double angle)
{
return angle - 360d * Math.Round(angle / 360d, MidpointRounding.AwayFromZero);
}
public double WrapBetweenPI(double angle)
{
const double twopi = 2d * Math.PI;
return angle - twopi * Math.Round(angle / twopi, MidpointRounding.AwayFromZero);
}
which yields the following results
WrapBetween180(-180) = -180
WrapBetween180( 180) = 180
WrapBetweenPI(-Math.PI) = Math.PI
WrapBetweenPI( Math.PI) = -Math.PI
none of which is what I want. What I wanted is:
WrapBetween180(-180) = 180
WrapBetween180( 180) = 180
WrapBetweenPI(-Math.PI) = Math.PI
WrapBetweenPI( Math.PI) = Math.PI
I tryied playing around with the rounding methods, but still cannot get the desired results. The problem is pronounced because sometimes the angles I deal with are only approximately close to -π or π and I am getting discontinuities it my results.
Any suggestions on how to best implement angle wrapping functions with non-inclusive low limit and inclusive high limits?
For the angle in degrees, if x is between -180 and 180, then 180 - x is between 0 and 360. What you want is equivalent to asking that 180 - x is between 0 (inclusive), and 360 (exclusive). So, as soon as 180 - x reaches 360, we want to add 360 to the angle. This gives us:
return angle + 360d * Math.Floor((180d - angle) / 360d);
Same thing for the angle in radians:
return angle + twopi * Math.Floor((Math.PI - angle) / twopi);
It does not address the rounding issue, but here is how I would to what you want to do :
private static double ConvertAngle(double angle)
{
bool isNegative = angle < 0;
if (isNegative)
angle *= -1;
angle = angle % 360;
if (isNegative)
angle = -1 * angle + 360;
if (angle > 180)
angle = (angle - 360);
return angle;
}
Note: This way supposes you want "behind" to be 180 degrees, not -180 degrees.
Isn't this a case for a modulo operation?
private double Wrap180(double value)
{
// exact rounding of corner values
if (value == 180) return 180.0;
if (value == -180) return 180.0;
// "shift" by 180 and use module, then shift back.
double wrapped = ((Math.Abs(value) + 180.0) % 360.0) - 180.0;
// handle negative values correctly
if (value < 0) return -wrapped;
return wrapped;
}
It passes this tests
Assert.AreEqual(170.0, wrap(-190.0));
Assert.AreEqual(180.0, wrap(-180.0));
Assert.AreEqual(-170.0, wrap(-170.0));
Assert.AreEqual(0.0, wrap(0.0));
Assert.AreEqual(10.0, wrap(10.0));
Assert.AreEqual(170.0, wrap(170.0));
Assert.AreEqual(180.0, wrap(180.0));
Assert.AreEqual(-170.0, wrap(190.0));

C# Drawing Arc with 3 Points

I need to draw an arc using GraphicsPath and having initial, median and final points. The arc has to pass on them.
I tried .DrawCurve and .DrawBezier but the result isn't exactly an arc.
What can I do?
SOLUTION:
After a couple of hours of code writing I managed to draw what I wanted with this algorithm (give 3 Point a,b,c and a GraphicsPath path):
double d = 2 * (a.X - c.X) * (c.Y - b.Y) + 2 * (b.X - c.X) * (a.Y - c.Y);
double m1 = (Math.Pow(a.X, 2) - Math.Pow(c.X, 2) + Math.Pow(a.Y, 2) - Math.Pow(c.Y, 2));
double m2 = (Math.Pow(c.X, 2) - Math.Pow(b.X, 2) + Math.Pow(c.Y, 2) - Math.Pow(b.Y, 2));
double nx = m1 * (c.Y - b.Y) + m2 * (c.Y - a.Y);
double ny = m1 * (b.X - c.X) + m2 * (a.X - c.X);
double cx = nx / d;
double cy = ny / d;
double dx = cx - a.X;
double dy = cy - a.Y;
double distance = Math.Sqrt(dx * dx + dy * dy);
Vector va = new Vector(a.X - cx, a.Y - cy);
Vector vb = new Vector(b.X - cx, b.Y - cy);
Vector vc = new Vector(c.X - cx, c.Y - cy);
Vector xaxis = new Vector(1, 0);
float startAngle = (float)Vector.AngleBetween(xaxis, va);
float sweepAngle = (float)(Vector.AngleBetween(va, vb) + Vector.AngleBetween(vb, vc));
path.AddArc(
(float)(cx - distance), (float)(cy - distance),
(float)(distance * 2), (float)(distance * 2),
startAngle, sweepAngle);
I would use DrawArc() as suggested by ANC_Michael. To find an arc that passes through 3 points you want to calculate the circumcircle of the triangle formed by the points.
Once you have the circumcircle calculate a bounding box for the circle to use with DrawArc using the min/max dimensions (center +/- radius). Now calculate your start and stop angles by translating the points so that the circumcircle is centered on the origin (translate by -circumcenter) and take the dot-product of the normalized start and end vectors with the X-axis:
double startAngle = Math.Acos(VectorToLeftPoint.Dot(XAxis));
double stopAngle = Math.Acos(VectorToRightPoint.Dot(XAxis));
Note that DrawArc expects angles clockwise from the X-axis so you should add Math.PI if the calculated vector is above the x-axis. That should be enough information to call DrawArc().
Edit: This method will find a circular arc and not necessarily the 'best fit' arc depending on your expected endpoint behavior.
Have you tried the DrawArc method and seeing if u can manipulate your 3 points somehow?
maybe
Pen blackPen= new Pen(Color.Black, 3);
// Create rectangle to bound ellipse.
Rectangle rect = new Rectangle(initial x, initial y, final x, median y);
// Create start and sweep angles on ellipse.
float startAngle = 0F;
float sweepAngle = 270.0F;
// Draw arc to screen.
e.Graphics.DrawArc(blackPen, rect, startAngle, sweepAngle);
http://msdn.microsoft.com/en-us/library/system.drawing.graphics.drawarc%28VS.71%29.aspx

Categories

Resources