Skip to content

Commit

Permalink
Circle-rect intersection
Browse files Browse the repository at this point in the history
  • Loading branch information
awgil committed Jan 23, 2025
1 parent 8d845c3 commit 658339b
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 9 deletions.
15 changes: 15 additions & 0 deletions BossMod/Util/Intersect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,19 @@ public static bool CircleCone(WDir circleOffset, float circleRadius, float coneR
return (circleOffset - corner).LengthSq() <= rsq;
}
public static bool CircleCone(WPos circleCenter, float circleRadius, WPos coneCenter, float coneRadius, WDir coneDir, Angle halfAngle) => CircleCone(circleCenter - coneCenter, circleRadius, coneRadius, coneDir, halfAngle);

public static bool CircleAARect(WDir circleOffset, float circleRadius, float halfExtentX, float halfExtentZ)
{
circleOffset = circleOffset.Abs(); // symmetrical along X/Z, consider only positive quadrant
var cornerOffset = circleOffset - new WDir(halfExtentX, halfExtentZ); // relative to corner
if (cornerOffset.X > circleRadius || cornerOffset.Z > circleRadius)
return false; // circle is too far from one of the edges, so can't intersect
if (cornerOffset.X <= 0 || cornerOffset.Z <= 0)
return true; // circle center is inside/on the edge, or close enough to one of the edges to intersect
return cornerOffset.LengthSq() <= circleRadius * circleRadius; // check whether circle touches the corner
}
public static bool CircleAARect(WPos circleCenter, float circleRadius, WPos rectCenter, float halfExtentX, float halfExtentZ) => CircleAARect(circleCenter - rectCenter, circleRadius, halfExtentX, halfExtentZ);

public static bool CircleRect(WDir circleOffset, float circleRadius, WDir rectZDir, float halfExtentX, float halfExtentZ) => CircleAARect(circleOffset.Rotate(rectZDir.MirrorX()), circleRadius, halfExtentX, halfExtentZ);
public static bool CircleRect(WPos circleCenter, float circleRadius, WPos rectCenter, WDir rectZDir, float halfExtentX, float halfExtentZ) => CircleRect(circleCenter - rectCenter, circleRadius, rectZDir, halfExtentX, halfExtentZ);
}
2 changes: 2 additions & 0 deletions BossMod/Util/WPosDir.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public WDir(Vector2 v) : this(v.X, v.Y) { }
public readonly WDir Sign() => new(Math.Sign(X), Math.Sign(Z));
public readonly WDir OrthoL() => new(Z, -X); // CCW, same length
public readonly WDir OrthoR() => new(-Z, X); // CW, same length
public readonly WDir MirrorX() => new(-X, Z);
public readonly WDir MirrorZ() => new(X, -Z);
public static float Dot(WDir a, WDir b) => a.X * b.X + a.Z * b.Z;
public readonly float Dot(WDir a) => X * a.X + Z * a.Z;
public static float Cross(WDir a, WDir b) => a.X * b.Z - a.Z * b.X;
Expand Down
1 change: 0 additions & 1 deletion TODO
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
immediate plans
- circle vs rect/cone intersection
- proper circle bounds -> revise everything in fru
- ai refactoring
-- high-level ai modules
Expand Down
32 changes: 24 additions & 8 deletions UIDev/IntersectionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ class IntersectionTest : TestWindow
private float _cirleOffset = 5.5f;
private float _circleDir = 0;
private float _circleRadius = 0.5f;
private float _coneRadius = 5;
private float _coneHalfAngleDeg = 30;
private float _coneDirDeg = 90;
private float _shapeExtentPrimary = 5; // z/r
private float _shapeExtentSecondary = 30; // x/phi (deg)
private float _shapeDirDeg = 90;
private bool _shapeIsRect;
private readonly MiniArena _arena = new(new(), default, new ArenaBoundsSquare(10));

public IntersectionTest() : base("Intersection test", new(400, 400), ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)
Expand All @@ -22,15 +23,30 @@ public override void Draw()
ImGui.DragFloat("Circle offset", ref _cirleOffset, 0.1f, 0, 10);
ImGui.DragFloat("Circle dir", ref _circleDir, 1, -180, 180);
ImGui.DragFloat("Circle radius", ref _circleRadius, 0.1f, 0.1f, 5);
ImGui.DragFloat("Cone radius", ref _coneRadius, 0.1f, 0.1f, 10);
ImGui.DragFloat("Cone half-angle", ref _coneHalfAngleDeg, 1, 0, 180);
ImGui.DragFloat("Cone dir", ref _coneDirDeg, 1, -180, 180);
ImGui.Checkbox("Intersect with rect", ref _shapeIsRect);
if (_shapeIsRect)
{
ImGui.DragFloat("Rect half-Z", ref _shapeExtentPrimary, 0.1f, 0.1f, 10);
ImGui.DragFloat("Rect half-X", ref _shapeExtentSecondary, 0.1f, 0.1f, 30);
ImGui.DragFloat("Rect dir", ref _shapeDirDeg, 1, -180, 180);
}
else
{
ImGui.DragFloat("Cone radius", ref _shapeExtentPrimary, 0.1f, 0.1f, 10);
ImGui.DragFloat("Cone half-angle", ref _shapeExtentSecondary, 1, 0, 180);
ImGui.DragFloat("Cone dir", ref _shapeDirDeg, 1, -180, 180);
}
var circleCenter = _cirleOffset * _circleDir.Degrees().ToDirection();
var intersect = Intersect.CircleCone(circleCenter, _circleRadius, _coneRadius, _coneDirDeg.Degrees().ToDirection(), _coneHalfAngleDeg.Degrees());
var intersect = _shapeIsRect
? Intersect.CircleRect(circleCenter, _circleRadius, _shapeDirDeg.Degrees().ToDirection(), _shapeExtentSecondary, _shapeExtentPrimary)
: Intersect.CircleCone(circleCenter, _circleRadius, _shapeExtentPrimary, _shapeDirDeg.Degrees().ToDirection(), _shapeExtentSecondary.Degrees());
ImGui.TextUnformatted($"Intersect: {intersect}");

_arena.Begin(default);
_arena.AddCone(default, _coneRadius, _coneDirDeg.Degrees(), _coneHalfAngleDeg.Degrees(), ArenaColor.Safe);
if (_shapeIsRect)
_arena.AddRect(default, _shapeDirDeg.Degrees().ToDirection(), _shapeExtentPrimary, _shapeExtentPrimary, _shapeExtentSecondary, ArenaColor.Safe);
else
_arena.AddCone(default, _shapeExtentPrimary, _shapeDirDeg.Degrees(), _shapeExtentSecondary.Degrees(), ArenaColor.Safe);
_arena.AddCircle(circleCenter.ToWPos(), _circleRadius, ArenaColor.Danger);
_arena.End();
}
Expand Down

0 comments on commit 658339b

Please sign in to comment.