Circle collision formula - c#

I have this formula for simple circle collision detection:
private bool CircleCollision(Rectangle Circle1, Rectangle Circle2)
{
int X1 = Circle1.Left;
int Y1 = Circle1.Top;
int X2 = Circle2.Left;
int Y2 = Circle2.Top;
int R1 = Circle1.Width / 2;
int R2 = Circle2.Width / 2;
int Radius = R1 + R2;
int dX = X2 - X1;
int dY = Y2 - Y1;
if (Math.Sqrt((dX * dX) + (dY * dY)) <= Math.Sqrt(Radius * Radius))
return true;
else
return false;
}
But it just expose detection whenever the two circles have same radius. What am I doing wrong?
Solved
int X1 = Circle1.Left + (Circle1.Width / 2);
int Y1 = Circle1.Top + (Circle1.Height / 2);
int X2 = Circle2.Left + (Circle2.Width / 2);
int Y2 = Circle2.Top + (Circle2.Height / 2);

To check if two circles overlap you can do:
var radius=circle1.Radius+circle2.Radius;
var deltaX=circle1.CenterX-circle2.CenterX;
var deltaY=circle1.CenterY-circle2.CenterY;
return deltaX*deltaX + deltaY*deltaY <= radius*radius;
Note that I'm calculating the distance of the centers, not of the top left corners. I'm also comparing with the squared radius so I don't need to use the expensive Math.Sqrt function, but that doesn't affect correctness.
Your code doesn't work because you use Left and Top instead of the position of the center. The difference between the top-left corners is the same as the difference between the centers if the radius is the same. This explains why your code only works in that special case.
Not sure why you use a rectangle to represent a circle. You can calculate the center as centerX = 0.5*(Left+Right). You should also add a check that Width==Height, else you might get an ellipse as parameter, and then this algorithm won't work.

Related

Issue translating World Coordinate system to cartesian coordinate

I have two fits images from the WFC3 that I am trying to combine with a C# program. When I try to combine multiple images I don't think the values I am getting for my x/y coordinates (calculated from Right Ascension/Declination) are correct. I am expecting a final image that is about the same width as the two images combined, but it turns out to be about the same width and about twice the height. I know the final image should be about double the width of a single image because I manually combined the images in photoshop and the final image was about twice as wide as either of the two original images.
NOTE: when I say "image" they are fits images, so they are just a bunch of single values in a file, so to combine them I create a new file and initialize the correct number of single values (width * height) to zero, and then fill in the values from the images I am using to combine. They are not jpg or tif or png.
I am using the following formula to change from world coordinate system to cartesian:
formula is (since distance is the same for everything):
x = cos(dec) * cos(ra)
y = cos(dec) * sin(ra)
I get the right ascension and declination from the header in the fits file.
For the final image dimensions, I calculate the distance between x1 and x2 and create a new image that is 1/2 image 1 width + distance + 1/2 image 2 width. For the final height I do a similar calculation with y and image heights.
The images do also have a rotational component, but I am ignoring that as both images share the same rotation. This could be part of my problem.
public const double PixelsPerArcSecond = .039; // per WFC3 spec from Nasa
public static ImageDataModel Combine(List<ImageDataModel> inputImages)
{
// Right ascension is CRVAL1
// Declination is CRVAL2
// formula is (since distance is the same for everything):
// x = cos(dec) * cos(ra)
// y = cos(dec) * sin(ra)
ImageDataModel returnImage = new ImageDataModel();
ImageDataModel bm = inputImages[0];
double x1, y1, x2, y2;
x1 = Math.Cos(bm.CRVAL2) * Math.Cos(bm.CRVAL1);
y1 = Math.Cos(bm.CRVAL2) * Math.Sin(bm.CRVAL1);
int mult = 4; // todo: set this based off of the bitpix of the incoming images.
for (int i = 1; i < inputImages.Count; i++)
{
ImageDataModel cm = inputImages[i];
x2 = Math.Cos(cm.CRVAL2) * Math.Cos(cm.CRVAL1);
y2 = Math.Cos(cm.CRVAL2) * Math.Sin(cm.CRVAL1);
double dx = x1 - x2;
double dy = y1 - y2;
int distX = (int)((dx * 3600) / PixelsPerArcSecond);
int distY = (int)((dy * 3600) / PixelsPerArcSecond);
// This is what I expect to be wider than tall, but the converse is true.
int w = Math.Abs(distX) + (bm.ImageWidth / 2) + (cm.ImageWidth / 2);
int h = Math.Abs(distY) + (bm.ImageHeight / 2) + (cm.ImageHeight / 2);
// This is where the two images are combined into the final image.
ImageDataModel imd = CombineTwoImages(bm, cm, i, w, h, mult);
bm = imd;
}
return returnImage;
}
I am expecting an image that turns out like this:
http://wierdling.net/stack-overflow-images/ManuallyCombined.png
But getting this:
http://wierdling.net/stack-overflow-images/CombinedTest.png
The stats for the first image are: Width = 4139, Height = 4535, RA = 350.1584456860353 (CRVAL1), DEC = 61.16155335032816 (CRVAL2), ORIENTAT = -125
The stats for the second image are:Width = 4139, Height = 4535, RA = 350.1159150008405 (CRVAL1), DEC = 61.19543100394401 (CRVAL2), ORIENTAT = -125
The final expected width is close to 7733 with a height near 4773.
The final actual width is 4284, and the height is 7662.
Does anyone have any insight into what I am doing wrong?
The full source code for the program can be downloaded from https://bitbucket.org/wierdling/fitscombiner/src/master/
It currently only works with WFC3 data, and the program is very much a work in progress.
I think that your image program already does the rotation and you should do it too.
If I rotate the coordinates you calculated by 125 degrees and then calculate how far away the coordinate x1 is from the left side and the same for x2 and the y coordiantes I get a width of 6725 and height of 6166.
Not perfect but I think it goes in the right direction.
Hope that helped.
public static ImageDataModel Combine(List<ImageDataModel> inputImages)
{
// Right ascension is CRVAL1
// Declination is CRVAL2
// formula is (since distance is the same for everything):
// x = cos(dec) * cos(ra)
// y = cos(dec) * sin(ra)
ImageDataModel returnImage = new ImageDataModel();
ImageDataModel bm = inputImages[0];
double x1, y1, x2, y2;
x1 = Math.Cos(bm.CRVAL2) * Math.Cos(bm.CRVAL1);
y1 = Math.Cos(bm.CRVAL2) * Math.Sin(bm.CRVAL1);
var values = Rotate(0 - bm.Orientation, x1, y1);
x1 = values.x;
y1 = values.y;
int mult = 4; // todo: set this based off of the bitpix of the incoming images.
for (int i = 1; i < inputImages.Count; i++)
{
ImageDataModel cm = inputImages[i];
x2 = Math.Cos(cm.CRVAL2) * Math.Cos(cm.CRVAL1);
y2 = Math.Cos(cm.CRVAL2) * Math.Sin(cm.CRVAL1);
var values2 = Rotate(0 - bm.Orientation, x2, y2);
x2 = values2.x;
y2 = values2.y;
double dx = x1 - x2;
double dy = y1 - y2;
int distX = (int)((dx * 3600) / PixelsPerArcSecond);
int distY = (int)((dy * 3600) / PixelsPerArcSecond);
double width = (1.0 + x1) * (bm.ImageWidth / 2) + (1.0 - x2) * (cm.ImageWidth / 2) + Math.Abs(distX);
double height = (1.0 + y1) * (bm.ImageHeight / 2) + (1.0 - y2) * (cm.ImageHeight / 2) + Math.Abs(distY);
// This is what I expect to be wider than tall, but the converse is true.
int w = Math.Abs(distX) + (bm.ImageWidth / 2) + (cm.ImageWidth / 2);
int h = Math.Abs(distY) + (bm.ImageHeight / 2) + (cm.ImageHeight / 2);
// This is where the two images are combined into the final image.
ImageDataModel imd = CombineTwoImages(bm, cm, i, w, h, mult);
bm = imd;
}
return returnImage;
}
private static (double x, double y) Rotate(int angle, double x, double y)
{
double rad = Math.PI * angle / 180.0;
return (x * Math.Cos(rad) - y * Math.Sin(rad), x * Math.Sin(rad) + y * Math.Cos(rad));
}

Function that calculates diffrent positions from an array c#

I have a problem that I need help solving. Im supposed to create a function that calculates the distance between two positions. The positions are stored in two arrays, I can use as many parameters that I need to do this calculation
static double[] Latitudes = new double[] {
59.3261917, 57.7010496, 59.8939529, 65.5867395, 60.11021, 52.5069312, 48.859
};
static double[] Longitudes = new double[] {
17.7018773, 11.6136602, 10.6450348, 22.0422998, 24.7385057, 13.1445521, 2.2069765
};
I have been given an equation that will help me calculate the distance
distance = Math.sqrt( (x1 - x2)2 + (y1 - y2)2 )
My problem is that I can't get the elements from the arrays to the variables inside the function
Extract methods, split you problem into minor ones:
// Initial step:
// Distance between points
private static double Distance(double x1, double y1, double x2, double y2) {
return Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
// Next step:
// Distance between points given as arrays' items indexes
private static double Distance(double[] xs, double[] ys, int indexFrom, indexTo) {
return Distance(xs[indexFrom], ys[indexFrom], xs[indexTo], ys[indexTo]);
}
Then use
// What is the distance between 0-th and 2-nd point?
double result = Distance(Latitudes, Longitudes, 0, 2);
Console.WriteLine(result);
// What is the distance between all the points?
for (int from = 0; from < Math.Min(Latitudes.Length, Longitudes.Length); ++from)
for (int to = from + 1; to < Math.Min(Latitudes.Length, Longitudes.Length); ++to) {
Console.WriteLine($"Distance from item #{from} to item #{to} is {Distance(Latitudes, Longitudes, from, to)}");
}
Well first you need to decide which positions you want to compare. This would be done by index. Lets says you want to compare positions at index 0 with positions at index 2. Then the code to get the correct variables would be:
double x1 = Latitudes[0];
double y1 = Longitudes[0];
double x2 = Latitudes[2];
double y2 = Longitudes[2];
You can then feed those values into your function. Your function code is wrong and won't compile. The correct call for the function would be:
double distance = Math.Sqrt(Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2));
In the interest of providing a more complete function for your class, and keeping in mind that your arrays are static, this will allow you to get the distance from any two given points based on index. Also, I expect this is a homework assignment so I am leaning towards your requirement to be creating a function similar to this:
double CalculateDistance(int index1, int index2)
{
double x1 = Latitudes[index1];
double y1 = Longitudes[index1];
double x2 = Latitudes[index2];
double y2 = Longitudes[index2];
return Math.Sqrt(Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2));
}
You can then call this function as follows:
double distance = CalculateDistance(0, 2);
Show distance for all pairs
if (Latitudes.Length == Longitudes.Length)
{
for (int i = 0; i < Latitudes.Length - 1; i = i + 2)
{
double x1 = Longitudes[i];
double x2 = Longitudes[i + 1];
double y1 = Latitudes[i];
double y2 = Latitudes[i + 1];
double distance = Math.Sqrt(Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2));
Console.WriteLine($"x1 = {x1}; x2 = {x2}; y1 = {y1}; y2 = {y2}; distance {distance}");
}
}

Determine If Two Points Are Near

I have the following:
bool AreNear(Point Old, Point Current)
{
int x1 = Convert.ToInt32(Old.X);
int x2 = Convert.ToInt32(Current.X);
int y1 = Convert.ToInt32(Old.Y);
int y2 = Convert.ToInt32(Current.Y);
if (x1 == x2) {
if (y1 == y2) {
return true;
}
}
return false;
}
I want to return true in the function if the current point is in 25 pixels radius of the old point. Can anyone tell me how to do that?
You can use the Pythagorean formula to calculate the distance between two points. In C#:
var d = Math.Sqrt(Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2))
Why does this work? Have a look at the following diagram and remember that a^2 + b^2 = c^2 holds for right triangles:
Just calculate the square of the distance using Pythagoras' theorem, and compare to the square of the radius:
bool ComparePoints(Point Old, Point Current)
{
int x1 = Convert.ToInt32(Old.X);
int x2 = Convert.ToInt32(Current.X);
int y1 = Convert.ToInt32(Old.Y);
int y2 = Convert.ToInt32(Current.Y);
int dx = x1 - x2;
int dy = y1 - y2;
return (dx*dx + dy*dy) < 25*25;
}
You can use Math.Abs to get the distance:
public static bool InDistance(Point Old, Point Current, int distance)
{
int diffX = Math.Abs(Old.X - Current.X);
int diffY = Math.Abs(Old.Y - Current.Y);
return diffX <= distance && diffY <= distance;
}
use it:
bool arePointsInDistance = InDistance(new Point(100, 120), new Point(120, 99), 25);
Try using the distance formula http://www.purplemath.com/modules/distform.htm and compare the distance <=25

Detect if circle A is completely inside circle B

Below is a function that detects if two circles intersect. I want to change it to only detect if the periferi of the circles intersect. Hence, if circle A is completely inside circle B, there is no collision!
How?
private bool IsCircleCollision(
int x1, int y1, int radius1,
int x2, int y2, int radius2)
{
int dx = x2 - x1;
int dy = y2 - y1;
int distance = (dx * dx) + (dy * dy);
int radii = radius1 + radius2;
if (distance < radii * radii)
{
return true;
}
else
{
return false;
}
}
You work this out by calculating the distance between the two centres, D say. There is an intersection if
abs(R1-R2) < D < R1+R2
where R1 and R2 are the radii of the two circles.
The first test, abs(R1-R2) < D handles the case when one circle's centre is inside the other's. And the second test, D < R1+R2, handles the case when neither circle contains the other's centre.
So, adapting your code we have:
private bool IsCircleCollision(
int x1, int y1, int radius1,
int x2, int y2, int radius2)
{
int dx = x2 - x1;
int dy = y2 - y1;
double D = Math.Sqrt(dx*dx + dy*dy);
return Math.Abs(radius1-radius2)<D && D<radius1+radius2;
}
If performance is important here, you can do without the call to Math.Sqrt like this:
private bool IsCircleCollision(
int x1, int y1, int radius1,
int x2, int y2, int radius2)
{
int dx = x2 - x1;
int dy = y2 - y1;
int Dsqr = dx*dx + dy*dy;
int rdiff = Math.Abs(radius1-radius2);
int rsum = radius1+radius2
return rdiff*rdiff<Dsqr && D<rsum*rsum;
}
The perimeters will be intersecting if and only if the distance between the two centers is less than or equal to the sum of the two radii but greater than or equal to their absolute difference. With this fact, it shouldn't be hard to re-write the function.
You could add a check to see if the distance + radius1 is less than radius2 or distance + radius2 is less than radius1, but then you'll need distance to be the actual distance rather than its square.
else if (Math.Sqrt(dx * dx + dy * dy) < Math.Abs(radius1 - radius2))

Shorten a line by a number of pixels

I'm drawing a custom diagram of business objects using .NET GDI+. Among other things, the diagram consists of several lines that are connecting the objects.
In a particular scenario, I need to shorten a line by a specific number of pixels, let's say 10 pixels, i.e. find the point on the line that lies 10 pixels before the end point of the line.
Imagine a circle with radius r = 10 pixels, and a line with start point (x1, y1) and end point (x2, y2). The circle is centered at the end point of the line, as in the following illustration.
How do I calculate the point marked with a red circle, i.e. the intersection between circle and line? This would give me the new end point of the line, shortening it by 10 pixels.
Solution
Thank you for your answers from which I was able to put together the following procedure. I named it LengthenLine, since I find it more natural to pass a negative number of pixels if I want the line shortened.
Specifically, I was trying to put together a function that could draw a line with rounded corners, which can be found here.
public void LengthenLine(PointF startPoint, ref PointF endPoint, float pixelCount)
{
if (startPoint.Equals(endPoint))
return; // not a line
double dx = endPoint.X - startPoint.X;
double dy = endPoint.Y - startPoint.Y;
if (dx == 0)
{
// vertical line:
if (endPoint.Y < startPoint.Y)
endPoint.Y -= pixelCount;
else
endPoint.Y += pixelCount;
}
else if (dy == 0)
{
// horizontal line:
if (endPoint.X < startPoint.X)
endPoint.X -= pixelCount;
else
endPoint.X += pixelCount;
}
else
{
// non-horizontal, non-vertical line:
double length = Math.Sqrt(dx * dx + dy * dy);
double scale = (length + pixelCount) / length;
dx *= scale;
dy *= scale;
endPoint.X = startPoint.X + Convert.ToSingle(dx);
endPoint.Y = startPoint.Y + Convert.ToSingle(dy);
}
}
Find the direction vector, i.e. let the position vectors be (using floats) B = (x2, y2) and A = (x1, y1), then AB = B - A. Normalize that vector by dividing by its length ( Math.Sqrt(xx + yy) ). Then multiply the direction vector AB by the original length minus the circle's radius, and add back to the lines starting position:
double dx = x2 - x1;
double dy = y2 - y1;
double length = Math.Sqrt(dx * dx + dy * dy);
if (length > 0)
{
dx /= length;
dy /= length;
}
dx *= length - radius;
dy *= length - radius;
int x3 = (int)(x1 + dx);
int y3 = (int)(y1 + dy);
Edit: Fixed the code, aaand fixed the initial explanation (thought you wanted the line to go out from the circle's center to its perimeter :P)
I'm not sure why you even had to introduce the circle. For a line stretching from (x2,y2) to (x1,y1), you can calculate any point on that line as:
(x2+p*(x1-x2),y2+p*(y1-y2))
where p is the percentage along the line you wish to go.
To calculate the percentage, you just need:
p = r/L
So in your case, (x3,y3) can be calculated as:
(x2+(10/L)*(x1-x2),y2+(10/L)*(y1-y2))
For example, if you have the two points (x2=1,y2=5) and (x1=-6,y1=22), they have a length of sqrt(72 + 172 or 18.38477631 and 10 divided by that is 0.543928293. Putting all those figures into the equation above:
(x2 + (10/l) * (x1-x2) , y2 + (10/l) * (y1-y2))
= (1 + 0.543928293 * (-6- 1) , 5 + 0.543928293 * (22- 5))
= (1 + 0.543928293 * -7 , 5 + 0.543928293 * 17 )
= (x3=-2.807498053,y3=14.24678098)
The distance between (x3,y3) and (x1,y1) is sqrt(3.1925019472 + 7.7532190152) or 8.384776311, a difference of 10 to within one part in a thousand million, and that's only because of rounding errors on my calculator.
You can use similar triangles. For the main triangle, d is the hypotenuses and the extension of r is the vertical line that meets the right angle. Inside the circle you will have a smaller triangle with a hypotenuses of length r.
r/d = (x2-a0)/(x2-x1) = (y2-b0)/(y2-y1)
a0 = x2 + (x2-x1)r/d
b0 = y2 + (y2-y1)r/d

Categories

Resources