diff --git a/__test__/Booloperate/bool.test.ts b/__test__/Booloperate/bool.test.ts index 5165a1292..51dda6db6 100644 --- a/__test__/Booloperate/bool.test.ts +++ b/__test__/Booloperate/bool.test.ts @@ -134,6 +134,29 @@ test("有交点时", () => expect(regs[1].Explode().length).toBe(2); }); +//#IQ937 +test.only('特殊相交', () => +{ + testSubtract( + [2, "Region", 1, 1, 13, false, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2147.1101515831265, -2428.922411894075, 0, 1], 1, 1, 1, 1, "Polyline", 1, 1, 0, false, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2147.1101515831265, -2428.922411894075, 0, 1], 2, 8, [286.3247863247866, -182.3361823361823], 0, [5.698005698005659, -192.3076923076923], 0, [-207.97720797720808, -166.66666666666663], 0, [-215.09971509971524, 45.584045584045604], 0, [-215.09971509971524, 313.39031339031345], 0, [52.70655270655277, 274.92877492877494], 0, [293.44729344729353, 250.71225071225066], 0, [286.3247863247866, 18.518518518518544], 0, true, 0, "Region", 1, 1, 12, false, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2147.1101515831265, -2428.922411894075, 0, 1], 1, 1, 1, 1, "Polyline", 1, 1, 0, false, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2147.1101515831265, -2428.922411894075, 0, 1], 2, 4, [286.3247863247866, 18.518518518518544], 0, [5.698005698005659, -192.3076923076923], 0, [-215.09971509971524, 45.584045584045604], 0, [52.70655270655277, 274.92877492877494], 0, true, 0] + , 4); + testSubtract( + [2, "Region", 1, 1, 7, false, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -165.24216524216527, -477.2079772079772, 0, 1], 1, 1, 1, 1, "Polyline", 1, 1, 0, false, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -165.24216524216527, -477.2079772079772, 0, 1], 2, 4, [0, 0], 0, [600, 0], 0, [600, 1200], 0, [0, 1200], 0, true, 0, "Region", 1, 1, 8, false, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -165.24216524216527, -477.2079772079772, 0, 1], 1, 1, 1, 1, "Polyline", 1, 1, 0, false, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -165.24216524216527, -477.2079772079772, 0, 1], 2, 17, [260.68452291480645, 694.8814057850639], 0, [268.61637170569537, 691.99444892248], 0, [569.9719350514073, 582.3099939341178], 0, [563.8155724715452, 565.3955267599715], 0, [262.46000912583327, 675.0799817483337], 0, [244.5953021712127, 681.582203323704], 0, [234.37028331333872, 685.3038058327808], 0, [-5.684341886080802e-14, 599.9999989101887], 0, [-6.156362579862105, 616.9144660843351], 0, [208.05604371187087, 694.881405785064], 0, [0, 770.6076127553729], 0, [6.156362579862048, 787.5220799295191], 0, [234.37028331333858, 704.4590057373472], 0, [245.7073243841179, 708.5853512317643], 0, [247.65394027111975, 709.2938614721828], 0, [557.659209066764, 822.1265517794905], 0, [563.815571646626, 805.212084605344], 0, true, 0] + , 2); + + testSubtract( + [2, "Region", 1, 1, 63, false, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 1, 1, 1, 1, "Polyline", 1, 1, 0, false, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 5, [4456.332258552945, -1732.8363833524356], 0, [4915.293761397059, -1732.8363833524356], 0, [4915.293761397059, -1334.2078480183466], 0, [4456.332258552945, -1334.2078480183466], 0, [4456.332258552945, -1732.8363833524356], 0, true, 0, "Region", 1, 1, 64, false, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 1, 1, 1, 1, "Polyline", 1, 1, 0, false, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 4, [4456.332258552945, -1533.522115685391], 0, [4685.8130099750015, -1334.2078480183466], 0, [4915.293761397059, -1533.522115685391], 0, [4685.8130099750015, -1732.8363833524356], 0, true, 0] + , 4); +}); + +function testSubtract(data, count) +{ + let regs = LoadRegionsFromFileData(data); + regs[0].BooleanOper(regs[1], BoolOpeartionType.Subtract); + let expregs = regs[0].Explode(); + expect(expregs.length).toBe(count); +} + function testRegion(reg) { if (reg) @@ -200,5 +223,4 @@ function testRegionsBool(regs, uShapeCount, uHoleCount, iShapeCount, iHoleCount, expect(holeCount2).toBe(iHoleCount); expect(shapeCount3).toBe(sShapeCount); expect(holeCount3).toBe(sHoleCount); - } diff --git a/src/Common/ArrayExt.ts b/src/Common/ArrayExt.ts index 5f5ec9cf3..98b6c4435 100644 --- a/src/Common/ArrayExt.ts +++ b/src/Common/ArrayExt.ts @@ -86,7 +86,7 @@ export function arrayRemoveDuplicateBySort(arr: Array, checkFuction: (e1: if (arr.length < 2) return arr; let j = 1; for (let i = 1, l = arr.length; i < l; i++) - if (!checkFuction(arr[i], arr[j - 1])) + if (!checkFuction(arr[j - 1], arr[i])) arr[j++] = arr[i]; arr.length = j; return arr; @@ -108,7 +108,7 @@ export function arrayRemoveDuplicateBySort2(arr: Array, checkFuction: (e1: for (let i = 1, len = arr.length; i < len; i++) { now = arr[i]; - if (!checkEqual(now, pre)) + if (!checkFuction(pre, now)) { newArr.push(now); pre = now; @@ -143,7 +143,17 @@ function checkEqual(e1, e2): boolean * @param arr 需要改变初始值位置的数组 * @param index //将index位置以后的值放到起始位置 */ -export function changeArrayStartIndex(arr: any[], index: number) +export function changeArrayStartIndex(arr: T[], index: number): T[] { - arr.unshift(...arr.splice(index)) + arr.unshift(...arr.splice(index)); + return arr; +} + +export function equalArray(a: T[], b: T[], checkF = checkEqual) +{ + if (a === b) return true; + if (a.length !== b.length) return false; + for (var i = 0; i < a.length; ++i) + if (!checkF(a[i], b[i])) return false; + return true; } diff --git a/src/Common/CurveUtils.ts b/src/Common/CurveUtils.ts index 3d9c5210f..d9651bf1e 100644 --- a/src/Common/CurveUtils.ts +++ b/src/Common/CurveUtils.ts @@ -12,6 +12,7 @@ import { Stand } from '../Geometry/RegionParse'; import { FixIndex } from './Utils'; import { PlaneExt } from '../Geometry/Plane'; import { IntersectOption } from '../GraphicsSystem/IntersectWith'; +import { arrayLast, changeArrayStartIndex, equalArray } from './ArrayExt'; //3点获取圆心 export function getCircleCenter(pt1: Vector3, pt2: Vector3, pt3: Vector3) @@ -178,31 +179,41 @@ export function curveLinkGroup(cus: Curve[]): Array> export function equalCurveAndCurve(cu1: Curve, cu2: Curve) { - if (cu1.EndParam !== cu2.EndParam || !equaln(cu1.Length, cu2.Length)) return false; - let parAtPl2 = cu2.GetParamAtPoint(cu1.StartPoint); - if ((cu1 instanceof Polyline) && (cu2 instanceof Polyline)) { - let ptsAndBuls1 = cu1.PtsBuls; - let ptsAndBuls2 = cu2.PtsBuls; - // 判断凸度是否相等 - let buls2 = ptsAndBuls2.buls.slice(parAtPl2); - buls2.push(...ptsAndBuls2.buls.slice(0, parAtPl2)); - let isEqual = ptsAndBuls1.buls.every((bul, i) => - { - return equaln(bul, buls2[i]) - }) - if (!isEqual) return false; + if (cu1.IsClose !== cu2.IsClose) + return false; + + let ptsBuls1 = cu1.PtsBuls; + let ptsBuls2 = cu2.PtsBuls; - let pts2 = ptsAndBuls2.pts.slice(parAtPl2); - pts2.push(...ptsAndBuls2.pts.slice(0, parAtPl2)); + let pts1 = ptsBuls1.pts; + let pts2 = ptsBuls2.pts; + let buls1 = ptsBuls1.buls; + let buls2 = ptsBuls2.buls; - return ptsAndBuls1.pts.every((pt, i) => + if (cu1.IsClose && equalv2(pts1[0], arrayLast(pts1))) + { + pts1.pop(); + buls1.pop(); + } + if (cu2.IsClose && equalv2(pts2[0], arrayLast(pts2))) { - return equalv3( - Vec2DTo3D(pt).applyMatrix4(cu1.OCS), - Vec2DTo3D(pts2[i]).applyMatrix4(cu2.OCS)); - }) + pts2.pop(); + buls2.pop(); + } + + let parAtPl2 = cu2.GetParamAtPoint(cu1.StartPoint); + changeArrayStartIndex(buls2, parAtPl2); + changeArrayStartIndex(pts2, parAtPl2); + + return equalArray(buls1, buls2, equaln) && + equalArray(pts1, pts2, (p1: Vector2, p2: Vector2) => + equalv3( + Vec2DTo3D(p1).applyMatrix4(cu1.OCS), + Vec2DTo3D(p2).applyMatrix4(cu2.OCS) + ) + ); } else if (cu1 instanceof Circle && cu2 instanceof Circle) { @@ -211,7 +222,10 @@ export function equalCurveAndCurve(cu1: Curve, cu2: Curve) else if (cu1 instanceof Arc && cu2 instanceof Arc) { if (!equalv3(cu1.StartPoint, cu2.EndPoint)) cu1.Reverse(); - return equalv3(cu1.Center, cu2.Center) && equaln(cu1.Radius, cu2.Radius, 1e-6) && equaln(cu1.StartAngle, cu2.StartAngle) && equaln(cu1.EndAngle, cu2.EndAngle) + return equalv3(cu1.Center, cu2.Center) + && equaln(cu1.Radius, cu2.Radius, 1e-6) + && equaln(cu1.StartAngle, cu2.StartAngle) + && equaln(cu1.EndAngle, cu2.EndAngle); } return false; } diff --git a/src/DatabaseServices/Contour.ts b/src/DatabaseServices/Contour.ts index c4e23d6e7..605e58843 100644 --- a/src/DatabaseServices/Contour.ts +++ b/src/DatabaseServices/Contour.ts @@ -1,14 +1,19 @@ import * as THREE from "three"; +import { Vector3 } from "three"; import { curveLinkGroup, equalCurveAndCurve, Vec3DTo2D } from "../Common/CurveUtils"; +import { FixIndex } from "../Common/Utils"; import { equaln, rotatePoint } from "../Geometry/GeUtils"; +import { RegionParse, Route } from "../Geometry/RegionParse"; import { isTargetCurInOrOnSourceCur } from "../GraphicsSystem/BoolOperateUtils"; import { IntersectOption } from "../GraphicsSystem/IntersectWith"; import { Arc } from "./Arc"; import { Circle } from "./Circle"; import { Curve } from "./Curve"; import { Polyline } from "./Polyline"; -import { Vector3 } from "three"; -import { FixIndex } from "../Common/Utils"; +import { arrayRemoveDuplicateBySort } from "../Common/ArrayExt"; +import { Status } from "../Common/Status"; + +let cache = new WeakMap(); export class Contour { @@ -113,7 +118,29 @@ export class Contour SubstactBoolOperation(target: Contour): Contour[] { let subtractList = this.getSubtractList(target); - return Contour.GetAllContour(subtractList); + + //纯网洞 + if (subtractList.every(c => c.IsClose)) + return Contour.GetAllContour(subtractList); + + let cuGroups: Curve[][] = []; + let regParse = new RegionParse(subtractList); + //分析封闭包围区域 + const parseRoute = (routeSet: Set[]) => + { + for (let routes of routeSet) + { + let cs: Curve[] = []; + for (let r of routes) + cs.push(r.curve); + let c = Contour.Combine(cs, false); + if (!equalCurveAndCurve(c, this.Curve) && !equalCurveAndCurve(c, target.Curve)) + cuGroups.push(cs); + } + } + parseRoute(regParse.m_RegionsOutline); + parseRoute(regParse.m_RegionsInternal); + return Contour.GetAllContour(cuGroups); } /** * 计算与目标轮廓布尔运算后的结果曲线. @@ -299,44 +326,44 @@ export class Contour let contours: Contour[] = [] - cuGroups.forEach(c => - { - contours.push(Contour.CreateContour(c, false)); - }) + for (let g of cuGroups) + contours.push(Contour.CreateContour(g, false)); return contours.filter(c => c !== undefined && !equaln(c.Area, 0, 1e-6)); } /** - * 合并曲线数组 - * 首尾不相连会返回多条 - * @param {Curve[]} cus - * @returns - * @memberof Polyline + * 合并曲线组成为多段线 + * @param cus 曲线组 + * @param [needLink=true] 需要解析成首尾连接状态 + * @returns 单一曲线,如果返回超过1个,其他的将被遗弃. */ static Combine(cus: Curve[], needLink = true): Curve { if (cus.length === 0) return undefined; let groups = needLink ? curveLinkGroup(cus) : [cus]; - - let joinCus: Curve[] = []; - for (let g of groups) { if (g.length === 1) - { - joinCus.push(g[0].Clone() as Curve); - } + return g[0].Clone(); else { + if (cache.has(g)) + return cache.get(g) + let pl = new Polyline(); - for (let cu of g) - { + + let gclone = g.map(c => c.Clone()); + + arrayRemoveDuplicateBySort(gclone, (cu1: Curve, cu2: Curve) => cu1.Join(cu2) === Status.True); + + for (let cu of gclone) pl.Join(cu); - } - joinCus.push(pl); + + cache.set(g, pl); + + return pl; } } - return joinCus[0]; } get Shape(): THREE.Shape {