using System; using UnitTestSharp; namespace Blacklight.Bezier.UnitTests { public class PlacedPieceTests : TestFixture { public static readonly PlacedPiece Piece = new PlacedPiece(3, -2, 0.7, 2.5, -1, 1.5); public class ToPixelTests : TestFixture { public void Vertex_IsTheVertexPosition() { Piece.ToPixel(0, out double x, out double y); CheckEqual(3.0, x); CheckEqual(-2.0, y); } public void Point_IsItsRadiusTimesFartherAtDoubleTheRadius() { var doubled = new PlacedPiece(3, -2, 0.7, 5, -1, 1.5); Piece.ToPixel(1, out double x, out double y); doubled.ToPixel(1, out double doubledX, out double doubledY); CheckLess(Math.Abs(2 * (x - 3) - (doubledX - 3)), 1e-12); CheckLess(Math.Abs(2 * (y + 2) - (doubledY + 2)), 1e-12); } } public class ToSampleTests : TestFixture { public void CurvePoint_MapsBackOntoTheCurve() { Piece.ToPixel(1.2, out double x, out double y); ShellSample sample = Piece.ToSample(x, y); CheckLess(Math.Abs(1.2 - sample.X), 1e-12); CheckLess(Math.Abs(sample.Phi), 1e-12); CheckEqual(1.5, sample.End); } public void PixelDistance_IsTheCanonicalDistanceTimesTheRadius() { ShellSample sample = Piece.ToSample(4, 1); double pixelDistance = ReferenceDistance.Solve(sample).Distance * Piece.Radius; double brute = double.PositiveInfinity; for (int i = 0; i <= 100000; i++) { Piece.ToPixel(Piece.End * i / 100000, out double x, out double y); brute = Math.Min(brute, Math.Sqrt((x - 4) * (x - 4) + (y - 1) * (y - 1))); } CheckLess(Math.Abs(brute - pixelDistance), 1e-6); } } public class TangentTests : TestFixture { public void PointsAlongTheCurve() { Piece.Tangent(1, out double directionX, out double directionY); Piece.ToPixel(1, out double x, out double y); Piece.ToPixel(1 + 1e-6, out double nextX, out double nextY); double length = Math.Sqrt((nextX - x) * (nextX - x) + (nextY - y) * (nextY - y)); CheckLess(Math.Abs(directionX - (nextX - x) / length), 1e-5); CheckLess(Math.Abs(directionY - (nextY - y) / length), 1e-5); } } } public class TwoCurveSetsTests : TestFixture { public class CornerTests : TestFixture { public void PiecesShareTheirEnds() { var random = new Random(4); for (int i = 0; i < 50; i++) { TwoCurveSets.Corner(random, out PlacedPiece first, out PlacedPiece second); first.ToPixel(first.End, out double firstX, out double firstY); second.ToPixel(second.End, out double secondX, out double secondY); CheckLess(Math.Abs(firstX - secondX) + Math.Abs(firstY - secondY), 1e-9); } } public void TurnsByAtMostAHundredAndFiftyDegrees() { var random = new Random(4); for (int i = 0; i < 50; i++) { TwoCurveSets.Corner(random, out PlacedPiece first, out PlacedPiece second); first.Tangent(first.End, out double inX, out double inY); second.Tangent(second.End, out double backX, out double backY); double cosine = -(inX * backX + inY * backY); CheckTrue(cosine >= Math.Cos(150 * Math.PI / 180) - 1e-9); } } } public class NeckTests : TestFixture { public void PiecesAreTheGapApart() { var random = new Random(4); for (int i = 0; i < 10; i++) { TwoCurveSets.Neck(random, out PlacedPiece first, out PlacedPiece second, out double gap); CheckLess(Math.Abs(gap - TwoCurveSets.PieceDistance(first, second, points: 64)), 1e-6 * gap); } } } public class NearEdgeTests : TestFixture { public void ThicknessIsNearTheNearerPiece() { TwoCurveSet set = TwoCurveSets.NearEdge("Name", TwoCurveSets.CornerPlacer, count: 256, seed: 4); CheckEqual("Name", set.Name); CheckEqual(256, set.Count); for (int i = 0; i < set.Count; i++) { double nearestRatio = Math.Min(set.FirstReferences[i].Distance / set.FirstThickness[i], set.SecondReferences[i].Distance / set.SecondThickness[i]); CheckTrue(nearestRatio >= 1 / 1.1 * (1 - 1e-9) && nearestRatio <= 1 / 0.9 * (1 + 1e-9)); } } } public class RenderedTests : TestFixture { public void PixelsInOneTile_ShareTheirPieces() { TwoCurveSet set = TwoCurveSets.Rendered("Name", TwoCurveSets.NeckPlacer, side: 64, seed: 4); int last = PieceShellSets.TileSide - 1; CheckEqual(64 * 64, set.Count); CheckEqual(set.First[0].End, set.First[last * 64 + last].End); CheckEqual(set.Second[0].End, set.Second[last * 64 + last].End); CheckNotEqual(set.First[0].End, set.First[PieceShellSets.TileSide].End); } } public class IsInsideTests : TestFixture { public static TwoCurveSet SetWith(double firstDistance, double secondDistance) { var sample = new ShellSample(1, 1, 0.5, 2); return new TwoCurveSet("Name", new[] { sample }, new[] { sample }, new[] { 1.0 }, new[] { 1.0 }, new[] { new ReferenceResult(firstDistance, 1) }, new[] { new ReferenceResult(secondDistance, 1) }); } public void WithinEitherThickness_IsInside() { CheckTrue(SetWith(0.5, 2).IsInside(0)); CheckTrue(SetWith(2, 0.5).IsInside(0)); } public void BeyondBoth_IsOutside() { CheckFalse(SetWith(2, 3).IsInside(0)); } } public class MeasureTests : TestFixture { public void RightAnswers_AreNoMisses() { TwoCurveSet set = IsInsideTests.SetWith(0.5, 2); CheckEqual(0, TwoCurveSets.Measure(new[] { 1f }, set).Misclassified); } public void InsideCalledOutside_ScoresTheMostCertainNearPiece() { TwoCurveSet set = IsInsideTests.SetWith(0.5, 0.9); double clearer = TwoCurveSets.Margin(set.First[0], set.FirstReferences[0], 1.0); ShellStatistics result = TwoCurveSets.Measure(new[] { 0f }, set); CheckEqual(1, result.Misclassified); CheckEqual(clearer, result.WorstSeverity); } public void OutsideCalledInside_ScoresTheLeastCertainPiece() { TwoCurveSet set = IsInsideTests.SetWith(1.1, 3); double closer = TwoCurveSets.Margin(set.First[0], set.FirstReferences[0], 1.0); ShellStatistics result = TwoCurveSets.Measure(new[] { 1f }, set); CheckEqual(closer, result.WorstSeverity); } } public class FatLineSkipsTests : TestFixture { public void CountsEachPieceTheFatLineRulesOut() { ShellSample farAbove = ShellSample.FromPosition(0, 5, 2); ShellSample onCurve = ShellSample.FromPosition(1, 0.5, 2); var set = new TwoCurveSet("Name", new[] { farAbove, onCurve }, new[] { farAbove, farAbove }, new[] { 1.0, 1.0 }, new[] { 1.0, 4.0 }, new ReferenceResult[2], new ReferenceResult[2]); CheckEqual(2, TwoCurveSets.FatLineSkips(set)); } } } }