/// <summary> /// Adds a new polygon ONLY if there is a unifications. /// Assumes current set is distinct. /// </summary> /// <param name="pPoly">The polygon to add if there is a union.</param> public bool AddIfUnify(C2DHoledPolyBase pPoly) { var TempSet = new C2DHoledPolyBaseSet(); var UnionSet = new C2DHoledPolyBaseSet(); var grid = new CGrid(); while (Count > 0 && pPoly != null) { var pLast = this[Count - 1]; this.RemoveAt(Count - 1); pLast.GetUnion(pPoly, UnionSet, grid); if (UnionSet.Count == 1) { pPoly = null; pLast = null; AddAndUnify(UnionSet[0]); UnionSet.Clear(); } else { //Debug.Assert(UnionSet.Count == 0); UnionSet.Clear(); TempSet.Add(pLast); } } this.AddRange(TempSet); TempSet.Clear(); return(pPoly == null); }
/// <summary> /// Adds a new polygon set and looks for a possible unifications. /// Assumes both sets are distinct. /// </summary> /// <param name="pOther">The polygon set to add and possible unify.</param> public void AddAndUnify(C2DHoledPolyBaseSet pOther) { var TempSet = new C2DHoledPolyBaseSet(); while (pOther.Count > 0) { var pLast = pOther[pOther.Count - 1]; pOther.RemoveAt(pOther.Count - 1); if (!AddIfUnify(pLast)) { TempSet.Add(pLast); } } this.AddRange(TempSet); }
/// <summary> /// Basic multiple unification. /// </summary> public void UnifyBasic() { var TempSet = new C2DHoledPolyBaseSet(); var UnionSet = new C2DHoledPolyBaseSet(); while (Count > 0) { var pLast = this[Count - 1]; this.RemoveAt(Count - 1); var bIntersect = false; var i = 0; while (i < Count && !bIntersect) { var grid = new CGrid(); this[i].GetUnion(pLast, UnionSet, grid); if (UnionSet.Count == 1) { this[i] = UnionSet[0]; bIntersect = true; } else { //Debug.Assert(UnionSet.Count == 0); UnionSet.Clear(); i++; } } if (!bIntersect) { TempSet.Add(pLast); } } this.AddRange(TempSet); }
/// <summary> /// Gets the boolean operation with the other. e.g. union / intersection. /// </summary> /// <param name="Other">The other polygon.</param> /// <param name="HoledPolys">The set to recieve the result.</param> /// <param name="bThisInside">The flag to indicate routes inside.</param> /// <param name="bOtherInside">The flag to indicate routes inside for the other.</param> /// <param name="grid">The degenerate settings.</param> public void GetBoolean(C2DPolyBase Other, List <C2DHoledPolyBase> HoledPolys, bool bThisInside, bool bOtherInside, CGrid grid) { if (BoundingRect.Overlaps(Other.BoundingRect)) { switch (grid.DegenerateHandling) { case CGrid.eDegenerateHandling.None: { var Routes1 = new C2DLineBaseSetSet(); var Routes2 = new C2DLineBaseSetSet(); C2DPolyBase.GetRoutes(this, bThisInside, Other, bOtherInside, Routes1, Routes2); Routes1.ExtractAllOf(Routes2); if (Routes1.Count > 0) { // Add all the joining routes together to form closed routes Routes1.MergeJoining(); // Set up some temporary polygons. var Polygons = new List <C2DPolyBase>(); // Turn the routes into polygons. for (var i = Routes1.Count - 1; i >= 0; i--) { if (Routes1[i].IsClosed(true) && Routes1[i].Count > 2) { Polygons.Add(new C2DPolyBase()); Polygons[Polygons.Count - 1].CreateDirect(Routes1[i]); } else { // Debug.Assert(false); grid.LogDegenerateError(); } } // Set up some temporary holed polygons var NewComPolys = new C2DHoledPolyBaseSet(); // Turn the set of polygons into holed polygons. Not needed for intersection. if (!(bThisInside && bOtherInside)) { C2DHoledPolyBase.PolygonsToHoledPolygons(NewComPolys, Polygons); if (NewComPolys.Count != 1) { // Debug.Assert(false); grid.LogDegenerateError(); } } else { for (var i = 0; i < Polygons.Count; i++) { HoledPolys.Add(new C2DHoledPolyBase(Polygons[i])); } } // Now add them all to the provided set. for (var i = 0; i < NewComPolys.Count; i++) { HoledPolys.Add(NewComPolys[i]); } } } break; case CGrid.eDegenerateHandling.RandomPerturbation: { var OtherCopy = new C2DPolyBase(Other); OtherCopy.RandomPerturb(); grid.DegenerateHandling = CGrid.eDegenerateHandling.None; GetBoolean(OtherCopy, HoledPolys, bThisInside, bOtherInside, grid); grid.DegenerateHandling = CGrid.eDegenerateHandling.RandomPerturbation; } break; case CGrid.eDegenerateHandling.DynamicGrid: { var Rect = new C2DRect(); if (this.BoundingRect.Overlaps(Other.BoundingRect, Rect)) { var dOldGrid = grid.GridSize; grid.SetToMinGridSize(Rect, false); grid.DegenerateHandling = CGrid.eDegenerateHandling.PreDefinedGrid; GetBoolean(Other, HoledPolys, bThisInside, bOtherInside, grid); grid.DegenerateHandling = CGrid.eDegenerateHandling.DynamicGrid; } } break; case CGrid.eDegenerateHandling.PreDefinedGrid: { var P1 = new C2DPolyBase(this); var P2 = new C2DPolyBase(Other); P1.SnapToGrid(grid); P2.SnapToGrid(grid); var V1 = new C2DVector(P1.BoundingRect.TopLeft, P2.BoundingRect.TopLeft); var dPerturbation = grid.GridSize; // ensure it snaps back to original grid positions. if (V1.i > 0) { V1.i = dPerturbation; } else { V1.i = -dPerturbation; // move away slightly if possible } if (V1.j > 0) { V1.j = dPerturbation; } else { V1.j = -dPerturbation; // move away slightly if possible } V1.i *= 0.411923; // ensure it snaps back to original grid positions. V1.j *= 0.313131; // ensure it snaps back to original grid positions. P2.Move(V1); grid.DegenerateHandling = CGrid.eDegenerateHandling.None; P1.GetBoolean(P2, HoledPolys, bThisInside, bOtherInside, grid); for (var i = 0; i < HoledPolys.Count; i++) { HoledPolys[i].SnapToGrid(grid); } grid.DegenerateHandling = CGrid.eDegenerateHandling.PreDefinedGrid; } break; case CGrid.eDegenerateHandling.PreDefinedGridPreSnapped: { var P2 = new C2DPolyBase(Other); var V1 = new C2DVector(this.BoundingRect.TopLeft, P2.BoundingRect.TopLeft); var dPerturbation = grid.GridSize; // ensure it snaps back to original grid positions. if (V1.i > 0) { V1.i = dPerturbation; } else { V1.i = -dPerturbation; // move away slightly if possible } if (V1.j > 0) { V1.j = dPerturbation; } else { V1.j = -dPerturbation; // move away slightly if possible } V1.i *= 0.411923; // ensure it snaps back to original grid positions. V1.j *= 0.313131; // ensure it snaps back to original grid positions. P2.Move(V1); grid.DegenerateHandling = CGrid.eDegenerateHandling.None; GetBoolean(P2, HoledPolys, bThisInside, bOtherInside, grid); for (var i = 0; i < HoledPolys.Count; i++) { HoledPolys[i].SnapToGrid(grid); } grid.DegenerateHandling = CGrid.eDegenerateHandling.PreDefinedGridPreSnapped; } break; } } }
/// <summary> /// Unification by growing shapes of fairly equal size (fastest for large groups). /// </summary> /// <param name="grid">The CGrid with the degenerate settings.</param> public void UnifyProgressive(CGrid grid) { // Record the degenerate handling so we can reset. CGrid.eDegenerateHandling DegenerateHandling = grid.DegenerateHandling; switch (grid.DegenerateHandling) { case CGrid.eDegenerateHandling.RandomPerturbation: for (var i = 0; i < Count; i++) { this[i].RandomPerturb(); } grid.DegenerateHandling = CGrid.eDegenerateHandling.None; break; case CGrid.eDegenerateHandling.DynamicGrid: break; case CGrid.eDegenerateHandling.PreDefinedGrid: for (var i = 0; i < Count; i++) { this[i].SnapToGrid(grid); } grid.DegenerateHandling = CGrid.eDegenerateHandling.PreDefinedGridPreSnapped; break; case CGrid.eDegenerateHandling.PreDefinedGridPreSnapped: break; } var NoUnionSet = new C2DHoledPolyBaseSet(); var PossUnionSet = new C2DHoledPolyBaseSet(); var SizeHoldSet = new C2DHoledPolyBaseSet(); var UnionSet = new C2DHoledPolyBaseSet(); var TempSet = new C2DHoledPolyBaseSet(); var nThreshold = GetMinLineCount(); if (nThreshold == 0) { nThreshold = 10; // avoid infinate loop. } // Assumed all are size held to start SizeHoldSet.AddRange(this); this.Clear(); do { // double the threshold nThreshold *= 3; // Put all the possible intersects back in this. this.AddRange(PossUnionSet); PossUnionSet.Clear(); // Put all the size held that are small enough back (or in to start with) while (SizeHoldSet.Count > 0) { var pLast = SizeHoldSet[SizeHoldSet.Count - 1]; SizeHoldSet.RemoveAt(SizeHoldSet.Count - 1); if (pLast.GetLineCount() > nThreshold) { TempSet.Add(pLast); } else { this.Add(pLast); } } SizeHoldSet.AddRange(TempSet); TempSet.Clear(); // Cycle through all popping the last and finding a union while (Count > 0) { var pLast = this[Count - 1]; this.RemoveAt(Count - 1); var bIntersect = false; var i = 0; while (i < Count && !bIntersect) { this[i].GetUnion(pLast, UnionSet, grid); if (UnionSet.Count == 1) { var pUnion = UnionSet[UnionSet.Count - 1]; UnionSet.RemoveAt(UnionSet.Count - 1); if (pUnion.GetLineCount() > nThreshold) { RemoveAt(i); SizeHoldSet.Add(pUnion); } else { this[i] = pUnion; i++; } bIntersect = true; } else { if (UnionSet.Count != 0) { grid.LogDegenerateError(); } UnionSet.Clear(); i++; } } if (!bIntersect) { var bPosInterSect = false; for (var j = 0; j < SizeHoldSet.Count; j++) { if (pLast.Rim.BoundingRect.Overlaps( SizeHoldSet[j].Rim.BoundingRect)) { bPosInterSect = true; break; } } if (bPosInterSect) { PossUnionSet.Add(pLast); } else { NoUnionSet.Add(pLast); } } } }while (SizeHoldSet.Count != 0); this.AddRange(NoUnionSet); NoUnionSet.Clear(); grid.DegenerateHandling = DegenerateHandling; }
/// <summary> /// Returns the boolean result (e.g. union) of 2 shapes. Boolean Operation defined by /// the inside / outside flags. /// </summary> /// <param name="Other">Other polygon.</param> /// <param name="HoledPolys">Set of polygons to recieve the result.</param> /// <param name="bThisInside">Does the operation require elements of this INSIDE the other.</param> /// <param name="bOtherInside">Does the operation require elements of the other INSIDE this.</param> /// <param name="grid">The grid with the degenerate settings.</param> public void GetBoolean(C2DHoledPolyBase Other, List <C2DHoledPolyBase> HoledPolys, bool bThisInside, bool bOtherInside, CGrid grid) { if (_Rim.Lines.Count == 0 || Other.Rim.Lines.Count == 0) { return; } if (_Rim.BoundingRect.Overlaps(Other.Rim.BoundingRect)) { switch (grid.DegenerateHandling) { case CGrid.eDegenerateHandling.None: { var CompleteHoles1 = new List <C2DPolyBase>(); var CompleteHoles2 = new List <C2DPolyBase>(); var Routes1 = new C2DLineBaseSetSet(); var Routes2 = new C2DLineBaseSetSet(); GetRoutes(this, bThisInside, Other, bOtherInside, Routes1, Routes2, CompleteHoles1, CompleteHoles2, grid); Routes1.ExtractAllOf(Routes2); if (Routes1.Count > 0) { Routes1.MergeJoining(); var Polygons = new List <C2DPolyBase>(); for (var i = Routes1.Count - 1; i >= 0; i--) { C2DLineBaseSet pRoute = Routes1[i]; if (pRoute.IsClosed(true)) { Polygons.Add(new C2DPolyBase()); Polygons[Polygons.Count - 1].CreateDirect(pRoute); } else { // Debug.Assert(false); grid.LogDegenerateError(); } } var NewComPolys = new C2DHoledPolyBaseSet(); PolygonsToHoledPolygons(NewComPolys, Polygons); NewComPolys.AddKnownHoles(CompleteHoles1); NewComPolys.AddKnownHoles(CompleteHoles2); if (!bThisInside && !bOtherInside && NewComPolys.Count != 1) { // Debug.Assert(false); grid.LogDegenerateError(); } HoledPolys.AddRange(NewComPolys); NewComPolys.Clear(); } } break; case CGrid.eDegenerateHandling.RandomPerturbation: { var OtherCopy = new C2DHoledPolyBase(Other); OtherCopy.RandomPerturb(); grid.DegenerateHandling = CGrid.eDegenerateHandling.None; GetBoolean(OtherCopy, HoledPolys, bThisInside, bOtherInside, grid); grid.DegenerateHandling = CGrid.eDegenerateHandling.RandomPerturbation; } break; case CGrid.eDegenerateHandling.DynamicGrid: { var Rect = new C2DRect(); if (_Rim.BoundingRect.Overlaps(Other.Rim.BoundingRect, Rect)) { //double dOldGrid = CGrid::GetGridSize(); grid.SetToMinGridSize(Rect, false); grid.DegenerateHandling = CGrid.eDegenerateHandling.PreDefinedGrid; GetBoolean(Other, HoledPolys, bThisInside, bOtherInside, grid); grid.DegenerateHandling = CGrid.eDegenerateHandling.DynamicGrid; } } break; case CGrid.eDegenerateHandling.PreDefinedGrid: { var P1 = new C2DHoledPolyBase(this); var P2 = new C2DHoledPolyBase(Other); P1.SnapToGrid(grid); P2.SnapToGrid(grid); var V1 = new C2DVector(P1.Rim.BoundingRect.TopLeft, P2.Rim.BoundingRect.TopLeft); var dPerturbation = grid.GridSize; // ensure it snaps back to original grid positions. if (V1.i > 0) { V1.i = dPerturbation; } else { V1.i = -dPerturbation; // move away slightly if possible } if (V1.j > 0) { V1.j = dPerturbation; } else { V1.j = -dPerturbation; // move away slightly if possible } V1.i *= 0.411923; // ensure it snaps back to original grid positions. V1.j *= 0.313131; // ensure it snaps back to original grid positions. P2.Move(V1); grid.DegenerateHandling = CGrid.eDegenerateHandling.None; P1.GetBoolean(P2, HoledPolys, bThisInside, bOtherInside, grid); for (var i = 0; i < HoledPolys.Count; i++) { HoledPolys[i].SnapToGrid(grid); } grid.DegenerateHandling = CGrid.eDegenerateHandling.PreDefinedGrid; } break; case CGrid.eDegenerateHandling.PreDefinedGridPreSnapped: { var P2 = new C2DHoledPolyBase(Other); var V1 = new C2DVector(_Rim.BoundingRect.TopLeft, P2.Rim.BoundingRect.TopLeft); var dPerturbation = grid.GridSize; if (V1.i > 0) { V1.i = dPerturbation; } else { V1.i = -dPerturbation; // move away slightly if possible } if (V1.j > 0) { V1.j = dPerturbation; } else { V1.j = -dPerturbation; // move away slightly if possible } V1.i *= 0.411923; // ensure it snaps back to original grid positions. V1.j *= 0.313131; // ensure it snaps back to original grid positions. P2.Move(V1); grid.DegenerateHandling = CGrid.eDegenerateHandling.None; GetBoolean(P2, HoledPolys, bThisInside, bOtherInside, grid); for (var i = 0; i < HoledPolys.Count; i++) { HoledPolys[i].SnapToGrid(grid); } grid.DegenerateHandling = CGrid.eDegenerateHandling.PreDefinedGridPreSnapped; } break; } // switch } }