public static VectorArray2d SampleArcLen(IParametricCurve2d curve, double fSpacing) { if (curve.HasArcLength == false) { throw new InvalidOperationException("CurveSampler2.SampleArcLen: curve does not support arc length sampling!"); } double fLen = curve.ArcLength; if (fLen < MathUtil.ZeroTolerance) { VectorArray2d degen = new VectorArray2d(2); degen[0] = curve.SampleArcLength(0); degen[1] = curve.SampleArcLength(1); return(degen); } int nSteps = Math.Max((int)(fLen / fSpacing) + 1, 2); VectorArray2d vec = new VectorArray2d(nSteps); for (int i = 0; i < nSteps; ++i) { double t = (double)i / (double)(nSteps - 1); vec[i] = curve.SampleArcLength(t * fLen); } return(vec); }
public Element Add(IParametricCurve2d curve) { if (curve.IsClosed) { SmoothLoopElement e = new SmoothLoopElement { ID = id_generator++, source = curve }; UpdateSampling(e); vElements.Add(e); return(e); } else { SmoothCurveElement e = new SmoothCurveElement { ID = id_generator++, source = curve }; UpdateSampling(e); vElements.Add(e); return(e); } }
public static void Restore(out IParametricCurve2d curve, BinaryReader reader) { curve = null; int nType = reader.ReadInt32(); if (nType == 1) { Segment2d segment = new Segment2d(); Restore(ref segment, reader); curve = segment; } else if (nType == 2) { Circle2d circle = new Circle2d(Vector2d.Zero, 1.0); Restore(ref circle, reader); curve = circle; } else if (nType == 3) { Arc2d arc = new Arc2d(Vector2d.Zero, 1.0, 0, 1); Restore(ref arc, reader); curve = arc; } else if (nType == 100) { ParametricCurveSequence2 seq = new ParametricCurveSequence2(); Restore(ref seq, reader); curve = seq; } else { throw new Exception("gSerialization.Restore: IParametricCurve2D : unknown curve type " + nType.ToString()); } }
public static VectorArray2d SampleTRange(IParametricCurve2d curve, int N, double t0, double t1) { VectorArray2d vec = new VectorArray2d(N); for (int i = 0; i < N; ++i) { double alpha = (double)i / (double)(N - 1); double t = (1 - alpha) * t0 + alpha * t1; vec[i] = curve.SampleT(t); } return(vec); }
public void AddHole(IParametricCurve2d hole) { if (outer == null) { throw new Exception("PlanarSolid2d.AddHole: outer polygon not set!"); } // if ( (bOuterIsCW && hole.IsClockwise) || (bOuterIsCW == false && hole.IsClockwise == false) ) //throw new Exception("PlanarSolid2d.AddHole: new hole has same orientation as outer polygon!"); holes.Add(hole); }
public static VectorArray2d SampleT(IParametricCurve2d curve, int N) { double fLenT = curve.ParamLength; VectorArray2d vec = new VectorArray2d(N); double divide = (curve.IsClosed) ? (double)N : (double)(N - 1); for (int i = 0; i < N; ++i) { double t = (double)i / divide; vec[i] = curve.SampleT(t * fLenT); } return(vec); }
// 2D curve utils? public static double SampledDistance(IParametricCurve2d c, Vector2d point, int N = 100) { double tMax = c.ParamLength; double min_dist = double.MaxValue; for ( int i = 0; i <= N; ++i ) { double fT = (double)i / (double)N; fT *= tMax; Vector2d p = c.SampleT(fT); double d = p.DistanceSquared(point); if ( d < min_dist ) min_dist = d; } return Math.Sqrt(min_dist); }
public static VectorArray2d SampleT(IParametricCurve2d curve, double fSpacing) { double fLenT = 1.0f; // assumption for now is that all curves span [0,1] t-range int nSteps = Math.Max((int)(fLenT / fSpacing) + 1, 2); VectorArray2d vec = new VectorArray2d(nSteps); for (int i = 0; i < nSteps; ++i) { double t = (double)i / (double)(nSteps - 1); vec[i] = curve.SampleT(t * fLenT); } return(vec); }
public static VectorArray2d SampleT(IParametricCurve2d curve, double fSpacing) { double fLenT = curve.ParamLength; int nSteps = Math.Max((int)(fLenT / fSpacing) + 1, 2); VectorArray2d vec = new VectorArray2d(nSteps); for (int i = 0; i < nSteps; ++i) { double t = (double)i / (double)(nSteps - 1); vec[i] = curve.SampleT(t * fLenT); } return(vec); }
// returns set of closed loops (not necessarily solids) public ClosedLoopsInfo FindClosedLoops(double fSimplifyDeviationTol = 0.1) { var loopElems = new List <SmoothLoopElement>(LoopsItr()); int N = loopElems.Count; int maxid = 0; foreach (var v in loopElems) { maxid = Math.Max(maxid, v.ID + 1); } // copy polygons, simplify if desired double fClusterTol = 0.0; // don't do simple clustering, can lose corners double fDeviationTol = fSimplifyDeviationTol; var polygons = new Polygon2d[maxid]; var curves = new IParametricCurve2d[maxid]; foreach (var v in loopElems) { var p = new Polygon2d(v.polygon); if (fClusterTol > 0 || fDeviationTol > 0) { p.Simplify(fClusterTol, fDeviationTol); } polygons[v.ID] = p; curves[v.ID] = v.source; } var ci = new ClosedLoopsInfo() { Polygons = new List <Polygon2d>(), Loops = new List <IParametricCurve2d>() }; for (int i = 0; i < polygons.Length; ++i) { if (polygons[i] != null && polygons[i].VertexCount > 0) { ci.Polygons.Add(polygons[i]); ci.Loops.Add(curves[i]); } } return(ci); }
// returns set of open curves (ie non-solids) public OpenCurvesInfo FindOpenCurves(double fSimplifyDeviationTol = 0.1) { List <SmoothCurveElement> curveElems = new List <SmoothCurveElement>(CurvesItr()); int N = curveElems.Count; int maxid = 0; foreach (var v in curveElems) { maxid = Math.Max(maxid, v.ID + 1); } // copy polygons, simplify if desired double fClusterTol = 0.0; // don't do simple clustering, can lose corners double fDeviationTol = fSimplifyDeviationTol; PolyLine2d[] polylines = new PolyLine2d[maxid]; IParametricCurve2d[] curves = new IParametricCurve2d[maxid]; foreach (var v in curveElems) { PolyLine2d p = new PolyLine2d(v.polyLine); if (fClusterTol > 0 || fDeviationTol > 0) { p.Simplify(fClusterTol, fDeviationTol); } polylines[v.ID] = p; curves[v.ID] = v.source; } OpenCurvesInfo ci = new OpenCurvesInfo() { Polylines = new List <PolyLine2d>(), Curves = new List <IParametricCurve2d>() }; for (int i = 0; i < polylines.Length; ++i) { if (polylines[i] != null && polylines[i].VertexCount > 0) { ci.Polylines.Add(polylines[i]); ci.Curves.Add(curves[i]); } } return(ci); }
/// <summary> /// if the children of C are a tree, iterate through all the leaves /// </summary> public static IEnumerable <IParametricCurve2d> LeafCurvesIteration(IParametricCurve2d c) { if (c is IMultiCurve2d) { var multiC = c as IMultiCurve2d; foreach (IParametricCurve2d c2 in multiC.Curves) { foreach (var c3 in LeafCurvesIteration(c2)) { yield return(c3); } } } else { yield return(c); } }
public void Add(IParametricCurve2d curve) { if (curve.IsClosed) { SmoothLoopElement e = new SmoothLoopElement(); e.ID = id_generator++; e.source = curve; UpdateSampling(e); vElements.Add(e); } else { SmoothCurveElement e = new SmoothCurveElement(); e.ID = id_generator++; e.source = curve; UpdateSampling(e); vElements.Add(e); } }
/// <summary> /// iterate through "leaf" curves, ie all the IParametricCurve2D's /// embedded in loops that do not contain any child curves /// </summary> public IEnumerable <IParametricCurve2d> LoopLeafComponentsItr() { foreach (Element e in vElements) { if (e is SmoothLoopElement) { IParametricCurve2d source = e.source; if (source is IMultiCurve2d) { foreach (var c in CurveUtils2.LeafCurvesIteration(source)) { yield return(c); } } else { yield return(source); } } } }
public static void Store(IParametricCurve2d curve, BinaryWriter writer) { if (curve is Segment2d) { writer.Write((int)1); Store((Segment2d)curve, writer); } else if (curve is Circle2d) { writer.Write((int)2); Store((Circle2d)curve, writer); } else if (curve is Arc2d) { writer.Write((int)3); Store((Arc2d)curve, writer); } else if (curve is ParametricCurveSequence2) { writer.Write((int)100); Store(curve as ParametricCurveSequence2, writer); } }
public static VectorArray2d AutoSample(IParametricCurve2d curve, double fSpacingLength, double fSpacingT) { if (curve is ParametricCurveSequence2) { return(AutoSample(curve as ParametricCurveSequence2, fSpacingLength, fSpacingT)); } if (curve.HasArcLength) { if (curve is NURBSCurve2) { return(SampleNURBSHybrid(curve as NURBSCurve2, fSpacingLength)); } else { return(SampleArcLen(curve, fSpacingLength)); } } else { return(SampleT(curve, fSpacingT)); } }
// 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 }); }
public static List <IParametricCurve2d> Flatten(IParametricCurve2d curve) { return(new List <IParametricCurve2d>(LeafCurvesIteration(curve))); }
public void SetOuter(IParametricCurve2d loop, bool bIsClockwise) { Debug.Assert(loop.IsClosed); outer = loop; //bOuterIsCW = bIsClockwise; }
public void Append(IParametricCurve2d c) { // sanity checking?? curves.Add(c); }
// 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 }); }
public void Prepend(IParametricCurve2d c) { curves.Insert(0, c); }