Reputation: 1109
Hi i'm trying to detect when two objects connect , either a circle or rectangle. I can't figure it out how to do it. Most examples/tutorials online are really math heavy Or doesn't use the Angle for rectangle
This is what i have so far.
public abstract record Location
{
public abstract double DistanceTo(Location location);
public abstract double DirectionTo(Location location);
}
public sealed record CircleLocation(double X, double Y, int Radius) : Location
{
public override double DistanceTo(Location location)
{
return location switch
{
CircleLocation circleLocation => Math.Sqrt(Math.Pow(X - circleLocation.X, 2) + Math.Pow(Y - circleLocation.Y, 2)) - circleLocation.Radius,
RectangleLocation rectangleLocation => Math.Max(rectangleLocation.Left - X, X - rectangleLocation.Right) + Math.Max(rectangleLocation.Top - Y, Y - rectangleLocation.Bottom),
_ => throw new Exception("Invalid location type")
};
}
public override double DirectionTo(Location location)
{
return location switch
{
CircleLocation circleLocation => Math.Atan2(circleLocation.Y - Y, circleLocation.X - X),
RectangleLocation rectangleLocation => Math.Atan2(rectangleLocation.CenterY - Y, rectangleLocation.CenterX - X),
_ => throw new Exception("Invalid location type")
};
}
}
// Angle in radians
public sealed record RectangleLocation(double X, double Y, double Width, double Height, double Angle) : Location
{
public double Left => X - Math.Cos(Angle) * Width / 2;
public double Top => Y - Math.Sin(Angle) * Height / 2;
public double Right => Left + (double)Math.Cos(Angle) * Width;
public double Bottom => Top + Math.Sin(Angle) * Height;
public double CenterX => X - Width / 2;
public double CenterY => Y - Height / 2;
public override double DistanceTo(Location location)
{
return location switch
{
CircleLocation circleLocation => Math.Sqrt(Math.Pow(X - circleLocation.X, 2) + Math.Pow(Y - circleLocation.Y, 2)) - circleLocation.Radius,
RectangleLocation rectangleLocation => Math.Max(rectangleLocation.Left - X, X - rectangleLocation.Right) + Math.Max(rectangleLocation.Top - Y, Y - rectangleLocation.Bottom),
_ => throw new Exception("Invalid location type")
};
}
public override double DirectionTo(Location location)
{
return location switch
{
CircleLocation circleLocation => Math.Atan2(circleLocation.Y - Y, circleLocation.X - X),
RectangleLocation rectangleLocation => Math.Atan2(rectangleLocation.CenterY - CenterY, rectangleLocation.CenterX - CenterX),
_ => throw new Exception("Invalid location type")
};
}
}
Here's the unit tests i'm playing with
public class UnitTest1
{
[Fact]
public void CircleToCircleTouches()
{
var circle1 = new CircleLocation(0, 0, 1);
var circle2 = new CircleLocation(2, 0, 1);
var distance = circle1.DistanceTo(circle2);
Assert.Equal(0, distance);
}
[Fact]
public void CircleToCircleDoesNotTouch()
{
var circle1 = new CircleLocation(0, 0, 1);
var circle2 = new CircleLocation(4, 0, 1);
var distance = circle1.DistanceTo(circle2);
Assert.Equal(2, distance);
}
[Fact]
public void RectangleToRectangleTouches()
{
var rectangle1 = new RectangleLocation(0, 0, 2, 2, 0);
var rectangle2 = new RectangleLocation(2, 2, 2, 2, 0);
var distance = rectangle1.DistanceTo(rectangle2);
Assert.Equal(0, distance);
}
[Fact]
public void RectangleToRectangleDoesNotTouch()
{
var rectangle1 = new RectangleLocation(0, 0, 2, 2, 0);
var rectangle2 = new RectangleLocation(3, 3, 2, 2, 0);
var distance = rectangle1.DistanceTo(rectangle2);
Assert.Equal(1, distance);
}
[Fact]
public void TestCircleToRectangleDistance()
{
var circle = new CircleLocation(0, 0, 1);
var rectangle = new RectangleLocation(3, 4, 2, 2, 0);
var distance = circle.DistanceTo(rectangle);
Assert.Equal(5, distance);
}
[Fact]
public void TestRectangleToCircleDistance()
{
var rectangle = new RectangleLocation(0, 0, 2, 2, 0);
var circle = new CircleLocation(3, 4, 1);
var distance = rectangle.DistanceTo(circle);
Assert.Equal(5, distance, 1);
}
}
Upvotes: 1
Views: 79
Reputation: 80232
Example of Python function. Acts like this but uses length of projections of vector (CircleCenter-RectangleCenter) onto rectangle axes
# cx, cy - circle center
# r - circle radius
# rcx, rcy - rectangle center
# halfw, halfh - half-width and half-height of rectangle
# dirx, diry - unit vector of rectangle "width" side
# it is equal to cos(angle) and sin(angle) for rectangle rotation angle relative to OX axis
# (1,0) for axis-aligned rectangle
def RotRectCircleIntersection(cx, cy, r, rcx, rcy, halfw, halfh, dirx, diry):
distw = max(0, abs(dirx*(cx-rcx) + diry*(cy-rcy)) - halfw)
disth = max(0, abs(diry*(cx-rcx) - dirx*(cy-rcy)) - halfh)
return(distw*distw+distw*distw <= r*r)
print(RotRectCircleIntersection(3,3,3,11,4,3,2,1,0))
print(RotRectCircleIntersection(3,3,3,7.5,4.5,2.92,1.94,0.507839,0.8614519))
print(RotRectCircleIntersection(5,0,2,7.5,4.5,2.92,1.94,0.507839,0.8614519))
Output
False
True
False
Upvotes: 0