修复:某些极端情况下多段线偏移的结果不闭合

polyine_comp
ChenX 1 year ago
parent 2c47c66d4b
commit 0a0b8db7c6

@ -1,6 +1,7 @@
import { Polyline } from "../../src/DatabaseServices/Entity/Polyline";
import { GetSealedBoardContour } from "../../src/GraphicsSystem/CalcEdgeSealing"; import { GetSealedBoardContour } from "../../src/GraphicsSystem/CalcEdgeSealing";
import "../Utils/jest.util"; import "../Utils/jest.util";
import { LoadBoardsFromFileData } from "../Utils/LoadEntity.util"; import { LoadBoardsFromFileData, LoadEntityFromFileData } from "../Utils/LoadEntity.util";
test('只有一个封边,并且失败的案例', () => test('只有一个封边,并且失败的案例', () =>
{ {
@ -57,9 +58,32 @@ test('偏移后曲线不闭合的问题', () =>
for (let br of brs) for (let br of brs)
{ {
let cus = br.ContourCurve.GetOffsetCurves(-1);
for (let c of cus)
{
expect(c.Length).toMatchNumberSnapshot();
expect(c.IsClose).toBeTruthy();
}
let con = GetSealedBoardContour(br, false); let con = GetSealedBoardContour(br, false);
expect(con.IsClose).toBeTruthy(); expect(con.IsClose).toBeTruthy();
expect(con.Area2).toMatchNumberSnapshot(); expect(con.Area2).toMatchNumberSnapshot();
expect(con.Length).toMatchNumberSnapshot(); expect(con.Length).toMatchNumberSnapshot();
} }
}); });
test('偏移后曲线不闭合的问题2', () =>
{
let d =
{ "file": [1, "Polyline", 10, 2, 209, 0, 1, 7, 71, [2.220446049250313e-16, 1, -1.8369701987210297e-16, 0, -1, 2.220446049250313e-16, -1.232595164407831e-32, 0, 1.232595164407831e-32, 1.8369701987210297e-16, 1, 0, 2800.6273980057904, -327.6194030341822, 154.3982712660218, 1], 0, 0, 1, [0, 1, -1.8369701987210297e-16, 0, -1, 0, 0, 0, 0, 1.8369701987210297e-16, 1, 0, 2800.6273980057904, -327.6194030341822, 154.3982712660218, 1], 0, 0, 1, 2, 37, [203.75274236917448, 0.6657821176631771], -0.12647817046362458, [216.58249214357465, 3.9639078952714044], 0, [216.70369214357606, 3.9639078952714044], 0, [216.70349214357384, 3.9670232852913614], 0, [216.7026921436518, 3.970172431976607], 0.005962190972429685, [216.04074477292943, 6.451513648837135], -0.002176672718212006, [215.35998088513043, 8.927953781329961], -0.00044376333274056043, [213.84759951148862, 14.544604951137444], -0.0003667819041204022, [212.34429214357397, 20.16382417328441], -0.001544194315380855, [206.4925267933031, 42.376867817670245], -0.0015310584877568476, [200.7774921435739, 64.62548417628375], -0.009890393483378371, [165.09260900743266, 218.00339530303336], -0.006193200118673486, [134.3552921435803, 372.4679898662846], -0.003954883402352878, [109.80113359540337, 510.43208818801577], -0.0012080368562184874, [86.67219214350916, 648.6462885313813], -0.00011581093482110962, [53.09282355173491, 852.6265914568794], 0.00002500101529673068, [19.906391149862884, 1054.4486651826255], 0, [21.720227807596984, 1087.039396742547], -0.00521091907627027, [26.7729988737874, 1161.6355313137447], -0.006073474799553129, [35.294008103393935, 1255.8468454906397], -0.0050452671458160485, [45.90799214357321, 1349.8476338032733], -0.009637488483441259, [73.09402035463063, 1540.2785908202159], -0.0021345969443920146, [83.22271449620018, 1600.9769090419366], 0, [62.8446583269706, 1600.9769090419366], 0.0036001887197470227, [48.08769214356744, 1510.076619646293], 0.009976279550128881, [20.351018578063304, 1303.993887760866], 0.01322079246633355, [2.2040921435805103, 1096.8779752522942], -0.0042031964376284, [0.3365602451176528, 1069.9961093543807], 0.06027708278844141, [1.4769921435768083, 1043.2679710122866], 0, [61.287392140351585, 679.720449795841], 0.00009084267787460379, [68.4354857399303, 636.314626773783], 0.0003367141025187746, [75.62069214250369, 592.9149412077645], 0.0017524780389864619, [96.762927535794, 468.4425780357958], 0.003912932302896014, [119.31359214358599, 344.2207988532739], 0.007554149456815115, [154.3982712660013, 173.3497428441044], 0.011551705859636422, [195.9783921435751, 3.9698256912733987], 0.3388891824181241, [201.16284145136657, -5.506706202140776e-13], 0.12647815605716622, true], "basePt": { "x": 1199.6504889638538, "y": -327.6194030341818, "z": 154.39827126602174 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3117.2750559817937, 228.82635435795817, 154.39827126600176, 1] };
let pl = LoadEntityFromFileData(d)[0] as Polyline;
let cus = pl.GetOffsetCurves(-1);
for (let c of cus)
{
expect(c.EndParam).toMatchNumberSnapshot(0);
expect(c.Length).toMatchNumberSnapshot();
expect(c.IsClose).toBeTruthy();
}
});

@ -1,8 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`偏移后曲线不闭合的问题 1`] = `"14601.05210"`; exports[`偏移后曲线不闭合的问题 1`] = `"1271.45306"`;
exports[`偏移后曲线不闭合的问题 2`] = `"1271.45304"`; exports[`偏移后曲线不闭合的问题 2`] = `"14601.00364"`;
exports[`偏移后曲线不闭合的问题 3`] = `"1271.45306"`;
exports[`偏移后曲线不闭合的问题2 1`] = `"33"`;
exports[`偏移后曲线不闭合的问题2 2`] = `"3274.96050"`;
exports[`只有一个封边,并且失败的案例 1`] = `"77140.00000"`; exports[`只有一个封边,并且失败的案例 1`] = `"77140.00000"`;

@ -184,7 +184,7 @@ exports[`精度不一致导致的错误 4`] = `"55.04455"`;
exports[`精度不一致导致的错误: 走刀数量 1`] = `4`; exports[`精度不一致导致的错误: 走刀数量 1`] = `4`;
exports[`超级复杂造型01 1`] = `"23338.03271"`; exports[`超级复杂造型01 1`] = `"23338.03272"`;
exports[`超级复杂造型01 2`] = `"11855.39981"`; exports[`超级复杂造型01 2`] = `"11855.39981"`;

@ -184,7 +184,7 @@ exports[`简单图形因为点在线内算法错误导致的丢失 3`] = `7.1494
exports[`简单图形因为点在线内算法错误导致的丢失 4`] = `6.693604273021893`; exports[`简单图形因为点在线内算法错误导致的丢失 4`] = `6.693604273021893`;
exports[`精度过高导致无法连接 1`] = `"6723.54642"`; exports[`精度过高导致无法连接 1`] = `"6723.54643"`;
exports[`精度过高导致的曲线丢失 1`] = `1`; exports[`精度过高导致的曲线丢失 1`] = `1`;
@ -200,15 +200,15 @@ exports[`精度过高导致直连失败 2`] = `"32040.01390"`;
exports[`精度过高导致直连失败 3`] = `1`; exports[`精度过高导致直连失败 3`] = `1`;
exports[`精度过高导致直连失败 4`] = `"32045.24858"`; exports[`精度过高导致直连失败 4`] = `"32045.24859"`;
exports[`精度过高导致连接失败 1`] = `1`; exports[`精度过高导致连接失败 1`] = `1`;
exports[`精度过高导致连接失败 2`] = `"75154.17787"`; exports[`精度过高导致连接失败 2`] = `"75154.17860"`;
exports[`精度过高导致连接失败 3`] = `1`; exports[`精度过高导致连接失败 3`] = `1`;
exports[`精度过高导致连接失败 4`] = `"91209.36321"`; exports[`精度过高导致连接失败 4`] = `"91209.36322"`;
exports[`精度问题导致的连接错误 1`] = `2`; exports[`精度问题导致的连接错误 1`] = `2`;

@ -4,8 +4,8 @@ exports[`因为错误的点在多段线内部导致的偏移错误 1`] = `"2029.
exports[`圆裁剪精度 1`] = `1`; exports[`圆裁剪精度 1`] = `1`;
exports[`圆裁剪精度 2`] = `"8360.62214"`; exports[`圆裁剪精度 2`] = `"8360.63312"`;
exports[`圆裁剪精度 3`] = `1`; exports[`圆裁剪精度 3`] = `1`;
exports[`圆裁剪精度 4`] = `"8385.98689"`; exports[`圆裁剪精度 4`] = `"8385.98861"`;

@ -1,5 +1,5 @@
import { Box3, Matrix4, Vector3 } from "three"; import { Box3, Matrix4, Vector3 } from "three";
import { arrayLast } from "../Common/ArrayExt"; import { arrayLast, arrayRemoveDuplicateBySort } from "../Common/ArrayExt";
import { ConverCircleToPolyline } from "../Common/CurveUtils"; import { ConverCircleToPolyline } from "../Common/CurveUtils";
import { Status } from "../Common/Status"; import { Status } from "../Common/Status";
import { FixIndex } from "../Common/Utils"; import { FixIndex } from "../Common/Utils";
@ -61,8 +61,36 @@ export class CurveTreeNode
} }
//交点参数列表 //交点参数列表
let iParams = this.curve.IntersectWith(contour.Curve, IntersectOption.ExtendNone) let iParams = this.curve.IntersectWith2(contour.Curve, IntersectOption.ExtendNone)
.map(p => this.curve.GetParamAtPoint2(p)); .map(p => p.thisParam);
//#region 交点参数优化,避免出现无意义长度的曲线被移除 CurveIsFine
let length = this.curve.Length;
if (!(this.curve instanceof Circle))
iParams.push(0, 1);
iParams.sort((e1, e2) => e1 - e2);
if (this.curve instanceof Arc || this.curve instanceof Circle)
{
let allAngle = this.curve instanceof Circle ? Math.PI * 2 : this.curve.AllAngle;
arrayRemoveDuplicateBySort(iParams, (p1, p2) =>
{
return allAngle * (p2 - p1) < ARC_MIN_ALLANGLE || length * (p2 - p1) < CURVE_MIN_LENGTH;
});
}
else
{
arrayRemoveDuplicateBySort(iParams, (p1, p2) =>
{
return length * (p2 - p1) < CURVE_MIN_LENGTH;
});
}
if (!(this.curve instanceof Circle))
{
iParams.shift();//remove 0
iParams.pop(); //remove 1 or 0.9999
}
//#endregion
let cus = this.curve.GetSplitCurves(iParams); let cus = this.curve.GetSplitCurves(iParams);
if (cus.length === 0) if (cus.length === 0)
@ -161,6 +189,19 @@ export class OffsetPolyline
} }
else else
this.GeneralTrimContours(); this.GeneralTrimContours();
// for (let c of this._SubOffsetedCurves)
// {
// TestDraw(c.curve.Clone(), 1);
// if (c.paddingCurve)
// for (let cc of c.paddingCurve)
// TestDraw(cc.Clone(), 2);
// }
// for (let c of this._TrimPolylineContours)
// {
// TestDraw(c.Curve.Clone(), 3);
// }
this.TrimByContours(); this.TrimByContours();
this.FilterInvalidCurve(); this.FilterInvalidCurve();
this.JoinCollinear(); this.JoinCollinear();
@ -174,7 +215,7 @@ export class OffsetPolyline
this._CacheOCS = this._Polyline.OCS; this._CacheOCS = this._Polyline.OCS;
this._IsClose = this._Polyline.IsClose; this._IsClose = this._Polyline.IsClose;
this._Polyline.OCS = IdentityMtx4; this._Polyline.OCS = IdentityMtx4;
this._SubCurves = this._Polyline.Explode().filter(c => c.Length > 1e-4); this._SubCurves = this._Polyline.Explode().filter(c => c.Length > 1e-4);//TODO: 简化这个 并且生成圆 顶点 圆
this._Polyline.OCS = this._CacheOCS; this._Polyline.OCS = this._CacheOCS;
return this; return this;
} }
@ -836,9 +877,12 @@ export function EntityEncode2(c1: Curve, c2: Curve)
return EntityEncode(c1) & EntityEncode(c2); return EntityEncode(c1) & EntityEncode(c2);
} }
const CURVE_MIN_LENGTH = 5e-5;
const ARC_MIN_ALLANGLE = 2e-6;
//表示这个是一个正常的曲线,不是0长度的线,也不是0长度的圆弧 //表示这个是一个正常的曲线,不是0长度的线,也不是0长度的圆弧
export function CurveIsFine(curve: Curve) export function CurveIsFine(curve: Curve)
{ {
if (curve instanceof Arc && curve.AllAngle < 2e-6) return false; if (curve instanceof Arc && curve.AllAngle < ARC_MIN_ALLANGLE) return false;
return curve.Length > 5e-5; return curve.Length > CURVE_MIN_LENGTH;
} }

@ -82,6 +82,9 @@ export class EntityModal extends React.Component<{}, {}> {
<li> <li>
: {ents[0].IsClockWise ? "顺时针" : "逆时针"} : {ents[0].IsClockWise ? "顺时针" : "逆时针"}
</li> </li>
<li>
: {ents[0].IsClose ? "闭合" : "不闭合"}
</li>
</> </>
} }
</> </>

@ -154,6 +154,9 @@ export class PropertiesPanel extends React.Component<{}, {}>
<li> <li>
: {(ents[0] as Curve).IsClockWise ? "顺时针" : "逆时针"} : {(ents[0] as Curve).IsClockWise ? "顺时针" : "逆时针"}
</li> </li>
<li>
: {ents[0].IsClose ? "闭合" : "不闭合"}
</li>
</> </>
} }
{ {

Loading…
Cancel
Save