public static List <NestPath> polygonOffset(NestPath polygon, double offset) { List <NestPath> result = new List <NestPath>(); if (offset == 0 || GeometryUtil.almostEqual(offset, 0)) { /** * return EmptyResult */ return(result); } Path p = new Path(); foreach (Segment s in polygon.getSegments()) { ClipperCoor cc = toClipperCoor(s.getX(), s.getY()); p.Add(new IntPoint(cc.getX(), cc.getY())); } int miterLimit = 2; ClipperOffset co = new ClipperOffset(miterLimit, Config.CURVE_TOLERANCE * Config.CLIIPER_SCALE); co.AddPath(p, JoinType.jtRound, EndType.etClosedPolygon); Paths newpaths = new Paths(); co.Execute(ref newpaths, offset * Config.CLIIPER_SCALE); /** * 这里的length是1的话就是我们想要的 */ for (int i = 0; i < newpaths.Count; i++) { result.Add(CommonUtil.clipperToNestPath(newpaths[i])); } if (offset > 0) { NestPath from = result[0]; if (GeometryUtil.polygonArea(from) > 0) { from.reverse(); } from.add(from.get(0)); from.getSegments().RemoveAt(0); } return(result); }
public static List <String> svgGenerator(List <NestPath> list, List <List <Placement> > applied, double binwidth, double binHeight) { List <String> strings = new List <String>(); int x = 10; int y = 0; foreach (List <Placement> binlist in applied) { String s = " <g transform=\"translate(" + x + " " + y + ")\">" + "\n"; s += " <rect x=\"0\" y=\"0\" width=\"" + binwidth + "\" height=\"" + binHeight + "\" fill=\"none\" stroke=\"#010101\" stroke-width=\"1\" />\n"; foreach (Placement placement in binlist) { int bid = placement.bid; NestPath nestPath = getNestPathByBid(bid, list); double ox = placement.translate.x; double oy = placement.translate.y; double rotate = placement.rotate; s += "<g transform=\"translate(" + ox + x + " " + oy + y + ") rotate(" + rotate + ")\"> \n"; s += "<path d=\""; for (int i = 0; i < nestPath.getSegments().Count; i++) { if (i == 0) { s += "M"; } else { s += "L"; } Segment segment = nestPath.get(i); s += segment.x + " " + segment.y + " "; } s += "Z\" fill=\"#8498d1\" stroke=\"#010101\" stroke-width=\"1\" />" + " \n"; s += "</g> \n"; } s += "</g> \n"; y += (int)(binHeight + 50); strings.Add(s); } return(strings); }
/** * 根据板件列表与旋转角列表,通过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)); }
/** * 开始进行Nest计算 * @return */ public List <List <Placement> > startNest() { List <NestPath> tree = CommonUtil.BuildTree(parts, Config.CURVE_TOLERANCE); CommonUtil.offsetTree(tree, 0.5 * config.SPACING); binPath.config = config; foreach (NestPath nestPath in parts) { nestPath.config = config; } NestPath binPolygon = NestPath.cleanNestPath(binPath); Bound binBound = GeometryUtil.getPolygonBounds(binPolygon); if (config.SPACING > 0) { List <NestPath> offsetBin = CommonUtil.polygonOffset(binPolygon, -0.5 * config.SPACING); if (offsetBin.Count == 1) { binPolygon = offsetBin[0]; } } binPolygon.setId(-1); List <int> integers = checkIfCanBePlaced(binPolygon, tree); List <NestPath> safeTree = new List <NestPath>(); foreach (int i in integers) { safeTree.Add(tree[i]); } tree = safeTree; double xbinmax = binPolygon.get(0).x; double xbinmin = binPolygon.get(0).x; double ybinmax = binPolygon.get(0).y; double ybinmin = binPolygon.get(0).y; for (int i = 1; i < binPolygon.size(); i++) { if (binPolygon.get(i).x > xbinmax) { xbinmax = binPolygon.get(i).x; } else if (binPolygon.get(i).x < xbinmin) { xbinmin = binPolygon.get(i).x; } if (binPolygon.get(i).y > ybinmax) { ybinmax = binPolygon.get(i).y; } else if (binPolygon.get(i).y < ybinmin) { ybinmin = binPolygon.get(i).y; } } for (int i = 0; i < binPolygon.size(); i++) { binPolygon.get(i).x -= xbinmin; binPolygon.get(i).y -= ybinmin; } double binPolygonWidth = xbinmax - xbinmin; double binPolygonHeight = ybinmax - ybinmin; if (GeometryUtil.polygonArea(binPolygon) > 0) { binPolygon.reverse(); } /** * 确保为逆时针 */ for (int i = 0; i < tree.Count; i++) { Segment start = tree[i].get(0); Segment end = tree[i].get(tree[i].size() - 1); if (start == end || GeometryUtil.almostEqual(start.x, end.x) && GeometryUtil.almostEqual(start.y, end.y)) { tree[i].pop(); } if (GeometryUtil.polygonArea(tree[i]) > 0) { tree[i].reverse(); } } launchcount = 0; Result best = null; // Tree Modification based on nest4J //List<NestPath> modifiedTree = new List<NestPath>(); //for (int i = 0; i < tree.Count; i++) //{ // List<Segment> modifiedSegment = new List<Segment>(); // NestPath currentTree = tree[i]; // List<Segment> currentTreeSegments = currentTree.getSegments(); // modifiedSegment.Add(currentTreeSegments[currentTreeSegments.Count-1]); // for (int j = 0; j < currentTreeSegments.Count-1; j++) // { // modifiedSegment.Add(currentTreeSegments[j]); // } // currentTree.setSegments(modifiedSegment); // modifiedTree.Add(currentTree); //} //tree = modifiedTree; for (int i = 0; i < loopCount; i++) { Result result = launchWorkers(tree, binPolygon, config); if (i == 0) { best = result; } else { if (best.fitness > result.fitness) { best = result; } } } double sumarea = 0; double totalarea = 0; for (int i = 0; i < best.placements.Count; i++) { totalarea += Math.Abs(GeometryUtil.polygonArea(binPolygon)); for (int j = 0; j < best.placements[i].Count; j++) { try { sumarea += Math.Abs(GeometryUtil.polygonArea(tree[best.placements[i][j].id])); } catch (Exception ex) { } } } double rate = (sumarea / totalarea) * 100; List <List <Placement> > appliedPlacement = applyPlacement(best, tree); return(appliedPlacement); }