using System; using Annulus.CSG; using Azimuth; namespace Annulus.UnitTests.CSG { public class BoundaryRegionsTests : UnitTestSharp.TestFixture { public static BoundaryInterval Interval(int own, int across) { return new BoundaryInterval { Own = own, Across = across, Region = -1 }; } public static BoundaryCut[] Cuts(int count, SpokeEnd spoke = SpokeEnd.Vertex) { BoundaryCut[] returnMe = new BoundaryCut[count]; for (int i = 0; i < count; ++i) { returnMe[i].Spoke = spoke; } return returnMe; } public static BoundaryVertex Vertex(params int[] cuts) { return new BoundaryVertex { Cuts = cuts }; } public class BuildTests : UnitTestSharp.TestFixture { public void ConsecutiveIntervalsWithTheSamePair_ShareARegionAndTheirCutIsNotLive() { BoundaryInterval[] intervals = { Interval(0, 1), Interval(1, 0), Interval(2, 3) }; BoundaryCut[] cuts = Cuts(3); BoundaryRegions.Build(cuts, new BoundaryVertex[0], intervals); CheckEqual(intervals[0].Region, intervals[1].Region); CheckFalse(cuts[1].IsLive); } public void ConsecutiveIntervalsWithDifferentPairs_AreSeparateRegionsDividedByALiveCut() { BoundaryInterval[] intervals = { Interval(0, 1), Interval(0, 2), Interval(0, 3) }; BoundaryCut[] cuts = Cuts(3); BoundaryRegions.Build(cuts, new BoundaryVertex[0], intervals); CheckEqual(0, intervals[0].Region); CheckEqual(1, intervals[1].Region); CheckEqual(2, intervals[2].Region); CheckTrue(cuts[1].IsLive); CheckTrue(cuts[2].IsLive); } public void LastIntervalWithTheFirstsPair_ShareARegionAcrossTheLoopsSeam() { BoundaryInterval[] intervals = { Interval(0, 1), Interval(0, 2), Interval(1, 0) }; BoundaryCut[] cuts = Cuts(3); BoundaryRegions.Build(cuts, new BoundaryVertex[0], intervals); CheckEqual(intervals[0].Region, intervals[2].Region); CheckFalse(cuts[0].IsLive); } public void VertexWedge_JoinsTheIntervalAfterOneFootToTheOneBeforeTheNext() { BoundaryInterval[] intervals = { Interval(0, 1), Interval(0, 2), Interval(1, 0), Interval(2, 3) }; BoundaryCut[] cuts = Cuts(4); BoundaryRegions.Build(cuts, new BoundaryVertex[] { Vertex(0, 3) }, intervals); CheckEqual(intervals[0].Region, intervals[2].Region); CheckFalse(intervals[1].Region == intervals[0].Region); } public void VertexWedgeWhoseSidesDiffer_Throws() { BoundaryInterval[] intervals = { Interval(0, 1), Interval(2, 3), Interval(0, 1), Interval(2, 3) }; CheckThrow(typeof(InvalidOperationException)); BoundaryRegions.Build(Cuts(4), new BoundaryVertex[] { Vertex(0, 2) }, intervals); } public void LiveCutWithoutAMedialVertex_Throws() { BoundaryInterval[] intervals = { Interval(0, 1), Interval(2, 3) }; CheckThrow(typeof(InvalidOperationException)); BoundaryRegions.Build(Cuts(2, SpokeEnd.Corner), new BoundaryVertex[0], intervals); } public void CornerCutBetweenTheSamePair_IsNotLive() { BoundaryInterval[] intervals = { Interval(0, 1), Interval(1, 0) }; BoundaryCut[] cuts = Cuts(2, SpokeEnd.Corner); BoundaryRegions.Build(cuts, new BoundaryVertex[0], intervals); CheckFalse(cuts[0].IsLive); CheckFalse(cuts[1].IsLive); } public void Candidates_AreOrderedLowerFirst() { BoundaryInterval[] intervals = { Interval(5, 2) }; BoundaryRegion[] regions = BoundaryRegions.Build(Cuts(1), new BoundaryVertex[0], intervals); CheckEqual(2, regions[0].First); CheckEqual(5, regions[0].Second); } public void SinglePieceRegion_HasThatPieceForBothCandidates() { BoundaryInterval[] intervals = { Interval(3, 3) }; BoundaryRegion[] regions = BoundaryRegions.Build(Cuts(1), new BoundaryVertex[0], intervals); CheckEqual(3, regions[0].First); CheckEqual(3, regions[0].Second); } public void Regions_AreNumberedByTheirFirstIntervalEvenWhenAVertexJoinsALaterOne() { BoundaryInterval[] intervals = { Interval(0, 1), Interval(0, 2), Interval(1, 0), Interval(2, 3) }; BoundaryRegion[] regions = BoundaryRegions.Build(Cuts(4), new BoundaryVertex[] { Vertex(0, 3) }, intervals); CheckEqual(3, regions.Length); CheckEqual(0, intervals[0].Region); CheckEqual(1, intervals[1].Region); CheckEqual(0, intervals[2].Region); CheckEqual(2, intervals[3].Region); } } public class ShapeTests : UnitTestSharp.TestFixture { public void Square_HasOneRegionPerCornerPairLiveOnlyAlongTheSpokes() { BoundaryLabelling labelling = new BoundaryLabelling(BoundaryLabellingTests.Rectangle(1, 1)); CheckEqual(4, labelling.Regions.Length); CheckEqual(0, labelling.Regions[0].First); CheckEqual(3, labelling.Regions[0].Second); CheckEqual(0, labelling.Regions[1].First); CheckEqual(1, labelling.Regions[1].Second); CheckEqual(1, labelling.Regions[2].First); CheckEqual(2, labelling.Regions[2].Second); CheckEqual(2, labelling.Regions[3].First); CheckEqual(3, labelling.Regions[3].Second); int[] expected = { 0, 1, 1, 2, 2, 3, 3, 0 }; for (int i = 0; i < 8; ++i) { CheckEqual(expected[i], labelling.Intervals[i].Region); } bool[] live = { false, true, false, true, false, true, false, true }; for (int i = 0; i < 8; ++i) { CheckEqual(live[i], labelling.Cuts[i].IsLive); } } public void LShape_JoinsTheFloorIntervalsOnEitherSideOfTheJointsResolvedLabel() { BoundaryLabelling labelling = new BoundaryLabelling(BoundaryLabellingTests.LShape()); CheckEqual(labelling.Intervals[1].Region, labelling.Intervals[2].Region); CheckFalse(labelling.Cuts[2].IsLive); } public void LShape_JoinsEachFanIntervalToTheNeighbourItMatches() { BoundaryLabelling labelling = new BoundaryLabelling(BoundaryLabellingTests.LShape()); CheckEqual(labelling.Intervals[7].Region, labelling.Intervals[8].Region); CheckEqual(labelling.Intervals[9].Region, labelling.Intervals[10].Region); CheckTrue(labelling.Cuts[9].IsLive); } public void LShape_HasSevenRegions() { BoundaryLabelling labelling = new BoundaryLabelling(BoundaryLabellingTests.LShape()); CheckEqual(7, labelling.Regions.Length); CheckEqual(0, labelling.Regions[0].First); CheckEqual(5, labelling.Regions[0].Second); CheckEqual(3, labelling.Regions[4].First); CheckEqual(5, labelling.Regions[4].Second); } public void TightLens_LeavesItsSingleCandidateWedgesAsRegionsOfTheirOwn() { BoundaryLabelling labelling = new BoundaryLabelling(DiskQueryTests.Lens(bulge: 2)); BoundaryRegion[] regions = BoundaryRegions.Build(labelling.Cuts, labelling.Vertices, labelling.Intervals); CheckEqual(4, regions.Length); CheckEqual(0, regions[1].First); CheckEqual(0, regions[1].Second); CheckEqual(1, regions[3].First); CheckEqual(1, regions[3].Second); } public void ShallowLens_IsOneRegion() { BoundaryLabelling labelling = new BoundaryLabelling(DiskQueryTests.Lens(bulge: 1)); CheckEqual(1, labelling.Regions.Length); CheckEqual(0, labelling.Regions[0].First); CheckEqual(1, labelling.Regions[0].Second); } public void CircleArcs_AreOneRegionEach() { BoundaryLabelling labelling = new BoundaryLabelling(BoundaryLabellingTests.CircleArcs(4)); CheckEqual(4, labelling.Regions.Length); for (int i = 0; i < 4; ++i) { CheckEqual(i, labelling.Regions[i].First); CheckEqual(i, labelling.Regions[i].Second); CheckTrue(labelling.Cuts[i].IsLive); } } } } }