using System; using System.Collections.Generic; using System.Linq; using System.Text; using Azimuth.RootFinding; using Annulus.SweptCollisionDetection; using Azimuth; namespace Annulus.UnitTests.SweptCollisionDetection { public class PolygonvsPointTests : UnitTestSharp.TestFixture { public void BoxLinearMotion_HitOnEdge() { var polygon = new SimplePolygon(new Vector[] { new Vector(-1, 1), new Vector( 1, 1), new Vector( 1,-1), new Vector(-1,-1), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { LinearVelocity = new Vector(-0.5, 0), LinearAcceleration = new Vector(-1.0 / 6.0, 0), InitialPosition = new Vector(7, 0), }, pointLeverArm = new Vector(0,0), impactWindow = new Interval(0, 10), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { new PolygonvsPoint.Hit { Edge = polygon.GetEdge(1), TOI = 6, HitFraction = 0.5, Transition = PolygonvsPoint.TransitionState.Entering, }, new PolygonvsPoint.Hit { Edge = polygon.GetEdge(3), TOI = 6 * (Math.Sqrt(35.0/12.0) - .5), HitFraction = 0.5, Transition = PolygonvsPoint.TransitionState.Exiting, }, }; CheckEqual(expected, hits); } public void BoxLinearMotion_HitOnCorner_Velocity() { var polygon = new SimplePolygon(new Vector[] { new Vector(-1, 1), new Vector( 1, 1), new Vector( 1,-1), new Vector(-1,-1), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { InitialPosition = new Vector(7, 7), LinearVelocity = new Vector(-1, -1), }, impactWindow = new Interval(0, 10), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(1), TOI = 6, Transition = PolygonvsPoint.TransitionState.Entering, }, new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(3), TOI = 8, Transition = PolygonvsPoint.TransitionState.Exiting, }, }; CheckEqual(expected, hits); } public void BoxLinearMotion_HitOnCorner_Acceleration() { var polygon = new SimplePolygon(new Vector[] { new Vector(-1, 1), new Vector( 1, 1), new Vector( 1,-1), new Vector(-1,-1), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { InitialPosition = new Vector(7, 7), LinearVelocity = new Vector(-1, 0), LinearAcceleration = new Vector(0, -1.0 / 3.0), }, impactWindow = new Interval(0, 10), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(1), TOI = 6, Transition = PolygonvsPoint.TransitionState.Entering, }, new PolygonvsPoint.Hit { Edge = polygon.GetEdge(2), HitFraction = -3.0 + Math.Sqrt(12), TOI = Math.Sqrt(48), Transition = PolygonvsPoint.TransitionState.Exiting, }, }; CheckEqual(expected, hits); } public void BoxLinearMotion_Scrape() { var polygon = new SimplePolygon(new Vector[] { new Vector(-1, 1), new Vector( 1, 1), new Vector( 1,-1), new Vector(-1,-1), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { InitialPosition = new Vector(7, 1), LinearVelocity = new Vector(-1, 0), }, impactWindow = new Interval(0, 10), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(1), TOI = 6, Transition = PolygonvsPoint.TransitionState.GlancingParallel | PolygonvsPoint.TransitionState.Entering, }, new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(0), TOI = 8, Transition = PolygonvsPoint.TransitionState.GlancingParallel | PolygonvsPoint.TransitionState.Exiting, }, }; CheckEqual(expected, hits); } public void BoxLinearMotion_JustTouch_Acceleration() { var polygon = new SimplePolygon(new Vector[] { new Vector(-1, 1), new Vector( 1, 1), new Vector( 1,-1), new Vector(-1,-1), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { InitialPosition = new Vector(0, 6), LinearVelocity = new Vector(0, -5), LinearAcceleration = new Vector(0, 2.5), }, impactWindow = new Interval(0, 10), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { new PolygonvsPoint.Hit { Edge = polygon.GetEdge(0), HitFraction = 0.5, TOI = 2, Transition = PolygonvsPoint.TransitionState.GlancingOutside, }, }; CheckEqual(expected, hits); } public void BoxNoMotion_Outside() { var polygon = new SimplePolygon(new Vector[] { new Vector(-1, 1), new Vector( 1, 1), new Vector( 1,-1), new Vector(-1,-1), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { InitialPosition = new Vector(7, 0), }, impactWindow = new Interval(0, 10), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { }; CheckEqual(expected, hits); } public void BoxNoMotion_Inside() { var polygon = new SimplePolygon(new Vector[] { new Vector(-1, 1), new Vector( 1, 1), new Vector( 1,-1), new Vector(-1,-1), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { }, impactWindow = new Interval(0, 10), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { }; CheckEqual(expected, hits); } public void BoxNoMotion_OnEdge() { var polygon = new SimplePolygon(new Vector[] { new Vector(-1, 1), new Vector( 1, 1), new Vector( 1,-1), new Vector(-1,-1), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { InitialPosition = new Vector(1, 0), }, impactWindow = new Interval(0, 10), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { new PolygonvsPoint.Hit { Edge = polygon.GetEdge(1), HitFraction = 0.5, TOI = Scalar.NaN, Transition = PolygonvsPoint.TransitionState.StationaryOnEdge, } }; CheckEqual(expected, hits); } public void BoxNoMotion_OnVertex() { var polygon = new SimplePolygon(new Vector[] { new Vector(-1, 1), new Vector( 1, 1), new Vector( 1,-1), new Vector(-1,-1), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { InitialPosition = new Vector(1, 1), }, impactWindow = new Interval(0, 10), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(1), TOI = Scalar.NaN, Transition = PolygonvsPoint.TransitionState.StationaryOnEdge, } }; CheckEqual(expected, hits); } public void BoxAndPointAngularMotion_NoHit() { var polygon = new SimplePolygon(new Vector[] { new Vector(-1, 1), new Vector( 1, 1), new Vector( 1,-1), new Vector(-1,-1), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { AngularVelocity = Math.PI / 4, }, pointMotion = new Motion { InitialPosition = new Vector(3, 0), AngularVelocity = Math.PI / 4 * 3, }, pointLeverArm = new Vector(-1, 0), impactWindow = new Interval(0, 10), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { }; CheckEqual(expected, hits); } public void BoxAndPointAngularMotion_EdgeHit() { var polygon = new SimplePolygon(new Vector[] { new Vector(-1, 1), new Vector( 1, 1), new Vector( 1,-1), new Vector(-1,-1), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { AngularVelocity = 3 * Math.PI, }, pointMotion = new Motion { InitialPosition = new Vector(2, 0), AngularVelocity = 1 * Math.PI, }, pointLeverArm = new Vector(2, 0), impactWindow = new Interval(0, 4), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { new PolygonvsPoint.Hit { Edge = polygon.GetEdge(1), TOI = 5.0/6.0, Transition = PolygonvsPoint.TransitionState.Entering, HitFraction = (3-Math.Sqrt(3)) / 2, }, new PolygonvsPoint.Hit { Edge = polygon.GetEdge(1), TOI = 7.0/6.0, Transition = PolygonvsPoint.TransitionState.Exiting, HitFraction = 1 - (3-Math.Sqrt(3)) / 2, }, new PolygonvsPoint.Hit { Edge = polygon.GetEdge(1), TOI = 5.0/6.0 + 2, Transition = PolygonvsPoint.TransitionState.Entering, HitFraction = (3-Math.Sqrt(3)) / 2, }, new PolygonvsPoint.Hit { Edge = polygon.GetEdge(1), TOI = 7.0/6.0 + 2, Transition = PolygonvsPoint.TransitionState.Exiting, HitFraction = 1 - (3-Math.Sqrt(3)) / 2, }, }; CheckEqual(expected, hits); } public void BoxAndPointAngularMotion_CornerHit() { var polygon = new SimplePolygon(new Vector[] { new Vector(0, 0), new Vector(1, 0), new Vector(1, 1), new Vector(0, 1), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { AngularVelocity = Math.PI / 2, }, pointMotion = new Motion { InitialPosition = new Vector(Math.Sqrt(2) / 2, -Math.Sqrt(2) / 2), }, pointLeverArm = new Vector(0, 0), impactWindow = new Interval(0, 10), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(3), TOI = 2.5, Transition = PolygonvsPoint.TransitionState.Entering, }, new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(1), TOI = 3.5, Transition = PolygonvsPoint.TransitionState.Exiting, }, new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(3), TOI = 6.5, Transition = PolygonvsPoint.TransitionState.Entering, }, new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(1), TOI = 7.5, Transition = PolygonvsPoint.TransitionState.Exiting, }, }; CheckEqual(expected, hits); } public void AggressiveFunction() { // This exact input caused a problem in the root finder once. var polygon = new SimplePolygon(new Vector[] { new Vector(-3, 3), new Vector( 3, 3), new Vector( 3,-3), new Vector(-3,-3), }); var boxAngularVelocity = Math.PI / (Math.PI - 2 * Math.Acos(3.0 / 5.0)); var firstImpactTime = Math.Acos(3.0 / 5.0) + Math.PI / 2; var input = new CollidingInput { collideeFrame = new RotatingFrame { AngularVelocity = boxAngularVelocity, InitialOrientation = -boxAngularVelocity * firstImpactTime, }, pointMotion = new Motion { InitialPosition = new Vector(7, 0), AngularVelocity = 1, }, pointLeverArm = new Vector(5, 0), impactWindow = new Interval(0, 15), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); // No check; if it doesn't completely crash out it should be fine. // Also, I never thought through what the actual results might look like. } public void PointAngularMotion_CornerHit() { var polygon = new SimplePolygon(new Vector[] { new Vector(-3, 3), new Vector( 3, 3), new Vector( 3,-3), new Vector(-3,-3), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { InitialPosition = new Vector(7, 0), AngularVelocity = 1, }, pointLeverArm = new Vector(5, 0), impactWindow = new Interval(0, 15), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(1), TOI = Math.Acos(3.0/5.0) + Math.PI / 2, Transition = PolygonvsPoint.TransitionState.Entering, }, new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(2), TOI = 3 * Math.PI / 2 - Math.Acos(3.0/5.0), Transition = PolygonvsPoint.TransitionState.Exiting, }, new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(1), TOI = Math.Acos(3.0/5.0) + Math.PI / 2 + 2 * Math.PI, Transition = PolygonvsPoint.TransitionState.Entering, }, new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(2), TOI = 3 * Math.PI / 2 - Math.Acos(3.0/5.0) + 2 * Math.PI, Transition = PolygonvsPoint.TransitionState.Exiting, }, }; CheckEqual(expected, hits); } public void PointAngularMotion_EdgeHit() { var polygon = new SimplePolygon(new Vector[] { new Vector(-3, 6), new Vector( 3, 6), new Vector( 3,-6), new Vector(-3,-6), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { InitialPosition = new Vector(7, 0), AngularVelocity = 1, }, pointLeverArm = new Vector(5, 0), impactWindow = new Interval(0, 15), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { new PolygonvsPoint.Hit { Edge = polygon.GetEdge(1), TOI = Math.Acos(3.0/5.0) + Math.PI / 2, Transition = PolygonvsPoint.TransitionState.Entering, HitFraction = 0.25, }, new PolygonvsPoint.Hit { Edge = polygon.GetEdge(1), TOI = 3 * Math.PI / 2 - Math.Acos(3.0/5.0), Transition = PolygonvsPoint.TransitionState.Exiting, HitFraction = 0.75, }, new PolygonvsPoint.Hit { Edge = polygon.GetEdge(1), TOI = Math.Acos(3.0/5.0) + Math.PI / 2 + 2 * Math.PI, Transition = PolygonvsPoint.TransitionState.Entering, HitFraction = 0.25, }, new PolygonvsPoint.Hit { Edge = polygon.GetEdge(1), TOI = 3 * Math.PI / 2 - Math.Acos(3.0/5.0) + 2 * Math.PI, Transition = PolygonvsPoint.TransitionState.Exiting, HitFraction = 0.75, }, }; CheckEqual(expected, hits); } public void PointAngularMotion_NoHit() { var polygon = new SimplePolygon(new Vector[] { new Vector( 0, 1), new Vector( 1, 0), new Vector( 0,-1), new Vector(-1, 0), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { InitialPosition = new Vector(2.1, 0), AngularVelocity = Math.PI, }, pointLeverArm = new Vector(1, 0), impactWindow = new Interval(0, 4), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { }; CheckEqual(expected, hits); } public void PointAngularMotion_ScrapeCorner_Outside() { var polygon = new SimplePolygon(new Vector[] { new Vector( 0, 1), new Vector( 1, 0), new Vector( 0,-1), new Vector(-1, 0), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { InitialPosition = new Vector(2, 0), AngularVelocity = Math.PI, }, pointLeverArm = new Vector(1, 0), impactWindow = new Interval(0, 4), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(1), TOI = 1, Transition = PolygonvsPoint.TransitionState.FeaturesAligning, }, new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(1), TOI = 3, Transition = PolygonvsPoint.TransitionState.FeaturesAligning, }, }; CheckEqual(expected, hits); } public void PointAngularMotion_ScrapeCorner_Outside_ReverseWinding() { var polygon = new SimplePolygon(new Vector[] { new Vector( 0, 1), new Vector(-1, 0), new Vector( 0,-1), new Vector( 1, 0), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { InitialPosition = new Vector(2, 0), AngularVelocity = Math.PI, }, pointLeverArm = new Vector(1, 0), impactWindow = new Interval(0, 4), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(3), TOI = 1, Transition = PolygonvsPoint.TransitionState.FeaturesAligning, }, new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(3), TOI = 3, Transition = PolygonvsPoint.TransitionState.FeaturesAligning, }, }; CheckEqual(expected, hits); } public void PointAngularMotion_ScrapeCorner_Outside_ReverseSpin() { var polygon = new SimplePolygon(new Vector[] { new Vector( 0, 1), new Vector( 1, 0), new Vector( 0,-1), new Vector(-1, 0), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { InitialPosition = new Vector(2, 0), AngularVelocity = -Math.PI, }, pointLeverArm = new Vector(1, 0), impactWindow = new Interval(0, 4), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(1), TOI = 1, Transition = PolygonvsPoint.TransitionState.FeaturesAligning, }, new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(1), TOI = 3, Transition = PolygonvsPoint.TransitionState.FeaturesAligning, }, }; CheckEqual(expected, hits); } public void PointAngularMotion_ScrapeCorner_Inside() { var polygon = new SimplePolygon(new Vector[] { new Vector(10, 7), new Vector( 0, 7), new Vector( 0, 1), new Vector( 1, 0), new Vector( 0,-1), new Vector( 0,-7), new Vector(10,-7), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { InitialPosition = new Vector(2, 0), AngularVelocity = Math.PI, }, pointLeverArm = new Vector(1, 0), impactWindow = new Interval(0, 4), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(3), TOI = 1, Transition = PolygonvsPoint.TransitionState.FeaturesAligning, }, new PolygonvsPoint.Hit { Vertex = polygon.GetVertex(3), TOI = 3, Transition = PolygonvsPoint.TransitionState.FeaturesAligning, }, }; CheckEqual(expected, hits); } public void PointAngularMotion_HitScrapeCorner() { var polygon = new SimplePolygon(new Vector[] { new Vector( 0, 1), new Vector( 1, 0), new Vector( 0,-1), new Vector(-1, 0), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { InitialPosition = new Vector(2 - 1e-5, 0), AngularVelocity = Math.PI, }, pointLeverArm = new Vector(1, 0), impactWindow = new Interval(0, 1), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { new PolygonvsPoint.Hit { Edge = polygon.GetEdge(0), TOI = 0.9999968169, Transition = PolygonvsPoint.TransitionState.Entering, HitFraction = 0.99999, }, }; CheckEqual(expected, hits); } public void PointAngularMotion_MissScrapeCorner() { var polygon = new SimplePolygon(new Vector[] { new Vector( 0, 1), new Vector( 1, 0), new Vector( 0,-1), new Vector(-1, 0), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { InitialPosition = new Vector(2 + 1e-5, 0), AngularVelocity = Math.PI, }, pointLeverArm = new Vector(1, 0), impactWindow = new Interval(0, 4), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, polygon); var expected = new PolygonvsPoint.Hit[] { }; CheckEqual(expected, hits); } public void ScrapingUp() { var diamond = new SimplePolygon(new Vector[] { new Vector( 0,-1), new Vector( 1, 0), new Vector( 0, 1), new Vector(-1, 0), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { InitialPosition = new Vector(0, -4), LinearVelocity = new Vector(0, 1), }, pointLeverArm = new Vector(-1, 0), impactWindow = new Interval(0, 10), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, diamond); var expected = new PolygonvsPoint.Hit[] { new PolygonvsPoint.Hit { Vertex = diamond.GetVertex(3), Transition = PolygonvsPoint.TransitionState.FeaturesAligning, TOI = 4, }, }; CheckEqual(expected, hits); } public void LinearMotionScrapingSide() { var box = new SimplePolygon(new Vector[] { new Vector(-10, -10), new Vector( 10, -10), new Vector( 10, 10), new Vector(-10, 10), }); var input = new CollidingInput { collideeFrame = new RotatingFrame { }, pointMotion = new Motion { InitialPosition = new Vector(0, BitConverter.Int64BitsToDouble(4630584437618525448)), //38.2813447361205 LinearVelocity = new Vector(0, BitConverter.Int64BitsToDouble(-4570653075098320976)), //-1137.8111389838 }, pointLeverArm = new Vector(-10, -10), impactWindow = new Interval(0, BitConverter.Int64BitsToDouble(4580461061010952465)), maxIterations = 1000, }; var hits = PolygonvsPoint.FindIntersectionsInWindow(input, box); var expected = new PolygonvsPoint.Hit[] { new PolygonvsPoint.Hit { TOI = 0.016067117, Vertex = box.GetVertex(3), Transition = PolygonvsPoint.TransitionState.GlancingParallelEntering, } }; CheckEqual(expected, hits); } } }