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); }
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 --- // } // }