using System; using Annulus.CSG; using Azimuth; namespace Annulus.UnitTests.CSG { public class BoundaryVerticesTests : UnitTestSharp.TestFixture { public static BoundaryCut Cut(Vector foot, Vector centre, SpokeEnd spoke = SpokeEnd.Vertex) { return new BoundaryCut { Foot = foot, Centre = centre, Spoke = spoke }; } public static BoundaryVertex Vertex(params int[] cuts) { return new BoundaryVertex { Cuts = cuts }; } public class GroupTests : UnitTestSharp.TestFixture { public void CutsWithTheSameCentre_ShareAVertexOfFeetInTheirOrder() { BoundaryCut[] cuts = new BoundaryCut[] { Cut(new Vector(0, -1), new Vector(0, 0)), Cut(new Vector(5, 5), new Vector(3, 3)), Cut(new Vector(1, 0), new Vector(0, 0)), Cut(new Vector(6, 6), new Vector(3, 3)), }; BoundaryVertex[] vertices = BoundaryVertices.Group(cuts); CheckEqual(2, vertices.Length); CheckEqual(new Vector(0, 0), vertices[0].Centre); CheckEqual(new int[] { 0, 2 }, vertices[0].Cuts); CheckEqual(new Vector(3, 3), vertices[1].Centre); CheckEqual(new int[] { 1, 3 }, vertices[1].Cuts); CheckEqual(0, cuts[2].Vertex); CheckEqual(1, cuts[3].Vertex); } public void CornerAndLeafSpokes_BelongToNoVertex() { BoundaryCut[] cuts = new BoundaryCut[] { Cut(new Vector(0, 0), new Vector(0, 0), SpokeEnd.Corner), Cut(new Vector(0, 1), new Vector(0, 1), SpokeEnd.Leaf), Cut(new Vector(0, -1), new Vector(0, 0)), Cut(new Vector(1, 0), new Vector(0, 0)), }; BoundaryVertex[] vertices = BoundaryVertices.Group(cuts); CheckEqual(1, vertices.Length); CheckEqual(new int[] { 2, 3 }, vertices[0].Cuts); CheckEqual(-1, cuts[0].Vertex); CheckEqual(-1, cuts[1].Vertex); } public void CentresThatDriftApartAlongAChain_ShareAVertex() { BoundaryCut[] cuts = new BoundaryCut[] { Cut(new Vector(0, -1), new Vector(0, 0)), Cut(new Vector(1, 0), new Vector(0.7e-6, 0)), Cut(new Vector(0, 1), new Vector(1.4e-6, 0)), }; CheckEqual(1, BoundaryVertices.Group(cuts).Length); } public void CentresFurtherApartThanTheFuzz_AreSeparateVertices() { BoundaryCut[] cuts = new BoundaryCut[] { Cut(new Vector(0, -1), new Vector(0, 0)), Cut(new Vector(1, 0), new Vector(0, 0)), Cut(new Vector(0, 1), new Vector(1e-3, 0)), Cut(new Vector(-1, 0), new Vector(1e-3, 0)), }; CheckEqual(2, BoundaryVertices.Group(cuts).Length); } public void NoCuts_HasNoVertices() { CheckEqual(0, BoundaryVertices.Group(new BoundaryCut[0]).Length); } public void CutAloneAtItsCentre_Throws() { BoundaryCut[] cuts = new BoundaryCut[] { Cut(new Vector(0, -1), new Vector(0, 0)), Cut(new Vector(1, 0), new Vector(0, 0)), Cut(new Vector(5, 5), new Vector(3, 3)), }; CheckThrow(typeof(InvalidOperationException)); BoundaryVertices.Group(cuts); } } public class CheckInterleavingTests : UnitTestSharp.TestFixture { public void AlternatingFeet_Throws() { BoundaryVertex[] vertices = new BoundaryVertex[] { Vertex(0, 2), Vertex(1, 3) }; CheckThrow(typeof(InvalidOperationException)); BoundaryVertices.CheckInterleaving(vertices); } public void SideBySideFeet_Pass() { BoundaryVertices.CheckInterleaving(new BoundaryVertex[] { Vertex(0, 1), Vertex(2, 3) }); } public void NestedFeet_Pass() { BoundaryVertices.CheckInterleaving(new BoundaryVertex[] { Vertex(0, 3), Vertex(1, 2) }); } public void FeetBothPastTheLastOfTheOther_Pass() { BoundaryVertices.CheckInterleaving(new BoundaryVertex[] { Vertex(0, 2), Vertex(3, 4) }); } public void FeetAllOutsideTheOthersRange_Pass() { BoundaryVertices.CheckInterleaving(new BoundaryVertex[] { Vertex(1, 2), Vertex(0, 3, 4) }); } public void ThreeFootedVertexWithAnotherInOneGap_Passes() { BoundaryVertices.CheckInterleaving(new BoundaryVertex[] { Vertex(0, 4, 8), Vertex(5, 6, 7) }); } public void ThreeFootedVertexWithAnotherSpanningTwoGaps_Throws() { BoundaryVertex[] vertices = new BoundaryVertex[] { Vertex(0, 4, 8), Vertex(3, 5) }; CheckThrow(typeof(InvalidOperationException)); BoundaryVertices.CheckInterleaving(vertices); } } public class CheckCyclicOrderTests : UnitTestSharp.TestFixture { public static BoundaryCut[] Feet(params Vector[] feet) { BoundaryCut[] returnMe = new BoundaryCut[feet.Length]; for (int i = 0; i < feet.Length; ++i) { returnMe[i] = Cut(feet[i], new Vector(0, 0)); } return returnMe; } public void FeetGoingCounterclockwise_Pass() { BoundaryCut[] cuts = Feet(new Vector(1, 0), new Vector(0, 1), new Vector(-1, 0)); BoundaryVertex[] vertices = new BoundaryVertex[] { Vertex(0, 1, 2) }; BoundaryVertices.CheckCyclicOrder(cuts, vertices); } public void FeetGoingClockwise_Throw() { BoundaryCut[] cuts = Feet(new Vector(1, 0), new Vector(0, -1), new Vector(-1, 0), new Vector(0, 1)); BoundaryVertex[] vertices = new BoundaryVertex[] { new BoundaryVertex { Cuts = new int[] { 0, 1, 2, 3 } }, }; CheckThrow(typeof(InvalidOperationException)); BoundaryVertices.CheckCyclicOrder(cuts, vertices); } public void FeetVisitedOutOfOrder_Throw() { BoundaryCut[] cuts = Feet(new Vector(1, 0), new Vector(-1, 0), new Vector(0, 1)); BoundaryVertex[] vertices = new BoundaryVertex[] { Vertex(0, 1, 2) }; CheckThrow(typeof(InvalidOperationException)); BoundaryVertices.CheckCyclicOrder(cuts, vertices); } public void TwoFeetAtDifferentPlaces_Pass() { BoundaryCut[] cuts = Feet(new Vector(1, 0), new Vector(-1, 0)); BoundaryVertex[] vertices = new BoundaryVertex[] { Vertex(0, 1) }; BoundaryVertices.CheckCyclicOrder(cuts, vertices); } public void TwoFeetAtTheSamePlace_Throw() { BoundaryCut[] cuts = Feet(new Vector(1, 0), new Vector(1, 0)); BoundaryVertex[] vertices = new BoundaryVertex[] { Vertex(0, 1) }; CheckThrow(typeof(InvalidOperationException)); BoundaryVertices.CheckCyclicOrder(cuts, vertices); } public void FeetStartingPartWayRoundTheCircle_Pass() { BoundaryCut[] cuts = Feet(new Vector(0, -1), new Vector(-1, 0), new Vector(1, 0)); BoundaryVertex[] vertices = new BoundaryVertex[] { Vertex(0, 2, 1) }; BoundaryVertices.CheckCyclicOrder(cuts, vertices); } } } }