This question already has answers here:
circle-circle collision
(5 answers)
Closed 9 years ago.
Just made a little theory to simplify my problem.. I want to know if it's possible or easier way to do this.
I'm making a game simulation in C# where robots play soccer with AI. But need to prevent them from walking in eachother, find the exact point of impact to update their position so they can't walk into eachother.
Thanks.
This is not a duplicate since I was asking another theory I was working out.
Well, from the answer I already provided from your previous question, is there anything you don't understand or need help with? It does pretty much exactly what you're asking here (so "yes, you can employ polar coordinates like this.")
However, your comment about the "last known position where they didn't collide" would mean you would have to track their positions and maintain their last good state and revert to it. Not sure if that's necessary in this case or if you would rather just calculate a new "best-fit" location.
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. :)
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.
Might be this is helpful for you:
http://www.emanueleferonato.com/2011/06/13/slicing-splitting-and-cutting-objects-with-box2d/
This tutorial consist of four parts and explain the 2D dynamics very well.
The faster way to see if the two circles are colliding is to constantly check their positions. Infact you have to check if the distance between their centers is smaller than the sum of their radius.
Some "pseudo code" could be:
distanceX = Math.Abs(circle1.X - cirlce2.X);
distanceY = Math.Abs(circle1.Y - cirlce2.Y);
distance = Math.Sqrt(distanceX * distanceX - distanceY * distanceY);
if(distance <= circle1.Radius + circle2.Radius){
//they're colliding and the point of collision is:
collisionX = distanceX / 2;
collisionY = distanceY / 2;
if(circle1.X < circle2.X)
collisionX += circle1.X;
else
collisionX += circle2.X;
if(circle1.Y < circle2.Y)
collisionY += circle1.Y;
else
collisionY += circle2.Y;
}
PS: Note that I didn't use Math.Pow() due to its performance.
Related
I am having trouble trying to write a method (in c#) that returns all of the integer lattice pairs within a circle of a given radius at a specific offset. I found this article https://en.wikipedia.org/wiki/Gauss_circle_problem but unfortunately it seems more interested in calculating the number of lattice pairs rather than identifying each individual lattice pair.
I am also having issues understanding some of the math terminology/symbols as my math is sadly somewhat lacking, so code examples or detailed explanations would be super helpful if possible.
my plan so far is to just check each integer value combination from the radius to negative radius and then simply check the distance to the origin, before applying the offset to the vectors that are within range.
Am i on the right track with this, or is there a more optimized way to achieve this?
example stub:
public class CircleTest ()
{
public List<Vector2>GetContainedIntegerVectors(float radius, Vector2 centerPosition)
{
//math goes here
}
}
Simple Vector2 class
public class Vector2 ()
{
public float x {get; set;}
public float y {get; set;}
}
Thanks!
For my understanding you are on the right track, but there are some optimizations possible:
use the System.Windows.Vector class from C# instead of your own
you only have to calculate the points of a quarter of the circle, eg for x>0 and y>=0 and mirror the rest (plus include centerpoint).
here a possible implementation:
List<Vector> tmpList = new List<Vector();
List<Vector> list = new List<Vector();
double rSquared=r*r; // using sqared reduces execution time (no square root needed)
for(int x=1; x<=r; x++)
for(int y=0; y<=r; y++) {
Vector v = new Vector(x,y);
if(v.LengthSquared<=rSquared)
tmpList.Add(v);
else
break;
}
list.Add(centerVector);
foreach(Vector v in tmpList) {
Vector vMirr = new Vector(v.X, -1*v.Y);
list.Add(Vector.Add(centerVector, v));
list.Add(Vector.Add(centerVector, v.Negate()));
list.Add(Vector.Add(centerVector, vMirr));
list.Add(Vector.Add(centerVector, vMirr.Negate));
}
public List<Vector2>GetContainedVectors(int radius, Vector2 offset)
{
List<Vector2> returnValues = new List<Vector2>();
for (int x = radius; x > -radius; x--)
{
for (int y = radius; y > -radius; y--)
{
if(Vector2.Distance(new Vector2(x,y), Vector2.zero) <= radius)
{
returnValues.Add(new Vector2(x + offset.x, y + offset.y));
}
}
}
return returnValues;
}
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?
I receive lat,long coordinates from the GPS of my vehicle and the coordinates of another object near the vehicle. What I want to do is to draw(using Graphics) points using those coordinates but as xys. My car would be the center so(0,0). My problem is with the object and how could I calculate its xy. I have a picturebox(556,536).
public struct XY
{
public float x;
public float y;
};
XY carPoint;
XY objPoint;
string incomming = Convert.ToString(spl.ReadLine());
carText.x = incomming.Substring(0, incomming.IndexOf("="));
carText.y = incomming.Substring(incomming.IndexOf("=") + 1,incomming.IndexOf("_") - incomming.IndexOf("=") - 1);
objText.x = incomming.Substring(incomming.IndexOf("_") + 1, incomming.IndexOf("*") - incomming.IndexOf("_") - 1);
objText.y = incomming.Substring(incomming.IndexOf("*") + 1, incomming.IndexOf("/") - incomming.IndexOf("*") - 1);
carPoint.x = float.Parse(carText.x);
carPoint.y = float.Parse(carText.y);
objPoint.x = float.Parse(objText.x);
objPoint.y = float.Parse(objText.y);
interPoint.x = float.Parse(interText.x);
interPoint.y = float.Parse(interText.y);
This is how I receive the coordinates, but I couldn't test with anything to solve my problem as I found none.
Thank you in advance!
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 :)
I have a simple object that allows you to assign three properties (x,y,z) (lets call this object a "point", because that is what it is). I then have a second object with a method that accepts two instances of the first object, and returns the distance between the two "points" in three dimensional space. I also need a method that will accept two "points"
and a double, representing distance traveled (from the first "point" parameter used) that returns a "point" object with its x,y,z coordinates.
I'm ok with everything except the calculation of the point coordinates that are on the original line between the two points supplied, that is at a certain distance from the first point.
"point" object:
public class POR
{
private double PORX;
private double PORY;
private double PORZ;
public double X
{
get { return PORX; }
set { PORX = value; }
}
public double Y
{
get { return PORY; }
set { PORY = value; }
}
public double Z
{
get { return PORZ; }
set { PORZ = value; }
}
public POR(double X, double Y, double Z)
{
PORX = X;
PORY = Y;
PORZ = Z;
}
I'm then using :
public double PorDistance(POR por1, POR por2)
{
return Math.Round(Math.Sqrt( Math.Pow((por1.X - por2.X),2) + Math.Pow((por1.Y - por2.Y),2) + Math.Pow((por1.Z - por2.Z),2)),2);
}
to return the distance between those two points I need something like
public POR IntersectPOR (POR por1, POR por2, double distance)
{
}
where distance is the distance traveled from por1 towards por2.
This can be done with a bit of help from vectors.
Let's say your starting point is called P, and the other point is Q, and the distance is d. You want to find the point on the line PQ at a distance d from P towards Q.
First you need to find the direction of travel. That's done by finding Q - P
v = Point(Q.x - P.x, Q.y - P.y, Q.z - P.z)
Now you need to find the unit vector in that direction, so
scale = sqrt(v.x*v.x + v.y*v.y + v.z*v.z)
unit = Point(v.x/scale, v.y/scale, v.z/scale)
Now you need to find the vector representing the distance traveled:
t = Point(unit.x*d, unit.y*d, unit.z*d)
To find the final position, add your traveled vector to your starting point:
final = Point(P.x + t.x, P.y + t.y, P.z + t.z)
It looks like you want something similar to:
public class Point
{
public double x, y, z;
// ctors, Add(), Subtract() omitted
public Point Normalize()
{
double m = Magnitude;
if (m != 0.0) return ScaleTo(1.0/m);
return new Point();
}
public double Magnitude
{
get { return Math.Sqrt(x * x + y * y + z * z); }
}
public Point ScaleTo(double s)
{
return new Point(x * s, y * s, z * s);
}
}
public Point PointOnLine(Point from, Point to, double dist)
{
return from.Add(to.Subtract(from).Normalize().ScaleTo(dist));
}
No actual code because I think this is more of a conceptual question.
This may not be the most efficient, but when I'm doing this I just apply the ratio between the total distance and the segment to the coordinate deltas.
For example if I have points at 0,0,0 and 1,2,3 the total 3D distance is 3.74, if I want to place a point 1 unit from the first point the ratio is 1/3.74 so the new coordinate would be .2673 of the total distance from the first point toward the second or .267 , .534 , .802