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); }
/** * 开始进行Nest计算 * @return */ public List <List <Placement> > startNest() { //去除parts点中有孔的点,只对最外围的零件进行排样 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; //计算多边形的面积。如果面积值大于零,说明多边形方向为反方向,需要进行方向转换,但是为什么要做这个操作呢? 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; //放置所有零件的面积 //placements中的Count数据就代表了使用了几个底板的数量,如果一个底板大小不够放置所有零件,那系统会自动增加一个底板 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); }
/** * 开始进行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); }