/** * 对应于JS项目中的getParts */ public static List <NestPath> BuildTree(List <NestPath> parts, double curve_tolerance) { List <NestPath> polygons = new List <NestPath>(); for (int i = 0; i < parts.Count; i++) { NestPath cleanPoly = NestPath.cleanNestPath(parts[i]); cleanPoly.bid = parts[i].bid; if (cleanPoly.size() > 2 && Math.Abs(GeometryUtil.polygonArea(cleanPoly)) > curve_tolerance * curve_tolerance) { cleanPoly.setSource(i); polygons.Add(cleanPoly); } } CommonUtil.toTree(polygons, 0); return(polygons); }
/** * 根据板件列表与旋转角列表,通过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); }