修复:布尔错误导致无法排钻 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
exports[`交点被优化成单个点,导致点在曲线外,导致求交错误 1`] = `"1260.00000"`;
exports[`分裂的点在参数周围 1`] = `"5782.01322"`;
exports[`差集共线(圆) 1`] = `"442426.78779"`;

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

@ -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();
});

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

@ -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)
{

@ -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));
}
//点在曲线中,不在起点或者终点.

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

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

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

@ -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)
{

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

@ -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)

Loading…
Cancel
Save