修复:布尔错误导致无法排钻 fix #I5XE5P

pull/2014/MERGE
ChenX 2 years ago
parent e13b70a623
commit 1e5a24e380

@ -40,4 +40,7 @@
} }
} }
], ],
"todohighlight.keywords": [
"注意:",
],
} }

@ -1,5 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`交点被优化成单个点,导致点在曲线外,导致求交错误 1`] = `"1260.00000"`;
exports[`分裂的点在参数周围 1`] = `"5782.01322"`; exports[`分裂的点在参数周围 1`] = `"5782.01322"`;
exports[`差集共线(圆) 1`] = `"442426.78779"`; exports[`差集共线(圆) 1`] = `"442426.78779"`;

@ -1,3 +1,4 @@
import { arraySum } from "../../src/Common/ArrayExt";
import { Region } from "../../src/DatabaseServices/Entity/Region"; import { Region } from "../../src/DatabaseServices/Entity/Region";
import { BoolOpeartionType } from "../../src/GraphicsSystem/BoolOperateUtils"; import { BoolOpeartionType } from "../../src/GraphicsSystem/BoolOperateUtils";
import { LoadRegionsFromFileData } from "../Utils/LoadEntity.util"; import { LoadRegionsFromFileData } from "../Utils/LoadEntity.util";
@ -158,7 +159,7 @@ test('特殊相交', () =>
, 4); , 4);
}); });
function testSubtract(data, count) function testSubtract(data: any[], count: number)
{ {
let regs = LoadRegionsFromFileData(data); let regs = LoadRegionsFromFileData(data);
regs[0].BooleanOper(regs[1], BoolOpeartionType.Subtract); regs[0].BooleanOper(regs[1], BoolOpeartionType.Subtract);
@ -166,17 +167,17 @@ function testSubtract(data, count)
expect(expregs.length).toBe(count); expect(expregs.length).toBe(count);
} }
function testRegion(reg) function testRegion(reg: Region)
{ {
if (reg) if (reg)
{ {
let shapeCount = reg.ShapeManager.ShapeList.length; 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 }; return { shapeCount, holeCount };
} else return { shapeCount: 0, holeCount: 0 }; } 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; let a = reg1.Clone() as Region;
a.BooleanOper(reg2, BoolOpeartionType.Union); a.BooleanOper(reg2, BoolOpeartionType.Union);
@ -215,7 +216,7 @@ function RegionsBoolOperate(reg: Region[], boolType: BoolOpeartionType): Region
} }
return firstReg; 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 unionReg = RegionsBoolOperate(regs, BoolOpeartionType.Union);
let shapeCount1 = testRegion(unionReg).shapeCount; let shapeCount1 = testRegion(unionReg).shapeCount;

@ -71,3 +71,16 @@ test("分裂的点在参数周围", () =>
for (let s of regs[0].ShapeManager.ShapeList) for (let s of regs[0].ShapeManager.ShapeList)
expect(s.Area).toMatchNumberSnapshot(); 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();
});

@ -240,13 +240,13 @@ export class Contour
let targetContainerSource: boolean; let targetContainerSource: boolean;
if (sourceOutline.Area > targetOutline.Area) 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; targetContainerSource = false;
} }
else else
{ {
sourceContainerTarget = false; 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; 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) Equal(tar: Contour)
{ {
return equalCurve(this._Curve, tar._Curve); return equalCurve(this._Curve, tar._Curve);
@ -598,16 +607,34 @@ function fastEqualCurve(c1: Curve, c2: Curve, tolerance = 1e-3)
} }
//对于双多段线互相切割后的结果,快速判断曲线是否在另一条曲线内部 /**
//也许有一天这个中点算法需要改一下, 使用.MidPoint比较稳妥 * 线(0),线线
function fastCurveInCurve(sourceCu: Polyline | Circle, targetCu: Curve) * @param bigCurve
* @param smallCurve
* @returns
*/
function fastCurveInCurve(bigCurve: Polyline | Circle, smallCurve: Curve): boolean
{ {
return sourceCu.PtInCurve(targetCu.Midpoint); return bigCurve.PtInCurve(smallCurve.Midpoint);
} }
//当交点小于等于1时 //当交点小于等于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) || if (!ipts)
sourceCu.PtInCurve(targetCu.Midpoint); 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);
} }

@ -11,7 +11,7 @@ import { Box3Ext } from '../../Geometry/Box';
import { BufferGeometryUtils } from '../../Geometry/BufferGeometryUtils'; import { BufferGeometryUtils } from '../../Geometry/BufferGeometryUtils';
import { angle, AsVector3, equaln, equalv2, MoveMatrix, polar, ZeroVec } from '../../Geometry/GeUtils'; import { angle, AsVector3, equaln, equalv2, MoveMatrix, polar, ZeroVec } from '../../Geometry/GeUtils';
import { Orbit } from '../../Geometry/Orbit'; 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 { RenderType } from '../../GraphicsSystem/RenderType';
import { Factory } from '../CADFactory'; import { Factory } from '../CADFactory';
import { CADFiler } from '../CADFiler'; import { CADFiler } from '../CADFiler';
@ -215,7 +215,7 @@ export class Circle extends Curve
return []; return [];
} }
IntersectWith2(curve: Curve, intType: IntersectOption) override IntersectWith2(curve: Curve, intType: IntersectOption): IntersectResult[]
{ {
if (curve instanceof Arc) if (curve instanceof Arc)
{ {

@ -85,8 +85,8 @@ export abstract class Curve extends Entity
GetPointAtDistance(distance: number): Vector3 { return; } GetPointAtDistance(distance: number): Vector3 { return; }
GetDistAtParam(param: number): number { return; } GetDistAtParam(param: number): number { return; }
GetDistAtPoint(pt: Vector3): number { return; } GetDistAtPoint(pt: Vector3): number { return; }
GetParamAtPoint(pt: Vector3): number { return; } GetParamAtPoint(pt: Vector3, fuzz = 1e-6): number { return; }
GetParamAtPoint2(pt: Vector3): number { return this.GetParamAtPoint(pt); } GetParamAtPoint2(pt: Vector3, fuzz = 1e-6): number { return this.GetParamAtPoint(pt, fuzz); }
GetParamAtDist(d: number): number { return; } GetParamAtDist(d: number): number { return; }
@ -144,9 +144,9 @@ export abstract class Curve extends Entity
Reverse(): this { return this; } 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));
} }
//点在曲线中,不在起点或者终点. //点在曲线中,不在起点或者终点.

@ -229,10 +229,10 @@ export class Line extends Curve
{ {
return this.StartPoint.add(this.GetFistDeriv(0).multiplyScalar(param)); 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); let { closestPt, param } = this.GetClosestAtPoint(pt, true);
if (!equalv3(closestPt, pt, 1e-5)) if (!equalv3(closestPt, pt, fuzz))
return NaN; return NaN;
return param; return param;
} }

@ -1085,12 +1085,12 @@ export class Polyline extends Curve
return pl; return pl;
} }
PtOnCurve(pt: Vector3): boolean override PtOnCurve(pt: Vector3, fuzz = 1e-6): boolean
{ {
for (let i = 0; i < this.EndParam; i++) for (let i = 0; i < this.EndParam; i++)
{ {
let c = this.GetCurveAtIndex(i); let c = this.GetCurveAtIndex(i);
if (c.PtOnCurve(pt)) if (c.PtOnCurve(pt, fuzz))
return true; return true;
} }
return false; return false;
@ -1262,7 +1262,7 @@ export class Polyline extends Curve
return 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); return IntersectPolylineAndCurve(this, curve, intType, tolerance);
} }

@ -266,8 +266,8 @@ export class Shape
{ {
srcHoles.forEach(cu => srcHoles.forEach(cu =>
{ {
let tmpContours = cu.SubstactBoolOperation(outline).sort((a, b) => b.Area - a.Area); 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 isAllContainered = tmpContours.length > 1 && tmpContours.slice(1).every((cu, index) => tmpContours[0].ContainerCurve(cu.Curve, true));
//洞是否被最大的洞包含,是,则把被包含的洞都提取出来加入形状数组 //洞是否被最大的洞包含,是,则把被包含的洞都提取出来加入形状数组
if (isAllContainered) if (isAllContainered)
@ -421,7 +421,7 @@ export class Shape
//取出包含的洞 //取出包含的洞
arrayRemoveIf(contours, (con: Contour) => arrayRemoveIf(contours, (con: Contour) =>
{ {
let bisIn = outline.CuInOutline(con.Curve); let bisIn = outline.ContainerCurve(con.Curve, true);
if (bisIn) tmpHoles.push(con); if (bisIn) tmpHoles.push(con);
return bisIn; return bisIn;
}); });
@ -526,14 +526,14 @@ export class Shape
{ {
let holes: Contour[] = []; let holes: Contour[] = [];
if (tmpHoles.length <= 1) return tmpHoles; 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) while (tmpHoles.length)
{ {
let srcHole = tmpHoles.shift(); let srcHole = tmpHoles.shift();
holes.push(srcHole); holes.push(srcHole);
//移除包含的洞 //移除包含的洞
arrayRemoveIf(tmpHoles, h => srcHole.CuInOutline(h.Curve)); arrayRemoveIf(tmpHoles, h => srcHole.ContainerCurve(h.Curve, true));
} }
return holes; return holes;
} }

@ -90,6 +90,11 @@ export class Face
if (!sideReg || !noSideFace.Region) return []; if (!sideReg || !noSideFace.Region) return [];
let toReg = noSideFace.Region.Clone().ApplyMatrix(diffMtx); let toReg = noSideFace.Region.Clone().ApplyMatrix(diffMtx);
//注意: 排钻因为布尔运算失败的重灾区
// TestDraw(sideReg.Clone(), 1);
// TestDraw(toReg.Clone(), 2);
isSuccess = sideReg.BooleanOper(toReg, BoolOpeartionType.Intersection); isSuccess = sideReg.BooleanOper(toReg, BoolOpeartionType.Intersection);
for (let s of sideReg.ShapeManager.ShapeList) for (let s of sideReg.ShapeManager.ShapeList)
{ {

@ -15,24 +15,24 @@ export enum BoolOpeartionType
const fuzz = 1e-3; const fuzz = 1e-3;
let fuzzV3 = new Vector3(fuzz, fuzz, fuzz); 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; return false;
let cus: Curve[] = []; let cus: Curve[] = [];
if (targetCur instanceof Polyline) if (smallCurve instanceof Polyline)
cus = targetCur.Explode(); cus = smallCurve.Explode();
else else
cus = [targetCur]; cus = [smallCurve];
return cus.every(c => return cus.every(c =>
{ {
let pts = getIntPtContextPts(sourceCur, c); let pts = getIntPtContextPts(bigCurve, c);
if (pts.length <= 1) if (pts.length <= 1)
pts.push(c.StartPoint, c.EndPoint); pts.push(c.StartPoint, c.EndPoint);
return IsPtsAllInOrOnReg(sourceCur, pts); return IsPtsAllInOrOnReg(bigCurve, pts);
}); });
} }

@ -105,7 +105,12 @@ export class FeedingToolPath extends Singleton
{ {
if (holes.length > 0) 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 (isInt) break;
} }
if (isOut && offsetDist === knifRadius) if (isOut && offsetDist === knifRadius)

Loading…
Cancel
Save