public static bool addSteinerPointsAtOffset(ref Polygon _polygon, ref ClipperOffset co, float offset, int seglenBigInt) { PolyTree resPolytree = new AXClipperLib.PolyTree(); co.Execute(ref resPolytree, (double)(-offset * AXGeometryTools.Utilities.IntPointPrecision)); Paths paths = Clipper.PolyTreeToPaths(resPolytree); if (paths != null && paths.Count > 0 && paths[0] != null && paths[0].Count > 0) { foreach (Path path in paths) { if (path != null && path.Count > 0) { Path ppp = Pather.segmentPath(path, seglenBigInt); if (ppp != null && ppp.Count > 0) { foreach (IntPoint ip in ppp) { _polygon.AddSteinerPoint(new TriangulationPoint((double)ip.X / (double)AXGeometryTools.Utilities.IntPointPrecision, (double)ip.Y / (double)AXGeometryTools.Utilities.IntPointPrecision)); } } } } return(true); } return(false); }
// TRANSFORM_POLY_TREE public static void shiftPolyTree(AXClipperLib.PolyTree polyTree, IntPoint ip) { if (polyTree == null) { return; } if (polyTree.Childs != null && polyTree.Childs.Count > 0) { shiftPolyNode(polyTree.Childs, ip); } }
public static PolyNode mergeSubjPathAndClipPath(Path subj_p, Path clip_p) { Clipper c = new Clipper(Clipper.ioPreserveCollinear); c.AddPath(subj_p, PolyType.ptSubject, true); c.AddPath(clip_p, PolyType.ptClip, true); PolyTree polyTree = new AXClipperLib.PolyTree(); c.Execute(ClipType.ctDifference, polyTree, PolyFillType.pftNonZero, PolyFillType.pftNonZero); return(polyTree); }
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); }
// RAIL public void genrateRail() { // generate a rail for all three: difference, intersection, union if (inputs.Count == 0) { return; } PolyFillType subjPolyFillType = PolyFillType.pftNonZero; //if (hasHoles) //subjPolyFillType = PolyFillType.pftPositive; //* organize by solids, holes (from SOLID designation) and clippers (from VOID designation) Paths subjPaths = new Paths(); Paths clipPaths = new Paths(); AXParameter inp = null; AXParameter src = null; Paths segments = new Paths(); Path tmp = null; for (int i = 0; i < inputs.Count; i++) { inp = inputs [i]; src = inp.DependsOn; if (src == null) { continue; } Paths srcPaths = src.getPaths(); if (srcPaths == null) { continue; } if (inp.polyType == PolyType.ptSubject) { subjPaths.AddRange(srcPaths); foreach (Path sp in srcPaths) { // When clipping open shapes, don't add a closingsegment: int ender = (inp.shapeState == ShapeState.Open) ? sp.Count - 1 : sp.Count; for (int j = 0; j < ender; j++) { int next_j = (j == sp.Count - 1) ? 0 : j + 1; tmp = new Path(); tmp.Add(sp[j]); tmp.Add(sp[next_j]); segments.Add(tmp); } } //subjPaths.AddRange (src.getTransformedHolePaths()); } else if (inp.polyType == PolyType.ptClip) { clipPaths.AddRange(srcPaths); //clipPaths.AddRange (src.getTransformedHolePaths()); } else { continue; } } // turn subjs and holes into segments to be clipped // foreach(Path sp in subjPaths) // { // // each path // //int ender = // for(int i=0; i<sp.Count-1; i++) // { // int next_i = (i == sp.Count-1) ? 0 : i+1; // tmp = new Path(); // tmp.Add (sp[i]); // tmp.Add (sp[next_i]); // segments.Add(tmp); // } // } //Debug.Log ("segments"); //Archimatix.printPaths(segments); Clipper railc = new Clipper(Clipper.ioPreserveCollinear); if (segments != null) { railc.AddPaths(segments, PolyType.ptSubject, false); } if (clipPaths != null) { railc.AddPaths(clipPaths, PolyType.ptClip, true); } AXClipperLib.PolyTree solutionRail = new AXClipperLib.PolyTree(); // DIFFERENCE_RAIL if ((differenceRail.Dependents != null && differenceRail.Dependents.Count > 0) || combineType == CombineType.DifferenceRail) { railc.Execute(ClipType.ctDifference, solutionRail, subjPolyFillType, PolyFillType.pftNonZero); differenceRail.polyTree = null; differenceRail.paths = assembleRailPathsFromClippedSegments(solutionRail); if (differenceRail.paths.Count == 0) { differenceRail.paths = subjPaths; } alignPathsDirestionsWithSource(ref differenceRail.paths, ref segments); thickenAndOffset(ref differenceRail, differenceRail); } // INTERSECTION RAIL if ((intersectionRail.Dependents != null && intersectionRail.Dependents.Count > 0) || combineType == CombineType.IntersectionRail) { //railc.Execute(ClipType.ctIntersection, solutionRail, subjPolyFillType, PolyFillType.pftNonZero); railc.Execute(ClipType.ctIntersection, solutionRail, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd); intersectionRail.polyTree = null; intersectionRail.paths = assembleRailPathsFromClippedSegments(solutionRail); if (intersectionRail.paths.Count == 0) { AXGeometryTools.Utilities.reversePaths(ref intersectionRail.paths); } alignPathsDirestionsWithSource(ref intersectionRail.paths, ref segments); thickenAndOffset(ref intersectionRail, intersectionRail); } }
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 --- // } // }
// This is used with rails since the Clipper Lib does not support // difference with the lines of the clip object removed. // In other words, to open a door in a closed polygon, // we use this to remove the edges that were originally on the clip object // leaving only the line segments of the subject that were not inside the clip shape. public Paths assembleRailPathsFromClippedSegments(AXClipperLib.PolyTree solutionRail) { // Take all the clipped segments and connect Paths clippedSegments = Clipper.OpenPathsFromPolyTree(solutionRail); //Debug.Log ("clipped segments " + clippedSegments.Count); //Archimatix.printPaths(clippedSegments); Dictionary <Path, AXSegment> axsegs = new Dictionary <Path, AXSegment>(); foreach (Path p in clippedSegments) { axsegs.Add(p, new AXSegment(p)); } int cc = 0; foreach (Path clipseg in clippedSegments) { //Debug.Log ("PASS["+cc+"] " + AXGeometryTools.Utilities.pathToString(clipseg)); if (axsegs[clipseg].prev != null && axsegs[clipseg].next != null) { //Debug.Log("Already done"); cc++; continue; } int ccc = 0; foreach (Path compseg in clippedSegments) { if (compseg == clipseg) { //Debug.Log("COMP["+cc+"]["+ccc+"] skip same"); ccc++; continue; } //Debug.Log ("COMP["+cc+"]["+ccc+"]"+AXGeometryTools.Utilities.pathToString(clipseg)+ " with "+AXGeometryTools.Utilities.pathToString(compseg)); if (axsegs[clipseg].prev == null && axsegs[compseg].next == null) { if (AXSegment.pointsAreEqual(clipseg[0], compseg[1])) { axsegs[clipseg].prev = axsegs[compseg]; axsegs[compseg].next = axsegs[clipseg]; //Debug.Log ("prev"); } } if (axsegs[clipseg].prev == null && axsegs[compseg].prev == null) { if (AXSegment.pointsAreEqual(clipseg[0], compseg[0])) { compseg.Reverse(); axsegs[clipseg].prev = axsegs[compseg]; axsegs[compseg].next = axsegs[clipseg]; } } if (axsegs[clipseg].next == null && axsegs[compseg].prev == null) { if (AXSegment.pointsAreEqual(clipseg[1], compseg[0])) { axsegs[clipseg].next = axsegs[compseg]; axsegs[compseg].prev = axsegs[clipseg]; } } if (axsegs[clipseg].next == null && axsegs[compseg].next == null) { if (AXSegment.pointsAreEqual(clipseg[1], compseg[1])) { compseg.Reverse(); axsegs[clipseg].next = axsegs[compseg]; axsegs[compseg].prev = axsegs[clipseg]; } } ccc++; } cc++; } // now all segs should be linked. // go through each that has no prev and create new paths... Paths retPaths = new Paths(); foreach (KeyValuePair <Path, AXSegment> item in axsegs) { Path path = item.Key; if (item.Value.prev == null) { // now crawl up the nexts adding pt[1]s AXSegment next = item.Value; while ((next = next.next) != null) { path.Add(next.path[1]); } retPaths.Add(path); } } return(retPaths); }
// GENERATE WINWALL public override GameObject generate(bool makeGameObjects, AXParametricObject initiator_po, bool renderToOutputParameter) { if (parametricObject == null || !parametricObject.isActive) { return(null); } //Debug.Log("PlanSweep::generate()"); // RESULTING MESHES ax_meshes = new List <AXMesh>(); preGenerate(); // PLAN // The plan may have multiple paths. Each may generate a separate GO. if (P_Plan == null) { return(null); } planSrc_p = getUpstreamSourceParameter(P_Plan); planSrc_po = (planSrc_p != null) ? planSrc_p.parametricObject : null; if (planSrc_p == null || !planSrc_p.parametricObject.isActive) { return(null); } planIsClosed = (P_Plan.hasThickness || P_Plan.shapeState == ShapeState.Closed) ? true : false; P_Plan.polyTree = null; Paths planPaths = planSrc_p.getPaths(); Path planPath = planPaths[0]; Spline planSpline = new Spline(planPath, planIsClosed, P_Plan.breakGeom, P_Plan.breakNorm); Paths offsetPaths = Pather.wallOffsets(planSpline, .5f, .5f); Pather.printPaths(offsetPaths); // each path, step through and mak a rectangle // segment wide and height and then subtract windows. //Then make poly and add to combiner Path window = AXTurtle.Rectangle(1, 1, false); //window.Reverse(); Pather.shiftPath(window, new IntPoint(10000, 5000)); Debug.Log("=========="); Pather.printPath(window); Pather rightPather = new Pather(offsetPaths[0]); int[] rightLengths = rightPather.segment_lengths; for (int i = 0; i < rightLengths.Length; i++) { Debug.Log(rightLengths[i]); int next_i = (i == rightLengths.Length - 1) ? 0 : i + 1; Path rect = AXTurtle.Rectangle(rightLengths[next_i] / 10000f, 3, false); Clipper c = new Clipper(); c.AddPath(rect, PolyType.ptSubject, true); // fenestration c.AddPath(window, PolyType.ptClip, true); AXClipperLib.PolyTree polytree = new AXClipperLib.PolyTree(); c.Execute(ClipType.ctDifference, polytree, PolyFillType.pftNonZero, PolyFillType.pftNonZero); Paths pathResult = Clipper.PolyTreeToPaths(polytree); Pather.printPaths(pathResult); Mesh mesh = AXPolygon.triangulate(polytree, new AXTexCoords()); Matrix4x4 wallm = Matrix4x4.TRS(new Vector3(offsetPaths[0][i].X / 10000f, 0, offsetPaths[0][i].Y / 10000f), Quaternion.Euler(-90, planSpline.edgeRotations[i], 0), Vector3.one); ax_meshes.Add(new AXMesh(mesh, wallm)); } parametricObject.finishMultiAXMeshAndOutput(ax_meshes, renderToOutputParameter); // FINISH BOUNDING setBoundaryFromAXMeshes(ax_meshes); if (makeGameObjects) { return(parametricObject.makeGameObjectsFromAXMeshes(ax_meshes)); } return(null); }