public void alignPathsDirectionsWithSource(ref Paths segs, ref Paths sourceSegs) { // The assumption is that a seg in segs will be on only one of the sourceSegs. foreach (Path seg in segs) { if (seg == null || seg.Count < 2) { continue; } IntPoint first = seg[0]; IntPoint second = seg[1]; bool foundSource = false; foreach (Path src in sourceSegs) { // setp through each src line to see if first falls on it. for (int k = 0; k < src.Count - 1; k++) { if (PointOnLineSegment(first, src[k], src[k + 1], true)) { //Debug.Log(first.X + ", "+ first.Y + " on line " + src[k].X + ", " +src[k].Y + "->" +src[k+1].X + ", " + src[k+1].Y); if (AXSegment.pointsAreEqual(second, src[k]) || (Pather.Distance(src[k], second) < Pather.Distance(src[k], first))) { foundSource = true; seg.Reverse(); } break; } } if (foundSource) { break; } } } }
/// <summary> /// Home-grown offsetter that can handle open paths (which clipper can't) /// and returns paths for left and right. /// </summary> /// <returns>The offsets.</returns> /// <param name="planSpline">Plan spline.</param> /// <param name="thickR">Thick r.</param> /// <param name="thickL">Thick l.</param> public static Paths wallOffsets(Spline planSpline, float thickR, float thickL) { /* * When generating a PlanSweep, we need to know the breaking angles of each plan layer. * This is because the original plan node might be convex at a certain section offset, it is concave, depending on how far out that section goes. * * To do this: * 1. for each section node, do an offset using clipper - or use bevelAngles of the orginnal plan and make a new spline, whichever is more efficient * 2. store these offset plans as AX.Splines in a list. * 3. use these Splines for the isSharp and isBlend conditionals * */ if (planSpline == null || planSpline.controlVertices == null || planSpline.controlVertices.Count == 0) { return(null); } // if (tex == null) // return null; Paths paths = new Paths(); Path pathR = new Path(); Path pathL = new Path(); float samePointTolerence = .001f; int terminIndexSubtractor = 0; if (Vector2.Distance(planSpline.controlVertices[0], planSpline.controlVertices[planSpline.controlVertices.Count - 1]) < samePointTolerence) { terminIndexSubtractor = 1; } Matrix4x4 prevBevelTransform; for (int i = 0; i < planSpline.controlVertices.Count - terminIndexSubtractor; i++) { Matrix4x4 bevelTransform = planSpline.nodeTransforms[i]; if (planSpline.shapeState == ShapeState.Open) { if (i == 0) { bevelTransform = planSpline.begTransform; } else if (i == planSpline.controlVertices.Count - 1) { bevelTransform = planSpline.endTransform; } } // Transform plan vert Vector3 vertr = bevelTransform.MultiplyPoint(new Vector3(thickR, 0, 0)); Debug.Log(vertr); pathR.Add(Pather.Vector2IPWithPrecision(new Vector2(vertr.x, vertr.z))); Vector3 vertl = bevelTransform.MultiplyPoint(new Vector3(thickL, 0, 0)); pathL.Add(Pather.Vector2IPWithPrecision(new Vector2(vertl.x, vertl.z))); } paths.Add(pathR); paths.Add(pathL); return(paths); }
public static Mesh triangulatePolyNode(PolyNode node, AXTexCoords tex, int seglenBigInt = 1000000) { //Debug.Log ("D " + seglenBigInt); Polygon _polygon = null; if (seglenBigInt < 10) { seglenBigInt = 999999; } List <Mesh> meshes = new List <Mesh>(); // Contour is Solid PolygonPoints _points = null; if (seglenBigInt > 0 && seglenBigInt != 9999999) { _points = AXGeometryTools.Utilities.path2polygonPts(Pather.segmentPath(Clipper.CleanPolygon(node.Contour), seglenBigInt)); } else { _points = AXGeometryTools.Utilities.path2polygonPts(Clipper.CleanPolygon(node.Contour)); } // POLYGON if (_points.Count >= 3) { _polygon = new Polygon(_points); } //Debug.Log ("_polygon="+_points.Count); // ADD HOLES TO POLYGON foreach (PolyNode subnode in node.Childs) { PolygonPoints hpoints = null; if (seglenBigInt > 0 && seglenBigInt != 9999999) { hpoints = AXGeometryTools.Utilities.path2polygonPts(Pather.segmentPath(Clipper.CleanPolygon(subnode.Contour), seglenBigInt)); } else { hpoints = AXGeometryTools.Utilities.path2polygonPts(Clipper.CleanPolygon(subnode.Contour)); } if (hpoints.Count >= 3) { _polygon.AddHole(new Polygon(hpoints)); } } try { // STEINER POINTS ClipperOffset co = new ClipperOffset(); co.AddPath(node.Contour, AXClipperLib.JoinType.jtSquare, AXClipperLib.EndType.etClosedPolygon); //addSteinerPointsAtAllOffsets(ref _polygon, ref co, seglenBigInt/AXGeometryTools.Utilities.IntPointPrecision, seglenBigInt); addSteinerPointsAtAllOffsets(ref _polygon, ref co, (float)seglenBigInt / ((float)AXGeometryTools.Utilities.IntPointPrecision), seglenBigInt); P2T.Triangulate(_polygon); meshes.Add(polygon2mesh(_polygon, tex)); } catch { //Debug.Log ("Can't triangulate: probably point on edge."); } // Continue down the tree... /* * foreach(PolyNode cnode in node.Childs) * { * Mesh submesh = triangulatePolyNode(cnode, tex); * if (submesh != null) * meshes.Add(submesh); * } */ CombineInstance[] combine = new CombineInstance[meshes.Count]; for (int i = 0; i < meshes.Count; i++) { combine[i].mesh = meshes[i]; combine[i].transform = Matrix4x4.identity; } Mesh mesh = new Mesh(); mesh.CombineMeshes(combine); mesh.RecalculateNormals(); return(mesh); }
/* bridges to poly2tri * */ public static Mesh triangulate(Path path, AXTexCoords tex, int seglen = 0) { /* Assume a single path with no holes * and return a mesh. */ if (path == null || path.Count < 3) { return(null); } if (path[path.Count - 1].X == path[0].X && path[path.Count - 1].Y == path[0].Y) { path.RemoveAt(path.Count - 1); } else if (AXGeometryTools.Utilities.IntPointsAreNear(path[0], path[path.Count - 1])) { path.RemoveAt(path.Count - 1); } //Paths tmpPaths = Clipper.SimplifyPolygon (path); //CombineInstance[] combinator = new CombineInstance[tmpPaths.Count]; //for (int i = 0; i < tmpPaths.Count; i++) { Mesh mesh = null; PolygonPoints _points; // = AXGeometryTools.Utilities.path2polygonPts (Pather.cleanPath(path)); if (seglen > 0) { _points = AXGeometryTools.Utilities.path2polygonPts(Pather.segmentPath(Clipper.CleanPolygon(path), seglen)); } else { _points = AXGeometryTools.Utilities.path2polygonPts(Clipper.CleanPolygon(path)); } Polygon _polygon = null; if (_points.Count >= 3) { _polygon = new Polygon(_points); if (_polygon != null) { try { // Testing Steiner // for (int j = -10; j<10; j++) // { // for (int k = -10; k<10; k++) // _polygon.AddSteinerPoint(new TriangulationPoint(.1f*j, k*.1f)); // } P2T.Triangulate(_polygon); //foreach (DelaunayTriangle triangle in _polygon.Triangles) mesh = polygon2mesh(_polygon, tex); } catch { Debug.Log("Can't triangulate: probably point on edge."); } } //combinator[i].mesh = mesh; //combinator [i].transform = Matrix4x4.identity; //return mesh; //} } // Mesh returnMesh = new Mesh(); // returnMesh.CombineMeshes(combinator); // return returnMesh; return(mesh); }
public static Mesh triangulate(List <PolyNode> childs, AXTexCoords tex, int seglenBigInt = 1000000) { //Debug.Log ("C " + seglenBigInt); Polygon _polygon = null; List <Mesh> meshes = new List <Mesh>(); if (seglenBigInt < 10) { seglenBigInt = 100000; } //int count = 0; foreach (PolyNode node in childs) { // Contour is Solid // List<TriangulationPoint> tripoints = new List<TriangulationPoint>(); // // // Testing Steiner // int cells = 6; // for (int j = -cells/2; j<cells/2; j++) // { // for (int k = -cells/3; k<cells/2; k++) // if (Clipper.PointInPolygon( AXGeometryTools.Utilities.Vec2_2_IntPt(new Vector2(2f*j, k*2f)), node.Contour) > 0) // { // //Debug.Log("add steiner " + .4f*j +", " + k*.2f); // tripoints.Add(new TriangulationPoint(.4f*j, k*.2f)); // } // } PolygonPoints _points = null; if (seglenBigInt > 0 && seglenBigInt != 9999999) { _points = AXGeometryTools.Utilities.path2polygonPts(Pather.segmentPath(Clipper.CleanPolygon(node.Contour), seglenBigInt)); } else { _points = AXGeometryTools.Utilities.path2polygonPts(Clipper.CleanPolygon(node.Contour)); } // POLYGON if (_points.Count >= 3) { _polygon = new Polygon(_points); } // ADD HOLES TO POLYGON foreach (PolyNode subnode in node.Childs) { PolygonPoints hpoints = null; if (seglenBigInt > 0 && seglenBigInt != 9999999) { hpoints = AXGeometryTools.Utilities.path2polygonPts(Pather.segmentPath(Clipper.CleanPolygon(subnode.Contour), seglenBigInt)); } else { hpoints = AXGeometryTools.Utilities.path2polygonPts(Clipper.CleanPolygon(subnode.Contour)); } if (hpoints.Count >= 3) { _polygon.AddHole(new Polygon(hpoints)); } } try { // STEINER POINTS ClipperOffset co = new ClipperOffset(); co.AddPath(node.Contour, AXClipperLib.JoinType.jtSquare, AXClipperLib.EndType.etClosedPolygon); addSteinerPointsAtAllOffsets(ref _polygon, ref co, (float)seglenBigInt / ((float)AXGeometryTools.Utilities.IntPointPrecision), seglenBigInt); P2T.Triangulate(_polygon); meshes.Add(polygon2mesh(_polygon, tex)); } catch { //Debug.Log ("Can't triangulate: probably point on edge."); } // Continue down the tree... foreach (PolyNode cnode in node.Childs) { Mesh submesh = triangulate(cnode, tex); if (submesh != null) { meshes.Add(submesh); } } } CombineInstance[] combine = new CombineInstance[meshes.Count]; for (int i = 0; i < meshes.Count; i++) { combine[i].mesh = meshes[i]; combine[i].transform = Matrix4x4.identity; } Mesh mesh = new Mesh(); mesh.CombineMeshes(combine); mesh.RecalculateNormals(); return(mesh); }
// 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) { // Execute Difference railc.Execute(ClipType.ctDifference, solutionRail, subjPolyFillType, PolyFillType.pftNonZero); differenceRail.polyTree = null; differenceRail.paths = assembleRailPathsFromClippedSegments(solutionRail); // Debug.Log("******** " + differenceRail.paths.Count); // Pather.printPaths(differenceRail.paths); if (differenceRail.paths.Count == 0) { differenceRail.paths = subjPaths; } if (differenceRail.paths.Count > 1) { differenceRail.paths = Pather.cleanPaths(differenceRail.paths); } // Debug.Log("-- " + differenceRail.paths.Count); // Pather.printPaths(differenceRail.paths); alignPathsDirectionsWithSource(ref differenceRail.paths, ref segments); if (differenceRail.paths.Count > 1) { joinPathsIfEndpointsMatch(ref differenceRail.paths); } 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); } alignPathsDirectionsWithSource(ref intersectionRail.paths, ref segments); thickenAndOffset(ref intersectionRail, intersectionRail); } }