private static IPolygon Clopening(IPolygon p, Box2 boundingBox, double offset) { if (p.IsEmpty) { return(Polygon.Empty); } double scale = Math.Max(boundingBox.Width, boundingBox.Height); var fixedPointRange = new Box2(boundingBox.MinCorner, new Vector2(scale, scale)); var fixedP = ConvertToFixedPoint(p, fixedPointRange); var fixedScaleOffset = offset * _fixedPointRange / scale; try { var offsetter = new ClipperOffset(); offsetter.AddPaths(fixedP, JoinType.jtMiter, EndType.etClosedPolygon); var fixedIntermediate = new ClipperPolygon(); offsetter.Execute(ref fixedIntermediate, fixedScaleOffset); offsetter.Clear(); offsetter.AddPaths(fixedIntermediate, JoinType.jtMiter, EndType.etClosedPolygon); var fixedAnswer = new ClipperPolygon(); offsetter.Execute(ref fixedAnswer, -fixedScaleOffset); return(ConvertToFloatingPoint(fixedAnswer, fixedPointRange)); } catch (Exception e) { Console.WriteLine("EXCEPTION: {0}", e); return(p); } }
private Paths GetOffsetSolution(Dictionary <float, Paths> roads) { foreach (var carRoadEntry in roads) { var offsetSolution = new Paths(); _offset.AddPaths(carRoadEntry.Value, JoinType.jtMiter, EndType.etOpenSquare); _offset.Execute(ref offsetSolution, carRoadEntry.Key); _clipper.AddPaths(offsetSolution, PolyType.ptSubject, true); _offset.Clear(); } var polySolution = new Paths(); _clipper.Execute(ClipType.ctUnion, polySolution, PolyFillType.pftPositive, PolyFillType.pftPositive); _clipper.Clear(); return(polySolution); }
public static List <List <Vector2> > Outline(List <Vector2> polygon, FillMode fillMode, bool closed, StrokeStyle strokeStyle, float strokeWidth, out PolyTree tree) { List <List <Vector2> > simplified = Clipper.SimplifyPolygon(polygon, fillMode.ToPolyFillType()); Offsetter.Clear(); Offsetter.MiterLimit = strokeStyle.MiterLimit; Offsetter.AddPaths(simplified, (JoinType)strokeStyle.LineJoin, closed ? EndType.etClosedLine : strokeStyle.CapStyle.ToEndType()); tree = new PolyTree(); Offsetter.Execute(ref tree, strokeWidth / 2); return(Clipper.ClosedPathsFromPolyTree(tree)); }
public static Paths offset(Paths paths, float offset) { // Set cleaning precision IntRect brect = Clipper.GetBounds(paths); int cleanPolygonPrecision = 2; if ((brect.right - brect.left) > 10000) { cleanPolygonPrecision = 30; } // Clean... AXClipperLib.JoinType jt = AXClipperLib.JoinType.jtSquare; paths = AXGeometryTools.Utilities.cleanPaths(paths, cleanPolygonPrecision); Paths resPaths = new Paths(); AXClipperLib.PolyTree resPolytree = null; // OFFSETTER ClipperOffset co = new ClipperOffset(); co.MiterLimit = 2.0f; foreach (Path path in paths) { co.Clear(); resPolytree = null; co.AddPath(path, jt, AXClipperLib.EndType.etClosedPolygon); //JoinType.jtSquare, AXClipperLib.EndType.etClosedPolygon); // this resPolytree has transformed curves in it resPolytree = new AXClipperLib.PolyTree(); co.Execute(ref resPolytree, (double)(offset * AXGeometryTools.Utilities.IntPointPrecision)); resPaths.AddRange(Clipper.ClosedPathsFromPolyTree(resPolytree)); } return(resPaths); }
/// <summary> /// Simplifies an list INTPolygons using expand and shrink technique. /// </summary> /// <param name="polygons">The INTPolygons.</param> /// <param name="value">The value used for expand and shrink.</param> /// <returns>INTPolygons.</returns> public INTPolygons SimplifyINTPolygons(INTPolygons polygons, double value) { double simplificationFactor = Math.Pow(10.0, this.PolygonalBooleanPrecision) * UnitConversion.Convert(value, Length_Unit_Types.FEET, UnitType); ClipperOffset clipperOffset = new ClipperOffset(); clipperOffset.AddPaths(polygons, ClipperLib.JoinType.jtMiter, EndType.etClosedPolygon); INTPolygons shrink = new INTPolygons(); clipperOffset.Execute(ref shrink, -simplificationFactor); //expanding to return the polygons to their original position clipperOffset.Clear(); clipperOffset.AddPaths(shrink, ClipperLib.JoinType.jtMiter, EndType.etClosedPolygon); INTPolygons expand = new INTPolygons(); clipperOffset.Execute(ref expand, simplificationFactor); shrink = null; clipperOffset = null; return(expand); }
internal void ExtendThePolygon(ref List <IntPoint> enterPoints, ref Vector2[] extendedPoints, float extraOffset) { double distance = (AgentRadius + extraOffset) * (float)_Accuracy; _ClipperOffset.Clear(); _ExitExtendPoints.Clear(); _ClipperOffset.AddPath(enterPoints, JoinType.jtMiter, EndType.etClosedPolygon); _ClipperOffset.Execute(ref _ExitExtendPoints, distance); if (_ExitExtendPoints[0].Count != extendedPoints.Length) { extendedPoints = new Vector2[_ExitExtendPoints[0].Count]; } for (int i = 0; i < _ExitExtendPoints[0].Count; ++i) { extendedPoints[i] = ParseToVector2(_ExitExtendPoints[0][i]); } }
/// <summary> /// Returns an array of barriers that is used for autonomous walking scenarios /// </summary> /// <param name="offsetValue">Human body size which is the distance that you want the agents from barriers</param> /// <returns>Expanded version of all barrier polygons including field naked edges and holes, visual barriers and physical barriers </returns> public BarrierPolygon[] ExpandAllBarrierPolygons(double offsetValue) { ClipperOffset clipperOffset = new ClipperOffset(); //clipperOffset.AddPaths(this.FootPrintOfAllBarriers, JoinType.jtSquare, EndType.etClosedPolygon); clipperOffset.AddPaths(this.FootPrintPolygonsOfFieldWithVoids, JoinType.jtSquare, EndType.etClosedPolygon); INTPolygons plygns = new INTPolygons(); double uniqueOffsetValue = -Math.Pow(10.0, this.PolygonalBooleanPrecision) * offsetValue; clipperOffset.Execute(ref plygns, uniqueOffsetValue); List <BarrierPolygon> brrs = new List <BarrierPolygon>(); for (int i = 0; i < plygns.Count; i++) { BarrierPolygon brr = this.ConvertINTPolygonToBarrierPolygon(plygns[i]); if (brr.Length > 0) { brrs.Add(brr); } } clipperOffset.Clear(); plygns.Clear(); return(brrs.ToArray()); }
internal static List <SliceLine2D> ConvertPolyTreeWithOffsetToSliceLine2D(float sliceHeight, List <PolyTree> polytrees, float insideOffset, float outsideOffset) { //calc model points using polygon offset var result = new List <SliceLine2D>(); if (sliceHeight != 10.65f) { return(result); } var decimalCorrectionFactor = 10000f; var selectedPrinterProjectorVector = new Vector2(RenderEngine.PrintJob.SelectedPrinter.ProjectorResolutionX, RenderEngine.PrintJob.SelectedPrinter.ProjectorResolutionY); var clipperOffset = new ClipperOffset(); var holes = new List <PolyNode>(); var outsides = new List <PolyNode>(); foreach (var polygon in polytrees) { foreach (var pointInPath in polygon._allPolys) { if (pointInPath.Contour.Count > 2) { clipperOffset.Clear(); clipperOffset = new ClipperOffset(); var results = new PolyTree(); clipperOffset.AddPath(pointInPath.Contour, JoinType.jtMiter, EndType.etClosedPolygon); if (!pointInPath.IsHole) { //detax -2 //abs -1 clipperOffset.Execute(ref results, outsideOffset * decimalCorrectionFactor); foreach (var offsetPolygon in results.Childs) { outsides.Add(offsetPolygon); } } else { //detax 2 //abs 1 clipperOffset.Execute(ref results, insideOffset * decimalCorrectionFactor); foreach (var offsetPolygon in results.Childs) { holes.Add(offsetPolygon); } } } } //prevent line crossing first union the holes (removes holes intersections) var unionHolesPolyTree = new PolyTree(); if (holes.Count > 1) { var clipper = new Clipper(); foreach (var hole in holes) { clipper.AddPath(hole.Contour, PolyType.ptSubject, true); } clipper.Execute(ClipType.ctUnion, unionHolesPolyTree, PolyFillType.pftNonZero, PolyFillType.pftNonZero); } else if (holes.Count == 1) { unionHolesPolyTree.AddChild(holes[0]); } //prevent line crossing second union the outsides (removes outside intersections) var unionOutsidesPolyTree = new PolyTree(); if (outsides.Count > 1) { var clipper = new Clipper(); foreach (var outside in outsides) { clipper.AddPath(outside.Contour, PolyType.ptSubject, true); } clipper.Execute(ClipType.ctUnion, unionOutsidesPolyTree, PolyFillType.pftNonZero, PolyFillType.pftNonZero); } else if (outsides.Count == 1) { unionOutsidesPolyTree.AddChild(outsides[0]); } //subtract holes from outsides var diffPolyTree = new PolyTree(); var diffPolyTreeClipper = new Clipper(); foreach (var unionPolyNode in unionOutsidesPolyTree.Childs) { diffPolyTreeClipper.AddPath(unionPolyNode.Contour, PolyType.ptSubject, true); } foreach (var unionPolyNode in unionHolesPolyTree.Childs) { diffPolyTreeClipper.AddPath(unionPolyNode.Contour, PolyType.ptClip, true); } diffPolyTreeClipper.Execute(ClipType.ctDifference, diffPolyTree, PolyFillType.pftNonZero); // var triangle3D = new Triangle(); // foreach (var offsetPolygon in results.Childs) // { // //change order to CCW // var directionIsCW = Clipper.Orientation(offsetPolygon.Contour); // if (directionIsCW) // { // Clipper.ReversePaths(new List<List<IntPoint>>() { offsetPolygon.Contour }); // } // //convert to 3d triangle and determine normal // for (var contourIndex = 0; contourIndex < offsetPolygon.Contour.Count; contourIndex++) // { // triangle3D.Vectors[0].Position = new Vector3(offsetPolygon.Contour[contourIndex].X, offsetPolygon.Contour[contourIndex].Y, 0); // if (contourIndex == offsetPolygon.Contour.Count - 1) // { // triangle3D.Vectors[1].Position = new Vector3(offsetPolygon.Contour[0].X, offsetPolygon.Contour[0].Y, 0); // triangle3D.Vectors[2].Position = new Vector3(offsetPolygon.Contour[0].X, offsetPolygon.Contour[0].Y, 1); // } // else // { // triangle3D.Vectors[1].Position = new Vector3(offsetPolygon.Contour[contourIndex + 1].X, offsetPolygon.Contour[contourIndex + 1].Y, 0); // triangle3D.Vectors[2].Position = new Vector3(offsetPolygon.Contour[contourIndex + 1].X, offsetPolygon.Contour[contourIndex + 1].Y, 1); // } // triangle3D.Vectors[0].Position /= decimalCorrectionFactor; // triangle3D.Vectors[1].Position /= decimalCorrectionFactor; // triangle3D.Vectors[2].Position /= decimalCorrectionFactor; // triangle3D.CalcNormal(); // var triangleNormal = triangle3D.Normal; // if (!pointInPath.IsHole) // { // triangleNormal = triangleNormal * -1; // } // var line = new SliceLine2D(); // line.Normal = triangleNormal; // if (contourIndex == offsetPolygon.Contour.Count - 1) // { // line.p1 = new SlicePoint2D() { X = triangle3D.Vectors[0].Position.X, Y = triangle3D.Vectors[0].Position.Y}; // //line.p2 = new SlicePoint2D() { X = offsetPolygon.Contour[0].X / decimalCorrectionFactor, Y = offsetPolygon.Contour[0].Y / decimalCorrectionFactor }; // // // line.p2 = new SlicePoint2D() { X = triangle3D.Vectors[1].Position.X, Y = triangle3D.Vectors[1].Position.Y }; // } // else // { // line.p1 = new SlicePoint2D() { X = triangle3D.Vectors[0].Position.X, Y = triangle3D.Vectors[0].Position.Y}; // line.p2 = new SlicePoint2D() { X = triangle3D.Vectors[1].Position.X, Y = triangle3D.Vectors[1].Position.Y}; // } // result.Add(line); // } // } // } // } } return(result); }
public static void thickenAndOffset(ref AXParameter sp, AXParameter src) { if (sp == null || src == null) { return; } //sp.polyTree = null; float thickness = sp.thickness; float roundness = sp.roundness; float offset = sp.offset; bool flipX = sp.flipX; //Debug.Log(sp.parametricObject.Name + "." + sp.Name +"."+ sp.offset); //bool srcIsCC = src.isCCW(); Paths subjPaths = src.getClonePaths(); if (subjPaths == null) { return; } // FLIP_X if (flipX) { //Debug.Log(subjPaths.Count); //AXGeometryTools.Utilities.printPaths(subjPaths); subjPaths = AXGeometryTools.Utilities.transformPaths(subjPaths, Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(-1, 1, 1))); sp.transformedControlPaths = AXGeometryTools.Utilities.transformPaths(sp.transformedControlPaths, Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(-1, 1, 1))); } // SYMMETRY if (sp.symmetry) { if (subjPaths != null && subjPaths.Count > 0) { for (int i = 0; i < subjPaths.Count; i++) { Path orig = subjPaths[i]; Path sym = new Path(); float seperation = sp.symSeperation * AXGeometryTools.Utilities.IntPointPrecision; float apex = .1f * seperation; for (int j = 0; j < orig.Count; j++) { sym.Add(new IntPoint(orig[j].X + seperation / 2, orig[j].Y)); } // midpoint. slightly raised sym.Add(new IntPoint(0, orig[orig.Count - 1].Y + apex)); for (int j = orig.Count - 1; j >= 0; j--) { sym.Add(new IntPoint(-orig[j].X - seperation / 2, orig[j].Y)); } subjPaths[i] = sym; } } } AX.Generators.Generator2D gener2D = sp.parametricObject.generator as AX.Generators.Generator2D; if (subjPaths != null && gener2D != null && (gener2D.scaleX != 1 || gener2D.scaleY != 1)) { sp.transformedButUnscaledOutputPaths = AX.Generators.Generator2D.transformPaths(subjPaths, gener2D.localUnscaledMatrix); } else { sp.transformedButUnscaledOutputPaths = null; } //cleanPolygonPrecision IntRect brect = Clipper.GetBounds(subjPaths); if ((brect.right - brect.left) < 10000) { cleanPolygonPrecision = 2; } else { cleanPolygonPrecision = 30; } //Debug.Log("cleanPolygonPrecision="+cleanPolygonPrecision); /* * if (offset_p.FloatVal == 0 && wallthick_p.FloatVal == 0) * { * sp.polyTree = src.polyTree; * sp.paths = src.paths; * return; * } * */ //sp.polyTree = null; //Debug.Log("new count a = " + subjPaths[0].Count); bool hasOffset = false; sp.hasThickness = false; Paths resPaths = new Paths(); AXClipperLib.PolyTree resPolytree = null; ClipperOffset co = new ClipperOffset(); //co.ArcTolerance = sp.arcTolerance; float smooth = (float)(120 / (sp.arcTolerance * sp.arcTolerance)); float smoothLOD = ((smooth - .048f) * sp.parametricObject.model.segmentReductionFactor) + .048f; co.ArcTolerance = (float)(Mathf.Sqrt(120 / smoothLOD)); co.MiterLimit = 2.0f; //if (offset != 0) // 1. Offset? Can't offset an open shape if (sp.shapeState == ShapeState.Closed && (sp.endType == AXClipperLib.EndType.etClosedLine || sp.endType == AXClipperLib.EndType.etClosedPolygon)) { //AXClipperLib.JoinType jt = (sp.endType == AXClipperLib.EndType.etClosedLine) ? JoinType.jtMiter : sp.joinType;//output.joinType; AXClipperLib.JoinType jt = (sp.parametricObject.model.segmentReductionFactor < .15f) ? AXClipperLib.JoinType.jtSquare : sp.joinType; //output.joinType; //Debug.Log ("sp.endType="+sp.endType+", jt="+jt); if (roundness != 0) { // reduce co.Clear(); if (subjPaths != null) { co.AddPaths(AXGeometryTools.Utilities.cleanPaths(subjPaths, cleanPolygonPrecision), jt, AXClipperLib.EndType.etClosedPolygon); //JoinType.jtSquare, AXClipperLib.EndType.etClosedPolygon); } co.Execute(ref subjPaths, (double)(-roundness * AXGeometryTools.Utilities.IntPointPrecision)); } offset += roundness; if (subjPaths != null) { subjPaths = Clipper.SimplifyPolygons(subjPaths, PolyFillType.pftNonZero); } co.Clear(); hasOffset = true; // if (offset != 0 || thickness == 0) { // --! PUC ** Removed because the pass thru was causing a problem with Instance2D of a ShapeMerger // Do Offset // = true; if (subjPaths != null) { // After changes made mid April to allow for FlipX, this started doubling the localMatrix and thus became redundent, though not sure why. //if (gener2D != null) // sp.transformedAndScaledButNotOffsetdOutputPaths = AX.Generators.Generator2D.transformPaths(subjPaths, gener2D.localMatrix); co.AddPaths(AXGeometryTools.Utilities.cleanPaths(subjPaths, cleanPolygonPrecision), jt, AXClipperLib.EndType.etClosedPolygon); //JoinType.jtSquare, AXClipperLib.EndType.etClosedPolygon); // this resPolytree has transformed curves in it resPolytree = new AXClipperLib.PolyTree(); co.Execute(ref resPolytree, (double)(offset * AXGeometryTools.Utilities.IntPointPrecision)); } // } if (thickness > 0) { // No offset, but is closed sp.transformedAndScaledButNotOffsetdOutputPaths = null; if (src.polyTree != null) { if (thickness > 0) { resPaths = subjPaths; // Clipper.PolyTreeToPaths(src.polyTree); } else { resPolytree = src.polyTree; } } else { resPaths = subjPaths; } } } else { //resPolytree = src.polyTree; if (src.polyTree != null) { if (thickness > 0) { resPaths = subjPaths; // Clipper.PolyTreeToPaths(src.polyTree); } else { resPolytree = src.polyTree; } } else { resPaths = subjPaths; } } // 2. Thickness? //subjPaths = sp.getPaths(); if ((sp.endType != AXClipperLib.EndType.etClosedPolygon) && thickness > 0) //input.endType != AXClipperLib.EndType.etClosedPolygon) { { // this is a wall if (resPaths != null && gener2D != null) { sp.transformedFullyAndOffsetdButNotThickenedOutputPaths = AX.Generators.Generator2D.transformPaths(resPaths, gener2D.localMatrix); } sp.hasThickness = true; co.Clear(); if (resPolytree != null) // closed block has happened { co.AddPaths(AXGeometryTools.Utilities.cleanPaths(Clipper.PolyTreeToPaths(resPolytree), cleanPolygonPrecision), sp.joinType, sp.endType); //input.endType); } else if (resPaths != null) { co.AddPaths(AXGeometryTools.Utilities.cleanPaths(resPaths, cleanPolygonPrecision), sp.joinType, sp.endType); //input.endType); } resPolytree = new AXClipperLib.PolyTree(); co.Execute(ref resPolytree, (double)(thickness * AXGeometryTools.Utilities.IntPointPrecision)); } else { sp.transformedFullyAndOffsetdButNotThickenedOutputPaths = null; } // 3. Update input data //Debug.Log(sp.parametricObject.Name + "." + sp.Name + " here ["+hasOffset+"] " + (! sp.symmetry) + " " + (! flipX) + " " + (! hasOffset) + " " + (! hasThicken) + " " + (roundness == 0)); // SIMPLE PASSTHRU? if (!sp.symmetry && !flipX && !hasOffset && !sp.hasThickness && roundness == 0) { // SIMPLE PASS THROUGH sp.polyTree = src.polyTree; sp.paths = src.paths; } else { if (resPolytree == null) { //sp.paths = resPaths; //Generator2D.transformPaths(resPaths, gener2D.localMatrix); //if (Clipper.Orientation(resPaths[0]) != srcIsCC) // AXGeometryTools.Utilities.reversePaths(ref resPaths); sp.paths = AXGeometryTools.Utilities.cleanPaths(resPaths, cleanPolygonPrecision); } else { //Generator2D.transformPolyTree(resPolytree, gener2D.localMatrix); //if (resPolytree != null && resPolytree.Childs.Count > 0 && Clipper.Orientation(resPolytree.Childs[0].Contour) != srcIsCC) // AXGeometryTools.Utilities.reversePolyTree(resPolytree); sp.polyTree = resPolytree; } } // REVERSE if (sp.reverse) { if (sp.polyTree != null) { AXGeometryTools.Utilities.reversePolyTree(sp.polyTree); } else if (sp.paths != null && sp.paths.Count > 0) { for (int i = 0; i < sp.paths.Count; i++) { sp.paths[i].Reverse(); } } } // if (sp.paths != null && sp.paths.Count > 0) // { // // SUBDIVISION // Debug.Log("sp.paths.Count="+sp.paths.Count); // // for(int i=0; i<sp.paths.Count; i++) // { // // // Path path = sp.paths[i]; // Path subdivPath = new Path(); // // for (int j=0; j<path.Count-1; j++) // { // subdivPath.Add(path[j]); // Vector2 v0 = new Vector2(path[j].X, path[j].Y); // Vector2 v1 = new Vector2(path[j+1].X, path[j+1].Y); // // Debug.Log("["+i+"]["+j+"] " + Vector2.Distance(v0, v1)/10000); // Vector2 newp = Vector2.Lerp(v0, v1, .5f); // // subdivPath.Add(new IntPoint(newp.x, newp.y)); // } // subdivPath.Add(path[path.Count-1]); // // // sp.paths[i] = subdivPath; // // Debug.Log("------------"); // AXGeometryTools.Utilities.printPath(sp.paths[i]); // } // // SUBDIVISION --- // } // }
/// <summary> /// Clears all paths from the ClipperOffset object, allowing new paths to be assigned. /// </summary> public void Clear() { clipperOffset.Clear(); }
/// <summary> /// Gets the Isovist polygon. /// </summary> /// <param name="vantagePoint">The vantage point.</param> /// <param name="viewDepth">The view depth.</param> /// <param name="edges">The edges.</param> /// <returns>BarrierPolygons.</returns> public BarrierPolygon IsovistPolygon(UV vantagePoint, double viewDepth, HashSet <UVLine> edges) { /*first and expand and shrink operation is performed to merge the shadowing edges*/ double expandShrinkFactor = Math.Pow(10.0, this.PolygonalBooleanPrecision) * UnitConversion.Convert(0.075, Length_Unit_Types.FEET, UnitType); //offsetting the excluded area of each edge INTPolygons offsetedPolygons = new INTPolygons(); ClipperOffset clipperOffset = new ClipperOffset(); foreach (UVLine edgeItem in edges) { clipperOffset.AddPath(this.excludedArea(vantagePoint, viewDepth + 1, edgeItem), ClipperLib.JoinType.jtMiter, EndType.etClosedPolygon); INTPolygons plygns = new INTPolygons(); clipperOffset.Execute(ref plygns, expandShrinkFactor); offsetedPolygons.AddRange(plygns); clipperOffset.Clear(); } //Unioning the expanded exclusions INTPolygons offsetUnioned = new INTPolygons(); Clipper c = new Clipper(); c.AddPaths(offsetedPolygons, PolyType.ptSubject, true); c.Execute(ClipType.ctUnion, offsetUnioned, PolyFillType.pftNonZero, PolyFillType.pftNonZero); //shrink the polygons to retain their original size INTPolygons results = new INTPolygons(); clipperOffset.Clear(); clipperOffset.AddPaths(offsetUnioned, JoinType.jtMiter, EndType.etClosedPolygon); clipperOffset.Execute(ref results, -expandShrinkFactor); clipperOffset.Clear(); offsetUnioned.Clear(); /* * What ever is a hole in the resulting mereged polygon is the visibility polygon * Now we classify the polygons based on being a hole or not */ //filtering out the holes that do not include the center INTPolygons holesNOT = new INTPolygons(); INTPolygons holesIncludingCenter = new INTPolygons(); IntPoint iCenter = ConvertUVToIntPoint(vantagePoint); foreach (INTPolygon item in results) { if (!Clipper.Orientation(item)) { if (Clipper.PointInPolygon(iCenter, item) == 1) { holesIncludingCenter.Add(item); } } else { holesNOT.Add(item); } } if (holesIncludingCenter.Count == 0) { //there is no hole. The shadow polygones should clip the potential field of visibility (i.e. circle) by an subtraction operation INTPolygon circle = createCircle(vantagePoint, viewDepth); //subtraction c.Clear(); c.AddPath(circle, PolyType.ptSubject, true); c.AddPaths(holesNOT, PolyType.ptClip, true); INTPolygons isovistPolygon = new INTPolygons(); c.Execute(ClipType.ctDifference, isovistPolygon); //searching for a polygon that includes the center foreach (INTPolygon item in isovistPolygon) { if (Clipper.PointInPolygon(iCenter, item) == 1) { BarrierPolygon isovist = this.ConvertINTPolygonToBarrierPolygon(item); results = null; c = null; clipperOffset = null; offsetedPolygons = null; circle = null; holesNOT = null; holesIncludingCenter = null; isovistPolygon = null; return(isovist); } } MessageBox.Show(string.Format("Isovist not found!\nNo hole detected\n{0} polygons can be isovist", isovistPolygon.Count.ToString())); } else if (holesIncludingCenter.Count == 1) { INTPolygons isovistPolygon = holesIncludingCenter; foreach (INTPolygon item in isovistPolygon) { if (Clipper.PointInPolygon(iCenter, item) == 1) { item.Reverse(); BarrierPolygon isovist = this.ConvertINTPolygonToBarrierPolygon(item); results = null; c = null; clipperOffset = null; offsetedPolygons = null; holesNOT = null; holesIncludingCenter = null; isovistPolygon = null; return(isovist); } } MessageBox.Show(string.Format("Isovist not found!\nOne hole detected\n{0} polygons can be isovist", isovistPolygon.Count.ToString())); } else if (holesIncludingCenter.Count > 1) { MessageBox.Show("Isovist not found!\nMore than one hole found that can include the vantage point"); } return(null); }
public override void Calculate() { ResultPaths = new List <Polyline>(); if (Stock == null || Stock.Count < 1 || DriveGeometry == null || DriveGeometry.Count < 1) { throw new Exception("Stock or drive geometry not set!"); } BoundingBox bb = Stock[0].GetBoundingBox(Workplane); foreach (Mesh m in Stock) { bb = BoundingBox.Union(bb, m.GetBoundingBox(Workplane)); } if (bb.IsDegenerate(0.1) > 0) { throw new Exception("Bounding Box is degenerate.");// return; } Point3d top = bb.Center; top.Z = bb.Corner(false, false, false).Z; Point3d bottom = bb.Center; bottom.Z = bb.Corner(true, true, true).Z; double TotalDepth = top.DistanceTo(bottom); int N = (int)(Math.Ceiling(Math.Min(MaxDepth, TotalDepth) / Tool.StepDown)); Plane CuttingPlane = new Plane(Workplane); Point3d top_xform = new Point3d(top); top_xform.Transform(Transform.PlaneToPlane(Plane.WorldXY, Workplane)); CuttingPlane.Origin = top_xform; Tuple <CPaths, CPaths, CPaths> Polygons; Polygons = GeneratePolygons(CuttingPlane); CPaths Shadow = new CPaths(Polygons.Item1); double Area = PathsArea(Shadow); for (int i = 0; i <= N; ++i) { // for each layer CuttingPlane.Origin = CuttingPlane.Origin - Workplane.ZAxis * Tool.StepDown; Polygons = GeneratePolygons(CuttingPlane, Shadow); if (Polygons == null) { throw new Exception("Failed to generate polygons!"); } double AreaNew = PathsArea(Polygons.Item1); if (AreaNew > Area) { Shadow = new CPaths(Polygons.Item1); Area = AreaNew; } ClipperOffset offset = new ClipperOffset(0.25, 0.25); offset.AddPaths(Polygons.Item3, JoinType.jtMiter, EndType.etClosedPolygon); PolyTree tree = new PolyTree(); offset.Execute(ref tree, -(Tool.Diameter / 2 + RestHorizontal) / Tolerance); CPaths WorkingPaths = new CPaths(); List <Polyline> Output = new List <Polyline>(); foreach (PolyNode pn in tree.Iterate()) { if (pn.Contour.Count > 0) { Output.Add(pn.Contour.ToPolyline(CuttingPlane, Tolerance, true)); WorkingPaths.Add(pn.Contour); } } int counter = 0; do { offset.Clear(); offset.AddPaths(WorkingPaths, JoinType.jtMiter, EndType.etClosedPolygon); offset.Execute(ref tree, -Tool.StepOver / Tolerance); WorkingPaths = new List <List <IntPoint> >(); foreach (PolyNode pn in tree.Iterate()) { if (pn.Contour.Count > 0) { Output.Add(pn.Contour.ToPolyline(CuttingPlane, Tolerance, true)); WorkingPaths.Add(pn.Contour); } } counter++; }while (tree.Total > 0 && counter < LOOP_LIMIT); ResultPaths.AddRange(Output); } ShadowPolylines = new List <Polyline>(); foreach (CPath p in Shadow) { ShadowPolylines.Add(p.ToPolyline(CuttingPlane, Tolerance, true)); } }
Polygon OffsetPolygon(float offset, bool simplify, bool rounded) { // Calculate Polygon-Clipper scale. float maximum = Mathf.Max(bounds.width, bounds.height) + offset * 2.0f + offset; float maximumScale = (float)Int32.MaxValue / maximum; float scale = Mathf.Min(clipperScale, maximumScale); // Convert to Clipper. Paths paths = new Paths(); { Path path = new Path(); EnumeratePoints((Vector2 eachPoint) => { path.Add(new IntPoint(eachPoint.x * scale, eachPoint.y * scale)); }); paths.Add(path); } foreach (Polygon eachPolygon in polygons) { Path path = new Path(); eachPolygon.EnumeratePoints((Vector2 eachPoint) => { path.Add(new IntPoint(eachPoint.x * scale, eachPoint.y * scale)); }); paths.Add(path); } // Mode. JoinType joinType = (rounded) ? JoinType.jtRound : JoinType.jtMiter; // Clipper offset. Paths offsetPaths = new Paths(); ClipperOffset clipperOffset = new ClipperOffset(); if (rounded) { clipperOffset.ArcTolerance = 0.25 * clipperArcTolerance; } // "The default ArcTolerance is 0.25 units." from http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/Properties/ArcTolerance.htm clipperOffset.AddPaths(paths, joinType, EndType.etClosedPolygon); clipperOffset.Execute(ref offsetPaths, (double)offset * scale); clipperOffset.Clear(); //Clipper.CleanPolygon( paths [ 0 ] ); // Remove self intersections (if requested). if (simplify) { offsetPaths = Clipper.SimplifyPolygons(offsetPaths); } // Convert from Clipper. Polygon offsetPolygon = null; for (int index = 0; index < offsetPaths.Count; index++) { Path eachSolutionPath = offsetPaths[index]; Polygon eachSolutionPolygon = PolygonFromClipperPath(eachSolutionPath, scale); if (index == 0) { offsetPolygon = Polygon.PolygonWithPoints(eachSolutionPolygon.points); // Copy } else { offsetPolygon.AddPolygon(eachSolutionPolygon); } } // Back to Polygon. return(offsetPolygon); }