using System;
using System.Collections.Generic;
using System.Linq;
using Azimuth;
using UnitTestSharp;
namespace Blacklight.Bezier.UnitTests
{
public class CubicSampleSetTests : TestFixture
{
public class CtorTests : TestFixture
{
public void AssignsBothFields()
{
var samples = new CubicSample[2];
var set = new CubicSampleSet("Name", samples);
CheckEqual("Name", set.Name);
CheckEqual(samples, set.Samples);
}
}
}
public class CubicSampleSetsTests : TestFixture
{
public const int Draws = 20;
public static readonly CubicPiece Straight = CubicSample.PieceInFrame(new Vector(0.3, 0), new Vector(0.6, 0));
public static bool SameBits(Vector first, Vector second)
{
return first.X.Value == second.X.Value && first.Y.Value == second.Y.Value;
}
///
/// Returns the parameter, on a fine grid, where the piece moves slowest.
///
public static double SlowestParameter(CubicPiece piece)
{
double returnMe = 0;
double slowest = double.PositiveInfinity;
for (int i = 0; i <= 100000; i++)
{
double t = i / 100000.0;
double speed = piece.DerivativeAt(t).Length();
if (speed < slowest)
{
slowest = speed;
returnMe = t;
}
}
return returnMe;
}
public class SizeTests : TestFixture
{
public void SmallPiece_IsOne()
{
CheckEqual(1.0, CubicSampleSets.Size(CubicSample.PieceInFrame(new Vector(0.3, 0.4), Vector.Zero)));
}
public void LargeInnerPoint_IsItsLength()
{
CheckEqual(5.0, CubicSampleSets.Size(CubicSample.PieceInFrame(Vector.Zero, new Vector(3, 4))));
}
}
public class RandomDirectionTests : TestFixture
{
public void IsUnitLength()
{
var random = new Random(1);
for (int i = 0; i < Draws; i++)
{
CheckGreater(1e-15, Math.Abs(CubicSampleSets.RandomDirection(random).Length() - 1));
}
}
}
public class RandomControlPointTests : TestFixture
{
public void StaysInTheBoxAroundTheFrame()
{
var random = new Random(1);
for (int i = 0; i < Draws; i++)
{
Vector point = CubicSampleSets.RandomControlPoint(random);
CheckTrue(point.X.Value >= -1 && point.X.Value <= 2);
CheckTrue(point.Y.Value >= -1.5 && point.Y.Value <= 1.5);
}
}
}
public class DrawTests : TestFixture
{
public void ReturnsTheFirstAcceptedPiece()
{
CubicPiece drawn = CubicSampleSets.Draw(new Random(1), piece => true);
var random = new Random(1);
CheckEqual(CubicSampleSets.RandomControlPoint(random), drawn.P1);
CheckEqual(CubicSampleSets.RandomControlPoint(random), drawn.P2);
}
public void NothingAccepted_Throws()
{
CheckThrow(typeof(InvalidOperationException));
CubicSampleSets.Draw(new Random(1), piece => false);
}
}
public class HasLoopTests : TestFixture
{
public void Loop_IsTrue()
{
CheckTrue(CubicSampleSets.HasLoop(CubicReferenceDistanceTests.Loop));
}
public void Arch_IsFalse()
{
CheckFalse(CubicSampleSets.HasLoop(CubicFeaturesTests.Arch));
}
}
public class ArchTests : TestFixture
{
public void HasNoInflectionOrLoop()
{
var random = new Random(1);
for (int i = 0; i < Draws; i++)
{
CubicPiece piece = CubicSampleSets.Arch(random);
CheckEqual(0, CubicFeatures.Inflections(piece).Count);
CheckFalse(CubicSampleSets.HasLoop(piece));
}
}
}
public class OneInflectionTests : TestFixture
{
public void HasOneInflectionAndNoLoop()
{
var random = new Random(1);
for (int i = 0; i < Draws; i++)
{
CubicPiece piece = CubicSampleSets.OneInflection(random);
CheckEqual(1, CubicFeatures.Inflections(piece).Count);
CheckFalse(CubicSampleSets.HasLoop(piece));
}
}
}
public class TwoInflectionsTests : TestFixture
{
public void HasTwoInflections()
{
var random = new Random(1);
for (int i = 0; i < Draws; i++)
{
CheckEqual(2, CubicFeatures.Inflections(CubicSampleSets.TwoInflections(random)).Count);
}
}
}
public class LoopTests : TestFixture
{
public void CrossesItself()
{
var random = new Random(1);
for (int i = 0; i < Draws; i++)
{
CheckTrue(CubicSampleSets.HasLoop(CubicSampleSets.Loop(random)));
}
}
}
public class CuspPieceTests : TestFixture
{
public void StopsAtTheCusp()
{
CubicPiece piece = CubicSampleSets.CuspPiece(0.3, new Vector(0.5, 1));
CheckGreater(1e-14, piece.DerivativeAt(0.3).Length().Value);
}
public void KeepsTheFrameAndTheFirstInnerPoint()
{
CubicPiece piece = CubicSampleSets.CuspPiece(0.3, new Vector(0.5, 1));
CheckEqual(Vector.Zero, piece.P0);
CheckEqual(new Vector(0.5, 1), piece.P1);
CheckEqual(Vector.UnitX, piece.P3);
}
}
public class CuspTests : TestFixture
{
public void StopsSomewhereInTheMiddle()
{
var random = new Random(1);
for (int i = 0; i < 3; i++)
{
CubicPiece piece = CubicSampleSets.Cusp(random);
double cuspT = SlowestParameter(piece);
CheckTrue(cuspT >= 0.1 && cuspT <= 0.6);
CheckGreater(1e-3 * CubicSampleSets.Size(piece), piece.DerivativeAt(cuspT).Length().Value);
}
}
}
public class NearCuspTests : TestFixture
{
public void IsACuspWithItsSecondInnerPointNudged()
{
for (int seed = 0; seed < Draws; seed++)
{
CubicPiece cusp = CubicSampleSets.Cusp(new Random(seed));
CubicPiece nearCusp = CubicSampleSets.NearCusp(new Random(seed));
double nudge = (nearCusp.P2 - cusp.P2).Length();
CheckEqual(cusp.P1, nearCusp.P1);
CheckTrue(nudge >= 1e-9 && nudge <= 1e-2);
}
}
}
public class NearQuadraticTests : TestFixture
{
public void HasASmallButNonzeroCubicTerm()
{
var random = new Random(1);
for (int i = 0; i < Draws; i++)
{
CubicFeatures.PowerBasis(CubicSampleSets.NearQuadratic(random), out Vector cubic, out _, out _);
CheckTrue(cubic.Length() > 0 && cubic.Length() <= 6e-2);
}
}
}
public class NearLineTests : TestFixture
{
public void InnerPointsSitJustOffTheChord()
{
var random = new Random(1);
for (int i = 0; i < Draws; i++)
{
CubicPiece piece = CubicSampleSets.NearLine(random);
CheckTrue(Math.Abs(piece.P1.Y) >= 1e-9 && Math.Abs(piece.P1.Y) <= 1e-2);
CheckTrue(Math.Abs(piece.P2.Y) >= 1e-9 && Math.Abs(piece.P2.Y) <= 1e-2);
}
}
}
public class NearlyClosedTests : TestFixture
{
public void InnerPointsDwarfTheChord()
{
var random = new Random(1);
for (int i = 0; i < Draws; i++)
{
CubicPiece piece = CubicSampleSets.NearlyClosed(random);
CheckTrue(piece.P1.Length() >= 10 && piece.P2.Length() >= 10);
}
}
}
public class AlongNormalTests : TestFixture
{
public void IsThatFarFromTheCurveWithItsFootThere()
{
CubicPiece piece = CubicPieceTests.Wave;
CubicReferenceResult result = CubicReferenceDistance.Solve(piece,
CubicSampleSets.AlongNormal(piece, t: 0.3, offset: 1e-3));
CheckGreater(1e-15, Math.Abs(result.Distance - 1e-3));
CheckGreater(1e-9, Math.Abs(result.ClosestT - 0.3));
}
public void PositiveOffset_IsToTheLeftOfTravel()
{
Vector pixel = CubicSampleSets.AlongNormal(CubicFeaturesTests.UpwardParabola, t: 0.5, offset: 2);
CheckGreater(1e-14, (pixel - new Vector(0, 2)).Length().Value);
}
}
public class NearCurveTests : TestFixture
{
public void IsNoFartherThanThePiecesSize()
{
var random = new Random(1);
CubicPiece piece = CubicPieceTests.Wave;
for (int i = 0; i < Draws; i++)
{
double distance = CubicReferenceDistance.Solve(piece, CubicSampleSets.NearCurve(random, piece))
.Distance;
CheckTrue(distance <= CubicSampleSets.Size(piece));
}
}
}
public class NearEvoluteTests : TestFixture
{
public void LandsJustOffTheCentreOfCurvatureAtItsDrawnParameter()
{
CubicPiece piece = CubicPieceTests.Wave;
for (int seed = 0; seed < Draws; seed++)
{
Vector pixel = CubicSampleSets.NearEvolute(new Random(seed), piece);
double t = SampleSets.Uniform(new Random(seed), 0, 1);
Vector centre = CubicFeatures.CentreOfCurvature(piece, t);
double radius = (centre - piece.PointAt(t)).Length();
CheckGreater(1e-2 * radius, (pixel - centre).Length().Value);
}
}
public void Straight_Throws()
{
CheckThrow(typeof(InvalidOperationException));
CubicSampleSets.NearEvolute(new Random(1), Straight);
}
}
public class NearEndsTests : TestFixture
{
public void IsWithinThePiecesSizeOfAnEnd()
{
var random = new Random(1);
CubicPiece piece = CubicPieceTests.Wave;
for (int i = 0; i < Draws; i++)
{
Vector pixel = CubicSampleSets.NearEnds(random, piece);
double nearerEnd = Math.Min((pixel - piece.P0).Length(), (pixel - piece.P3).Length());
CheckTrue(nearerEnd <= CubicSampleSets.Size(piece));
}
}
public void VisitsBothEnds()
{
var random = new Random(1);
CubicPiece piece = CubicPieceTests.Wave;
int nearStart = 0;
for (int i = 0; i < Draws; i++)
{
Vector pixel = CubicSampleSets.NearEnds(random, piece);
if ((pixel - piece.P0).Length() < (pixel - piece.P3).Length())
{
nearStart++;
}
}
CheckTrue(nearStart > 0 && nearStart < Draws);
}
}
public class WholePlaneTests : TestFixture
{
public void SpreadsFromNearTheMiddleToFarAway()
{
var random = new Random(1);
CubicPiece piece = CubicPieceTests.Wave;
double size = CubicSampleSets.Size(piece);
for (int i = 0; i < Draws; i++)
{
double radius = (CubicSampleSets.WholePlane(random, piece) - piece.PointAt(0.5)).Length();
CheckTrue(radius >= 1e-4 * size * (1 - 1e-12) && radius <= 1e6 * size * (1 + 1e-12));
}
}
}
public class IsLocalMinimumTests : TestFixture
{
public void StartTheCurveLeavesFrom_IsOne()
{
CheckTrue(CubicSampleSets.IsLocalMinimum(Straight, new Vector(-1, 1), t: 0));
}
public void StartTheCurveHeadsToward_IsNot()
{
CheckFalse(CubicSampleSets.IsLocalMinimum(Straight, new Vector(0.5, 1), t: 0));
}
public void EndTheCurveArrivesAwayFrom_IsOne()
{
CheckTrue(CubicSampleSets.IsLocalMinimum(Straight, new Vector(2, 1), t: 1));
}
public void EndTheCurveArrivesFrom_IsNot()
{
CheckFalse(CubicSampleSets.IsLocalMinimum(Straight, new Vector(0.5, 1), t: 1));
}
public void Interior_IsOne()
{
CheckTrue(CubicSampleSets.IsLocalMinimum(Straight, new Vector(0.5, 1), t: 0.5));
}
}
public class OwnershipBoundaryTests : TestFixture
{
public void TwoBranchesTieThere()
{
var random = new Random(1);
CubicPiece piece = CubicReferenceDistanceTests.Loop;
for (int i = 0; i < Draws; i++)
{
Vector pixel = CubicSampleSets.OwnershipBoundary(random, piece, CubicSampleSets.AttemptLimit);
var parameters = new List { 0, 1 };
CubicReferenceDistance.FootCandidates(CubicReferenceDistance.FootCoefficients(piece, pixel),
parameters, CubicReferenceDistance.SmallestInterval, CubicReferenceDistance.IntervalBudget);
double closest = parameters.Min(t => CubicReferenceDistance.SquaredDistanceAt(piece, pixel, t));
List tied = parameters
.Where(t => CubicReferenceDistance.SquaredDistanceAt(piece, pixel, t) <= closest * (1 + 1e-6))
.ToList();
CheckGreater(tied.Max() - tied.Min(), CubicSampleSets.BranchGap);
}
}
public void NoCompetingBranches_Throws()
{
CheckThrow(typeof(InvalidOperationException));
CubicSampleSets.OwnershipBoundary(new Random(1), Straight, attemptLimit: 10);
}
}
public class NearCuspPointTests : TestFixture
{
public void IsCloseToTheCusp()
{
var random = new Random(1);
for (int i = 0; i < 3; i++)
{
CubicSample sample = CubicSampleSets.NearCuspPoint(random);
CubicPiece piece = sample.ToPiece();
double cuspT = SlowestParameter(piece);
double size = CubicSampleSets.Size(piece);
CheckGreater(1e-3 * size, piece.DerivativeAt(cuspT).Length().Value);
CheckGreater(0.1 * size + 1e-3, (sample.Pixel - piece.PointAt(cuspT)).Length().Value);
}
}
}
public class NearDoublePointTests : TestFixture
{
public void IsCloseToWhereTheLoopCrosses()
{
var random = new Random(1);
for (int i = 0; i < Draws; i++)
{
CubicSample sample = CubicSampleSets.NearDoublePoint(random);
CubicPiece piece = sample.ToPiece();
CheckTrue(CubicFeatures.SelfIntersection(piece, out double first, out _));
CheckTrue((sample.Pixel - piece.PointAt(first)).Length() <= 0.1 * CubicSampleSets.Size(piece));
}
}
}
public class CreateTests : TestFixture
{
public void OneSetPerShapeAndPixelPlusTheFeatureSets()
{
List sets = CubicSampleSets.Create(samplesPerSet: 2, seed: 1);
CheckEqual(9 * 5 + 2, sets.Count);
CheckEqual(sets.Count, sets.Select(set => set.Name).Distinct().Count());
CheckTrue(sets.All(set => set.Samples.Length == 2));
}
public void SameSeed_GivesTheSameSamples()
{
List first = CubicSampleSets.Create(samplesPerSet: 2, seed: 1);
List second = CubicSampleSets.Create(samplesPerSet: 2, seed: 1);
for (int setIndex = 0; setIndex < first.Count; setIndex++)
{
for (int i = 0; i < 2; i++)
{
CubicSample expected = first[setIndex].Samples[i];
CubicSample actual = second[setIndex].Samples[i];
CheckTrue(SameBits(expected.Pixel, actual.Pixel) && SameBits(expected.P1, actual.P1)
&& SameBits(expected.P2, actual.P2));
}
}
}
}
}
}