From 1e5a24e38009d7ac8aa9b03dd02cae7b381843b2 Mon Sep 17 00:00:00 2001 From: ChenX Date: Mon, 24 Oct 2022 17:20:08 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=E5=B8=83=E5=B0=94=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E5=AF=BC=E8=87=B4=E6=97=A0=E6=B3=95=E6=8E=92=E9=92=BB?= =?UTF-8?q?=20fix=20#I5XE5P?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 3 ++ .../__snapshots__/bool5.test.ts.snap | 2 + __test__/Booloperate/bool.test.ts | 11 +++-- __test__/Booloperate/bool5.test.ts | 13 +++++ src/DatabaseServices/Contour.ts | 49 ++++++++++++++----- src/DatabaseServices/Entity/Circle.ts | 4 +- src/DatabaseServices/Entity/Curve.ts | 8 +-- src/DatabaseServices/Entity/Line.ts | 4 +- src/DatabaseServices/Entity/Polyline.ts | 6 +-- src/DatabaseServices/Shape.ts | 10 ++-- src/Geometry/DrillParse/Face.ts | 5 ++ src/GraphicsSystem/BoolOperateUtils.ts | 16 +++--- .../ToolPath/FeedingToolPath.ts | 7 ++- 13 files changed, 97 insertions(+), 41 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 9bbacb98b..61bd2c7a3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -40,4 +40,7 @@ } } ], + "todohighlight.keywords": [ + "注意:", + ], } diff --git a/__test__/Booloperate/__snapshots__/bool5.test.ts.snap b/__test__/Booloperate/__snapshots__/bool5.test.ts.snap index d891f08f9..d50e704f7 100644 --- a/__test__/Booloperate/__snapshots__/bool5.test.ts.snap +++ b/__test__/Booloperate/__snapshots__/bool5.test.ts.snap @@ -1,5 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`交点被优化成单个点,导致点在曲线外,导致求交错误 1`] = `"1260.00000"`; + exports[`分裂的点在参数周围 1`] = `"5782.01322"`; exports[`差集共线(圆) 1`] = `"442426.78779"`; diff --git a/__test__/Booloperate/bool.test.ts b/__test__/Booloperate/bool.test.ts index 5fbe14788..0c3e62a53 100644 --- a/__test__/Booloperate/bool.test.ts +++ b/__test__/Booloperate/bool.test.ts @@ -1,3 +1,4 @@ +import { arraySum } from "../../src/Common/ArrayExt"; import { Region } from "../../src/DatabaseServices/Entity/Region"; import { BoolOpeartionType } from "../../src/GraphicsSystem/BoolOperateUtils"; import { LoadRegionsFromFileData } from "../Utils/LoadEntity.util"; @@ -158,7 +159,7 @@ test('特殊相交', () => , 4); }); -function testSubtract(data, count) +function testSubtract(data: any[], count: number) { let regs = LoadRegionsFromFileData(data); regs[0].BooleanOper(regs[1], BoolOpeartionType.Subtract); @@ -166,17 +167,17 @@ function testSubtract(data, count) expect(expregs.length).toBe(count); } -function testRegion(reg) +function testRegion(reg: Region) { if (reg) { let shapeCount = reg.ShapeManager.ShapeList.length; - let holeCount = reg.ShapeManager.ShapeList.map(s => s.Holes.length).reduce((c1, c2) => c1 + c2, 0); + let holeCount = arraySum(reg.ShapeManager.ShapeList.map(s => s.Holes.length)); return { shapeCount, holeCount }; } else return { shapeCount: 0, holeCount: 0 }; } -function testBool(reg1, reg2, uShapeCount, uHoleCount, iShapeCount, iHoleCount, sShapeCount, sHoleCount) +function testBool(reg1: Region, reg2: Region, uShapeCount: number, uHoleCount: number, iShapeCount: number, iHoleCount: number, sShapeCount: number, sHoleCount: number) { let a = reg1.Clone() as Region; a.BooleanOper(reg2, BoolOpeartionType.Union); @@ -215,7 +216,7 @@ function RegionsBoolOperate(reg: Region[], boolType: BoolOpeartionType): Region } return firstReg; } -function testRegionsBool(regs, uShapeCount, uHoleCount, iShapeCount, iHoleCount, sShapeCount, sHoleCount) +function testRegionsBool(regs: Region[], uShapeCount: number, uHoleCount: number, iShapeCount: number, iHoleCount: number, sShapeCount: number, sHoleCount: number) { let unionReg = RegionsBoolOperate(regs, BoolOpeartionType.Union); let shapeCount1 = testRegion(unionReg).shapeCount; diff --git a/__test__/Booloperate/bool5.test.ts b/__test__/Booloperate/bool5.test.ts index b7a762f28..c5d179535 100644 --- a/__test__/Booloperate/bool5.test.ts +++ b/__test__/Booloperate/bool5.test.ts @@ -71,3 +71,16 @@ test("分裂的点在参数周围", () => for (let s of regs[0].ShapeManager.ShapeList) expect(s.Area).toMatchNumberSnapshot(); }); + +test('交点被优化成单个点,导致点在曲线外,导致求交错误', () => +{ + let d = + { "file": [2, "Region", 8, 2, 6655, false, 1, 1, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, true, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 1, 1, 1, 1, "Polyline", 8, 2, 0, false, 0, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 0, true, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 0, 2, 4, [0, 0], 0, [70, 0], 0, [70, 18], 0, [0, 18], 0, true, 0, "Region", 8, 2, 6656, false, 1, 2, 0, [0.7071067811865476, -0.7071067811865475, 0, 0, -0.7071067811865475, -0.7071067811865476, 0, 0, 0, 0, -1, 0, 183.22473504170694, 554.4594094334525, 0, 1], 0, 0, true, [0.7071067811865476, -0.7071067811865475, 0, 0, -0.7071067811865475, -0.7071067811865476, 0, 0, 0, 0, -1, 0, 183.22473504170694, 554.4594094334525, 0, 1], 0, 1, 1, 1, 1, "Polyline", 8, 2, 0, false, 0, 7, 0, [0.7071067811865476, -0.7071067811865475, 0, 0, -0.7071067811865475, -0.7071067811865476, 0, 0, 0, 0, -1, 0, 183.22473504170694, 554.4594094334525, 0, 1], 0, 0, true, [0.7071067811865476, -0.7071067811865475, 0, 0, -0.7071067811865475, -0.7071067811865476, 0, 0, 0, 0, -1, 0, 183.22473504170694, 554.4594094334525, 0, 1], 0, 2, 4, [0, 0], 0, [312, 0], 0, [312, 1313], 0, [0, 1313], 0, true, 0], "basePt": { "x": -745.2064686562298, "y": -594.5891099946873, "z": 0 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }; + + let regs = LoadRegionsFromFileData(d); + + regs[0].BooleanOper(regs[1], BoolOpeartionType.Intersection); + expect(regs[0].ShapeManager.ShapeList.length).toBe(1); + for (let s of regs[0].ShapeManager.ShapeList) + expect(s.Area).toMatchNumberSnapshot(); +}); diff --git a/src/DatabaseServices/Contour.ts b/src/DatabaseServices/Contour.ts index 3369df863..72b7dd30c 100644 --- a/src/DatabaseServices/Contour.ts +++ b/src/DatabaseServices/Contour.ts @@ -240,13 +240,13 @@ export class Contour let targetContainerSource: boolean; if (sourceOutline.Area > targetOutline.Area) { - sourceContainerTarget = interPts.length === 0 ? fastCurveInCurve(sourceOutline as Polyline, targetOutline as Polyline) : this.CuInOutline(targetOutline); + sourceContainerTarget = CurveContainerCurve(sourceOutline, targetOutline, interPts); targetContainerSource = false; } else { sourceContainerTarget = false; - targetContainerSource = interPts.length === 0 ? fastCurveInCurve(targetOutline as Polyline, sourceOutline as Polyline) : target.CuInOutline(sourceOutline); + targetContainerSource = CurveContainerCurve(targetOutline, sourceOutline, interPts); } //包含.相交.分离(三种状态) @@ -568,10 +568,19 @@ export class Contour { return this._Curve.Shape; } - CuInOutline(targetCur: Curve) + + /** + * 判断是否完全包含曲线 + * @param smallCurve 传入的这个曲线不能比本轮廓还大(这个需要自己优化?) + * @returns + */ + ContainerCurve(smallCurve: Polyline | Circle | Curve, isAreaCheckd = false, ipts: any[] = undefined): boolean { - return isTargetCurInOrOnSourceCur(this._Curve, targetCur); + if (!isAreaCheckd && this.Area < smallCurve.Area) + return false; + return CurveContainerCurve(this._Curve, smallCurve, ipts); } + Equal(tar: Contour) { return equalCurve(this._Curve, tar._Curve); @@ -598,16 +607,34 @@ function fastEqualCurve(c1: Curve, c2: Curve, tolerance = 1e-3) } -//对于双多段线互相切割后的结果,快速判断曲线是否在另一条曲线内部 -//也许有一天这个中点算法需要改一下, 使用.MidPoint比较稳妥 -function fastCurveInCurve(sourceCu: Polyline | Circle, targetCu: Curve) +/** + * 对于双多段线互相切割后的结果(或者交点个数为0),快速判断曲线是否在另一条曲线内部 + * @param bigCurve + * @param smallCurve + * @returns + */ +function fastCurveInCurve(bigCurve: Polyline | Circle, smallCurve: Curve): boolean { - return sourceCu.PtInCurve(targetCu.Midpoint); + return bigCurve.PtInCurve(smallCurve.Midpoint); } //当交点小于等于1时 -export function fastCurveInCurve2(sourceCu: Polyline | Circle, targetCu: Curve) +export function fastCurveInCurve2(bigCurve: Polyline | Circle, smallCurve: Curve) +{ + return bigCurve.PtInCurve(smallCurve.StartPoint) || + bigCurve.PtInCurve(smallCurve.Midpoint); +} + +//大曲线是否完全包含小曲线(或者重合) +function CurveContainerCurve(bigCurve: Polyline | Circle, smallCurve: Polyline | Circle | Curve, ipts: any[] = undefined, fuzz = COMBINE_FUZZ) { - return sourceCu.PtInCurve(targetCu.StartPoint) || - sourceCu.PtInCurve(targetCu.Midpoint); + if (!ipts) + ipts = bigCurve.IntersectWith2(smallCurve, IntersectOption.ExtendNone, fuzz); + + if (ipts.length === 0) + return fastCurveInCurve(bigCurve, smallCurve); + else if (ipts.length === 1) + return fastCurveInCurve2(bigCurve, smallCurve); + else + return isTargetCurInOrOnSourceCur(bigCurve, smallCurve); } diff --git a/src/DatabaseServices/Entity/Circle.ts b/src/DatabaseServices/Entity/Circle.ts index 55b662cd4..406e7598d 100644 --- a/src/DatabaseServices/Entity/Circle.ts +++ b/src/DatabaseServices/Entity/Circle.ts @@ -11,7 +11,7 @@ import { Box3Ext } from '../../Geometry/Box'; import { BufferGeometryUtils } from '../../Geometry/BufferGeometryUtils'; import { angle, AsVector3, equaln, equalv2, MoveMatrix, polar, ZeroVec } from '../../Geometry/GeUtils'; import { Orbit } from '../../Geometry/Orbit'; -import { IntersectCircleAndArc, IntersectCircleAndCircle, IntersectEllipseAndCircleOrArc, IntersectLineAndCircle, IntersectOption, IntersectPolylineAndCurve, reverseIntersectOption } from '../../GraphicsSystem/IntersectWith'; +import { IntersectCircleAndArc, IntersectCircleAndCircle, IntersectEllipseAndCircleOrArc, IntersectLineAndCircle, IntersectOption, IntersectPolylineAndCurve, IntersectResult, reverseIntersectOption } from '../../GraphicsSystem/IntersectWith'; import { RenderType } from '../../GraphicsSystem/RenderType'; import { Factory } from '../CADFactory'; import { CADFiler } from '../CADFiler'; @@ -215,7 +215,7 @@ export class Circle extends Curve return []; } - IntersectWith2(curve: Curve, intType: IntersectOption) + override IntersectWith2(curve: Curve, intType: IntersectOption): IntersectResult[] { if (curve instanceof Arc) { diff --git a/src/DatabaseServices/Entity/Curve.ts b/src/DatabaseServices/Entity/Curve.ts index b3deecf60..af17efa76 100644 --- a/src/DatabaseServices/Entity/Curve.ts +++ b/src/DatabaseServices/Entity/Curve.ts @@ -85,8 +85,8 @@ export abstract class Curve extends Entity GetPointAtDistance(distance: number): Vector3 { return; } GetDistAtParam(param: number): number { return; } GetDistAtPoint(pt: Vector3): number { return; } - GetParamAtPoint(pt: Vector3): number { return; } - GetParamAtPoint2(pt: Vector3): number { return this.GetParamAtPoint(pt); } + GetParamAtPoint(pt: Vector3, fuzz = 1e-6): number { return; } + GetParamAtPoint2(pt: Vector3, fuzz = 1e-6): number { return this.GetParamAtPoint(pt, fuzz); } GetParamAtDist(d: number): number { return; } @@ -144,9 +144,9 @@ export abstract class Curve extends Entity Reverse(): this { return this; } //点在曲线上 - PtOnCurve(pt: Vector3, fuzz = 1e-6): boolean + PtOnCurve(pt: Vector3, fuzz = 1e-5): boolean { - return equalv3(this.StartPoint, pt, fuzz) || equalv3(this.EndPoint, pt, fuzz) || this.ParamOnCurve(this.GetParamAtPoint(pt)); + return equalv3(this.StartPoint, pt, fuzz) || equalv3(this.EndPoint, pt, fuzz) || this.ParamOnCurve(this.GetParamAtPoint(pt, fuzz)); } //点在曲线中,不在起点或者终点. diff --git a/src/DatabaseServices/Entity/Line.ts b/src/DatabaseServices/Entity/Line.ts index 96dbf22d5..b24247fbb 100644 --- a/src/DatabaseServices/Entity/Line.ts +++ b/src/DatabaseServices/Entity/Line.ts @@ -229,10 +229,10 @@ export class Line extends Curve { return this.StartPoint.add(this.GetFistDeriv(0).multiplyScalar(param)); } - GetParamAtPoint(pt: Vector3): number + override GetParamAtPoint(pt: Vector3, fuzz = 1e-5): number { let { closestPt, param } = this.GetClosestAtPoint(pt, true); - if (!equalv3(closestPt, pt, 1e-5)) + if (!equalv3(closestPt, pt, fuzz)) return NaN; return param; } diff --git a/src/DatabaseServices/Entity/Polyline.ts b/src/DatabaseServices/Entity/Polyline.ts index 40c756d0c..7a99937ee 100644 --- a/src/DatabaseServices/Entity/Polyline.ts +++ b/src/DatabaseServices/Entity/Polyline.ts @@ -1085,12 +1085,12 @@ export class Polyline extends Curve return pl; } - PtOnCurve(pt: Vector3): boolean + override PtOnCurve(pt: Vector3, fuzz = 1e-6): boolean { for (let i = 0; i < this.EndParam; i++) { let c = this.GetCurveAtIndex(i); - if (c.PtOnCurve(pt)) + if (c.PtOnCurve(pt, fuzz)) return true; } return false; @@ -1262,7 +1262,7 @@ export class Polyline extends Curve return curve; } - IntersectWith2(curve: Curve, intType: IntersectOption, tolerance = 1e-5) + override IntersectWith2(curve: Curve, intType: IntersectOption, tolerance = 1e-5) { return IntersectPolylineAndCurve(this, curve, intType, tolerance); } diff --git a/src/DatabaseServices/Shape.ts b/src/DatabaseServices/Shape.ts index 85d4554ce..339ce48e8 100644 --- a/src/DatabaseServices/Shape.ts +++ b/src/DatabaseServices/Shape.ts @@ -266,8 +266,8 @@ export class Shape { srcHoles.forEach(cu => { - let tmpContours = cu.SubstactBoolOperation(outline).sort((a, b) => b.Area - a.Area); - let isAllContainered = tmpContours.length > 1 && tmpContours.slice(1).every((cu, index) => tmpContours[0].CuInOutline(cu.Curve)); + let tmpContours = cu.SubstactBoolOperation(outline).sort((a, b) => b.Area - a.Area);//面积从大到校 + let isAllContainered = tmpContours.length > 1 && tmpContours.slice(1).every((cu, index) => tmpContours[0].ContainerCurve(cu.Curve, true)); //洞是否被最大的洞包含,是,则把被包含的洞都提取出来加入形状数组 if (isAllContainered) @@ -421,7 +421,7 @@ export class Shape //取出包含的洞 arrayRemoveIf(contours, (con: Contour) => { - let bisIn = outline.CuInOutline(con.Curve); + let bisIn = outline.ContainerCurve(con.Curve, true); if (bisIn) tmpHoles.push(con); return bisIn; }); @@ -526,14 +526,14 @@ export class Shape { let holes: Contour[] = []; if (tmpHoles.length <= 1) return tmpHoles; - tmpHoles.sort((a, b) => b.Area - a.Area); + tmpHoles.sort((a, b) => b.Area - a.Area);//面积从大到小排序 while (tmpHoles.length) { let srcHole = tmpHoles.shift(); holes.push(srcHole); //移除包含的洞 - arrayRemoveIf(tmpHoles, h => srcHole.CuInOutline(h.Curve)); + arrayRemoveIf(tmpHoles, h => srcHole.ContainerCurve(h.Curve, true)); } return holes; } diff --git a/src/Geometry/DrillParse/Face.ts b/src/Geometry/DrillParse/Face.ts index 3f8454620..fac4f8389 100644 --- a/src/Geometry/DrillParse/Face.ts +++ b/src/Geometry/DrillParse/Face.ts @@ -90,6 +90,11 @@ export class Face if (!sideReg || !noSideFace.Region) return []; let toReg = noSideFace.Region.Clone().ApplyMatrix(diffMtx); + + //注意: 排钻因为布尔运算失败的重灾区 + // TestDraw(sideReg.Clone(), 1); + // TestDraw(toReg.Clone(), 2); + isSuccess = sideReg.BooleanOper(toReg, BoolOpeartionType.Intersection); for (let s of sideReg.ShapeManager.ShapeList) { diff --git a/src/GraphicsSystem/BoolOperateUtils.ts b/src/GraphicsSystem/BoolOperateUtils.ts index f44ddc8c5..a7f0a22f4 100644 --- a/src/GraphicsSystem/BoolOperateUtils.ts +++ b/src/GraphicsSystem/BoolOperateUtils.ts @@ -15,24 +15,24 @@ export enum BoolOpeartionType const fuzz = 1e-3; let fuzzV3 = new Vector3(fuzz, fuzz, fuzz); -//判断曲线是否在源封闭曲线内 -export function isTargetCurInOrOnSourceCur(sourceCur: Polyline | Circle | Ellipse, targetCur: Curve) +//判断小曲线是不是被大曲线包含(或者重叠?) +export function isTargetCurInOrOnSourceCur(bigCurve: Polyline | Circle | Ellipse, smallCurve: Curve) { - if (!sourceCur.BoundingBox.expandByVector(fuzzV3).containsBox(targetCur.BoundingBox)) + if (!bigCurve.BoundingBox.expandByVector(fuzzV3).containsBox(smallCurve.BoundingBox)) return false; let cus: Curve[] = []; - if (targetCur instanceof Polyline) - cus = targetCur.Explode(); + if (smallCurve instanceof Polyline) + cus = smallCurve.Explode(); else - cus = [targetCur]; + cus = [smallCurve]; return cus.every(c => { - let pts = getIntPtContextPts(sourceCur, c); + let pts = getIntPtContextPts(bigCurve, c); if (pts.length <= 1) pts.push(c.StartPoint, c.EndPoint); - return IsPtsAllInOrOnReg(sourceCur, pts); + return IsPtsAllInOrOnReg(bigCurve, pts); }); } diff --git a/src/GraphicsSystem/ToolPath/FeedingToolPath.ts b/src/GraphicsSystem/ToolPath/FeedingToolPath.ts index f9e3f2652..ef71f1fe0 100644 --- a/src/GraphicsSystem/ToolPath/FeedingToolPath.ts +++ b/src/GraphicsSystem/ToolPath/FeedingToolPath.ts @@ -105,7 +105,12 @@ export class FeedingToolPath extends Singleton { if (holes.length > 0) { - isInt = holes.some(h => h.Curve.IntersectWith(c, 0).length > 0 || h.CuInOutline(c)); + isInt = holes.some(h => + { + let ipts = h.Curve.IntersectWith2(c, 0); + return ipts.length > 0 || h.ContainerCurve(c, false, ipts); + }); + if (isInt) break; } if (isOut && offsetDist === knifRadius)