From 4f37d3326c0112d87ba260dcf2a50214bfe1c1ae Mon Sep 17 00:00:00 2001 From: ChenX Date: Mon, 29 Mar 2021 18:33:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=80=E5=8F=91:=E8=A1=A5=E5=85=85=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __test__/Nest/__snapshots__/nfp.test.ts.snap | 63 ++++++++++++++ __test__/Nest/nfp.test.ts | 20 +++++ src/Nest/Converter/ConverBoard2Part.ts | 86 ++------------------ src/Nest/Converter/Curves2Parts.ts | 6 +- src/Nest/Converter/Curves2Points.ts | 77 ++++++++++++++++++ src/Nest/Test/TestNFP.ts | 2 +- src/Nest/Test/TestSum.ts | 3 +- 7 files changed, 173 insertions(+), 84 deletions(-) create mode 100644 __test__/Nest/__snapshots__/nfp.test.ts.snap create mode 100644 __test__/Nest/nfp.test.ts create mode 100644 src/Nest/Converter/Curves2Points.ts diff --git a/__test__/Nest/__snapshots__/nfp.test.ts.snap b/__test__/Nest/__snapshots__/nfp.test.ts.snap new file mode 100644 index 000000000..49c0633b0 --- /dev/null +++ b/__test__/Nest/__snapshots__/nfp.test.ts.snap @@ -0,0 +1,63 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`NFP结果错误 1`] = ` +Array [ + Array [ + Object { + "x": -1254001, + "y": 10174000, + }, + Object { + "x": -3154000, + "y": 10174000, + }, + Object { + "x": -3154000, + "y": -3784000, + }, + Object { + "x": 5054000, + "y": -3784000, + }, + Object { + "x": 5053999, + "y": 7010000, + }, + Object { + "x": 5053999, + "y": 10794000, + }, + Object { + "x": 1899999, + "y": 10794000, + }, + Object { + "x": -1254001, + "y": 10794000, + }, + ], +] +`; + +exports[`NFP结果错误 2`] = ` +Array [ + Array [ + Object { + "x": 1900000, + "y": 0, + }, + Object { + "x": 0, + "y": 0, + }, + Object { + "x": 0, + "y": 6390000, + }, + Object { + "x": 1899999, + "y": 6390000, + }, + ], +] +`; diff --git a/__test__/Nest/nfp.test.ts b/__test__/Nest/nfp.test.ts new file mode 100644 index 000000000..89d295ab2 --- /dev/null +++ b/__test__/Nest/nfp.test.ts @@ -0,0 +1,20 @@ +import { Polyline } from "../../src/DatabaseServices/Entity/Polyline"; +import { InitClipperCpp } from "../../src/Nest/Common/ClipperCpp"; +import { Curves2Points } from "../../src/Nest/Converter/Curves2Points"; +import { Path } from "../../src/Nest/Core/Path"; +import { LoadEntityFromFileData } from "../Utils/LoadEntity.util"; + +test('NFP结果错误', async () => +{ + await InitClipperCpp(); + let dd = { "file": [2, "Polyline", 8, 2, 120, false, 1, 10, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -1706.0998589562755, -98.9984767277852, 0, 1], 0, 0, true, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -2396.3998589562757, -476.89847672778524, 0, 1], 0, 2, 4, [0, 0], 0, [315.4, 0], 0, [315.4, 378.4], 0, [0, 378.4], 0, true, "Polyline", 8, 2, 121, false, 1, 5, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -1706.0998589562755, -477.39847672778524, 0, 1], 0, 0, true, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -2396.3998589562757, -476.89847672778524, 0, 1], 0, 2, 6, [505.3999, 1079.4], 0, [189.9999, 1079.4], 0, [189.9999, 1017.4000000000001], 0, [0, 1017.4000000000001], 0, [0, 0], 0, [505.40000000000003, 0], 0, true], "basePt": { "x": -1706.0998589562755, "y": -477.39847672778524, "z": 0 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }; + let d = dd.file; + let [pl1, pl2] = LoadEntityFromFileData(d) as [Polyline, Polyline]; + + let binPath = new Path(Curves2Points(pl2, false, 0)[1]); + + let path = new Path(Curves2Points(pl1, true, 0)[1]); + + expect(binPath.GetNFPs(path, true)).toMatchSnapshot(); + expect(binPath.GetNFPs(path, false)).toMatchSnapshot(); +}); diff --git a/src/Nest/Converter/ConverBoard2Part.ts b/src/Nest/Converter/ConverBoard2Part.ts index c7a93d400..6273b6f1d 100644 --- a/src/Nest/Converter/ConverBoard2Part.ts +++ b/src/Nest/Converter/ConverBoard2Part.ts @@ -1,88 +1,18 @@ -import { EndType, JoinType } from "js-angusj-clipper/web"; import { Matrix4 } from "three"; import { TestDraw } from "../../Add-on/test/TestUtil"; import { arrayRemoveDuplicateBySort } from "../../Common/ArrayExt"; -import { Arc } from "../../DatabaseServices/Entity/Arc"; import { Board } from "../../DatabaseServices/Entity/Board"; import { Circle } from "../../DatabaseServices/Entity/Circle"; import { Polyline } from "../../DatabaseServices/Entity/Polyline"; -import { equaln, equalv2, equalv3, polar, ZeroVec } from "../../Geometry/GeUtils"; -import { clipperCpp } from "../Common/ClipperCpp"; -import { Point } from "../Common/Point"; +import { equaln, equalv2, equalv3, ZeroVec } from "../../Geometry/GeUtils"; import { DefaultBin } from "../Core/DefaultBin"; import { GNestConfig } from "../Core/GNestConfig"; import { NestCache } from "../Core/NestCache"; import { Part } from "../Core/Part"; -import { Path, PathScale } from "../Core/Path"; -import { Circle2Points, CurveWrap } from "./Curves2Parts"; +import { Path } from "../Core/Path"; +import { CurveWrap } from "./Curves2Parts"; +import { Rotations, Polylin2Points } from "./Curves2Points"; -let Rotations = [ - [0, Math.PI], - [Math.PI / 2, Math.PI * 1.5], - [0, Math.PI, Math.PI / 2, Math.PI * 1.5], -]; - -/** - * 针对板件的曲线变点表做的特殊优化 - */ -export function Curves2Points(cu: Circle | Polyline, outside: boolean, knifRadius: number): [(Circle | Polyline), Point[]] -{ - if (cu instanceof Circle) - return [cu.Clone(), Circle2Points(cu, knifRadius, 8, outside)]; - else - return Polylin2Points(cu, outside, knifRadius); -} - -export function Polylin2Points(pl: Polyline, outside: boolean, knifRadius: number): [Polyline, Point[]] -{ - let pts: Point[] = []; - - if (!outside) knifRadius = -knifRadius; - if (pl.IsClockWise) pl.Reverse(); - for (let i = 0; i < pl.EndParam; i++) - { - pts.push(pl.GetPointAtParam(i)); - - let bul = pl.GetBuilgeAt(i); - if (bul !== 0) - { - let arc = pl.GetCurveAtIndex(i) as Arc; - - let allAngle = arc.AllAngle; - let arcLength = arc.Length; - - let minCount = Math.floor(allAngle * 4 / Math.PI); - - let splitCount = Math.round(allAngle / 0.4); - if (arcLength < 300) - splitCount = Math.max(2, minCount); - else - splitCount = Math.max(arcLength / 200, splitCount, 2, minCount); - - let radius = arc.Radius; - if (outside === bul > 0) - radius = radius / Math.cos(allAngle / (splitCount * 2)); - - let cp = arc.Center; - for (let j = 0.5; j < splitCount; j++) - { - let a = arc.GetAngleAtParam(j * (1 / splitCount)); - let p = polar(cp.clone(), a, radius); - pts.push(p); - } - } - } - - if (knifRadius !== 0) - { - pts = clipperCpp.lib.offsetToPaths({ - delta: knifRadius * 1e4, - offsetInputs: [{ data: PathScale(pts, 1e4), joinType: JoinType.Miter, endType: EndType.ClosedPolygon }] - })[0]; - PathScale(pts, 1e-4); - } - return [pl, pts]; -} export function ConverBoard2Part(board: Board, knifRadius = 3.5): Part { @@ -99,6 +29,7 @@ export function ConverBoard2Part(board: Board, knifRadius = 3.5): Part path = NestCache.CreatePath(board.Width, board.Height, knifRadius); part.Init2(path, DefaultBin, Rotations[board.BoardProcessOption.lines]); } + else { if (contour instanceof Polyline && GNestConfig.UseOffsetSimplify) @@ -122,7 +53,7 @@ export function ConverBoard2Part(board: Board, knifRadius = 3.5): Part arrayRemoveDuplicateBySort(pts, (p1, p2) => equalv2(p1, p2, 1e-2)); path = new Path(pts); - if (!(contour instanceof Circle))//如果是圆则不用优化成矩形 + if (!(contour instanceof Circle)) //如果是圆则不用优化成矩形 { let area = path.BoundingBox.area - path.Area; if (area < 15000 && pts.length > 6) @@ -142,13 +73,12 @@ export function ConverBoard2Part(board: Board, knifRadius = 3.5): Part } //如果要强制设置旋转状态,来测试代码,需要在这里添加代码,从而保证零件的内部网洞也是正确的! - for (let m of board.BoardModeling) { if (equaln(m.thickness, board.Thickness)) { if (m.shape.Outline.Curve instanceof Polyline) - m.shape.Outline.Curve.UpdateMatrixTo(new Matrix4);//内部造型没有对其到标准世界坐标系,所以我们在这里对齐一下 + m.shape.Outline.Curve.UpdateMatrixTo(new Matrix4); //内部造型没有对其到标准世界坐标系,所以我们在这里对齐一下 let curveW = new CurveWrap(m.shape.Outline.Curve, knifRadius, false); let pts = curveW.GetInsidePoints(); @@ -158,7 +88,7 @@ export function ConverBoard2Part(board: Board, knifRadius = 3.5): Part part.UserData.push(m.shape.Outline.Curve.Clone()); let path = new Path(pts); - if ((m.shape.Outline.Curve instanceof Circle) || path.Area > 15000)//如果是圆,则不优化过滤小孔洞 + if ((m.shape.Outline.Curve instanceof Circle) || path.Area > 15000) //如果是圆,则不优化过滤小孔洞 part.AppendHole(path); } } diff --git a/src/Nest/Converter/Curves2Parts.ts b/src/Nest/Converter/Curves2Parts.ts index 945009499..5e41e504a 100644 --- a/src/Nest/Converter/Curves2Parts.ts +++ b/src/Nest/Converter/Curves2Parts.ts @@ -1,6 +1,6 @@ import { EndType, JoinType } from "js-angusj-clipper/web"; import { Box3, Vector3 } from "three"; -import { app } from "../../ApplicationServices/Application"; +import { Log } from "../../Common/Log"; import { Circle } from "../../DatabaseServices/Entity/Circle"; import { Curve } from "../../DatabaseServices/Entity/Curve"; import { Polyline } from "../../DatabaseServices/Entity/Polyline"; @@ -9,7 +9,7 @@ import { clipperCpp } from "../Common/ClipperCpp"; import { Point } from "../Common/Point"; import { Part } from "../Core/Part"; import { Path, PathScale } from "../Core/Path"; -import { Polylin2Points } from "./ConverBoard2Part"; +import { Polylin2Points } from "./Curves2Points"; import { Path2Polyline } from "./Path2Polyline"; import { IOffset, SimplifyDouglasPeucker } from "./Simplify2"; @@ -204,7 +204,7 @@ export function Curves2Parts(curves: (Polyline | Circle)[], binPath: Path, KnifR } catch (error) { - app.Editor.Prompt("曲线转零件失败!"); + Log("曲线转零件失败!"); } } return parts; diff --git a/src/Nest/Converter/Curves2Points.ts b/src/Nest/Converter/Curves2Points.ts new file mode 100644 index 000000000..b2f02e5d1 --- /dev/null +++ b/src/Nest/Converter/Curves2Points.ts @@ -0,0 +1,77 @@ +import { EndType, JoinType } from "js-angusj-clipper/web"; +import { Arc } from "../../DatabaseServices/Entity/Arc"; +import { Circle } from "../../DatabaseServices/Entity/Circle"; +import { Polyline } from "../../DatabaseServices/Entity/Polyline"; +import { polar } from "../../Geometry/GeUtils"; +import { clipperCpp } from "../Common/ClipperCpp"; +import { Point } from "../Common/Point"; +import { PathScale } from "../Core/Path"; +import { Circle2Points } from "./Curves2Parts"; + +export let Rotations = [ + [0, Math.PI], + [Math.PI / 2, Math.PI * 1.5], + [0, Math.PI, Math.PI / 2, Math.PI * 1.5], +]; + +/** + * 针对板件的曲线变点表做的特殊优化 + */ +export function Curves2Points(cu: Circle | Polyline, outside: boolean, knifRadius: number): [(Circle | Polyline), Point[]] +{ + if (cu instanceof Circle) + return [cu.Clone(), Circle2Points(cu, knifRadius, 8, outside)]; + else + return Polylin2Points(cu, outside, knifRadius); +} + +export function Polylin2Points(pl: Polyline, outside: boolean, knifRadius: number): [Polyline, Point[]] +{ + let pts: Point[] = []; + + if (!outside) knifRadius = -knifRadius; + if (pl.IsClockWise) pl.Reverse(); + for (let i = 0; i < pl.EndParam; i++) + { + pts.push(pl.GetPointAtParam(i)); + + let bul = pl.GetBuilgeAt(i); + if (bul !== 0) + { + let arc = pl.GetCurveAtIndex(i) as Arc; + + let allAngle = arc.AllAngle; + let arcLength = arc.Length; + + let minCount = Math.floor(allAngle * 4 / Math.PI); + + let splitCount = Math.round(allAngle / 0.4); + if (arcLength < 300) + splitCount = Math.max(2, minCount); + else + splitCount = Math.max(arcLength / 200, splitCount, 2, minCount); + + let radius = arc.Radius; + if (outside === bul > 0) + radius = radius / Math.cos(allAngle / (splitCount * 2)); + + let cp = arc.Center; + for (let j = 0.5; j < splitCount; j++) + { + let a = arc.GetAngleAtParam(j * (1 / splitCount)); + let p = polar(cp.clone(), a, radius); + pts.push(p); + } + } + } + + if (knifRadius !== 0) + { + pts = clipperCpp.lib.offsetToPaths({ + delta: knifRadius * 1e4, + offsetInputs: [{ data: PathScale(pts, 1e4), joinType: JoinType.Miter, endType: EndType.ClosedPolygon }] + })[0]; + PathScale(pts, 1e-4); + } + return [pl, pts]; +} diff --git a/src/Nest/Test/TestNFP.ts b/src/Nest/Test/TestNFP.ts index 001ba1951..7954947c0 100644 --- a/src/Nest/Test/TestNFP.ts +++ b/src/Nest/Test/TestNFP.ts @@ -4,7 +4,7 @@ import { Circle } from "../../DatabaseServices/Entity/Circle"; import { Polyline } from "../../DatabaseServices/Entity/Polyline"; import { Command } from "../../Editor/CommandMachine"; import { PromptStatus } from "../../Editor/PromptResult"; -import { Curves2Points } from "../Converter/ConverBoard2Part"; +import { Curves2Points } from "../Converter/Curves2Points"; import { Path2Polyline } from "../Converter/Path2Polyline"; import { Path, PathScale } from "../Core/Path"; import { InitClipperCpp } from "../Common/ClipperCpp"; diff --git a/src/Nest/Test/TestSum.ts b/src/Nest/Test/TestSum.ts index acef6ef1c..165b27f97 100644 --- a/src/Nest/Test/TestSum.ts +++ b/src/Nest/Test/TestSum.ts @@ -6,9 +6,8 @@ import { Command } from "../../Editor/CommandMachine"; import { PromptStatus } from "../../Editor/PromptResult"; import { AsVector3 } from "../../Geometry/GeUtils"; import { clipperCpp, InitClipperCpp } from "../Common/ClipperCpp"; -import { Curves2Points } from "../Converter/ConverBoard2Part"; +import { Curves2Points } from "../Converter/Curves2Points"; import { Path2Polyline } from "../Converter/Path2Polyline"; -import { NestCache } from "../Core/NestCache"; import { Path, PathScale } from "../Core/Path"; export class Command_TestSum implements Command