public void CleanPolygonsTest() { var polygon = new Polygon(); var length = 500; for (int i = 0; i < 360; i++) { var angle = MathHelper.DegreesToRadians(i); polygon.Add(new IntPoint(Math.Cos(angle) * length, Math.Sin(angle) * length)); } var cleanedPolygon = Clipper.CleanPolygon(polygon, 2); var originalPolygons = new Polygons() { polygon }; var cleanPolygons = new Polygons() { cleanedPolygon }; //this.WriteSvg("before.svg", originalPolygons.CreateVertexStorage(1).GetSvgDString()); //this.WriteSvg("after.svg", cleanPolygons.CreateVertexStorage(1).GetSvgDString()); // Ensure mid-point between points is within threshold for (int i = 0; i < cleanedPolygon.Count - 1; i++) { var centerPoint = (cleanedPolygon[i + 1] + cleanedPolygon[i]) / 2; var distToCenter = centerPoint.Length(); Assert.AreEqual(length, distToCenter, 3); } }
public static Mesh triangulate(Paths paths, AXTexCoords tex) { //Debug.Log ("B"); List <Mesh> meshes = new List <Mesh>(); foreach (Path path in paths) { meshes.Add(AXPolygon.triangulate(Clipper.CleanPolygon(path), tex)); } // combine 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); }
public static void Prune(this PolyNode polyTree, double actorRadius, double areaPruneThreshold = kMinAreaPrune) { var cleaned = Clipper.CleanPolygon(polyTree.Contour, actorRadius / 5 + 2); if (cleaned.Count > 0) { polyTree.Contour.Clear(); polyTree.Contour.AddRange(cleaned); } for (var i = polyTree.Childs.Count - 1; i >= 0; i--) { var child = polyTree.Childs[i]; var childArea = Math.Abs(Clipper.Area(child.Contour)); if (childArea < areaPruneThreshold) { // Console.WriteLine("Prune: " + Clipper.Area(child.Contour) + " " + child.Contour.Count); polyTree.Childs.RemoveAt(i); continue; } var kMinimumChildRelativeArea = 1 / 1000.0; // prev 1 / 1000, 1 / 1000000 child.Prune(actorRadius, Math.Max(kMinAreaPrune, childArea * kMinimumChildRelativeArea)); } }
public void CleanPolygons() { // remove a single point that is going to be coincident { List <IntPoint> testPath = new List <IntPoint>(); testPath.Add(new IntPoint(0, 0)); testPath.Add(new IntPoint(5, 0)); testPath.Add(new IntPoint(11, 0)); testPath.Add(new IntPoint(5, 20)); List <IntPoint> cleanedPath = Clipper.CleanPolygon(testPath, 10); Assert.IsTrue(cleanedPath.Count == 3); } // don't remove a non collinear point { List <IntPoint> testPath = new List <IntPoint>(); testPath.Add(new IntPoint(0, 0)); testPath.Add(new IntPoint(50, 5)); testPath.Add(new IntPoint(100, 0)); testPath.Add(new IntPoint(50, 200)); List <IntPoint> cleanedPath = Clipper.CleanPolygon(testPath, 4); Assert.IsTrue(cleanedPath.Count == 4); } // now remove that point with a higher tolerance { List <IntPoint> testPath = new List <IntPoint>(); testPath.Add(new IntPoint(0, 0)); testPath.Add(new IntPoint(50, 5)); testPath.Add(new IntPoint(100, 0)); testPath.Add(new IntPoint(50, 200)); List <IntPoint> cleanedPath = Clipper.CleanPolygon(testPath, 6); Assert.IsTrue(cleanedPath.Count == 3); } // now remove a bunch of points { int mergeDist = 10; List <IntPoint> testPath = new List <IntPoint>(); testPath.Add(new IntPoint(0, 0)); Random randY = new Random(0); for (int i = 2; i < 58; i++) // for (int i = 2; i < 98; i++) { testPath.Add(new IntPoint(i, (int)(randY.NextDouble() * mergeDist - mergeDist / 2))); } testPath.Add(new IntPoint(100, 0)); testPath.Add(new IntPoint(50, 200)); List <IntPoint> cleanedPath = Clipper.CleanPolygon(testPath, mergeDist); Assert.IsTrue(cleanedPath.Count == 3); //Assert.IsTrue(cleanedPath.Contains(new IntPoint(0, 0))); //Assert.IsTrue(cleanedPath.Contains(new IntPoint(100, 0))); //Assert.IsTrue(cleanedPath.Contains(new IntPoint(50, 200))); } }
// GROUPER::GENERATE public override GameObject generate(bool makeGameObjects, AXParametricObject initiator_po, bool isReplica) { //if (ArchimatixUtils.doDebug) //Debug.Log (parametricObject.Name + " generate +++++++++++++++++++++++++++++++++++++++++++++++++ " + inputs.Count); if (!parametricObject.isActive) { return(null); } preGenerate(); Path path = new Path(); if (inputs != null && inputs.Count > 0) { for (int i = 0; i < inputs.Count; i++) { AXParameter input = inputs[i]; if (input != null && input.DependsOn != null) { if (input.DependsOn.parametricObject.generator is FreeCurve) { FreeCurve fc = (FreeCurve)input.DependsOn.parametricObject.generator; Path output = AX.Generators.Generator2D.transformPath(fc.getPathFromCurve(), fc.localMatrix); path.AddRange(output); } else { Paths tmpPaths = input.DependsOn.getPaths(); if (tmpPaths != null) { for (int j = 0; j < tmpPaths.Count; j++) { path.AddRange(tmpPaths[j]); } } } } } path = Clipper.CleanPolygon(path); P_Output.paths = new Paths(); P_Output.paths.Add(path); } return(null); }
//将自相交多边形转化为简单多边形 public static NestPath cleanNestPath(NestPath srcPath) { /** * Convert NestPath 2 Clipper */ Path path = CommonUtil.NestPath2Path(srcPath); Paths simple = Clipper.SimplifyPolygon(path, PolyFillType.pftEvenOdd); if (simple.Count == 0) { return(null); } Path biggest = simple[0]; double biggestArea = Math.Abs(Clipper.Area(biggest)); for (int i = 0; i < simple.Count; i++) { double area = Math.Abs(Clipper.Area(simple[i])); if (area > biggestArea) { biggest = simple[i]; biggestArea = area; } } //Path clean = biggest.Cleaned(Config.CURVE_TOLERANCE * Config.CLIIPER_SCALE); Path clean = Clipper.CleanPolygon(biggest, Config.CURVE_TOLERANCE * Config.CLIIPER_SCALE); if (clean.Count == 0) { return(null); } /** * Convert Clipper 2 NestPath */ NestPath cleanPath = CommonUtil.Path2NestPath(clean); cleanPath.bid = srcPath.bid; cleanPath.setRotation(srcPath.rotation); return(cleanPath); }
/// <summary> /// This will find the largest turn in a given models. It prefers concave turns to convex turns. /// If turn amount is the same bias towards the smallest y position. /// </summary> /// <param name="inputPolygon"></param> /// <param name="considerAsSameY">Range to treat y positions as the same value.</param> /// <returns></returns> public static IntPoint FindGreatestTurnPosition(this Polygon inputPolygon, long considerAsSameY, int layerIndex, IntPoint?startPosition = null) { Polygon currentPolygon = Clipper.CleanPolygon(inputPolygon, considerAsSameY / 8); // collect & bucket options and then choose the closest if (currentPolygon.Count == 0) { return(inputPolygon[0]); } double totalTurns = 0; CandidateGroup positiveGroup = new CandidateGroup(DegreesToRadians(35)); CandidateGroup negativeGroup = new CandidateGroup(DegreesToRadians(10)); IntPoint currentFurthestBack = new IntPoint(long.MaxValue, long.MinValue); int furthestBackIndex = 0; double minTurnToChoose = DegreesToRadians(1); long minSegmentLengthToConsiderSquared = 50 * 50; int pointCount = currentPolygon.Count; for (int pointIndex = 0; pointIndex < pointCount; pointIndex++) { int prevIndex = ((pointIndex + pointCount - 1) % pointCount); int nextIndex = ((pointIndex + 1) % pointCount); IntPoint prevPoint = currentPolygon[prevIndex]; IntPoint currentPoint = currentPolygon[pointIndex]; IntPoint nextPoint = currentPolygon[nextIndex]; if (currentPoint.Y >= currentFurthestBack.Y) { if (currentPoint.Y > currentFurthestBack.Y || currentPoint.X < currentFurthestBack.X) { furthestBackIndex = pointIndex; currentFurthestBack = currentPoint; } } long lengthPrevToCurSquared = (prevPoint - currentPoint).LengthSquared(); long lengthCurToNextSquared = (nextPoint - currentPoint).LengthSquared(); bool distanceLongeEnough = lengthCurToNextSquared > minSegmentLengthToConsiderSquared && lengthPrevToCurSquared > minSegmentLengthToConsiderSquared; double turnAmount = currentPoint.GetTurnAmount(prevPoint, nextPoint); totalTurns += turnAmount; if (turnAmount < 0) { // threshold angles, don't pick angles that are too shallow // threshold line lengths, don't pick big angles hiding in TINY lines if (Math.Abs(turnAmount) > minTurnToChoose && distanceLongeEnough) { negativeGroup.ConditionalAdd(new CandidatePoint(turnAmount, pointIndex, currentPoint)); } } else { if (Math.Abs(turnAmount) > minTurnToChoose && distanceLongeEnough) { positiveGroup.ConditionalAdd(new CandidatePoint(turnAmount, pointIndex, currentPoint)); } } } if (negativeGroup.Count > 0) { if (positiveGroup.Count > 0 // the negative group is a small turn and the positive group is a big turn && ((Math.Abs(negativeGroup[0].turnAmount) < Math.PI / 4 && Math.Abs(positiveGroup[0].turnAmount) > Math.PI / 4) // the negative turn amount is very small || Math.Abs(negativeGroup[0].turnAmount) < Math.PI / 8)) { // return the positive rather than the negative turn return(currentPolygon[positiveGroup.GetBestIndex(layerIndex, startPosition)]); } return(currentPolygon[negativeGroup.GetBestIndex(layerIndex, startPosition)]); } else if (positiveGroup.Count > 0) { return(currentPolygon[positiveGroup.GetBestIndex(layerIndex, startPosition)]); } else { // If can't find good candidate go with vertex most in a single direction return(currentPolygon[furthestBackIndex]); } }
public static IntPoint GetBestPosition(Polygon inputPolygon, long lineWidth) { IntPoint currentFurthestBackActual = new IntPoint(long.MaxValue, long.MinValue); { int actualFurthestBack = 0; for (int pointIndex = 0; pointIndex < inputPolygon.Count; pointIndex++) { IntPoint currentPoint = inputPolygon[pointIndex]; if (currentPoint.Y >= currentFurthestBackActual.Y) { if (currentPoint.Y > currentFurthestBackActual.Y || currentPoint.X < currentFurthestBackActual.X) { actualFurthestBack = pointIndex; currentFurthestBackActual = currentPoint; } } } } Polygon currentPolygon = Clipper.CleanPolygon(inputPolygon, lineWidth / 4); // TODO: other considerations // collect & bucket options and then choose the closest if (currentPolygon.Count == 0) { return(inputPolygon[0]); } double totalTurns = 0; CandidateGroup positiveGroup = new CandidateGroup(DegreesToRadians(35)); CandidateGroup negativeGroup = new CandidateGroup(DegreesToRadians(10)); IntPoint currentFurthestBack = new IntPoint(long.MaxValue, long.MinValue); int furthestBackIndex = 0; double minTurnToChoose = DegreesToRadians(1); long minSegmentLengthToConsiderSquared = 50 * 50; int pointCount = currentPolygon.Count; for (int pointIndex = 0; pointIndex < pointCount; pointIndex++) { int prevIndex = ((pointIndex + pointCount - 1) % pointCount); int nextIndex = ((pointIndex + 1) % pointCount); IntPoint prevPoint = currentPolygon[prevIndex]; IntPoint currentPoint = currentPolygon[pointIndex]; IntPoint nextPoint = currentPolygon[nextIndex]; if (currentPoint.Y >= currentFurthestBack.Y) { if (currentPoint.Y > currentFurthestBack.Y || currentPoint.X < currentFurthestBack.X) { furthestBackIndex = pointIndex; currentFurthestBack = currentPoint; } } long lengthPrevToCurSquared = (prevPoint - currentPoint).LengthSquared(); long lengthCurToNextSquared = (nextPoint - currentPoint).LengthSquared(); bool distanceLongeEnough = lengthCurToNextSquared > minSegmentLengthToConsiderSquared && lengthPrevToCurSquared > minSegmentLengthToConsiderSquared; double turnAmount = GetTurnAmount(prevPoint, currentPoint, nextPoint); totalTurns += turnAmount; if (turnAmount < 0) { // threshold angles, don't pick angles that are too shallow // threshold line lengths, don't pick big angles hiding in TINY lines if (Math.Abs(turnAmount) > minTurnToChoose && distanceLongeEnough) { negativeGroup.ConditionalAdd(new CandidatePoint(turnAmount, pointIndex, currentPoint)); } } else { if (Math.Abs(turnAmount) > minTurnToChoose && distanceLongeEnough) { positiveGroup.ConditionalAdd(new CandidatePoint(turnAmount, pointIndex, currentPoint)); } } } IntPoint positionToReturn = new IntPoint(); if (totalTurns > 0) // ccw { if (negativeGroup.Count > 0) { positionToReturn = currentPolygon[negativeGroup.BestIndex]; } else if (positiveGroup.Count > 0) { positionToReturn = currentPolygon[positiveGroup.BestIndex]; } else { // If can't find good candidate go with vertex most in a single direction positionToReturn = currentPolygon[furthestBackIndex]; } } else // cw { if (negativeGroup.Count > 0) { positionToReturn = currentPolygon[negativeGroup.BestIndex]; } else if (positiveGroup.Count > 0) { positionToReturn = currentPolygon[positiveGroup.BestIndex]; } else { // If can't find good candidate go with vertex most in a single direction positionToReturn = currentPolygon[furthestBackIndex]; } } if (Math.Abs(currentFurthestBackActual.Y - positionToReturn.Y) < lineWidth) { return(currentFurthestBackActual); } return(positionToReturn); }
/** * 根据板件列表与旋转角列表,通过nfp,计算板件在底板上的位置,并返回这个种群的fitness * @param paths * @return */ public Result placePaths(List <NestPath> paths) { List <NestPath> rotated = new List <NestPath>(); for (int i = 0; i < paths.Count; i++) { NestPath r = GeometryUtil.rotatePolygon2Polygon(paths[i], paths[i].getRotation()); r.setRotation(paths[i].getRotation()); r.setSource(paths[i].getSource()); r.setId(paths[i].getId()); rotated.Add(r); } paths = rotated; List <List <Vector> > allplacements = new List <List <Vector> >(); double fitness = 0; double binarea = Math.Abs(GeometryUtil.polygonArea(this.binPolygon)); String key = null; List <NestPath> nfp = null; while (paths.Count > 0) { List <NestPath> placed = new List <NestPath>(); List <Vector> placements = new List <Vector>(); fitness += 1; double minwidth = Double.MaxValue; for (int i = 0; i < paths.Count; i++) { NestPath path = paths[i]; //inner NFP key = new JavaScriptSerializer().Serialize(new NfpKey(-1, path.getId(), true, 0, path.getRotation())); //key = gson.toJson(new NfpKey(-1, path.getId(), true, 0, path.getRotation())); if (!nfpCache.ContainsKey(key)) { continue; } List <NestPath> binNfp = nfpCache[key]; // ensure exists bool error = false; for (int j = 0; j < placed.Count; j++) { key = new JavaScriptSerializer().Serialize(new NfpKey(placed[j].getId(), path.getId(), false, placed[j].getRotation(), path.getRotation())); // key = gson.toJson(new NfpKey(placed[j].getId(), path.getId(), false, placed[j].getRotation(), path.getRotation())); if (nfpCache.ContainsKey(key)) { nfp = nfpCache[key]; } else { error = true; break; } } if (error) { continue; } Vector position = null; if (placed.Count == 0) { // first placement , put it on the lefth for (int j = 0; j < binNfp.Count; j++) { for (int k = 0; k < binNfp[j].size(); k++) { if (position == null || binNfp[j].get(k).x - path.get(0).x < position.x) { position = new Vector( binNfp[j].get(k).x - path.get(0).x, binNfp[j].get(k).y - path.get(0).y, path.getId(), path.getRotation() ); } } } placements.Add(position); placed.Add(path); continue; } Paths clipperBinNfp = new Paths(); for (int j = 0; j < binNfp.Count; j++) { NestPath binNfpj = binNfp[j]; clipperBinNfp.Add(scaleUp2ClipperCoordinates(binNfpj)); } Clipper clipper = new Clipper(); Paths combinedNfp = new Paths(); for (int j = 0; j < placed.Count; j++) { key = new JavaScriptSerializer().Serialize(new NfpKey(placed[j].getId(), path.getId(), false, placed[j].getRotation(), path.getRotation())); //key = gson.toJson(new NfpKey(placed[j].getId(), path.getId(), false, placed[j].getRotation(), path.getRotation())); nfp = nfpCache[key]; if (nfp == null) { continue; } for (int k = 0; k < nfp.Count; k++) { Path clone = PlacementWorker.scaleUp2ClipperCoordinates(nfp[k]); for (int m = 0; m < clone.Count; m++) { long clx = (long)clone[m].X; long cly = (long)clone[m].Y; IntPoint intPoint = clone[m]; intPoint.X = (clx + (long)(placements[j].x * Config.CLIIPER_SCALE)); intPoint.Y = (cly + (long)(placements[j].y * Config.CLIIPER_SCALE)); clone[m] = intPoint; } //clone = clone.Cleaned(0.0001 * Config.CLIIPER_SCALE); clone = Clipper.CleanPolygon(clone, 0.0001 * Config.CLIIPER_SCALE); double areaPoly = Math.Abs(Clipper.Area(clone)); if (clone.Count > 2 && areaPoly > 0.1 * Config.CLIIPER_SCALE * Config.CLIIPER_SCALE) { clipper.AddPath(clone, PolyType.ptSubject, true); } } } if (!clipper.Execute(ClipType.ctUnion, combinedNfp, PolyFillType.pftNonZero, PolyFillType.pftNonZero)) { continue; } //difference with bin polygon Paths finalNfp = new Paths(); clipper = new Clipper(); clipper.AddPaths(combinedNfp, PolyType.ptClip, true); clipper.AddPaths(clipperBinNfp, PolyType.ptSubject, true); if (!clipper.Execute(ClipType.ctDifference, finalNfp, PolyFillType.pftNonZero, PolyFillType.pftNonZero)) { continue; } // finalNfp = finalNfp.Cleaned(0.0001 * Config.CLIIPER_SCALE); finalNfp = Clipper.CleanPolygons(finalNfp, 0.0001 * Config.CLIIPER_SCALE); for (int j = 0; j < finalNfp.Count(); j++) { //double areaPoly = Math.Abs(finalNfp[j].Area); double areaPoly = Math.Abs(Clipper.Area(finalNfp[j])); if (finalNfp[j].Count < 3 || areaPoly < 0.1 * Config.CLIIPER_SCALE * Config.CLIIPER_SCALE) { finalNfp.RemoveAt(j); j--; } } if (finalNfp == null || finalNfp.Count == 0) { continue; } List <NestPath> f = new List <NestPath>(); for (int j = 0; j < finalNfp.Count; j++) { f.Add(toNestCoordinates(finalNfp[j])); } List <NestPath> finalNfpf = f; double minarea = Double.MinValue; double minX = Double.MaxValue; NestPath nf = null; double area = Double.MinValue; Vector shifvector = null; for (int j = 0; j < finalNfpf.Count; j++) { nf = finalNfpf[j]; if (Math.Abs(GeometryUtil.polygonArea(nf)) < 2) { continue; } for (int k = 0; k < nf.size(); k++) { NestPath allpoints = new NestPath(); for (int m = 0; m < placed.Count; m++) { for (int n = 0; n < placed[m].size(); n++) { allpoints.add(new Segment(placed[m].get(n).x + placements[m].x, placed[m].get(n).y + placements[m].y)); } } shifvector = new Vector(nf.get(k).x - path.get(0).x, nf.get(k).y - path.get(0).y, path.getId(), path.getRotation(), combinedNfp); for (int m = 0; m < path.size(); m++) { allpoints.add(new Segment(path.get(m).x + shifvector.x, path.get(m).y + shifvector.y)); } Bound rectBounds = GeometryUtil.getPolygonBounds(allpoints); area = rectBounds.getWidth() * 2 + rectBounds.getHeight(); if (minarea == Double.MinValue || area < minarea || (GeometryUtil.almostEqual(minarea, area) && (minX == Double.MinValue || shifvector.x < minX))) { minarea = area; minwidth = rectBounds.getWidth(); position = shifvector; minX = shifvector.x; } } } if (position != null) { placed.Add(path); placements.Add(position); } } if (minwidth != Double.MinValue) { fitness += minwidth / binarea; } for (int i = 0; i < placed.Count; i++) { int index = paths.IndexOf(placed[i]); if (index >= 0) { paths.RemoveAt(index); } } if (placements != null && placements.Count > 0) { allplacements.Add(placements); } else { break; // something went wrong } } // there were paths that couldn't be placed fitness += 2 * paths.Count; return(new Result(allplacements, fitness, paths, binarea)); }
/// <summary> /// Create shell over the line /// </summary> /// <param name="polygons"></param> /// <param name="length"></param> /// <param name="scale"></param> /// <param name="threshold"></param> /// <returns></returns> public static List <List <Vector2> > MakeShell(this List <List <Vector2> > polygons, float length, float scale = 1000, float threshold = 0.2f) { List <List <IntPoint> > solution = new List <List <IntPoint> >(); ClipperOffset co = new ClipperOffset(); for (int i = 0; i < polygons.Count; ++i) { co.AddPath(ToIntPoint(polygons[i], scale), JoinType.jtRound, EndType.etClosedPolygon); } co.Execute(ref solution, length * scale); if (solution.Count == 0) { return(new List <List <Vector2> >()); } solution = Clipper.SimplifyPolygons(solution); if (solution.Count == 0) { return(new List <List <Vector2> >()); } for (int i = 0; i < solution.Count; ++i) { solution[i] = Clipper.CleanPolygon(solution[i], -1); } if (solution.Count == 0) { return(new List <List <Vector2> >()); } List <List <Vector2> > current = new List <List <Vector2> >(); for (int s = 0; s < solution.Count; ++s) { List <Vector2> level = ToVector2(solution[s], 1.0f / scale); for (int i = 1; i < level.Count; ++i) { if ((level[i] - level[i - 1]).magnitude < Mathf.Abs(length) * (1 - threshold)) { level.RemoveAt(i); i = 0; continue; } } if (level.Count <= 1) { return(new List <List <Vector2> >()); } if ((level[0] - level[level.Count - 1]).magnitude < Mathf.Abs(length) * (1 - threshold)) { level.RemoveAt(level.Count - 1); } if (level.Count <= 1) { return(new List <List <Vector2> >()); } for (int i = 1; i < level.Count; ++i) { if ((level[i] - level[i - 1]).magnitude > Mathf.Abs(length) * (1 + threshold)) { level.Insert(i, level[i - 1] + (level[i] - level[i - 1]) / 2 + Vector2.one * 0.01f); i = 0; continue; } } if ((level[0] - level[level.Count - 1]).magnitude > Mathf.Abs(length) * (1 + threshold)) { level.Insert(0, level[level.Count - 1] + (level[0] - level[level.Count - 1]) / 2 + Vector2.one * 0.01f); } current.Add(level); } return(current); }
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); }