Draw arc between two lines. I need to calculate points - c#

I can't find a way to drawing ARC between two lines. My constraint is : I have to calculate this Arc stroke points. Because i am using InkCanvas and i have to draw this arc point by point, i can't put any object to screen or canvas. So I know i can draw any arc with PATH object and use ArcSegment. With this method yes i can draw arc but it isn't stroke point on the Canvas. For this reason i cannot delete or save it.
Anyway i need calculate this arch point by point.
I have code for drawing circle on canvas like this :
Stroke GetCircleStroke(int centerX, int centerY, int radiusX, int radiusY,double angletoDraw=2.0)
{
StylusPointCollection strokePoints = new StylusPointCollection();
int numTotalSteps = 180;
for (int i = 0; i <= numTotalSteps; i++)
{
double angle = angletoDraw * Math.PI * (double)i / (double)numTotalSteps;
StylusPoint sp = new StylusPoint();
//compute x and y points
sp.X = centerX + Math.Cos(angle) * radiusX;
sp.Y = centerY - Math.Sin(angle) * radiusY;
//add to the collection
strokePoints.Add(sp);
}
Stroke newStroke = new Stroke(strokePoints);
return newStroke;
}
I can draw circle easly, but i couldn't find a way to draw an arc :(
We know center point X,Y and we know Line1 and Line2 coordinates. I just don't know what is that arc..
Could you please help me for calculate arc points like this way ?

You have a few concepts flying around like Line/Segment, Point, Circle, etc. Instead of making a mess of hard to understand code, let's try to breakdown the problem into smaller parts that are easier to digest.
You have a notion of Point, ok, lets implement one:
public struct Point2D //omitted equality logic
{
public double X { get; }
public double Y { get; }
public Point2D(double x, double y)
{
X = x;
Y = y;
}
public override string ToString() => $"{X:N3}; {Y:N3}";
}
Ok, we also have a notion of Segment or a delimitted Line:
public struct Segment2D
{
public Point2D Start { get; }
public Point2D End { get; }
public double Argument => Math.Atan2(End.Y - Start.Y , End.X - Start.X);
public Segment2D(Point2D start, Point2D end)
{
Start = start;
End = end;
}
}
And last, but not least, we have the notion of Circle:
public struct Circle2D
{
private const double FullCircleAngle = 2 * Math.PI;
public Point2D Center { get; }
public double Radius { get; }
public Circle2D(Point2D center, double radius)
{
if (radius <= 0)
throw new ArgumentOutOfRangeException(nameof(radius));
Center = center;
Radius = radius;
}
public IEnumerable<Point2D> GetPointsOfArch(int numberOfPoints, double startAngle, double endAngle)
{
double normalizedEndAngle;
if (startAngle < endAngle)
{
normalizedEndAngle = endAngle;
}
else
{
normalizedEndAngle = endAngle + FullCircleAngle;
}
var angleRange = normalizedEndAngle - startAngle;
angleRange = angleRange > FullCircleAngle ? FullCircleAngle : angleRange;
var step = angleRange / numberOfPoints;
var currentAngle = startAngle;
while (currentAngle <= normalizedEndAngle)
{
var x = Center.X + Radius * Math.Cos(currentAngle);
var y = Center.Y + Radius * Math.Sin(currentAngle);
yield return new Point2D(x, y);
currentAngle += step;
}
}
public IEnumerable<Point2D> GetPoints(int numberOfPoints)
=> GetPointsOfArch(numberOfPoints, 0, FullCircleAngle);
}
Study the implementation of GetPointsOfArch, it shouldn't be too hard to understand.
And now, to solve your problem, you would do:
var myCircle = new Circle2D(new Point2D(centerX, centerY), radius);
var line1 = ....
var line2 = ....
var archPoints = myCircle.GetPointsOfArch(number, line2.Argument, line1.Argument);
Isn't that much easier to read, follow and understand?

Related

How to Reset Time of an analog clock by dragging it's handles C#

I'm very new to C#, the aim here is to edit the Time of an analog Clock by dragging it's handles. https://code.msdn.microsoft.com/windowsapps/Analog-Clock-Control-0e8ffcab#content this code has inpired me. I have three simple functions MouseDown, MouseMove and MouseUp but still I can not get Drag to work. Any suggestions please ?
public partial class Form1 : Form
{
#region Construct the clock
public Point Start { get; set; }
public Point End { get; set; }
public Form1()
{
InitializeComponent();
DoubleBuffered = true;
//Create the timer and start it
ClockTimer.Tick += ClockTimer_Tick;
ClockTimer.Enabled = true;
ClockTimer.Interval = 1;
ClockTimer.Start();
Start = p1;
End = p2;
}
#endregion
#region Update the clock
private void ClockTimer_Tick(object sender, EventArgs e)
{
Refresh();
}
private Timer ClockTimer = new Timer();
private Pen circle = new Pen(Color.Black, 2);
private Pen secondHandle = new Pen(Color.Red, 1);
private Pen minHandle = new Pen(Color.Black, 5);
private Pen hrHandle = new Pen(Color.Black, 5);
private Point p1;
private Point p2;
#endregion
#region On paint
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
//Clear the graphics to the back color of the control
pe.Graphics.Clear(BackColor);
//Draw the border of the clock
pe.Graphics.DrawEllipse(circle, 0, 0, 300, 300);
//Find the radius of the control by dividing the width by 2
float radius = (300 / 2);
//Find the origin of the circle by dividing the width and height of the control
PointF origin = new PointF(300 / 2, 300 / 2);
//Draw only if ShowMajorSegments is true;
if (ShowMajorSegments)
{
//Draw the Major segments for the clock
for (float i = 0f; i != 390f; i += 30f)
{
pe.Graphics.DrawLine(Pens.White, PointOnCircle(radius - 1, i, origin), PointOnCircle(radius - 21, i, origin));
}
}
//Draw only if ShowMinorSegments is true
if (ShowMinorSegments)
{
//Draw the minor segments for the control
for (float i = 0f; i != 366f; i += 6f)
{
pe.Graphics.DrawLine(Pens.Black, PointOnCircle(radius, i, origin), PointOnCircle(radius - 10, i, origin));
}
}
//Draw only if ShowSecondHand is true
if (ShowSecondhand)
//Draw the second hand
pe.Graphics.DrawLine(secondHandle, origin, PointOnCircle(radius, DateTime.Now.Second * 6f, origin));
//Draw only if ShowMinuteHand is true
if (ShowMinuteHand)
//Draw the minute hand
pe.Graphics.DrawLine(minHandle, origin, PointOnCircle(radius * 0.75f, DateTime.Now.Minute * 6f, origin));
minHandle.StartCap = LineCap.RoundAnchor;
minHandle.EndCap = LineCap.ArrowAnchor;
pe.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
pe.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//Draw only if ShowHourHand is true
if (ShowHourHand)
//Draw the hour hand
pe.Graphics.DrawLine(hrHandle, origin, PointOnCircle(radius * 0.50f, DateTime.Now.Hour * 30f, origin));
hrHandle.StartCap = LineCap.RoundAnchor;
hrHandle.EndCap = LineCap.ArrowAnchor;
pe.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
pe.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
}
#endregion
#region On size changed
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
//Make sure the control is square
if (Size.Height != Size.Width)
Size = new Size(Size.Width, Size.Width);
//Redraw the control
Refresh();
}
#endregion
#region Point on circle
private PointF PointOnCircle(float radius, float angleInDegrees, PointF origin)
{
//Find the x and y using the parametric equation for a circle
float x = (float)(radius * Math.Cos((angleInDegrees - 90f) * Math.PI / 180F)) + origin.X;
float y = (float)(radius * Math.Sin((angleInDegrees - 90f) * Math.PI / 180F)) + origin.Y;
return new PointF(x, y);
}
#endregion
#region Show Minor Segments
private bool showMinorSegments = true;
public bool ShowMinorSegments
{
get
{
return showMinorSegments;
}
set
{
showMinorSegments = value;
Refresh();
}
}
#endregion
#region Show Major Segments
private bool showMajorSegments = true;
public bool ShowMajorSegments
{
get
{
return showMajorSegments;
}
set
{
showMajorSegments = value;
Refresh();
}
}
#endregion
#region Show Second Hand
private bool showSecondHand = false;
public bool ShowSecondhand
{
get
{
return showSecondHand;
}
set
{
showSecondHand = value;
Refresh();
}
}
#endregion
#region Show Minute Hand
private bool showMinuteHand = true;
public bool ShowMinuteHand
{
get
{
return showMinuteHand;
}
set
{
showMinuteHand = value;
Refresh();
}
}
#endregion
#region Show Hour Hand
private bool showHourHand = true;
public bool ShowHourHand
{
get
{
return showHourHand;
}
set
{
showHourHand = value;
Refresh();
}
}
#endregion
public float slope
{
get
{
return (((float)p2.Y - (float)p1.Y) / ((float)p2.X - (float)p1.X));
}
}
public float YIntercept
{
get
{
return p1.Y - slope * p1.X;
}
}
public bool IsPointOnLine(Point p, int cushion)
{
float temp = (slope * p.X + YIntercept);
if (temp >= (p.Y - cushion) && temp <= (p.Y + cushion))
{
return true;
}
else
{
return false;
}
}
Point deltaStart;
Point deltaEnd;
bool dragging = false;
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left && IsPointOnLine(e.Location, 5))
{
dragging = true;
deltaStart = new Point(p1.X - e.Location.X, p1.Y - e.Location.Y);
deltaEnd = new Point(p2.X - e.Location.X, p2.Y - e.Location.Y);
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (dragging && deltaStart != null && deltaEnd != null)
{
p1 = new Point(deltaStart.X + e.Location.X, deltaStart.Y + e.Location.Y);
p2 = new Point(deltaEnd.X + e.Location.X, deltaEnd.Y + e.Location.Y);
this.Refresh();
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
dragging = false;
}
}
I give a partial answer about translating a X, Y coordinate to an angle (in degree) based on a circle, where the 0° angle is located at the top.
(Scroll down for a compact solution)
Following the directions of typical GUI coordinates, the absolute 0,0 Point is located top left, positive X values stretch to the right and positive Y values stretch to the bottom.
In order to simplify the math, I use a virtual 0,0 point at the center of the circle, so all coordinates need to be translated to locals before calculation and to globals before actual drawing.
Coordinate overview (imagine the circle around 0; 0):
(0;-1)
(-1; 0) (0; 0) (1; 0)
(0; 1)
Now the task is for any coordinate (X; Y) to find the clock-wise angle between the line (0; 0) - (0; -1) and the line (0; 0) - (X; Y)
The circle can be divided into 4 quarter-circles, each covering a combination of signed (X; Y) values.
Quarter 1 contains the angle values 0° to 90° and is represented by positive X values and negative Y values.
Quarter 2 contains the angle values 90° to 180° and is represented by positive X values and positive Y values.
Quarter 3 contains the angle values 180° to 270° and is represented by negative X values and positive Y values.
Quarter 4 contains the angle values 270° to 360° and is represented by negative X values and negative Y values.
Note that for the corner cases 0°, 90°, 180°, 270°, 360° it doesn't really matter which of the two quarters they are assigned to.
The easiest way to understand such problems is to stick to the normal circle -> read: to normalize the X; Y coordinate to a length of 1. Additionally I go with positive values (it would also work without, but a bit differently in the + and - combinations):
var len = Math.Sqrt(X * X + Y * Y);
var xNorm = Math.Abs(X) / len;
var yNorm = Math.Abs(Y) / len;
Now, the reverse sine / cosine can be used to translate the normalized coordinates back into angle values (there's some redundancy in my calculation for the sake of simplicity and completeness):
var angleFromX = Math.Asin(xNorm) * 180.0 / Math.PI;
var angleFromY = Math.Asin(yNorm) * 180.0 / Math.PI;
Now lets apply the appropriate angle for each of the quarter circle areas
var resultAngle = 0.0;
if (quarter_1)
{
resultAngle = 0 + angleFromX;
// same as
resultAngle = 90 - angleFromY;
}
if (quarter_2)
{
resultAngle = 90 + angleFromY;
// same as
resultAngle = 180 - angleFromX;
}
if (quarter_3)
{
resultAngle = 180 + angleFromX;
// same as
resultAngle = 270 - angleFromY;
}
if (quarter_4)
{
resultAngle = 270 + angleFromY;
// same as
resultAngle = 360 - angleFromX;
}
Ofcourse, the quarter_1 - quarter_4 are pseudo-variables that represent the quarter selection as explained.
A more compact solution can be found by analyzing the different properties of the full solution.
var angleFromYAxis = Math.Asin(Y / Math.Sqrt(X * X + Y * Y)) * 180.0 / Math.PI;
var resultAngle = 0.0;
if (X >= 0)
{
resultAngle = 90 + angleFromYAxis;
}
else
{
resultAngle = 270 - angleFromYAxis;
}

Math/Trig to get XYZ coordinates of dipping ellipse in C#

I need to get the coordinates of points along an ellipse that lies on a dipping plane. The ellipse is also rotated in the XY plane. I am able to get correct X and Y coordinates of points along a rotated horizontal ellipse, and the X and Y points of the horizontal projection of a dipping ellipse, but cannot figure out the maths/trig that I need to get the Z value of the points on the dipping ellipse. I am trying to do this by using the apparent dip of a vector from the center of the ellipse to each point along the dipping ellipse.
Here's the code I'm using:
class ellipsoid
{
public ellipsoid() { }
public double bearing { get; set; }
public double plunge { get; set; }
public double dip { get; set; }
public double x { get; set; }
public double y { get; set; }
public double z { get; set; }
}
class exampleCalc
{
public void createDippingEllipse(ellipsoid ellipsoid, double majorAxis, double semiMajorAxis)
// majorAxis is the longest axis, semiMajorAxis is the shorter axis
{
double semiMajorAxisApparent = 0;
Int32 strNum = 1;
point delta = new point();
point horz = new point();
point ep = new point();
point p = new point();
p.x = ellipsoid.x;
p.y = ellipsoid.y;
p.z = ellipsoid.z;
// az represents the angular interval along which points will be located
for (Int32 az = 0; az <= 360; az = az + 10)
{
double rakeAngle = 0;
if (az >= 0 && az <= 90)
{
rakeAngle = az;
}
if (az > 90 && az <= 180)
{
rakeAngle = 180 - az;
}
if (az > 180 && az <= 270)
{
rakeAngle = az - 180;
}
if (az > 270 && az <= 360)
{
rakeAngle = 360 - az;
}
if (ellipsoid.dip == 90)
{
semiMajorAxisApparent = semiMajorAxis;
}
else
{
semiMajorAxisApparent = semiMajorAxis * Math.Cos(ep.degreesToRadians(Math.Abs(ellipsoid.dip)));
}
double cosAz = Math.Cos(ep.degreesToRadians(az));
double sinAz = Math.Sin(ep.degreesToRadians(az));
// convert mathematical bearing to bearing where north is zero
double bearing0north = ellipsoid.bearing + 90;
if (bearing0north > 360) { bearing0north = 360 - bearing0north; }
double cosBearing = Math.Cos(ep.degreesToRadians(bearing0north));
double sinBearing = Math.Sin(ep.degreesToRadians(bearing0north));
// delta.x and delta.y are correct
delta.x = (majorAxis * cosBearing * cosAz) - (semiMajorAxisApparent * sinBearing * sinAz);
delta.y = (majorAxis * cosAz * sinBearing) + (semiMajorAxisApparent * sinAz * cosBearing);
double horzDist = Math.Sqrt(Math.Pow(delta.x, 2) + Math.Pow(delta.y, 2));
// uncorrected for apparent horz length
horz.x = (majorAxis * cosBearing * cosAz) - (semiMajorAxis * sinBearing * sinAz);
horz.y = (majorAxis * cosAz * sinBearing) + (semiMajorAxis * sinAz * cosBearing);
double apparentDip = Math.Atan(Math.Tan(ep.degreesToRadians(Math.Abs(ellipsoid.bearing))) * Math.Sin(ep.degreesToRadians(rakeAngle)));
// delta.z is not correct
delta.z = horzDist * Math.Atan(apparentDip);
ep.x = p.x + delta.x;
ep.y = p.y + delta.y;
ep.z = p.z + delta.z;
}
}
}
You are truly attempting the most complex possible solution to a relatively straightforward problem (except perhaps choosing a non-inertial frame).
Simply solve all the maths in an ellipse lying in the X-Y plane, about the origin, and calculate the affine matrix that translates and rotates the ellipse to the desired location and orientation. Then simply apply the matrix to each calculated point to get the correct oriented point.

How can I rotate a 3D array in c#?

I have a binary object in a 3d array located in the origin (0,0,0). I need rotate this object dinamically in z axis. How can I rotate that multidimensional array with no fixed size in any angle?
I have created a 3d Point Class:
public class Point3
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
}
I'm thinking in do a foreach in each point and rotate it:
foreach (Point3 p in listPoint3)
{
RotatePoint(p, angle);
}
Any suggestion?
You colud create a routine to rotate each point using the parametric equation for rotate the 3d object.
x' = x*cos(o)-y*sin(o)
y' = y*sin(o)-y*cos(o)
z' = z
private Point3 RotatePoint(Point3 p0, int angle)
{
Point3 p = new Point3()
{
X = p0.X * Math.Cos(angle) - p0.Y * Math.Sin(angle),
Y = p0.X * Math.Sin(angle) + p0.Y * Math.Cos(angle),
Z = p0.Z,
};
return p;
}
You need to know what axis you want to rotate on. But if this is only a question where to look at. (namespace System.Windows.Media.Media3D)
You can try this:
double angle = 45;
RotateTransform3D zrotation = new RotateTransform3D(new AxisAngleRotation3D(
new Vector3D(0, 0, 1), angle));
foreach (Point3D p in listPoint3)
{
Point3D rotatedPoint = zrotation.Transform(p);
}
You should use the build-in Point3D
Also if you want to Stack those: (multiple transforms)
double zAngle = 45;
double xAngle = 10;
Transform3DGroup group = new Transform3DGroup();
group.Children.Add( new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(1, 0, 0), xAngle)));
group.Children.Add( new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 0, 1), zAngle)));
foreach (Point3D p in listPoint3)
{
Point3D rotatedPoint = group.Transform(p);
}
So you have a "monochrome object" which is stored like a 3D bitmap which you want to rotate around the Z axis. You must first understand that after a number of rotations you will end up with optical aberrations caused by the fact that you're using an array index which is a natural number to represent a coordinate of an object's component.
Upon rotation, any integer value will most likely become an irrational number. The fact that traditionally (not talking about special programs and frameworks) people store approximations of irrational numbers in double or float or decimal variables (which can only store a small subset of the rational numbers set) is nothing compared to the approximation of an irrational number by storing it in an integer (an array index).
Furthermore, even if that quality loss if of no big importance in the case of your application, you must be sure you understand that mathematically speaking, after a number of rotations, your 3d shape will be trimmed by the cylinder which is inscribed in the original parallelepiped, alongside the Z axis.
It goes like this. You said you already made a class called Point3:
public class Point3 {
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
}
Maybe you should follow #Jeroen van Langen's advice and use a standard class, if such a class already exists. The benefit would be that if someone out there already built or will ever build a library which uses that class you can start using the library right away.
But that's not so important right now.
#Alpert already gave a great C# code for rotating a point around the oZ axis. This is n "extension method" adaption of that code:
public static class RotationHelpers {
public static Point3 RotatePoint(this Point3 point, int angle) {
var result = new Point3() {
X = point.X * Math.Cos(angle) - point.Y * Math.Sin(angle),
Y = point.X * Math.Sin(angle) + point.Y * Math.Cos(angle),
Z = point.Z,
};
return result;
}
...
}
You can go even further and make an extension method which rotates a sequence of points around the oZ axis:
public static class RotationHelpers {
...
public static IEnumerable<Point3> RotatePoints(this IEnumerable<Point3> points, int angle) {
foreach (var point in points)
yield return point.RotatePoint(angle);
}
...
}
Now you said you have a 3d primitive matrix with 1s and 0s in it:
int[,,] matrix;
You need to somehow convert the intrinsically defined points in that matrix into a sequence of Point3 instances, rotate those and then convert the resulting sequence back into an int[,,] matrix.
That could be achieved like so (remember about the loss of quality I was talking about earlier):
public static class RotationHelpers {
...
public static IEnumerable<Point3> ToPoints(this int[,,] matrix) {
int lx = matrix.GetLength(0);
int ly = matrix.GetLength(1);
int lz = matrix.GetLength(2);
for (int x = 0; x < lx; x++)
for (int y = 0; y < ly; y++)
for (int z = 0; z < lz; z++) {
bool is1 = matrix[x, y, z] != 0;
if (is1)
yield return new Point3 {
X = x - lx / 2,
Y = y - ly / 2,
Z = z - lz / 2
};
}
}
...
}
That will take all of the cells in the WIDTH x HEIGHT x DEPTH matrix and for every cell which is not equal to 0 it will yield a new Point3 instance with the coordinates of that particular position.
That sequence can then be rotate by an angle using the earlier described RotatePoints method and then the following method could be used to "render" back the resulting sequence of Point3 instances into the array:
public static class RotationHelpers {
...
public static void AssignPoints(this int[,,] matrix, IEnumerable<Point3> points) {
int lx = matrix.GetLength(0);
int ly = matrix.GetLength(1);
int lz = matrix.GetLength(2);
for (int x = 0; x < lx; x++)
for (int y = 0; y < ly; y++)
for (int z = 0; z < lz; z++)
matrix[x, y, z] = 0;
foreach (var point in points) {
// this is when quality is lost, because things like 1.7 and 1.71
// will both become =2
var x = (int)Math.Round(point.X) + lx / 2;
var y = (int)Math.Round(point.Y) + ly / 2;
var z = (int)Math.Round(point.Z) + lz / 2;
// this is where you loose parts of the object because
// it doesn't fit anymore inside the parallelepiped
if ((x >= 0) && (y >= 0) && (z >= 0) &&
(x < lx) && (y < ly) && (z < lz))
matrix[x, y, z] = 1;
}
}
...
}
To wrap it up, you can use all of these methods like so:
int[,,] matrix = ...
int angle = ...
IEnumerable<Point3> points = matrix.ToPoints();
IEnumerable<Point3> rotatedPoints = points.RotatePoints(angle);
matrix.AssignPoints(rotatedPoints);
// now you have the original matrix, rotated by angle

Maximum clockwise angles from 3 nearest points

Help me, because I'm rly tired of this...
I need to count angles between current point and the closest 3 points (look at an image below) - I need to sort the angles in descending order (to get the point with the largest angle - if it doesn't fit expectations, I have to get another one).
I tried to do something, but it doesn't work...
private static Vertex[] SortByAngle(IEnumerable<Vertex> vs, Vertex current, Vertex previous)
{
if (current.CompareTo(previous) == 0)
{
previous.X = previous.X - 1.0; // this is a trick to handle the first point
}
var vertices = new Dictionary<Vertex, double>();
foreach (var v in vs)
{
double priorAngle = Angle(previous, current);
double nextAngle = Angle(current, v);
double angleInBetween = 180.0 - (priorAngle + nextAngle);
vertices.Add((Vertex) v.Clone(), angleInBetween);
}
// here the angles are incorrect, because I want to sort them in desc order, but it's a real mess when I do OrderByDescending - something is wrong with my code:S
vertices = vertices.OrderBy(v => v.Value).ToDictionary(k => k.Key, v => v.Value);
return vertices.Select(v => new Vertex(v.Key.X, v.Key.Y)).ToArray();
}
private static double Angle(Vertex v1, Vertex v2, double offsetInDegrees = 0.0)
{
return (RadianToDegree(Math.Atan2(-v2.Y + v1.Y, -v2.X + v1.X)) + offsetInDegrees);
}
public static double RadianToDegree(double radian)
{
var degree = radian * (180.0 / Math.PI);
if (degree < 0)
degree = 360 + degree;
return degree;
}
vs is my set of 3 nearest points
current and previous are obvious:)
i didn't tested it, but i restyled a little, avoiding dictionaries. I think your mistake is in: double angleInBetween = 180.0 - (priorAngle + nextAngle); should be: double angleInBetween = (180.0 - priorAngle) + nextAngle;
public struct Vertex
{
public double X { get; set; }
public double Y { get; set; }
}
private static double CalcDistance(Vertex v1, Vertex v2)
{
double dX = (v2.X - v1.X);
double dY = (v2.Y - v1.Y);
return Math.Sqrt((dX * dX) + (dY * dY));
}
private static Vertex[] SortByAngle(IEnumerable<Vertex> vs, Vertex current, Vertex previous)
{
var verticesOnDistance = from vertex in vs
where !vertex.Equals(current)
let distance = CalcDistance(current, vertex)
orderby distance
select vertex;
double priorAngle = Angle(previous, current);
var verticeAngles = from vertex in verticesOnDistance.Take(3)
let nextAngle = Angle(current, vertex)
let angleInBetween = (180.0 - priorAngle) + nextAngle
orderby angleInBetween descending
select vertex;
return verticeAngles.ToArray();
}
private static double Angle(Vertex v1, Vertex v2, double offsetInDegrees = 0.0)
{
return (RadianToDegree(Math.Atan2(-v2.Y + v1.Y, -v2.X + v1.X)) + offsetInDegrees);
}
public static double RadianToDegree(double radian)
{
var degree = radian * (180.0 / Math.PI);
if (degree < 0)
degree = 360 + degree;
return degree;
}
I'm running a little out of time here. I'll be on later... I'm not sure this is correct, but maybe shine another light on it...
Good luck

Point of impact: circle collision

I'm doing a school project about robocup where robots are playing football with AI. As everything works fine I am still stuck with something.
The robots are simple spheres drawn from top view. Both player shouldn't be allowed to walk into each other and should get a new updated position on the point of impact.
As the collision handler just checks if they collide or not.. I was hoping if there was a way to detect where the circles collide. So I can update the position of the colliding sphere to the last known non-colliding position so they can't walk trough each other and maybe bounce off.
All points on a circle's circumference are the same distacne, the radius, from the center of the circle. This is true for every circle on the playing field.
Therefore: two circles have collided EXACTLY when the distance between their centres is <= the sum of their respectve radii.
Well, you already marked an answer, but I went and put together a fully functioning piece of code, so maybe you can use it anyway. :) From my comment:
Since they're just circles, just calculate the midpoint between the
circles' centre points. If the circles have different radii, choose
one circle and calculate the point along the line one radius away from
its centre.
This might be a simple implementation. I created some very meager helper classes for it; I would totally encourage extending them, making the structs truly immutable, and all that good jazz, but for now serve okay for demonstration purposes.
So for helper classes:
public struct Point
{
public double X;
public double Y;
public double Distance(Point otherPoint)
{
double deltaX = this.X - otherPoint.X;
double deltaY = this.Y - otherPoint.Y;
return System.Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
}
public override string ToString()
{
return String.Format("({0}, {1})", X, Y);
}
}
public struct Polar
{
public double Radius;
public double Angle;
public double X { get { return Radius * System.Math.Cos(Angle); } }
public double Y { get { return Radius * System.Math.Sin(Angle); } }
public Point ToCartesian()
{
return new Point() { X = X, Y = Y };
}
}
public class Circle
{
public double Radius { get; set; }
public Point Position { get; set; }
}
Our meat-and-potatoes class/method is this:
public class CollisionResult
{
public Circle Circle1 { get; private set; }
public Circle Circle2 { get; private set; }
public Point Circle1SafeLocation { get; private set; }
public Point Circle2SafeLocation { get; private set; }
public Point CollisionLocation { get; private set; }
public CollisionResult(Circle circle1, Circle circle2)
{
this.Circle1 = circle1;
this.Circle2 = circle2;
}
public bool CalculateCollision()
{
double distanceFromCentres = Circle1.Position.Distance(Circle2.Position);
if (distanceFromCentres >= Circle1.Radius + Circle2.Radius)
return false;
double angleBetweenCircles = System.Math.Atan2(Circle2.Position.Y - Circle1.Position.Y, Circle2.Position.X - Circle1.Position.X);
Point midpointBetweenCircles = new Point(){X = (Circle1.Position.X + Circle2.Position.X)/2, Y = (Circle1.Position.Y + Circle2.Position.Y)/2};
Point circle1Offset = (new Polar() { Radius = Circle1.Radius, Angle = System.Math.PI + angleBetweenCircles }).ToCartesian();
Point circle2Offset = (new Polar() { Radius = Circle2.Radius, Angle = angleBetweenCircles }).ToCartesian();
CollisionLocation = midpointBetweenCircles;
Circle1SafeLocation = new Point(){X = midpointBetweenCircles.X + circle1Offset.X, Y = midpointBetweenCircles.Y + circle1Offset.Y };
Circle2SafeLocation = new Point(){X = midpointBetweenCircles.X + circle2Offset.X, Y = midpointBetweenCircles.Y + circle2Offset.Y };
return true;
}
}
Usage might look like:
private void CheckCollision(Circle circle1, Circle circle2)
{
CollisionResult result = new CollisionResult(circle1, circle2);
if (result.CalculateCollision())
{
Console.WriteLine(String.Format("Collision detected at {0}! Safe location for circle 1: {1}, circle 2: {2}", result.CollisionLocation, result.Circle1SafeLocation, result.Circle2SafeLocation));
}
else
{
Console.WriteLine("Did not collide.");
}
}
var circle1 = new Circle() {Radius = 5, Position = new Point(){X = 0, Y = 0} };
var circle2 = new Circle() {Radius = 5, Position = new Point(){X = 10, Y = 0} };
var circle3 = new Circle() {Radius = 3, Position = new Point(){X = 0, Y = 1} };
var circle4 = new Circle() {Radius = 5, Position = new Point(){X = 3, Y = 7} };
CheckCollision(circle1, circle2);
CheckCollision(circle3, circle4);
Outputs:
Did not collide.
Collision detected at (1.5, 4)! Safe location for circle 1: (0.158359213500125, 1.31671842700025), circle 2: (3.73606797749979, 8.47213595499958)
I don't know if it's necessary in your case to deal with the complexity of calculating true intersections of two circles (where they would intersect at two points) and such. Likely something along these lines would be sufficient for you. I definitely encourage healthy unit tests and making the classes proper beyond what I have here. :)
EDIT: Significantly in this case, and this would depend on what you want to do with it for your application, is that when the circles overlap, it simply calculates the midpoint between them then moves each circle away from that midpoint their respective radii. So depending on the speed and size of the circles, or how they are moving, it might produce weird results. For example, if you had a big 10 radius circle sitting still, then you throw in a 1 radius circle only 0.5 distance from the big circle's centre, that big circle is going to shift about 9.75 units! If you don't get into big overlapping conditions, then maybe it's not much of an issue. I think at the very least this will give you some information about the collision and then how you want your circles to react as a result will be up to you.
What you will need to do is to find the point of intersection between the two circles, which could be two points, one point, or none. This is basically done by solving the equations of the two circles together. Here's the math:
http://www.analyzemath.com/CircleEq/circle_intersection.html
Since this is for school I'll leave the coding to you :)

Categories

Resources