!1463 功能:DXF导入和黏贴进WebCAD时,自动对复杂的多段线进行简化

pull/1463/MERGE
ChenX 4 years ago
parent 41153a3a14
commit e94d18fafa

@ -0,0 +1,201 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`小恐龙简化 1`] = `723`;
exports[`小恐龙简化 2`] = `671`;
exports[`小恐龙简化 3`] = `"7392.79910"`;
exports[`小恐龙简化 4`] = `"7397.08024"`;
exports[`小恐龙简化 5`] = `5`;
exports[`小恐龙简化 6`] = `11`;
exports[`小恐龙简化 7`] = `"312.62650"`;
exports[`小恐龙简化 8`] = `"312.60195"`;
exports[`小恐龙简化 9`] = `35`;
exports[`小恐龙简化 10`] = `30`;
exports[`小恐龙简化 11`] = `"1274.39343"`;
exports[`小恐龙简化 12`] = `"1275.37625"`;
exports[`小恐龙简化 13`] = `12`;
exports[`小恐龙简化 14`] = `17`;
exports[`小恐龙简化 15`] = `"682.96871"`;
exports[`小恐龙简化 16`] = `"682.97027"`;
exports[`小恐龙简化 17`] = `41`;
exports[`小恐龙简化 18`] = `34`;
exports[`小恐龙简化 19`] = `"1976.93033"`;
exports[`小恐龙简化 20`] = `"1976.92695"`;
exports[`小恐龙简化 21`] = `55`;
exports[`小恐龙简化 22`] = `47`;
exports[`小恐龙简化 23`] = `"1220.90675"`;
exports[`小恐龙简化 24`] = `"1220.88609"`;
exports[`小恐龙简化 25`] = `5`;
exports[`小恐龙简化 26`] = `12`;
exports[`小恐龙简化 27`] = `"1024.58103"`;
exports[`小恐龙简化 28`] = `"1024.58094"`;
exports[`小恐龙简化 29`] = `21`;
exports[`小恐龙简化 30`] = `21`;
exports[`小恐龙简化 31`] = `"1644.30507"`;
exports[`小恐龙简化 32`] = `"1644.30507"`;
exports[`小恐龙简化 33`] = `5`;
exports[`小恐龙简化 34`] = `5`;
exports[`小恐龙简化 35`] = `"1049.95998"`;
exports[`小恐龙简化 36`] = `"1049.95998"`;
exports[`小恐龙简化 37`] = `51`;
exports[`小恐龙简化 38`] = `46`;
exports[`小恐龙简化 39`] = `"1225.55997"`;
exports[`小恐龙简化 40`] = `"1225.54790"`;
exports[`小恐龙简化 41`] = `23`;
exports[`小恐龙简化 42`] = `17`;
exports[`小恐龙简化 43`] = `"783.79812"`;
exports[`小恐龙简化 44`] = `"783.80056"`;
exports[`小恐龙简化 45`] = `46`;
exports[`小恐龙简化 46`] = `41`;
exports[`小恐龙简化 47`] = `"1308.96068"`;
exports[`小恐龙简化 48`] = `"1308.92526"`;
exports[`小恐龙简化 49`] = `34`;
exports[`小恐龙简化 50`] = `35`;
exports[`小恐龙简化 51`] = `"646.03175"`;
exports[`小恐龙简化 52`] = `"645.95028"`;
exports[`小恐龙简化 53`] = `87`;
exports[`小恐龙简化 54`] = `75`;
exports[`小恐龙简化 55`] = `"949.48587"`;
exports[`小恐龙简化 56`] = `"948.69812"`;
exports[`小恐龙简化 57`] = `4`;
exports[`小恐龙简化 58`] = `4`;
exports[`小恐龙简化 59`] = `"341.03322"`;
exports[`小恐龙简化 60`] = `"341.03322"`;
exports[`小恐龙简化 61`] = `41`;
exports[`小恐龙简化 62`] = `25`;
exports[`小恐龙简化 63`] = `"503.59965"`;
exports[`小恐龙简化 64`] = `"503.15867"`;
exports[`小恐龙简化 65`] = `3`;
exports[`小恐龙简化 66`] = `3`;
exports[`小恐龙简化 67`] = `"158.44290"`;
exports[`小恐龙简化 68`] = `"158.44290"`;
exports[`简化多段线成为圆弧 1`] = `43`;
exports[`简化多段线成为圆弧 2`] = `33`;
exports[`简化多段线成为圆弧 3`] = `"1107.92600"`;
exports[`简化多段线成为圆弧 4`] = `"1107.90911"`;
exports[`简化多段线成为圆弧 5`] = `43`;
exports[`简化多段线成为圆弧 6`] = `33`;
exports[`简化多段线成为圆弧 7`] = `"1107.92600"`;
exports[`简化多段线成为圆弧 8`] = `"1107.90911"`;
exports[`简化多段线成为圆弧 9`] = `81`;
exports[`简化多段线成为圆弧 10`] = `59`;
exports[`简化多段线成为圆弧 11`] = `"2215.88135"`;
exports[`简化多段线成为圆弧 12`] = `"2215.85712"`;
exports[`简化多段线成为圆弧 13`] = `44`;
exports[`简化多段线成为圆弧 14`] = `34`;
exports[`简化多段线成为圆弧 15`] = `"1628.59895"`;
exports[`简化多段线成为圆弧 16`] = `"1628.58206"`;
exports[`简化多段线成为圆弧 17`] = `47`;
exports[`简化多段线成为圆弧 18`] = `37`;
exports[`简化多段线成为圆弧 19`] = `"1422.40410"`;
exports[`简化多段线成为圆弧 20`] = `"1422.38721"`;
exports[`简化多段线成为圆弧 21`] = `6`;
exports[`简化多段线成为圆弧 22`] = `23`;
exports[`简化多段线成为圆弧 23`] = `"2077.93854"`;
exports[`简化多段线成为圆弧 24`] = `"2078.01268"`;
exports[`简化多段线成为圆弧 25`] = `9`;
exports[`简化多段线成为圆弧 26`] = `97`;
exports[`简化多段线成为圆弧 27`] = `"2041.09919"`;
exports[`简化多段线成为圆弧 28`] = `"2041.97439"`;
exports[`简化多段线成为圆弧 29`] = `2`;
exports[`简化多段线成为圆弧 30`] = `39`;
exports[`简化多段线成为圆弧 31`] = `"705.34043"`;
exports[`简化多段线成为圆弧 32`] = `"705.83666"`;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,37 @@
import { Polyline } from "../../src/DatabaseServices/Entity/Polyline";
import { PointsSimplify2PolylineAndParseArc } from "../../src/DatabaseServices/SplineConver2Polyline";
import { LoadEntityFromFileData } from "../Utils/LoadEntity.util";
import "../Utils/jest.util";
test('简化多段线成为圆弧', () =>
{
let ents = LoadEntityFromFileData(require("./points_polyline_file.json")) as Polyline[];
for (let pl of ents)
{
let pl1 = PointsSimplify2PolylineAndParseArc(pl.LineData.map(p => p.pt), undefined, 0.1, true);
let pl2 = PointsSimplify2PolylineAndParseArc(pl.LineData.map(p => p.pt), undefined, 0.1, false);
expect(pl1.LineData.length).toMatchSnapshot();
expect(pl2.LineData.length).toMatchSnapshot();
expect(pl1.Length).toMatchNumberSnapshot();
expect(pl2.Length).toMatchNumberSnapshot();
}
});
test('小恐龙简化', () =>
{
let ents = LoadEntityFromFileData(require("./kl.json")) as Polyline[];
for (let pl of ents)
{
let pl1 = PointsSimplify2PolylineAndParseArc(pl.LineData.map(p => p.pt), undefined, 0.1, true);
let pl2 = PointsSimplify2PolylineAndParseArc(pl.LineData.map(p => p.pt), undefined, 0.1, false);
expect(pl1.LineData.length).toMatchSnapshot();
expect(pl2.LineData.length).toMatchSnapshot();
expect(pl1.Length).toMatchNumberSnapshot();
expect(pl2.Length).toMatchNumberSnapshot();
}
});

@ -1,22 +1,42 @@
import { app } from "../ApplicationServices/Application";
import { Ellipse } from "../DatabaseServices/Entity/Ellipse";
import { Polyline } from "../DatabaseServices/Entity/Polyline";
import { Spline } from "../DatabaseServices/Spline";
import { SmartPolylineSimply2Polyline } from "../DatabaseServices/SplineConver2Polyline";
import { Command } from "../Editor/CommandMachine";
import { PromptStatus } from "../Editor/PromptResult";
import { equaln } from "../Geometry/GeUtils";
export class Command_Conver2Polyline implements Command
{
async exec()
{
let ssRes = await app.Editor.GetSelection({ Filter: { filterTypes: [Spline, Ellipse] } });
let ssRes = await app.Editor.GetSelection({
Filter: {
filterFunction: (obj, ent) =>
{
if (ent instanceof Spline || ent instanceof Ellipse) return true;
if (ent instanceof Polyline && ent.EndParam > 150 && ent.LineData.every(p => equaln(p.bul, 0, 1e-4))) return true;
return false;
}
}
});
if (ssRes.Status !== PromptStatus.OK) return;
let ents = ssRes.SelectSet.SelectEntityList as (Spline | Ellipse)[];
let ents = ssRes.SelectSet.SelectEntityList as (Spline | Ellipse | Polyline)[];
for (let e of ents)
{
e.Erase();
let pl = e.Convert2Polyline();
app.Database.ModelSpace.Append(pl);
if (e instanceof Polyline)
{
let pl = SmartPolylineSimply2Polyline(e);
app.Database.ModelSpace.Append(pl);
}
else
{
let pl = e.Convert2Polyline();
app.Database.ModelSpace.Append(pl);
}
}
}
}

@ -13,6 +13,7 @@ import { Arc } from "../DatabaseServices/Entity/Arc";
import { Circle } from "../DatabaseServices/Entity/Circle";
import { Line } from "../DatabaseServices/Entity/Line";
import { Polyline } from "../DatabaseServices/Entity/Polyline";
import { SmartPolylineSimply2Polyline } from "../DatabaseServices/SplineConver2Polyline";
import { Text } from "../DatabaseServices/Text/Text";
import { CommandWrap } from "../Editor/CommandMachine";
import { AsVector2, AsVector3, equaln, equalv3 } from "../Geometry/GeUtils";
@ -57,7 +58,11 @@ export class Command_DXFImport
let nor = pl.Normal;
pl.Position = pl.Position.add(nor.multiplyScalar(en.elevation ?? 0));
if (pl.Length > 0)
{
if (pl.EndParam > 150 && pl.LineData.every(p => equaln(p.bul, 0, 1e-4)))
pl = SmartPolylineSimply2Polyline(pl);
app.Database.ModelSpace.Append(pl);
}
}
else if (en.type === "ARC")
{

@ -8,9 +8,11 @@ import { readClipboardText } from "../Common/Utils";
import { CADFiler } from "../DatabaseServices/CADFiler";
import { Curve } from "../DatabaseServices/Entity/Curve";
import { Entity } from "../DatabaseServices/Entity/Entity";
import { Polyline } from "../DatabaseServices/Entity/Polyline";
import { SmartPolylineSimply2Polyline } from "../DatabaseServices/SplineConver2Polyline";
import { JigUtils } from "../Editor/JigUtils";
import { PromptStatus } from "../Editor/PromptResult";
import { AsVector3 } from "../Geometry/GeUtils";
import { AsVector3, equaln } from "../Geometry/GeUtils";
export class PasteClip
{
@ -44,6 +46,11 @@ export class PasteClip
for (let i = 0; i < count; i++)
{
let en = f.ReadObject() as Entity;
if (en instanceof Polyline && en.EndParam > 150 && en.LineData.every(p => equaln(p.bul, 0, 1e-4)))
{
let pl = SmartPolylineSimply2Polyline(en);
en = pl;
}
JigUtils.Draw(en);
entityOCSCache.set(en, en.OCS);

@ -0,0 +1,41 @@
import { app } from "../../ApplicationServices/Application";
import { Polyline } from "../../DatabaseServices/Entity/Polyline";
import { SmartPolylineSimply2Polyline } from "../../DatabaseServices/SplineConver2Polyline";
import { Command } from "../../Editor/CommandMachine";
import { PromptStatus } from "../../Editor/PromptResult";
import { Path2Polyline } from "../../Nest/Converter/Path2Polyline";
import { TestDraw } from "../test/TestUtil";
//将多段线转换为碎点多段线
export class Command_TestPolyline2PointsPolyline implements Command
{
async exec()
{
let enRes = await app.Editor.GetEntity({ Filter: { filterTypes: [Polyline] } });
if (enRes.Status !== PromptStatus.OK) return;
let pl = enRes.Entity as Polyline;
let npl = Path2Polyline(pl.Shape.getPoints(8));
npl.OCS = pl.OCSNoClone;
await TestDraw(npl);
}
}
export class Command_SimplifyPolyline implements Command
{
async exec()
{
let ssRes = await app.Editor.GetSelection({ Filter: { filterTypes: [Polyline] } });
if (ssRes.Status !== PromptStatus.OK) return;
let pls = ssRes.SelectSet.SelectEntityList as Polyline[];
for (let pl of pls)
{
let npl = SmartPolylineSimply2Polyline(pl);
if (npl.EndParam < pl.EndParam)
{
pl.Erase();
app.Database.ModelSpace.Append(npl);
}
}
}
}

@ -1,5 +1,7 @@
import { Vec2, Vector3 } from "three";
import { AsVector3, equaln } from "../Geometry/GeUtils";
import { Vec2, Vector2, Vector3 } from "three";
import { arrayLast } from "../Common/ArrayExt";
import { orientation } from "../Geometry/DoIntersect";
import { AsVector3, equaln, equalv2 } from "../Geometry/GeUtils";
import { Arc } from "./Entity/Arc";
import { Curve } from "./Entity/Curve";
import { Line } from "./Entity/Line";
@ -91,6 +93,187 @@ export function SplineConver2Polyline(spl: Spline, tolerance = 0.1): Polyline
return polyline;
}
//传入的一定是碎点,不是碎点,没有6个点请不要进来
function PointsSimplify2Polyline(pts: Vector3[], tolerance = 0.1): Polyline
{
let cacheTange = new Map<number, Vector3>();
let ptsCount = pts.length;
const GetPointAtParam = (param: number) =>
{
if (param === 1) return pts[pts.length - 1];
let p = pts[Math.floor(ptsCount * param)];
return p;
};
const GetTangentAtParam = (param: number) =>
{
let t = cacheTange.get(param);
if (t) return t;
let index = Math.floor(ptsCount * param);
if (equaln(param, 1) || index + 1 >= pts.length)
t = arrayLast(pts).clone().sub(pts[pts.length - 1]).normalize();
else
t = pts[index + 1].clone().sub(pts[index]).normalize();
cacheTange.set(param, t);
return t;
};
let stepx = 1;
let curves: Curve[] = [];
for (let i = 0; i < 1;)
{
let step = 0.25 * stepx;//0.5的时候也可以有不错的收敛,但是在0.25的时候会有比较贴合的效果
while (true)
{
let param1 = i;
let param2 = Math.min(1, i + step);
let x = param2 - param1;
let midp1 = GetPointAtParam(param1 + x * 0.25);
let midp2 = GetPointAtParam(param1 + x * 0.75);
let p1 = GetPointAtParam(param1);
let p2 = GetPointAtParam(param2);
let t1 = GetTangentAtParam(param1);
let t2 = GetTangentAtParam(param2);
let [c1, c2] = ComputeBiarc(p1, p2, t1, t2);
if (
c1.GetClosestPointTo(midp1, false).distanceTo(midp1) < tolerance &&
c2.GetClosestPointTo(midp2, false).distanceTo(midp2) < tolerance
)
{
curves.push(c1, c2);
break;
}
else
step = step * 0.5;
}
i += step;
}
let polyline = Polyline.Combine(curves, 1e-3);
return polyline;
}
/**
* 线,(,使)
* @param pts
* @param [tolerance=0.1]
* @param [parseArc=true]
* @param [lineLengthSq=2000]
* @returns 线
*/
export function PointsSimplify2PolylineAndParseArc(pts: Vector2[], buls: number[] = undefined, tolerance = 0.1, parseArc = true, lineLengthSq = 2000): Polyline
{
let tolSq = tolerance * tolerance;
let ptsCount = pts.length;
const CreateLineOrArc = (startIndex: number, endIndex: number) =>
{
if (!buls || equaln(buls[startIndex], 0, 1e-4))
return new Line(AsVector3(pts[startIndex]), AsVector3(pts[endIndex]));
else
return new Arc().ParseFromBul(AsVector3(pts[startIndex]), AsVector3(pts[endIndex]), buls[startIndex]);
};
let retPolyline = new Polyline;
let start = 0;
for (let next = start + 1; next <= ptsCount; next++)
{
if (next === ptsCount || pts[next].distanceToSquared(pts[next - 1]) > lineLengthSq || (buls && !equaln(buls[next - 1], 0, 1e-4)))//长度大于50,为什么认为它是一条直线
{
//1.将start->next-1部分组成圆弧
if (parseArc || (next - start) < 6)
while (start < next - 1)
{
if ((next - 1) - start === 1)//直线
{
retPolyline.Join(CreateLineOrArc(start, next - 1));
break;
}
//第一个三角形的方向
let firstDir = orientation(pts[start], pts[start + 1], pts[start + 2]);
let to = start;
for (; to + 3 < next; to++)
{
let dir = orientation(pts[to + 1], pts[to + 2], pts[to + 3]);
if (dir !== firstDir)
break;
}
if (start === to)//三个点
{
retPolyline.Join(CreateLineOrArc(start, start + 1));
retPolyline.Join(CreateLineOrArc(start + 1, start + 2));
start = to + 2;
continue;
}
else
{
let sp = pts[start];
let ep = pts[to + 2];
let mp = pts[Math.floor((start + to + 2) / 2)];
let arc = new Arc().FromThreePoint(AsVector3(sp), AsVector3(mp), AsVector3(ep));
let c = to + 2 - start;
let p1 = AsVector3(pts[start + Math.floor(c * 0.25)]);
let p2 = AsVector3(pts[start + Math.floor(c * 0.75)]);
if (arc.GetClosestPointTo(p1, false).distanceToSquared(p1) < tolSq
&& arc.GetClosestPointTo(p2, false).distanceToSquared(p2) < tolSq)
retPolyline.Join(arc);
else
retPolyline.Join(PointsSimplify2Polyline(pts.slice(start, to + 2 + 1).map(AsVector3)));
}
start = to + 2;//闪烁到圆弧终点
if (start === next - 2) break;
}
else
retPolyline.Join(PointsSimplify2Polyline(pts.slice(start, next).map(AsVector3)));
//2.加入直线
if (next !== ptsCount)
retPolyline.Join(CreateLineOrArc(next - 1, next));
start = next;
}
}
return retPolyline;
}
export function SmartPointsSimply2Polyline(pts: Vector2[], buls: number[] = undefined, tolerance = 0.1, lineLengthSq = 2000)
{
let pl1 = PointsSimplify2PolylineAndParseArc(pts, buls, tolerance, true, lineLengthSq);
let pl2 = PointsSimplify2PolylineAndParseArc(pts, buls, tolerance, false, lineLengthSq);
if (pl1.EndParam < pl2.EndParam)
return pl1;
else
return pl2;
}
export function SmartPolylineSimply2Polyline(pl: Polyline, tolerance = 0.1, lineLengthSq = 2000)
{
let pts = pl.LineData.map(p => p.pt);
if (pl.CloseMark && !equalv2(pts[0], arrayLast(pts)))
pts.push(pts[0].clone());
let npl = SmartPointsSimply2Polyline(pts, pl.LineData.map(p => p.bul), tolerance, lineLengthSq);
npl.ApplyMatrix(pl.OCSNoClone);
npl.ColorIndex = pl.ColorIndex;
return npl;
}
//types
function Vec2(x: number, y: number)
{
@ -138,7 +321,6 @@ function Vec2_Dot(lhs: Vec2, rhs: Vec2)
return lhs.x * rhs.x + lhs.y * rhs.y;
}
//lengthsq
function Vec2_MagSqr(val: { x: number; y: number; })
{
return val.x * val.x + val.y * val.y;

@ -180,6 +180,7 @@ import { TestCollision } from "../Add-on/testEntity/testCollision";
import { TestTargeOnCurve } from "../Add-on/testEntity/TestCurve";
import { Command_TestDrawEdgeGeometry } from "../Add-on/testEntity/TestDrawEdgeGeometry";
import { TestFillet } from "../Add-on/testEntity/TestFilletCode";
import { Command_SimplifyPolyline, Command_TestPolyline2PointsPolyline } from "../Add-on/testEntity/TestPolyline2PointsPolyline";
import { Command_TestRegionParse } from "../Add-on/testEntity/TestRegionParse";
import { Command_TestSweepMaxLength } from "../Add-on/testEntity/TestSweepMaxLength";
import { Command_DeleteTemplate } from "../Add-on/testEntity/TestTemplateDelete";
@ -441,6 +442,12 @@ export function registerCommand()
commandMachine.RegisterCommand("tt2", new Command_INsTest());
commandMachine.RegisterCommand("outcur", new TestTargeOnCurve());
commandMachine.RegisterCommand("close", new Command_ClosePt());
//多段线变碎点多段线
commandMachine.RegisterCommand("TestPolyline2PointsPolyline", new Command_TestPolyline2PointsPolyline());
commandMachine.RegisterCommand("SimplifyPolyline", new Command_SimplifyPolyline());
//用于测试包围盒
commandMachine.RegisterCommand("box", new Command_TestBox());
commandMachine.RegisterCommand("testInt", new TestIntersect());

@ -18,7 +18,7 @@ function onSegment(p: Vec2, q: Vec2, r: Vec2): boolean
// 0 --> p, q and r are colinear
// 1 --> Clockwise
// 2 --> Counterclockwise
function orientation(p: Vec2, q: Vec2, r: Vec2): number
export function orientation(p: Vec2, q: Vec2, r: Vec2): number
{
// See https://www.geeksforgeeks.org/orientation-3-ordered-points/
// for details of below formula.

@ -113,6 +113,9 @@ export class PropertiesPanel extends React.Component<PropertiesPanelProps, {}>
<li>
: {store.GetEntitys()[0]['Area'].toFixed(2)}
</li>
<li>
:{(ents[0] as Curve).EndParam}
</li>
</>
}
{

@ -9,3 +9,4 @@ export * from "./Add-on/testEntity/SimplifyPolyline";
export { IsRect } from "./Common/CurveUtils";
export * from "./GraphicsSystem/ToolPath/VKnifToolPath";
export * from "./Production/Product";
export * from "./DatabaseServices/SplineConver2Polyline";

Loading…
Cancel
Save