!1112 优化:布尔运算性能

pull/1112/MERGE
ZoeLeeFZ 4 years ago committed by ChenX
parent 1ee95de3b0
commit 79e6e9a861

@ -13,7 +13,7 @@ test('板件与板件切割_分裂成多个', () =>
let splitBrs = CuttingBoard(br, brs); let splitBrs = CuttingBoard(br, brs);
splitBrs.push(br); splitBrs.push(br);
expect(splitBrs.length).toBe(4);
for (let b of splitBrs) for (let b of splitBrs)
{ {
expect(b.Volume).toMatchNumberSnapshot(); expect(b.Volume).toMatchNumberSnapshot();

@ -6,23 +6,23 @@ exports[`切割 2`] = `"103836.66905"`;
exports[`切割 3`] = `"12396235.11855"`; 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[`切圆环 3`] = `"15351393.37905"`;
exports[`斜切割 1`] = `"5198741.81897"`; exports[`斜切割 1`] = `"7492938.32873"`;
exports[`斜切割 2`] = `"7492938.32873"`; exports[`斜切割 2`] = `"5198741.81897"`;
exports[`斜切割 3`] = `"3476662.44783"`; exports[`斜切割 3`] = `"3476662.44783"`;
exports[`斜切割 4`] = `"9328820.12621"`; exports[`斜切割 4`] = `"9328820.12621"`;
exports[`板件与板件切割_分裂成多个 1`] = `"3707934.65330"`; exports[`板件与板件切割_分裂成多个 1`] = `"1432683.00820"`;
exports[`板件与板件切割_分裂成多个 2`] = `"1432683.00820"`; exports[`板件与板件切割_分裂成多个 2`] = `"3707934.65330"`;
exports[`板件与板件切割_分裂成多个 3`] = `"59855.95566"`; exports[`板件与板件切割_分裂成多个 3`] = `"59855.95566"`;
@ -30,17 +30,17 @@ exports[`板件与板件切割_分裂成多个 4`] = `"5558571.43374"`;
exports[`板件分裂后槽需要在正确的位置 1`] = `"6248188.54979"`; 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 3`] = `"7101610.37256"`;
exports[`板件切割测试2 4`] = `"12524853.26877"`; 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"`; exports[`板件切割测试3 3`] = `"7051142.41179"`;

@ -124,7 +124,7 @@ test("#IYX1P", () =>
let data = 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]; [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); let brs = LoadBoardsFromFileData(data);
testPathCount(brs[0], 6); testPathCount(brs[0], 7);
}); });
test("极限刀半径#I11UDE", () => test("极限刀半径#I11UDE", () =>

@ -2,17 +2,19 @@
exports[`#IYX1P 1`] = `"3600.00000"`; 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"`; exports[`刀切到外轮廓情况 1`] = `"3600.00000"`;
@ -256,123 +258,111 @@ exports[`超级复杂造型01 4`] = `"9.89484"`;
exports[`超级复杂造型01 5`] = `"34.11041"`; exports[`超级复杂造型01 5`] = `"34.11041"`;
exports[`超级复杂造型01 6`] = `"36.73893"`; exports[`超级复杂造型01 6`] = `"93.09682"`;
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 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"`; exports[`通孔造型测试 1`] = `"3600.00000"`;

@ -5,13 +5,12 @@ import { BoolOpeartionType } from '../GraphicsSystem/BoolOperateUtils';
function RegionsBoolOperate(regs: Region[], boolType: BoolOpeartionType): Region | undefined function RegionsBoolOperate(regs: Region[], boolType: BoolOpeartionType): Region | undefined
{ {
if (regs.length === 0) return;
let firstReg = regs[0]; let firstReg = regs[0];
for (let i = 1; i < regs.length; i++) for (let i = 1; i < regs.length; i++)
{ {
if (firstReg.ShapeManager.ShapeCount) if (firstReg.ShapeManager.ShapeCount)
{
firstReg.BooleanOper(regs[i], boolType); firstReg.BooleanOper(regs[i], boolType);
}
regs[i].Erase(); regs[i].Erase();
} }
return firstReg; return firstReg;

@ -1,5 +1,5 @@
import { Vector3 } from "three"; import { Vector3 } from "three";
import { arrayLast, arrayRemoveDuplicateBySort } from "../Common/ArrayExt"; import { arrayLast, arrayRemoveDuplicateBySort, arrayRemoveIf } from "../Common/ArrayExt";
import { curveLinkGroup, equalCurve } from "../Common/CurveUtils"; import { curveLinkGroup, equalCurve } from "../Common/CurveUtils";
import { Status } from "../Common/Status"; import { Status } from "../Common/Status";
import { FixIndex } from "../Common/Utils"; import { FixIndex } from "../Common/Utils";
@ -12,6 +12,7 @@ import { Arc } from "./Entity/Arc";
import { Circle } from "./Entity/Circle"; import { Circle } from "./Entity/Circle";
import { Curve } from "./Entity/Curve"; import { Curve } from "./Entity/Curve";
import { Polyline } from "./Entity/Polyline"; import { Polyline } from "./Entity/Polyline";
import { IntersectBox2 } from "../Geometry/Box";
let cache = new WeakMap(); let cache = new WeakMap();
@ -103,17 +104,22 @@ export class Contour
//交集:结果数组为空则失败 //交集:结果数组为空则失败
IntersectionBoolOperation(target: Contour): Contour[] IntersectionBoolOperation(target: Contour): Contour[]
{ {
if (!IntersectBox2(this.BoundingBox, target.BoundingBox))
return [];
let resultCus = this.GetIntersetAndUnionList(target); let resultCus = this.GetIntersetAndUnionList(target);
return Contour.GetAllContour(resultCus.intersectionList); return Contour.GetAllContour(resultCus.intersectionList);
} }
//并集:结果数组长度大于2,则失败.等于1则成功. //并集:结果轮廓数组长度大于2,则失败.等于1则成功.
UnionBoolOperation(target: Contour): Contour[] UnionBoolOperation(target: Contour): { contours: Contour[], holes: Contour[]; }
{ {
let resultCus = this.GetIntersetAndUnionList(target); let resultCus = this.GetIntersetAndUnionList(target);
//快速 //快速
if (resultCus.unionList.every(c => c.IsClose)) if (resultCus.unionList.every(c => c.IsClose))
return Contour.GetAllContour(resultCus.unionList); return {
contours: Contour.GetAllContour(resultCus.unionList),
holes: [],
};
//并集后的线段表如果有共线的直接合并起来 //并集后的线段表如果有共线的直接合并起来
let cus: Curve[] = []; 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完全被减去 //差集:等于0完全被减去
SubstactBoolOperation(target: Contour): Contour[] SubstactBoolOperation(target: Contour): Contour[]
@ -330,6 +355,134 @@ export class Contour
return subtractList; 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<Route>[]) =>
{
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<Curve, number[]>();
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<Curve, Curve>();
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 };
}
/** /**
* 线 * 线
* @线,线 * @线,线

@ -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 { arrayRemoveIf } from '../Common/ArrayExt';
import { ObjectSnapMode } from '../Editor/ObjectSnapMode'; import { ObjectSnapMode } from '../Editor/ObjectSnapMode';
import { CADFiler } from './CADFiler'; import { CADFiler } from './CADFiler';
@ -6,6 +6,8 @@ import { Contour } from './Contour';
import { Circle } from './Entity/Circle'; import { Circle } from './Entity/Circle';
import { Curve } from './Entity/Curve'; import { Curve } from './Entity/Curve';
import { Polyline } from './Entity/Polyline'; import { Polyline } from './Entity/Polyline';
import { equaln } from '../Geometry/GeUtils';
import { IntersectBox2 } from '../Geometry/Box';
export class Shape export class Shape
{ {
@ -233,7 +235,8 @@ export class Shape
//并集,如果成功返回1个形状,不成功返回2个形状 //并集,如果成功返回1个形状,不成功返回2个形状
UnionBoolOperation(targetShape: Shape): Shape[] UnionBoolOperation(targetShape: Shape): Shape[]
{ {
let resOutlines = this._Outline.UnionBoolOperation(targetShape._Outline); let { contours, holes } = this._Outline.UnionBoolOperation(targetShape._Outline);
let shapes: Shape[] = []; let shapes: Shape[] = [];
//提取出所有的孔洞, 目标线段孔洞和原线段差,如果孔洞和目标相减后有被包围轮廓,应把这个单独提取出来作为形状 //提取出所有的孔洞, 目标线段孔洞和原线段差,如果孔洞和目标相减后有被包围轮廓,应把这个单独提取出来作为形状
@ -250,8 +253,7 @@ export class Shape
//洞是否被最大的洞包含,是,则把被包含的洞都提取出来加入形状数组 //洞是否被最大的洞包含,是,则把被包含的洞都提取出来加入形状数组
if (isAllContainered) if (isAllContainered)
{ {
shapes.push(...Shape.pairHoleAndOutline(this.targetOutlineSubHoleOutline(tmpContours.slice(1), tarHoles))); shapes.push(...this.targetOutlinesSubHoles(tmpContours.slice(1).map(c => new Shape(c)), tarHoles.map(c => new Shape(c))));
unionHoles.push(tmpContours[0]);
} else } else
unionHoles.push(...tmpContours); unionHoles.push(...tmpContours);
}); });
@ -266,30 +268,63 @@ export class Shape
unionHoles.push(...c.IntersectionBoolOperation(cu)); 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; return shapes;
} }
//如果完全被减掉,就返回0个.其他的返回1个或者n个 //如果完全被减掉,就返回0个.其他的返回1个或者n个
SubstactBoolOperation(targetShape: Shape): Shape[] SubstactBoolOperation(targetShapes: Shape[])
{ {
let shapes: Shape[] = []; let originOutline = this.Outline;
//如果相减,则需要取出被减面域和减数面域的洞的交集,结果还需减去被减面域的全部孔洞 let targetOutlines = targetShapes.map(s => s.Outline);
targetShape._Holes.forEach(cu => const { holes, outlines } = originOutline.GetSubtractListByMoreTargets(targetOutlines);
{ holes.push(...this.Holes);
let tmpInterList = cu.IntersectionBoolOperation(this._Outline);
shapes.push(...Shape.pairHoleAndOutline(this.targetOutlineSubHoleOutline(tmpInterList, this._Holes))); let newShapes: Shape[] = [];
});
let fCus: Contour[]; if (outlines.length === 1 && equaln(outlines[0].Area, originOutline.Area))
if (this.Holes.length > 0) {
fCus = this.targetOutlineSubHoleOutline([this._Outline], Shape.mergeContours([targetShape._Outline, ...this._Holes])); newShapes = [new Shape(outlines[0], Shape.mergeContours(holes))];
}
else if (holes.length === 0)
{
newShapes = outlines.map(o => new Shape(o));
}
else else
fCus = this._Outline.SubstactBoolOperation(targetShape._Outline); {
shapes.push(...Shape.pairHoleAndOutline(fCus)); for (let outline of outlines)
return shapes; 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) Equal(targetShape: Shape)
{ {
@ -302,7 +337,17 @@ export class Shape
} }
return false; 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; if (holes.length <= 1) return holes;
let rets: Contour[] = [];//返回的合并轮廓 let rets: Contour[] = [];//返回的合并轮廓
let cache = new Map<Contour, Box3>();
while (holes.length > 0) while (holes.length > 0)
{ {
let c = holes.shift();//取第一个 let c = holes.shift();//取第一个
let b1 = cache.get(c);
if (!b1)
{
b1 = c.BoundingBox;
cache.set(c, b1);
}
while (true) while (true)
{ {
//剩余的 不相交的形状表 remaining //剩余的 不相交的形状表 remaining
let remHoles = holes.filter(ic => 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); 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和剩余的轮廓都不相交,那么退出 //如果c和剩余的轮廓都不相交,那么退出

@ -2,6 +2,7 @@ import { BoolOpeartionType } from '../GraphicsSystem/BoolOperateUtils';
import { CADFiler } from './CADFiler'; import { CADFiler } from './CADFiler';
import { Shape } from './Shape'; import { Shape } from './Shape';
import { Matrix4 } from 'three'; import { Matrix4 } from 'three';
import { IntersectBox2 } from '../Geometry/Box';
export class ShapeManager export class ShapeManager
@ -64,11 +65,26 @@ export class ShapeManager
let tarShapes = targetMg._ShapeList; let tarShapes = targetMg._ShapeList;
let alones: Shape[] = [];//孤立的形状 let alones: Shape[] = [];//孤立的形状
const boxCache = new WeakMap();
for (let src of srcShapes) for (let src of srcShapes)
{ {
let notUnions: Shape[] = [];//未被合并的形状列表 来自tarShapes let notUnions: Shape[] = [];//未被合并的形状列表 来自tarShapes
let srcBox = src.BoundingBox;
for (let tar of tarShapes) 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); let unions = src.UnionBoolOperation(tar);
if (unions.length === 1)//并集成功 if (unions.length === 1)//并集成功
{ {
@ -94,33 +110,13 @@ export class ShapeManager
} }
SubstactBoolOperation(target: ShapeManager) SubstactBoolOperation(target: ShapeManager)
{ {
//减数形状 let newShapes: Shape[] = [];
for (let subtrahendShape of target._ShapeList) for (let s of this._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 operatedShapes = minuendShape.SubstactBoolOperation(target); let ss = s.SubstactBoolOperation(target.ShapeList);
tmpShapes.push(...operatedShapes); newShapes.push(...ss);
} }
//迭代this形状列表,每次赋予新的结果 this._ShapeList = newShapes;
this._ShapeList = tmpShapes;
return true; return true;
} }

@ -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.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; 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;
}

@ -37,7 +37,7 @@ export function CreateWireframe(en3D: Board | ExtrudeSolid)
//正面 //正面
if (m.dir === FaceDirection.Front) if (m.dir === FaceDirection.Front)
{ {
let ss = upShape.SubstactBoolOperation(cloneShape); let ss = upShape.SubstactBoolOperation([cloneShape]);
if (ss.length > 0 && ss[0].Holes.length === 0) if (ss.length > 0 && ss[0].Holes.length === 0)
{ {
upShape = ss[0]; upShape = ss[0];
@ -46,7 +46,7 @@ export function CreateWireframe(en3D: Board | ExtrudeSolid)
} }
else else
{ {
let ss = downShape.SubstactBoolOperation(cloneShape); let ss = downShape.SubstactBoolOperation([cloneShape]);
if (ss.length > 0 && ss[0].Holes.length === 0) if (ss.length > 0 && ss[0].Holes.length === 0)
{ {
downShape = ss[0]; downShape = ss[0];

Loading…
Cancel
Save