diff --git a/src/Editor/CommandRegister.ts b/src/Editor/CommandRegister.ts index 9ebd218fe..64f2238fd 100644 --- a/src/Editor/CommandRegister.ts +++ b/src/Editor/CommandRegister.ts @@ -208,6 +208,7 @@ import { RenderType } from "../GraphicsSystem/RenderType"; import { Command_TestDrawYHData } from "../Nest/Test/TestDrawYHData"; import { Command_TestNFP } from "../Nest/Test/TestNFP"; import { Command_TestPlace } from "../Nest/Test/TestPlace"; +import { Command_TestParseOddments } from "../Nest/Test/TestPraseOddments"; import { Command_TestSaveYHData } from "../Nest/Test/TestSaveYHData"; import { Command_TestSimply } from "../Nest/Test/TestSimply"; import { Command_TestSimplyOddments } from "../Nest/Test/TestSimplyOddments"; @@ -516,6 +517,7 @@ export function registerCommand() commandMachine.RegisterCommand("testYHSave", new Command_TestSaveYHData()); commandMachine.RegisterCommand("testSum", new Command_TestSum()); commandMachine.RegisterCommand("TestSimplyOddments", new Command_TestSimplyOddments()); + commandMachine.RegisterCommand("TestParseOddments", new Command_TestParseOddments());//分析余料 } commandMachine.RegisterCommand("editorlattice", new EditorLattice()); diff --git a/src/Nest/Common/Box2.ts b/src/Nest/Common/Box2.ts index d3669d7bf..b0b70595b 100644 --- a/src/Nest/Common/Box2.ts +++ b/src/Nest/Common/Box2.ts @@ -22,12 +22,12 @@ export class Box2 this.max.copy(max); return this; } - setFromPoints(points: Vector2[]): Box2 + setFromPoints(points: Iterable): Box2 { this.makeEmpty(); - for (let i = 0, il = points.length; i < il; i++) + for (let p of points) { - this.expandByPoint(points[i]); + this.expandByPoint(p); } return this; } @@ -69,7 +69,7 @@ export class Box2 { return this.isEmpty() ? result.set(0, 0) : result.subVectors(this.max, this.min); } - expandByPoint(point: Vector2): Box2 + expandByPoint(point: Point): Box2 { this.min.min(point); this.max.max(point); diff --git a/src/Nest/Common/Vector2.ts b/src/Nest/Common/Vector2.ts index 0d5b283ec..ba4635a56 100644 --- a/src/Nest/Common/Vector2.ts +++ b/src/Nest/Common/Vector2.ts @@ -136,13 +136,13 @@ export class Vector2 { return this.multiplyScalar(1 / scalar); } - min(v: Vector2): Vector2 + min(v: Point): Vector2 { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); return this; } - max(v: Vector2): Vector2 + max(v: Point): Vector2 { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); diff --git a/src/Nest/Core/ParseOddments.ts b/src/Nest/Core/ParseOddments.ts index a2835578d..99540c488 100644 --- a/src/Nest/Core/ParseOddments.ts +++ b/src/Nest/Core/ParseOddments.ts @@ -1,4 +1,5 @@ import { ClipInput, ClipType, EndType, JoinType, PolyFillType } from "js-angusj-clipper/web"; +import { Box2 } from "../Common/Box2"; import { clipperCpp } from "../Common/ClipperCpp"; import { Container } from "./Container"; import { NestCache } from "./NestCache"; @@ -36,20 +37,90 @@ export function ParseOddments(container: Container, binPath: Path, knifeRadius: }); //所有的余料(使用布尔差集) - let oddmentsPolygon = clipperCpp.lib.clipToPaths({ + let oddmentsPolygon = clipperCpp.lib.clipToPolyTree({ subjectInputs: [{ data: binPath.BigIntPoints, closed: true }], clipInputs: partPaths, clipType: ClipType.Difference, subjectFillType: PolyFillType.NonZero }); + //现在我们用树状结构,应该不会自交了?(文档写了,返回的结果不可能重叠或者自交) //简化结果,避免自交 - oddmentsPolygon = clipperCpp.lib.simplifyPolygons(oddmentsPolygon); + // oddmentsPolygon = clipperCpp.lib.simplifyPolygons(oddmentsPolygon); + + function CreatePolygon(minx: number, miny: number, maxx: number, maxy: number) + { + return [ + { x: minx, y: miny }, + { x: maxx, y: miny }, + { x: maxx, y: maxy }, + { x: minx, y: maxy }, + ]; + } + + let splitPolygons: Path[] = []; + //由于手动排版可能造成余料网洞,我们将网洞的盒子投影,然后裁剪余料,避免余料有网洞 + for (let node of oddmentsPolygon.childs) + { + let nodePolygon = node.contour; + //减去网洞 + if (node.childs.length) + { + let box = new Box2().setFromPoints(nodePolygon); + + let childBoxPolygon = node.childs.map(cnode => + { + let cbox = new Box2().setFromPoints(cnode.contour); + let type = 0;//0左1右2上3下 + let minDist = Infinity; + + let letftDist = cbox.min.x - box.min.x; + let rightDist = box.max.x - cbox.max.x; + let topDist = box.max.y - cbox.max.y; + let downDist = cbox.min.y - box.min.y; + + if (rightDist < letftDist) + { + type = 1; + minDist = rightDist; + } + + if (topDist < minDist) + { + type = 2; + minDist = topDist; + } + + if (downDist < minDist) + type = 3; + + if (type === 0) + return CreatePolygon(box.min.x, cbox.min.y, cbox.max.x, cbox.max.y); + if (type === 1) + return CreatePolygon(cbox.min.x, cbox.min.y, box.max.x, cbox.max.y); + if (type === 2) + return CreatePolygon(cbox.min.x, cbox.min.y, cbox.max.x, box.max.y); + if (type === 3) + return CreatePolygon(cbox.min.x, box.min.y, cbox.max.x, cbox.max.y); + }); + + let splits = clipperCpp.lib.clipToPaths({ + subjectInputs: [{ data: nodePolygon, closed: true }], + clipInputs: childBoxPolygon.map(polygon => { return { data: polygon }; }), + clipType: ClipType.Difference, + subjectFillType: PolyFillType.NonZero + }); + + for (let p of splits) + splitPolygons.push(new Path(PathScale(p, 1e-4))); + } + else + splitPolygons.push(new Path(node.contour.map(p => { return { x: p.x * 1e-4, y: p.y * 1e-4 }; }),)); + } let OddmentsPaths: Path[] = []; - for (let polygon of oddmentsPolygon) + for (let polygonPath of splitPolygons) { - let polygonPath = new Path(PathScale(polygon, 1e-4)); //先获取内部的nfp let insideNFPS = polygonPath.GetInsideNFP(squarePath); diff --git a/src/Nest/Test/TestPraseOddments.ts b/src/Nest/Test/TestPraseOddments.ts new file mode 100644 index 000000000..8ffd1a840 --- /dev/null +++ b/src/Nest/Test/TestPraseOddments.ts @@ -0,0 +1,46 @@ +import { TestDraw } from "../../Add-on/test/TestUtil"; +import { app } from "../../ApplicationServices/Application"; +import { Polyline } from "../../DatabaseServices/Entity/Polyline"; +import { Command } from "../../Editor/CommandMachine"; +import { PromptStatus } from "../../Editor/PromptResult"; +import { AsVector2, AsVector3 } from "../../Geometry/GeUtils"; +import { HotCMD } from "../../Hot/HotCommand"; +import { InitClipperCpp } from "../Common/ClipperCpp"; +import { Path2Polyline } from "../Converter/Path2Polyline"; +import { Container } from "../Core/Container"; +import { ParseOddments } from "../Core/ParseOddments"; +import { Part } from "../Core/Part"; +import { Path } from "../Core/Path"; + +@HotCMD +export class Command_TestParseOddments implements Command +{ + async exec() + { + InitClipperCpp(); + let ssRes = await app.Editor.GetSelection({ Filter: { filterTypes: [Polyline] } }); + if (ssRes.Status !== PromptStatus.OK) return; + let ents = ssRes.SelectSet.SelectEntityList as Polyline[]; + + let binPath = new Path(ents[0].GetStretchPoints(), 0); + let container = new Container(); + for (let i = 1; i < ents.length; i++) + { + let part = new Part(); + let path = new Path(ents[i].GetStretchPoints(), 0); + part.Init2(path, binPath, [0]); + part.PlacePosition = AsVector2(path.OrigionMinPoint).multiplyScalar(1e4); + container.PlacedParts.push(part); + } + + let binP = AsVector3(binPath.OrigionMinPoint).multiplyScalar(1e4); + + let odd = ParseOddments(container, binPath); + for (let p of odd) + { + let pl = Path2Polyline(p.Points); + pl.Position = AsVector3(p.OrigionMinPoint).add(binP); + TestDraw(pl, 2); + } + } +}