public static void Store(PlanarSolid2d solid, BinaryWriter writer) { Store(solid.Outer, writer); writer.Write(solid.Holes.Count); for (int i = 0; i < solid.Holes.Count; ++i) { Store(solid.Holes[i], writer); } }
public static void Restore(PlanarSolid2d solid, BinaryReader reader) { IParametricCurve2d outer; Restore(out outer, reader); solid.SetOuter(outer, true); // !! currently CW/CCW is ignored! int hole_count = reader.ReadInt32(); for (int i = 0; i < hole_count; ++i) { IParametricCurve2d hole; Restore(out hole, reader); solid.AddHole(hole); } }
// Finds set of "solid" regions - eg boundary loops with interior holes. // Result has outer loops being clockwise, and holes counter-clockwise public SolidRegionInfo FindSolidRegions(double fSimplifyDeviationTol = 0.1, bool bWantCurveSolids = true) { List <SmoothLoopElement> valid = new List <SmoothLoopElement>(LoopsItr()); int N = valid.Count; // precompute bounding boxes int maxid = 0; foreach (var v in valid) { maxid = Math.Max(maxid, v.ID + 1); } AxisAlignedBox2d[] bounds = new AxisAlignedBox2d[maxid]; foreach (var v in valid) { bounds[v.ID] = v.Bounds(); } // copy polygons, simplify if desired double fClusterTol = 0.0; // don't do simple clustering, can lose corners double fDeviationTol = fSimplifyDeviationTol; Polygon2d[] polygons = new Polygon2d[maxid]; foreach (var v in valid) { Polygon2d p = new Polygon2d(v.polygon); if (fClusterTol > 0 || fDeviationTol > 0) { p.Simplify(fClusterTol, fDeviationTol); } polygons[v.ID] = p; } // sort by bbox containment to speed up testing (does it??) valid.Sort((x, y) => { return(bounds[x.ID].Contains(bounds[y.ID]) ? -1 : 1); }); // containment sets bool[] bIsContained = new bool[N]; Dictionary <int, List <int> > ContainSets = new Dictionary <int, List <int> >(); // construct containment sets for (int i = 0; i < N; ++i) { SmoothLoopElement loopi = valid[i]; Polygon2d polyi = polygons[loopi.ID]; for (int j = 0; j < N; ++j) { if (i == j) { continue; } SmoothLoopElement loopj = valid[j]; Polygon2d polyj = polygons[loopj.ID]; // cannot be contained if bounds are not contained if (bounds[loopi.ID].Contains(bounds[loopj.ID]) == false) { continue; } // any other early-outs?? if (polyi.Contains(polyj)) { if (ContainSets.ContainsKey(i) == false) { ContainSets.Add(i, new List <int>()); } ContainSets[i].Add(j); bIsContained[j] = true; } } } List <GeneralPolygon2d> polysolids = new List <GeneralPolygon2d>(); List <PlanarSolid2d> solids = new List <PlanarSolid2d>(); List <SmoothLoopElement> used = new List <SmoothLoopElement>(); // extract solids from containment relationships foreach (var i in ContainSets.Keys) { SmoothLoopElement outer_element = valid[i]; used.Add(outer_element); if (bIsContained[i]) { throw new Exception("PlanarComplex.FindSolidRegions: multiply-nested regions not supported!"); } Polygon2d outer_poly = polygons[outer_element.ID]; IParametricCurve2d outer_loop = (bWantCurveSolids) ? outer_element.source.Clone() : null; if (outer_poly.IsClockwise == false) { outer_poly.Reverse(); if (bWantCurveSolids) { outer_loop.Reverse(); } } GeneralPolygon2d g = new GeneralPolygon2d(); g.Outer = outer_poly; PlanarSolid2d s = new PlanarSolid2d(); if (bWantCurveSolids) { s.SetOuter(outer_loop, true); } foreach (int hi in ContainSets[i]) { SmoothLoopElement he = valid[hi]; used.Add(he); Polygon2d hole_poly = polygons[he.ID]; IParametricCurve2d hole_loop = (bWantCurveSolids) ? he.source.Clone() : null; if (hole_poly.IsClockwise) { hole_poly.Reverse(); if (bWantCurveSolids) { hole_loop.Reverse(); } } try { g.AddHole(hole_poly); if (hole_loop != null) { s.AddHole(hole_loop); } } catch { // don't add this hole - must intersect or something // We should have caught this earlier! } } polysolids.Add(g); if (bWantCurveSolids) { solids.Add(s); } } for (int i = 0; i < N; ++i) { SmoothLoopElement loopi = valid[i]; if (used.Contains(loopi)) { continue; } Polygon2d outer_poly = polygons[loopi.ID]; IParametricCurve2d outer_loop = (bWantCurveSolids) ? loopi.source.Clone() : null; if (outer_poly.IsClockwise == false) { outer_poly.Reverse(); if (bWantCurveSolids) { outer_loop.Reverse(); } } GeneralPolygon2d g = new GeneralPolygon2d(); g.Outer = outer_poly; PlanarSolid2d s = new PlanarSolid2d(); if (bWantCurveSolids) { s.SetOuter(outer_loop, true); } polysolids.Add(g); if (bWantCurveSolids) { solids.Add(s); } } return(new SolidRegionInfo() { Polygons = polysolids, Solids = (bWantCurveSolids) ? solids : null }); }
// Finds set of "solid" regions - eg boundary loops with interior holes. // Result has outer loops being clockwise, and holes counter-clockwise public SolidRegionInfo FindSolidRegions(FindSolidsOptions options) { var validLoops = new List <SmoothLoopElement>(LoopsItr()); int N = validLoops.Count; // precompute bounding boxes int maxid = 0; foreach (var v in validLoops) { maxid = Math.Max(maxid, v.ID + 1); } var bounds = new AxisAlignedBox2d[maxid]; foreach (var v in validLoops) { bounds[v.ID] = v.Bounds(); } // copy polygons, simplify if desired double fClusterTol = 0.0; // don't do simple clustering, can lose corners double fDeviationTol = options.SimplifyDeviationTolerance; var polygons = new Polygon2d[maxid]; foreach (var v in validLoops) { var p = new Polygon2d(v.polygon); if (fClusterTol > 0 || fDeviationTol > 0) { p.Simplify(fClusterTol, fDeviationTol); } polygons[v.ID] = p; } // sort by bbox containment to speed up testing (does it??) validLoops.Sort((x, y) => { return(bounds[x.ID].Contains(bounds[y.ID]) ? -1 : 1); }); // containment sets bool[] bIsContained = new bool[N]; var ContainSets = new Dictionary <int, List <int> >(); var ContainedParents = new Dictionary <int, List <int> >(); bool bUseOrient = options.TrustOrientations; bool bWantCurveSolids = options.WantCurveSolids; bool bCheckHoles = !options.AllowOverlappingHoles; // construct containment sets for (int i = 0; i < N; ++i) { SmoothLoopElement loopi = validLoops[i]; Polygon2d polyi = polygons[loopi.ID]; for (int j = 0; j < N; ++j) { if (i == j) { continue; } SmoothLoopElement loopj = validLoops[j]; Polygon2d polyj = polygons[loopj.ID]; // if we are preserving orientations, holes cannot contain holes and // outers cannot contain outers! if (bUseOrient && loopj.polygon.IsClockwise == loopi.polygon.IsClockwise) { continue; } // cannot be contained if bounds are not contained if (bounds[loopi.ID].Contains(bounds[loopj.ID]) == false) { continue; } // any other early-outs?? if (polyi.Contains(polyj)) { if (ContainSets.ContainsKey(i) == false) { ContainSets.Add(i, new List <int>()); } ContainSets[i].Add(j); bIsContained[j] = true; if (ContainedParents.ContainsKey(j) == false) { ContainedParents.Add(j, new List <int>()); } ContainedParents[j].Add(i); } } } var polysolids = new List <GeneralPolygon2d>(); var polySolidsInfo = new List <GeneralSolid>(); var solids = new List <PlanarSolid2d>(); var used = new HashSet <SmoothLoopElement>(); var LoopToOuterIndex = new Dictionary <SmoothLoopElement, int>(); var ParentsToProcess = new List <int>(); // The following is a lot of code but it is very similar, just not clear how // to refactor out the common functionality // 1) we find all the top-level uncontained polys and add them to the final polys list // 2a) for any poly contained in those parent-polys, that is not also contained in anything else, // add as hole to that poly // 2b) remove all those used parents & holes from consideration // 2c) now find all the "new" top-level polys // 3) repeat 2a-c until done all polys // 4) any remaining polys must be interior solids w/ no holes // **or** weird leftovers like intersecting polys... // add all top-level uncontained polys for (int i = 0; i < N; ++i) { SmoothLoopElement loopi = validLoops[i]; if (bIsContained[i]) { continue; } Polygon2d outer_poly = polygons[loopi.ID]; IParametricCurve2d outer_loop = (bWantCurveSolids) ? loopi.source.Clone() : null; if (outer_poly.IsClockwise == false) { outer_poly.Reverse(); if (bWantCurveSolids) { outer_loop.Reverse(); } } var g = new GeneralPolygon2d(); g.Outer = outer_poly; var s = new PlanarSolid2d(); if (bWantCurveSolids) { s.SetOuter(outer_loop, true); } int idx = polysolids.Count; LoopToOuterIndex[loopi] = idx; used.Add(loopi); if (ContainSets.ContainsKey(i)) { ParentsToProcess.Add(i); } polysolids.Add(g); polySolidsInfo.Add(new GeneralSolid() { Outer = loopi }); if (bWantCurveSolids) { solids.Add(s); } } // keep iterating until we processed all parent loops while (ParentsToProcess.Count > 0) { var ContainersToRemove = new List <int>(); // now for all top-level polys that contain children, add those children // as long as they do not have multiple contain-parents foreach (int i in ParentsToProcess) { SmoothLoopElement parentloop = validLoops[i]; int outer_idx = LoopToOuterIndex[parentloop]; List <int> children = ContainSets[i]; foreach (int childj in children) { SmoothLoopElement childLoop = validLoops[childj]; Debug.Assert(used.Contains(childLoop) == false); // skip multiply-contained children List <int> parents = ContainedParents[childj]; if (parents.Count > 1) { continue; } Polygon2d hole_poly = polygons[childLoop.ID]; IParametricCurve2d hole_loop = (bWantCurveSolids) ? childLoop.source.Clone() : null; if (hole_poly.IsClockwise) { hole_poly.Reverse(); if (bWantCurveSolids) { hole_loop.Reverse(); } } try { polysolids[outer_idx].AddHole(hole_poly, bCheckHoles); polySolidsInfo[outer_idx].Holes.Add(childLoop); if (hole_loop != null) { solids[outer_idx].AddHole(hole_loop); } } catch { // don't add this hole - must intersect or something? // We should have caught this earlier! } used.Add(childLoop); if (ContainSets.ContainsKey(childj)) { ContainersToRemove.Add(childj); } } ContainersToRemove.Add(i); } // remove all containers that are no longer valid foreach (int ci in ContainersToRemove) { ContainSets.Remove(ci); // have to remove from each ContainedParents list var keys = new List <int>(ContainedParents.Keys); foreach (int j in keys) { if (ContainedParents[j].Contains(ci)) { ContainedParents[j].Remove(ci); } } } ParentsToProcess.Clear(); // ok now find next-level uncontained parents... for (int i = 0; i < N; ++i) { SmoothLoopElement loopi = validLoops[i]; if (used.Contains(loopi)) { continue; } if (ContainSets.ContainsKey(i) == false) { continue; } List <int> parents = ContainedParents[i]; if (parents.Count > 0) { continue; } Polygon2d outer_poly = polygons[loopi.ID]; IParametricCurve2d outer_loop = (bWantCurveSolids) ? loopi.source.Clone() : null; if (outer_poly.IsClockwise == false) { outer_poly.Reverse(); if (bWantCurveSolids) { outer_loop.Reverse(); } } var g = new GeneralPolygon2d(); g.Outer = outer_poly; var s = new PlanarSolid2d(); if (bWantCurveSolids) { s.SetOuter(outer_loop, true); } int idx = polysolids.Count; LoopToOuterIndex[loopi] = idx; used.Add(loopi); if (ContainSets.ContainsKey(i)) { ParentsToProcess.Add(i); } polysolids.Add(g); polySolidsInfo.Add(new GeneralSolid() { Outer = loopi }); if (bWantCurveSolids) { solids.Add(s); } } } // any remaining loops must be top-level for (int i = 0; i < N; ++i) { SmoothLoopElement loopi = validLoops[i]; if (used.Contains(loopi)) { continue; } Polygon2d outer_poly = polygons[loopi.ID]; IParametricCurve2d outer_loop = (bWantCurveSolids) ? loopi.source.Clone() : null; if (outer_poly.IsClockwise == false) { outer_poly.Reverse(); if (bWantCurveSolids) { outer_loop.Reverse(); } } var g = new GeneralPolygon2d(); g.Outer = outer_poly; var s = new PlanarSolid2d(); if (bWantCurveSolids) { s.SetOuter(outer_loop, true); } polysolids.Add(g); polySolidsInfo.Add(new GeneralSolid() { Outer = loopi }); if (bWantCurveSolids) { solids.Add(s); } } return(new SolidRegionInfo() { Polygons = polysolids, PolygonsSources = polySolidsInfo, Solids = (bWantCurveSolids) ? solids : null }); }