diff --git a/__test__/Booloperate/BoardCutting.test.ts b/__test__/Booloperate/BoardCutting.test.ts index fa1cbe418..63d0f9071 100644 --- a/__test__/Booloperate/BoardCutting.test.ts +++ b/__test__/Booloperate/BoardCutting.test.ts @@ -13,7 +13,7 @@ test('板件与板件切割_分裂成多个', () => let splitBrs = CuttingBoard(br, brs); splitBrs.push(br); - + expect(splitBrs.length).toBe(4); for (let b of splitBrs) { expect(b.Volume).toMatchNumberSnapshot(); diff --git a/__test__/Booloperate/__snapshots__/BoardCutting.test.ts.snap b/__test__/Booloperate/__snapshots__/BoardCutting.test.ts.snap index 23238a1f5..58001a939 100644 --- a/__test__/Booloperate/__snapshots__/BoardCutting.test.ts.snap +++ b/__test__/Booloperate/__snapshots__/BoardCutting.test.ts.snap @@ -6,23 +6,23 @@ exports[`切割 2`] = `"103836.66905"`; exports[`切割 3`] = `"12396235.11855"`; -exports[`切圆环 1`] = `"6547348.76281"`; +exports[`切圆环 1`] = `"5634286.80144"`; -exports[`切圆环 2`] = `"5634286.80144"`; +exports[`切圆环 2`] = `"6547348.76281"`; exports[`切圆环 3`] = `"15351393.37905"`; -exports[`斜切割 1`] = `"5198741.81897"`; +exports[`斜切割 1`] = `"7492938.32873"`; -exports[`斜切割 2`] = `"7492938.32873"`; +exports[`斜切割 2`] = `"5198741.81897"`; exports[`斜切割 3`] = `"3476662.44783"`; exports[`斜切割 4`] = `"9328820.12621"`; -exports[`板件与板件切割_分裂成多个 1`] = `"3707934.65330"`; +exports[`板件与板件切割_分裂成多个 1`] = `"1432683.00820"`; -exports[`板件与板件切割_分裂成多个 2`] = `"1432683.00820"`; +exports[`板件与板件切割_分裂成多个 2`] = `"3707934.65330"`; exports[`板件与板件切割_分裂成多个 3`] = `"59855.95566"`; @@ -30,17 +30,17 @@ exports[`板件与板件切割_分裂成多个 4`] = `"5558571.43374"`; exports[`板件分裂后槽需要在正确的位置 1`] = `"6248188.54979"`; -exports[`板件切割测试2 1`] = `"1707974.98308"`; +exports[`板件切割测试2 1`] = `"1081121.00089"`; -exports[`板件切割测试2 2`] = `"1081121.00089"`; +exports[`板件切割测试2 2`] = `"1707974.98308"`; exports[`板件切割测试2 3`] = `"7101610.37256"`; exports[`板件切割测试2 4`] = `"12524853.26877"`; -exports[`板件切割测试3 1`] = `"1104841.62590"`; +exports[`板件切割测试3 1`] = `"1081121.00089"`; -exports[`板件切割测试3 2`] = `"1081121.00089"`; +exports[`板件切割测试3 2`] = `"1104841.62590"`; exports[`板件切割测试3 3`] = `"7051142.41179"`; diff --git a/__test__/FeedingToolPath/FeedingToolPath.test.ts b/__test__/FeedingToolPath/FeedingToolPath.test.ts index fc6b8eb52..82333c44e 100644 --- a/__test__/FeedingToolPath/FeedingToolPath.test.ts +++ b/__test__/FeedingToolPath/FeedingToolPath.test.ts @@ -124,7 +124,7 @@ test("#IYX1P", () => let data = [1, "Board", 5, 2, 101, false, 1, 11, 0, [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 25627.96702118174, -1046.5797499058865, 0, 1], 0, 0, 2, 1200, 600, 18, true, "Polyline", 5, 2, 0, false, 0, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, 2, 4, [0, 0], 0, [600, 0], 0, [600, 1200], 0, [0, 1200], 0, true, 1, 2, 416.58619999999996, 325.1405, 5, true, "Polyline", 5, 2, 0, false, 0, 1, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -1187.5114150409554, -619.5823942857144, 0, 1], 0, 0, 2, 4, [1187.5114150409554, 619.5823942857144], 0, [1512.6519150409554, 619.5823942857144], 0, [1512.6519150409554, 1036.1685942857143], 0, [1187.5114150409554, 1036.1685942857143], 0, true, 1, 2, 404.58619999999996, 313.1405, 5, false, "Polyline", 5, 2, 0, false, 0, 1, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -1193.5114150409554, -625.5823942857144, 0, 1], 0, 0, 2, 4, [1444.8769130439057, 968.3935922886646], 0, [1193.5114150409554, 1030.1685942857143], 0, [1193.5114150409554, 625.5823942857144], 0, [1506.6519150409554, 625.5823942857144], 0, true, 0, 3, 0, 0, 0, [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 25640.96702118174, -917.6659343956826, 455.37831265306136, 1], 3, 0, 0, 0, [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 25640.96702118174, -923.6659343956826, 449.37831265306136, 1], 3, 0, 0, 0, 3, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -1686.3109561311285, 160.2072462301978, 0, 1], 1, "左侧板", "", "", "", "", "", 0, 0, "three", 2, 0, "1", "1", "1", "1", "", "", "", 6, "three", "three", "three", "three", "three", "three", true, true, 0, 0]; let brs = LoadBoardsFromFileData(data); - testPathCount(brs[0], 6); + testPathCount(brs[0], 7); }); test("极限刀半径#I11UDE", () => diff --git a/__test__/FeedingToolPath/__snapshots__/FeedingToolPath.test.ts.snap b/__test__/FeedingToolPath/__snapshots__/FeedingToolPath.test.ts.snap index 2c0f17443..71dfe134b 100644 --- a/__test__/FeedingToolPath/__snapshots__/FeedingToolPath.test.ts.snap +++ b/__test__/FeedingToolPath/__snapshots__/FeedingToolPath.test.ts.snap @@ -2,17 +2,19 @@ exports[`#IYX1P 1`] = `"3600.00000"`; -exports[`#IYX1P 2`] = `"1346.76943"`; +exports[`#IYX1P 2`] = `"1356.19421"`; -exports[`#IYX1P 3`] = `"8462.39707"`; +exports[`#IYX1P 3`] = `"1336.05705"`; -exports[`#IYX1P 4`] = `"10.71239"`; +exports[`#IYX1P 4`] = `"7179.71308"`; -exports[`#IYX1P 5`] = `"1483.45340"`; +exports[`#IYX1P 5`] = `"10.71239"`; -exports[`#IYX1P 6`] = `"1324.90446"`; +exports[`#IYX1P 6`] = `"1483.45340"`; -exports[`#IYX1P: 走刀数量 1`] = `3`; +exports[`#IYX1P 7`] = `"1324.90446"`; + +exports[`#IYX1P: 走刀数量 1`] = `4`; exports[`刀切到外轮廓情况 1`] = `"3600.00000"`; @@ -256,123 +258,111 @@ exports[`超级复杂造型01 4`] = `"9.89484"`; exports[`超级复杂造型01 5`] = `"34.11041"`; -exports[`超级复杂造型01 6`] = `"36.73893"`; - -exports[`超级复杂造型01 7`] = `"93.09682"`; - -exports[`超级复杂造型01 8`] = `"3.00516"`; - -exports[`超级复杂造型01 9`] = `"15.55863"`; - -exports[`超级复杂造型01 10`] = `"1.50234"`; - -exports[`超级复杂造型01 11`] = `"34.11041"`; - -exports[`超级复杂造型01 12`] = `"36.73893"`; +exports[`超级复杂造型01 6`] = `"93.09682"`; -exports[`超级复杂造型01 13`] = `"23.17925"`; +exports[`超级复杂造型01 7`] = `"3.00516"`; -exports[`超级复杂造型01 14`] = `"3.00516"`; +exports[`超级复杂造型01 8`] = `"15.55863"`; -exports[`超级复杂造型01 15`] = `"3.00516"`; +exports[`超级复杂造型01 9`] = `"1.50234"`; -exports[`超级复杂造型01 16`] = `"15.55863"`; +exports[`超级复杂造型01 10`] = `"34.11041"`; -exports[`超级复杂造型01 17`] = `"1.50234"`; +exports[`超级复杂造型01 11`] = `"23.17925"`; -exports[`超级复杂造型01 18`] = `"9.89484"`; +exports[`超级复杂造型01 12`] = `"3.00516"`; -exports[`超级复杂造型01 19`] = `"2.27264"`; +exports[`超级复杂造型01 13`] = `"3.00516"`; -exports[`超级复杂造型01 20`] = `"23.17925"`; +exports[`超级复杂造型01 14`] = `"15.55863"`; -exports[`超级复杂造型01 21`] = `"36.73893"`; +exports[`超级复杂造型01 15`] = `"1.50234"`; -exports[`超级复杂造型01 22`] = `"34.11041"`; +exports[`超级复杂造型01 16`] = `"9.89484"`; -exports[`超级复杂造型01 23`] = `"3.00516"`; +exports[`超级复杂造型01 17`] = `"2.27264"`; -exports[`超级复杂造型01 24`] = `"3.00516"`; +exports[`超级复杂造型01 18`] = `"23.17925"`; -exports[`超级复杂造型01 25`] = `"15.55863"`; +exports[`超级复杂造型01 19`] = `"34.11041"`; -exports[`超级复杂造型01 26`] = `"1.50234"`; +exports[`超级复杂造型01 20`] = `"3.00516"`; -exports[`超级复杂造型01 27`] = `"9.89484"`; +exports[`超级复杂造型01 21`] = `"3.00516"`; -exports[`超级复杂造型01 28`] = `"2.27264"`; +exports[`超级复杂造型01 22`] = `"15.55863"`; -exports[`超级复杂造型01 29`] = `"23.17925"`; +exports[`超级复杂造型01 23`] = `"1.50234"`; -exports[`超级复杂造型01 30`] = `"36.73893"`; +exports[`超级复杂造型01 24`] = `"9.89484"`; -exports[`超级复杂造型01 31`] = `"34.11041"`; +exports[`超级复杂造型01 25`] = `"2.27264"`; -exports[`超级复杂造型01 32`] = `"3.00516"`; +exports[`超级复杂造型01 26`] = `"23.17925"`; -exports[`超级复杂造型01 33`] = `"3.00516"`; +exports[`超级复杂造型01 27`] = `"34.11041"`; -exports[`超级复杂造型01 34`] = `"15.55863"`; +exports[`超级复杂造型01 28`] = `"3.00516"`; -exports[`超级复杂造型01 35`] = `"1.50234"`; +exports[`超级复杂造型01 29`] = `"3.00516"`; -exports[`超级复杂造型01 36`] = `"9.89484"`; +exports[`超级复杂造型01 30`] = `"15.55863"`; -exports[`超级复杂造型01 37`] = `"2.27264"`; +exports[`超级复杂造型01 31`] = `"1.50234"`; -exports[`超级复杂造型01 38`] = `"23.17925"`; +exports[`超级复杂造型01 32`] = `"9.89484"`; -exports[`超级复杂造型01 39`] = `"36.73893"`; +exports[`超级复杂造型01 33`] = `"2.27264"`; -exports[`超级复杂造型01 40`] = `"34.11041"`; +exports[`超级复杂造型01 34`] = `"23.17925"`; -exports[`超级复杂造型01 41`] = `"3.00516"`; +exports[`超级复杂造型01 35`] = `"34.11041"`; -exports[`超级复杂造型01 42`] = `"3.00516"`; +exports[`超级复杂造型01 36`] = `"3.00516"`; -exports[`超级复杂造型01 43`] = `"15.55863"`; +exports[`超级复杂造型01 37`] = `"3.00516"`; -exports[`超级复杂造型01 44`] = `"1.50234"`; +exports[`超级复杂造型01 38`] = `"15.55863"`; -exports[`超级复杂造型01 45`] = `"9.89484"`; +exports[`超级复杂造型01 39`] = `"1.50234"`; -exports[`超级复杂造型01 46`] = `"2.27264"`; +exports[`超级复杂造型01 40`] = `"9.89484"`; -exports[`超级复杂造型01 47`] = `"23.17925"`; +exports[`超级复杂造型01 41`] = `"2.27264"`; -exports[`超级复杂造型01 48`] = `"34.11041"`; +exports[`超级复杂造型01 42`] = `"23.17925"`; -exports[`超级复杂造型01 49`] = `"36.73893"`; +exports[`超级复杂造型01 43`] = `"34.11041"`; -exports[`超级复杂造型01 50`] = `"3.00516"`; +exports[`超级复杂造型01 44`] = `"3.00516"`; -exports[`超级复杂造型01 51`] = `"3.00516"`; +exports[`超级复杂造型01 45`] = `"3.00516"`; -exports[`超级复杂造型01 52`] = `"9.89484"`; +exports[`超级复杂造型01 46`] = `"15.55863"`; -exports[`超级复杂造型01 53`] = `"2.27264"`; +exports[`超级复杂造型01 47`] = `"1.50234"`; -exports[`超级复杂造型01 54`] = `"15.55863"`; +exports[`超级复杂造型01 48`] = `"9.89484"`; -exports[`超级复杂造型01 55`] = `"1.50234"`; +exports[`超级复杂造型01 49`] = `"2.27264"`; -exports[`超级复杂造型01 56`] = `"11906.41153"`; +exports[`超级复杂造型01 50`] = `"11906.41153"`; -exports[`超级复杂造型01 57`] = `"464.88810"`; +exports[`超级复杂造型01 51`] = `"464.88810"`; -exports[`超级复杂造型01 58`] = `"464.88810"`; +exports[`超级复杂造型01 52`] = `"464.88810"`; -exports[`超级复杂造型01 59`] = `"464.88810"`; +exports[`超级复杂造型01 53`] = `"464.88810"`; -exports[`超级复杂造型01 60`] = `"464.88810"`; +exports[`超级复杂造型01 54`] = `"464.88810"`; -exports[`超级复杂造型01 61`] = `"464.88810"`; +exports[`超级复杂造型01 55`] = `"464.88810"`; -exports[`超级复杂造型01 62`] = `"464.88810"`; +exports[`超级复杂造型01 56`] = `"464.88810"`; -exports[`超级复杂造型01 63`] = `"9277.91064"`; +exports[`超级复杂造型01 57`] = `"9277.91064"`; -exports[`超级复杂造型01: 走刀数量 1`] = `54`; +exports[`超级复杂造型01: 走刀数量 1`] = `48`; exports[`通孔造型测试 1`] = `"3600.00000"`; diff --git a/src/Add-on/BoolOperation.ts b/src/Add-on/BoolOperation.ts index 9dd08a69e..07a34d8f9 100644 --- a/src/Add-on/BoolOperation.ts +++ b/src/Add-on/BoolOperation.ts @@ -5,13 +5,12 @@ import { BoolOpeartionType } from '../GraphicsSystem/BoolOperateUtils'; function RegionsBoolOperate(regs: Region[], boolType: BoolOpeartionType): Region | undefined { + if (regs.length === 0) return; let firstReg = regs[0]; for (let i = 1; i < regs.length; i++) { if (firstReg.ShapeManager.ShapeCount) - { firstReg.BooleanOper(regs[i], boolType); - } regs[i].Erase(); } return firstReg; diff --git a/src/DatabaseServices/Contour.ts b/src/DatabaseServices/Contour.ts index 27a1859e3..2e5ce4342 100644 --- a/src/DatabaseServices/Contour.ts +++ b/src/DatabaseServices/Contour.ts @@ -1,5 +1,5 @@ import { Vector3 } from "three"; -import { arrayLast, arrayRemoveDuplicateBySort } from "../Common/ArrayExt"; +import { arrayLast, arrayRemoveDuplicateBySort, arrayRemoveIf } from "../Common/ArrayExt"; import { curveLinkGroup, equalCurve } from "../Common/CurveUtils"; import { Status } from "../Common/Status"; import { FixIndex } from "../Common/Utils"; @@ -12,6 +12,7 @@ import { Arc } from "./Entity/Arc"; import { Circle } from "./Entity/Circle"; import { Curve } from "./Entity/Curve"; import { Polyline } from "./Entity/Polyline"; +import { IntersectBox2 } from "../Geometry/Box"; let cache = new WeakMap(); @@ -103,17 +104,22 @@ export class Contour //交集:结果数组为空则失败 IntersectionBoolOperation(target: Contour): Contour[] { + if (!IntersectBox2(this.BoundingBox, target.BoundingBox)) + return []; let resultCus = this.GetIntersetAndUnionList(target); return Contour.GetAllContour(resultCus.intersectionList); } - //并集:结果数组长度大于2,则失败.等于1则成功. - UnionBoolOperation(target: Contour): Contour[] + //并集:结果轮廓数组长度大于2,则失败.等于1则成功. + UnionBoolOperation(target: Contour): { contours: Contour[], holes: Contour[]; } { let resultCus = this.GetIntersetAndUnionList(target); //快速 if (resultCus.unionList.every(c => c.IsClose)) - return Contour.GetAllContour(resultCus.unionList); + return { + contours: Contour.GetAllContour(resultCus.unionList), + holes: [], + }; //并集后的线段表如果有共线的直接合并起来 let cus: Curve[] = []; @@ -148,7 +154,26 @@ export class Contour } } } - return Contour.GetAllContour(cuGroups); + let allContour = Contour.GetAllContour(cuGroups); + if (allContour.length < 2) + { + return { + contours: allContour, + holes: [], + }; + } + else + { + let cache = new WeakMap(); + for (let c of allContour) + cache.set(c, c.Area); + allContour.sort((a, b) => cache.get(b) - cache.get(a)); + return { + contours: [allContour[0]], + holes: allContour.slice(1) + }; + } + } //差集:等于0完全被减去 SubstactBoolOperation(target: Contour): Contour[] @@ -330,6 +355,134 @@ export class Contour return subtractList; } + GetSubtractListByMoreTargets(targets: Contour[]) + { + let { holes, subtractList } = this.GetSubListWithCus(targets); + + //纯网洞 + if (subtractList.every(c => c.IsClose)) + return { + holes: holes.map(h => Contour.CreateContour(h)), + outlines: Contour.GetAllContour(subtractList) + }; + + let regParse = new RegionParse(subtractList, 2); + + let contours: Contour[] = []; + //分析封闭包围区域 + const parseRoute = (routeSet: Array[]) => + { + for (let routes of routeSet) + { + let cs: Curve[] = routes.map(r => r.curve); + let c = Contour.CreateContour(cs, false); + if (c + && !equalCurve(c.Curve, this.Curve) + && targets.every(target => !equalCurve(c.Curve, target.Curve)) + && c.Area > 1e-3) + contours.push(c); + } + }; + parseRoute(regParse.RegionsOutline); + parseRoute(regParse.RegionsInternal); + + return { + holes: holes.map(h => Contour.CreateContour(h)), + outlines: contours + }; + + } + GetSubListWithCus(targets: Contour[]) + { + let sourceOutline = this._Curve as Polyline; + let subtractList: Polyline[] = []; + let holes: Polyline[] = []; + let intPars: number[] = []; + let cuMap = new Map(); + + let outBox = sourceOutline.BoundingBox; + + for (let con of targets) + { + const targetOutline = con.Curve as Polyline; + + if (!IntersectBox2(outBox, targetOutline.BoundingBox)) + continue; + + let pts = sourceOutline.IntersectWith2(con.Curve, IntersectOption.OnBothOperands, 1e-3); + if (pts.length <= 1) + { + //反包含 + if (fastCurveInCurve2(targetOutline, sourceOutline) || equalCurve(targetOutline, sourceOutline)) + return { holes, subtractList }; + //包含 + if (fastCurveInCurve2(sourceOutline, targetOutline)) + holes.push(targetOutline); + else//分离 + { + + } + } + else + { + intPars.push(...pts.map(r => r.thisParam)); + cuMap.set(targetOutline, pts.map(r => r.argParam)); + } + } + intPars.sort((a, b) => a - b); + arrayRemoveDuplicateBySort(intPars, (e1, e2) => equaln(e1, e2, 1e-8)); + let sourceCus = sourceOutline.GetSplitCurves(intPars) as Polyline[]; + let targetCus: Polyline[] = []; + + let targetMap = new WeakMap(); + + let isEqualNormal: boolean; + + for (let [c, pars] of cuMap) + { + let cus = c.GetSplitCurves(pars) as Polyline[]; + cus.forEach(cu => targetMap.set(cu, c)); + targetCus.push(...cus); + } + + for (let pl of sourceCus) + { + let plMidParam = pl.MidParam; + let plDir = pl.GetFistDeriv(plMidParam).normalize(); + + let index = targetCus.findIndex(cu => fastEqualCurve(cu, pl)); + if (index !== -1) + { + let cu = targetCus[index]; + isEqualNormal = equalv3(sourceOutline.Normal, targetMap.get(cu).Normal, 1e-3); + let cuMidParam = cu.MidParam; + let cuDir = cu.GetFistDeriv(cuMidParam).normalize(); + + if (isEqualNormal === !equalv3(cuDir, plDir, 1e-3))//不同向 + subtractList.push(pl); + + targetCus.splice(index, 1); + + continue; + } + + if (targets.every(t => !fastCurveInCurve(t.Curve as Polyline, pl))) + subtractList.push(pl); + } + + //源对象没有被破坏 + let sourceNotBreak = subtractList.length === sourceCus.length; + + for (let pl of targetCus) + if (fastCurveInCurve(sourceOutline, pl)) + subtractList.push(pl); + + if (sourceNotBreak && subtractList.length === sourceCus.length) + return { subtractList: [sourceOutline], holes }; + + return { subtractList, holes }; + + } /** * 获得全部闭合曲线 * @若传入二维曲线数据,将默认子数组为闭合曲线段 diff --git a/src/DatabaseServices/Shape.ts b/src/DatabaseServices/Shape.ts index 04b86a469..b3b1b52d0 100644 --- a/src/DatabaseServices/Shape.ts +++ b/src/DatabaseServices/Shape.ts @@ -1,4 +1,4 @@ -import { Matrix3, Matrix4, Path, Shape as TShape, Vector3 } from 'three'; +import { Matrix3, Matrix4, Path, Shape as TShape, Vector3, Box3 } from 'three'; import { arrayRemoveIf } from '../Common/ArrayExt'; import { ObjectSnapMode } from '../Editor/ObjectSnapMode'; import { CADFiler } from './CADFiler'; @@ -6,6 +6,8 @@ import { Contour } from './Contour'; import { Circle } from './Entity/Circle'; import { Curve } from './Entity/Curve'; import { Polyline } from './Entity/Polyline'; +import { equaln } from '../Geometry/GeUtils'; +import { IntersectBox2 } from '../Geometry/Box'; export class Shape { @@ -233,7 +235,8 @@ export class Shape //并集,如果成功返回1个形状,不成功返回2个形状 UnionBoolOperation(targetShape: Shape): Shape[] { - let resOutlines = this._Outline.UnionBoolOperation(targetShape._Outline); + let { contours, holes } = this._Outline.UnionBoolOperation(targetShape._Outline); + let shapes: Shape[] = []; //提取出所有的孔洞, 目标线段孔洞和原线段差,如果孔洞和目标相减后有被包围轮廓,应把这个单独提取出来作为形状 @@ -250,8 +253,7 @@ export class Shape //洞是否被最大的洞包含,是,则把被包含的洞都提取出来加入形状数组 if (isAllContainered) { - shapes.push(...Shape.pairHoleAndOutline(this.targetOutlineSubHoleOutline(tmpContours.slice(1), tarHoles))); - unionHoles.push(tmpContours[0]); + shapes.push(...this.targetOutlinesSubHoles(tmpContours.slice(1).map(c => new Shape(c)), tarHoles.map(c => new Shape(c)))); } else unionHoles.push(...tmpContours); }); @@ -266,30 +268,63 @@ export class Shape unionHoles.push(...c.IntersectionBoolOperation(cu)); }); }); - let finalCus = this.targetOutlineSubHoleOutline(resOutlines, unionHoles); - shapes.push(...Shape.pairHoleAndOutline(finalCus)); + + shapes.push(...this.targetOutlinesSubHoles(contours.map(c => new Shape(c, holes)), unionHoles.map(c => new Shape(c)))); return shapes; } //如果完全被减掉,就返回0个.其他的返回1个或者n个 - SubstactBoolOperation(targetShape: Shape): Shape[] + SubstactBoolOperation(targetShapes: Shape[]) { - let shapes: Shape[] = []; - //如果相减,则需要取出被减面域和减数面域的洞的交集,结果还需减去被减面域的全部孔洞 - targetShape._Holes.forEach(cu => - { - let tmpInterList = cu.IntersectionBoolOperation(this._Outline); + let originOutline = this.Outline; + let targetOutlines = targetShapes.map(s => s.Outline); + const { holes, outlines } = originOutline.GetSubtractListByMoreTargets(targetOutlines); + holes.push(...this.Holes); - shapes.push(...Shape.pairHoleAndOutline(this.targetOutlineSubHoleOutline(tmpInterList, this._Holes))); - }); + let newShapes: Shape[] = []; - let fCus: Contour[]; - if (this.Holes.length > 0) - fCus = this.targetOutlineSubHoleOutline([this._Outline], Shape.mergeContours([targetShape._Outline, ...this._Holes])); + if (outlines.length === 1 && equaln(outlines[0].Area, originOutline.Area)) + { + newShapes = [new Shape(outlines[0], Shape.mergeContours(holes))]; + } + else if (holes.length === 0) + { + newShapes = outlines.map(o => new Shape(o)); + } else - fCus = this._Outline.SubstactBoolOperation(targetShape._Outline); - shapes.push(...Shape.pairHoleAndOutline(fCus)); - return shapes; + { + for (let outline of outlines) + newShapes.push(...new Shape(outline).SubstactBoolOperation(holes.map(h => new Shape(h)))); + } + + let holeShape = this.Holes.map(h => new Shape(h)); + + for (let target of targetShapes) + { + let tmpInterList: Contour[] = []; + if (target.Holes.length === 0) continue; + for (let hole of target.Holes) + { + let list = hole.IntersectionBoolOperation(originOutline); + tmpInterList.push(...list); + } + + for (let ot of tmpInterList) + { + let subShapes: Shape[] = []; + subShapes.push(...holeShape); + for (let t of targetShapes) + { + if (t !== target) + subShapes.push(new Shape(t.Outline)); + } + + newShapes.push(...new Shape(ot).SubstactBoolOperation(subShapes)); + } + + } + + return newShapes; } Equal(targetShape: Shape) { @@ -302,7 +337,17 @@ export class Shape } return false; } + private targetOutlinesSubHoles(targetShapes: Shape[], holeShapes: Shape[]) + { + let resultShapes: Shape[] = []; + for (let ts of targetShapes) + { + let res = ts.SubstactBoolOperation(holeShapes); + resultShapes.push(...res); + } + return resultShapes; + } /** * 目标轮廓减去洞 * @@ -375,21 +420,46 @@ export class Shape { if (holes.length <= 1) return holes; let rets: Contour[] = [];//返回的合并轮廓 + let cache = new Map(); while (holes.length > 0) { let c = holes.shift();//取第一个 + let b1 = cache.get(c); + if (!b1) + { + b1 = c.BoundingBox; + cache.set(c, b1); + } while (true) { //剩余的 不相交的形状表 remaining let remHoles = holes.filter(ic => { + let b2 = cache.get(ic); + if (!b2) + { + b2 = ic.BoundingBox; + cache.set(ic, b2); + } + + if (!IntersectBox2(b1, b2)) + return true; + let unions = c.UnionBoolOperation(ic); - if (unions.length === 1)//并集成功 - c = unions[0]; //更新c - return unions.length !== 1; //过滤出并集失败的形状 + if (unions.holes.length > 0) + console.warn("未知情况"); + + if (unions.contours.length === 1)//并集成功 + { + c = unions.contours[0]; //更新c + b1 = c.BoundingBox; + cache.set(c, b1); + } + + return unions.contours.length !== 1; //过滤出并集失败的形状 }); //如果c和剩余的轮廓都不相交,那么退出 diff --git a/src/DatabaseServices/ShapeManager.ts b/src/DatabaseServices/ShapeManager.ts index b954c2f8d..081b43c58 100644 --- a/src/DatabaseServices/ShapeManager.ts +++ b/src/DatabaseServices/ShapeManager.ts @@ -2,6 +2,7 @@ import { BoolOpeartionType } from '../GraphicsSystem/BoolOperateUtils'; import { CADFiler } from './CADFiler'; import { Shape } from './Shape'; import { Matrix4 } from 'three'; +import { IntersectBox2 } from '../Geometry/Box'; export class ShapeManager @@ -64,11 +65,26 @@ export class ShapeManager let tarShapes = targetMg._ShapeList; let alones: Shape[] = [];//孤立的形状 + + const boxCache = new WeakMap(); + for (let src of srcShapes) { let notUnions: Shape[] = [];//未被合并的形状列表 来自tarShapes + let srcBox = src.BoundingBox; for (let tar of tarShapes) { + let tarBox = boxCache.get(tar); + if (!tarBox) + { + tarBox = tar.BoundingBox; + boxCache.set(tar, tarBox); + } + if (!IntersectBox2(srcBox, tarBox)) + { + notUnions.push(tar); + continue; + } let unions = src.UnionBoolOperation(tar); if (unions.length === 1)//并集成功 { @@ -94,33 +110,13 @@ export class ShapeManager } SubstactBoolOperation(target: ShapeManager) { - //减数形状 - for (let subtrahendShape of target._ShapeList) - { - let tmpShapes: Shape[] = []; - //被减形状 - for (let minuendShape of this._ShapeList) - { - let operatedShapes = minuendShape.SubstactBoolOperation(subtrahendShape); - tmpShapes.push(...operatedShapes); - } - //迭代this形状列表,每次赋予新的结果 - this._ShapeList = tmpShapes; - } - return true; - } - SubstactShape(target: Shape) - { - //减数形状 - let tmpShapes: Shape[] = []; - //被减形状 - for (let minuendShape of this._ShapeList) + let newShapes: Shape[] = []; + for (let s of this._ShapeList) { - let operatedShapes = minuendShape.SubstactBoolOperation(target); - tmpShapes.push(...operatedShapes); + let ss = s.SubstactBoolOperation(target.ShapeList); + newShapes.push(...ss); } - //迭代this形状列表,每次赋予新的结果 - this._ShapeList = tmpShapes; + this._ShapeList = newShapes; return true; } diff --git a/src/Geometry/Box.ts b/src/Geometry/Box.ts index 0b4cbb5e0..e047c0749 100644 --- a/src/Geometry/Box.ts +++ b/src/Geometry/Box.ts @@ -64,3 +64,10 @@ export function IntersectsBox(box1: Box3, box2: Box3, fuzz = 1e-6): boolean box2.max.y < box1.min.y - fuzz || box2.min.y > box1.max.y + fuzz || box2.max.z < box1.min.z - fuzz || box2.min.z > box1.max.z + fuzz ? false : true; } + +/**盒子二维面是否相交 */ +export function IntersectBox2(box1: Box3, box2: Box3, fuzz = 1e-3) +{ + return box2.max.x < box1.min.x - fuzz || box2.min.x > box1.max.x + fuzz || + box2.max.y < box1.min.y - fuzz || box2.min.y > box1.max.y + fuzz ? false : true; +} diff --git a/src/Geometry/CreateWireframe.ts b/src/Geometry/CreateWireframe.ts index 031444dc4..0e644306e 100644 --- a/src/Geometry/CreateWireframe.ts +++ b/src/Geometry/CreateWireframe.ts @@ -37,7 +37,7 @@ export function CreateWireframe(en3D: Board | ExtrudeSolid) //正面 if (m.dir === FaceDirection.Front) { - let ss = upShape.SubstactBoolOperation(cloneShape); + let ss = upShape.SubstactBoolOperation([cloneShape]); if (ss.length > 0 && ss[0].Holes.length === 0) { upShape = ss[0]; @@ -46,7 +46,7 @@ export function CreateWireframe(en3D: Board | ExtrudeSolid) } else { - let ss = downShape.SubstactBoolOperation(cloneShape); + let ss = downShape.SubstactBoolOperation([cloneShape]); if (ss.length > 0 && ss[0].Holes.length === 0) { downShape = ss[0];