using System; using System.Collections.Generic; using Annulus.BezierCurves; using Annulus.CSG; using Azimuth; namespace Annulus.UnitTests.CSG { public class DiskQueryTests : UnitTestSharp.TestFixture { public static QuadraticBezierCurve Piece(Vector start, Vector control, Vector end) { return new QuadraticBezierCurve { startPoint = start, controlPoint = control, endPoint = end }; } public static QuadraticBezierCurve Line(Vector start, Vector end) { return Piece(start, Vector.Lerp(start, end, 0.5), end); } public static QuadraticBezierCurve[] Lens(Scalar bulge) { return new QuadraticBezierCurve[] { Piece(new Vector(-1, 0), new Vector(0, -bulge), new Vector(1, 0)), Piece(new Vector(1, 0), new Vector(0, bulge), new Vector(-1, 0)), }; } public class StopTests : UnitTestSharp.TestFixture { public void OpposingPieceCloserThanTheCurvatureRadius_StopsAtTheOpposingPiece() { DiskStop stop = DiskQuery.Stop(Lens(bulge: 1), point: new Vector(0, -0.5), inward: new Vector(0, 1), ownPiece: 0, ownParameter: 0.5); CheckEqual((Scalar)0.5, stop.Radius); CheckEqual(1, stop.AcrossPiece); CheckEqual((Scalar)0.5, stop.AcrossParameter); CheckEqual(new Vector(0, 0), stop.Centre); CheckFalse(stop.IsCurvatureLimit); } public void CurvatureRadiusSmallerThanTheOpposingDistance_StopsAtTheCurvatureLimit() { DiskStop stop = DiskQuery.Stop(Lens(bulge: 2), point: new Vector(0, -1), inward: new Vector(0, 1), ownPiece: 0, ownParameter: 0.5); CheckEqual((Scalar)0.5, stop.Radius); CheckEqual(0, stop.AcrossPiece); CheckEqual((Scalar)0.5, stop.AcrossParameter); CheckEqual(new Vector(0, -0.5), stop.Centre); CheckTrue(stop.IsCurvatureLimit); } public void PointOffTheVertexWhereTheOwnPieceCurvesTighter_StopsAtTheOwnPieceFarSide() { Scalar side = Math.Sqrt(0.5); DiskStop stop = DiskQuery.Stop(Lens(bulge: 2), point: new Vector(0.5, -0.75), inward: new Vector(-side, side), ownPiece: 0, ownParameter: 0.75); CheckEqual(side, stop.Radius); CheckEqual(0, stop.AcrossPiece); CheckEqual((Scalar)0.25, stop.AcrossParameter); CheckEqual(new Vector(0, -0.25), stop.Centre); CheckFalse(stop.IsCurvatureLimit); } public void SmoothJointOnItsSharedNormal_StopsAtTheCurvatureRadiusOfTheCurvedPiece() { QuadraticBezierCurve[] pieces = new QuadraticBezierCurve[] { Line(new Vector(0, 0), new Vector(4, 0)), Piece(new Vector(4, 0), new Vector(6, 0), new Vector(8, 8)), Line(new Vector(8, 8), new Vector(0, 8)), Line(new Vector(0, 8), new Vector(0, 0)), }; DiskStop stop = DiskQuery.Stop(pieces, point: new Vector(4, 0), inward: new Vector(0, 1), ownPiece: 0, ownParameter: 1); CheckEqual((Scalar)1, stop.Radius); CheckEqual(1, stop.AcrossPiece); CheckEqual((Scalar)0, stop.AcrossParameter); CheckEqual(new Vector(4, 1), stop.Centre); CheckTrue(stop.IsCurvatureLimit); } public void CurvatureFallingAwayAfterTheJoint_StopsAtTheCurvatureRadius() { QuadraticBezierCurve[] pieces = new QuadraticBezierCurve[] { Line(new Vector(0, 0), new Vector(4, 0)), Piece(new Vector(4, 0), new Vector(6, 0), new Vector(9, 8)), Line(new Vector(9, 8), new Vector(0, 8)), Line(new Vector(0, 8), new Vector(0, 0)), }; DiskStop stop = DiskQuery.Stop(pieces, point: new Vector(4, 0), inward: new Vector(0, 1), ownPiece: 0, ownParameter: 1); CheckEqual((Scalar)1, stop.Radius); CheckEqual(1, stop.AcrossPiece); CheckEqual((Scalar)0, stop.AcrossParameter); CheckEqual(new Vector(4, 1), stop.Centre); CheckTrue(stop.IsCurvatureLimit); } public void CurvatureFallingAwayBeforeTheJoint_StopsAtTheCurvatureRadius() { QuadraticBezierCurve[] pieces = new QuadraticBezierCurve[] { Line(new Vector(0, 0), new Vector(0, 8)), Line(new Vector(0, 8), new Vector(-9, 8)), Piece(new Vector(-9, 8), new Vector(-6, 0), new Vector(-4, 0)), Line(new Vector(-4, 0), new Vector(0, 0)), }; DiskStop stop = DiskQuery.Stop(pieces, point: new Vector(-4, 0), inward: new Vector(0, 1), ownPiece: 2, ownParameter: 1); CheckEqual((Scalar)1, stop.Radius); CheckEqual(2, stop.AcrossPiece); CheckEqual((Scalar)1, stop.AcrossParameter); CheckEqual(new Vector(-4, 1), stop.Centre); CheckTrue(stop.IsCurvatureLimit); } public void ConvexCornerEndingTheOwnPiece_StopsAtZeroRadius() { DiskStop stop = DiskQuery.Stop(Lens(bulge: 1), point: new Vector(1, 0), inward: new Vector(-1, 0), ownPiece: 0, ownParameter: 1); CheckEqual((Scalar)0, stop.Radius); CheckEqual(0, stop.AcrossPiece); CheckEqual((Scalar)1, stop.AcrossParameter); CheckEqual(new Vector(1, 0), stop.Centre); CheckFalse(stop.IsCurvatureLimit); } public void ConvexCornerStartingTheOwnPiece_StopsAtZeroRadius() { DiskStop stop = DiskQuery.Stop(Lens(bulge: 1), point: new Vector(-1, 0), inward: new Vector(1, 0), ownPiece: 0, ownParameter: 0); CheckEqual((Scalar)0, stop.Radius); CheckEqual(0, stop.AcrossPiece); CheckEqual((Scalar)0, stop.AcrossParameter); CheckEqual(new Vector(-1, 0), stop.Centre); CheckFalse(stop.IsCurvatureLimit); } public void ReflexFanDirection_StopsAtTheDiskTangentToTheFarWall() { QuadraticBezierCurve[] pieces = new QuadraticBezierCurve[] { Line(new Vector(-1, 0), new Vector(4, 0)), Line(new Vector(4, 0), new Vector(4, 2)), Line(new Vector(4, 2), new Vector(2, 2)), Line(new Vector(2, 2), new Vector(2, 6)), Line(new Vector(2, 6), new Vector(-1, 6)), Line(new Vector(-1, 6), new Vector(-1, 0)), }; Scalar side = Math.Sqrt(0.5); Scalar radius = 4 - 2 * Math.Sqrt(2); DiskStop stop = DiskQuery.Stop(pieces, point: new Vector(2, 2), inward: new Vector(-side, -side), ownPiece: 2, ownParameter: 1); CheckEqual(radius, stop.Radius); CheckEqual(0, stop.AcrossPiece); CheckEqual((Scalar)((5 - 2 * Math.Sqrt(2)) / 5), stop.AcrossParameter); CheckEqual(new Vector(radius, radius), stop.Centre); CheckFalse(stop.IsCurvatureLimit); } public void CurvedPieceLeaningAwayFromTheRay_StopsAtItsInteriorMinimum() { QuadraticBezierCurve[] pieces = new QuadraticBezierCurve[] { Line(new Vector(-6, -6), new Vector(0, 0)), Piece(new Vector(0, 0), new Vector(1.5, 0), new Vector(3, 4)), Line(new Vector(3, 4), new Vector(3, 100)), Line(new Vector(3, 100), new Vector(-100, 100)), Line(new Vector(-100, 100), new Vector(-6, -6)), }; Scalar radius = 65.0 / 24; DiskStop stop = DiskQuery.Stop(pieces, point: new Vector(0, 0), inward: new Vector(-16.0 / 65, 63.0 / 65), ownPiece: 0, ownParameter: 1); CheckEqual(radius, stop.Radius); CheckEqual(1, stop.AcrossPiece); CheckEqual((Scalar)0.5, stop.AcrossParameter); CheckEqual(new Vector(-2.0 / 3, 21.0 / 8), stop.Centre); CheckFalse(stop.IsCurvatureLimit); } public void MinimumAtTheJointBetweenTwoOtherPieces_ReportsTheEarlierPiece() { QuadraticBezierCurve[] pieces = new QuadraticBezierCurve[] { Line(new Vector(-1, 0), new Vector(4, 0)), Line(new Vector(4, 0), new Vector(4, 2)), Line(new Vector(4, 2), new Vector(2, 2)), Line(new Vector(2, 2), new Vector(2, 6)), Line(new Vector(2, 6), new Vector(-1, 6)), Line(new Vector(-1, 6), new Vector(-1, 0)), }; DiskStop stop = DiskQuery.Stop(pieces, point: new Vector(1, 0), inward: new Vector(0, 1), ownPiece: 0, ownParameter: 0.4); CheckEqual((Scalar)1.25, stop.Radius); CheckEqual(2, stop.AcrossPiece); CheckEqual((Scalar)1, stop.AcrossParameter); CheckEqual(new Vector(1, 1.25), stop.Centre); CheckFalse(stop.IsCurvatureLimit); } public void RayLeavingTheShape_Throws() { CheckThrow(typeof(InvalidOperationException)); DiskQuery.Stop(Lens(bulge: 1), point: new Vector(0, -0.5), inward: new Vector(0, -1), ownPiece: 0, ownParameter: 0.5); } public void SinglePiece_Throws() { CheckThrow(typeof(ArgumentException)); DiskQuery.Stop(new QuadraticBezierCurve[] { Lens(bulge: 1)[0] }, point: new Vector(0, -0.5), inward: new Vector(0, 1), ownPiece: 0, ownParameter: 0.5); } public void ZeroTangentAtTheQueryPoint_Throws() { QuadraticBezierCurve[] pieces = new QuadraticBezierCurve[] { Piece(new Vector(0, 0), new Vector(0, 0), new Vector(1, 0)), Line(new Vector(1, 0), new Vector(0, 0)), }; CheckThrow(typeof(ArgumentException)); DiskQuery.Stop(pieces, point: new Vector(0, 0), inward: new Vector(0, 1), ownPiece: 0, ownParameter: 0); } } } }