diff --git a/.vscode/launch.json b/.vscode/launch.json index a4dae132f..040a35f78 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,13 +9,6 @@ "webRoot": "${workspaceRoot}" //, // "runtimeExecutable": "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe" }, - { - "type": "chrome", - "request": "attach", - "name": "Attach to Chrome", - "port": 7778, - "webRoot": "${workspaceRoot}" - }, //Ref: https://github.com/Microsoft/vscode-recipes/blob/master/debugging-jest-tests/.vscode/launch.json { "type": "node", diff --git a/__test__/Geometry/__snapshots__/GeUtils.test.ts.snap b/__test__/Geometry/__snapshots__/GeUtils.test.ts.snap index fdcd5b4b4..d52f83933 100644 --- a/__test__/Geometry/__snapshots__/GeUtils.test.ts.snap +++ b/__test__/Geometry/__snapshots__/GeUtils.test.ts.snap @@ -40,7 +40,7 @@ Vector3 { } `; -exports[`排序点Xyz 1`] = ` +exports[`排序测试 排序点Xyz 1`] = ` Array [ Vector3 { "x": 6, @@ -75,7 +75,7 @@ Array [ ] `; -exports[`排序点Zx 1`] = ` +exports[`排序测试 排序点Zx 1`] = ` Array [ Vector3 { "x": 6, @@ -110,7 +110,7 @@ Array [ ] `; -exports[`排序点xyz 1`] = ` +exports[`排序测试 排序点xyz 1`] = ` Array [ Vector3 { "x": 1, diff --git a/__test__/Geometry/__snapshots__/circle.test.ts.snap b/__test__/Geometry/__snapshots__/circle.test.ts.snap index ba912f250..18dfeb2b1 100644 --- a/__test__/Geometry/__snapshots__/circle.test.ts.snap +++ b/__test__/Geometry/__snapshots__/circle.test.ts.snap @@ -143,3 +143,11 @@ Vector3 { "z": 0, } `; + +exports[`最近点2 1`] = ` +Vector3 { + "x": 10, + "y": 0, + "z": 0, +} +`; diff --git a/__test__/Geometry/__snapshots__/intersect.test.ts.snap b/__test__/Geometry/__snapshots__/intersect.test.ts.snap index 3993a2672..5fdbadd8f 100644 --- a/__test__/Geometry/__snapshots__/intersect.test.ts.snap +++ b/__test__/Geometry/__snapshots__/intersect.test.ts.snap @@ -60,15 +60,7 @@ Array [ ] `; -exports[`三维空间圆圆相交测试 5`] = ` -Array [ - Vector3 { - "x": 2.5, - "y": 0, - "z": 0, - }, -] -`; +exports[`三维空间圆圆相交测试 5`] = `Array []`; exports[`三维空间直线和圆相交测试 1`] = ` Array [ @@ -140,9 +132,9 @@ Vector3 { exports[`三维空间直线相交测试 2`] = ` Vector3 { - "x": 4.75, - "y": 4.75, - "z": 4.75, + "x": 4.5, + "y": 4.5, + "z": 4.5, } `; @@ -164,7 +156,7 @@ Vector3 { exports[`相交测试 2`] = ` Vector3 { - "x": 2.5, + "x": 2, "y": 0, "z": 0, } diff --git a/__test__/Geometry/circle.test.ts b/__test__/Geometry/circle.test.ts index 16b923736..11595867d 100644 --- a/__test__/Geometry/circle.test.ts +++ b/__test__/Geometry/circle.test.ts @@ -129,3 +129,11 @@ test('最近点', () => expect(circle.GetClosestPointTo(new Vector3(8, 0, 0), true)/*?*/).toMatchSnapshot();//10,0,0. }); + + +test('最近点2', () => +{ + let circle = new Circle(new Vector3(5, 0, 0), 5); + let pt = circle.GetClosestPointTo(new Vector3(5), true); + expect(pt).toMatchSnapshot(); +}); diff --git a/__test__/Geometry/intersect.test.ts b/__test__/Geometry/intersect.test.ts index d618982c3..0afe5bc24 100644 --- a/__test__/Geometry/intersect.test.ts +++ b/__test__/Geometry/intersect.test.ts @@ -1,12 +1,12 @@ import * as THREE from 'three'; - -import { IntersectLAndLFor3D, IntersectCircleAndCircle, IntersectOption } from '../../src/GraphicsSystem/IntersectWith'; import { Vector3 } from 'three'; +import { Arc } from '../../src/DatabaseServices/Arc'; import { CADFile } from '../../src/DatabaseServices/CADFile'; -import { Curve } from '../../src/DatabaseServices/Curve'; import { Circle } from '../../src/DatabaseServices/Circle'; -import { Arc } from '../../src/DatabaseServices/Arc'; +import { Curve } from '../../src/DatabaseServices/Curve'; import { Line } from '../../src/DatabaseServices/Line'; +import { IntersectCircleAndCircle, IntersectLAndLFor3D, IntersectOption } from '../../src/GraphicsSystem/IntersectWith'; + test('相交测试', () => { @@ -90,7 +90,19 @@ test('三维空间直线和圆相交测试', () => testLineAndCirIntersect(data); }) - +test('补充相交测试', () => +{ + let data = [["Line", 1, 1, 2308, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1.3934337707580937, 0.039495588796686576, 0, 1], 1, [12.270990240675426, 8.520323796959827, 0], [12.270990240675426, 4.4951100391838885, 0]], ["Arc", 1, 1, 2313, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 13.662687223232803, 4.584664645783669, 0, 1], 2, 0.003163048700428762, 6.156784789283385, 3.3367334474492862, true]] + let cus = loadFile(data); + let [cu1, cu2] = cus[0] instanceof Line ? cus : cus.reverse(); + let pts = cu1.IntersectWith(cu2, 0); + expect(pts.length).toBe(1); + data = [["Line", 1, 1, 2308, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1.3934337707580937, 0.039495588796686576, 0, 1], 1, [12.270990240675426, 8.520323796959827, 0], [12.270990240675426, 4.4951100391838885, 0]], ["Circle", 1, 1, 2320, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 13.662687223232803, 4.584664645783669, 0, 1], 1, 0.0031630487004287015]]; + cus = loadFile(data); + [cu1, cu2] = cus[0] instanceof Line ? cus : cus.reverse(); + pts = cu1.IntersectWith(cu2, 0); + expect(pts.length).toBe(2); +}) function loadFile(data) { let file = new CADFile(); @@ -116,4 +128,14 @@ function testLineAndCirIntersect(data) expect(pts).toMatchSnapshot(); } +test('直线和圆 圆心和直线重合的时候', () => +{ + + let c = new Circle(new Vector3(0, 0, 0), 1); + + let l = new Line(new Vector3(), new Vector3()); + + let pts = c.IntersectWith(l, IntersectOption.ExtendBoth); + expect(pts.length).toBe(0); +}); diff --git a/__test__/Polyline/PointInPolyline.test.ts b/__test__/Polyline/PointInPolyline.test.ts index a31786ee5..5d6abb912 100644 --- a/__test__/Polyline/PointInPolyline.test.ts +++ b/__test__/Polyline/PointInPolyline.test.ts @@ -164,4 +164,46 @@ describe("", () => expect(pl.PtInCurve(p)).toBeFalsy(); }); + + test('精度过低导致的错误', () => + { + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 18237, false, 2, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.7938671363300003, 0.39666461398798525, 0, 1], 2, 5, [12.414545456418505, 8.796498405316765], 0, [12.015990240675425, 8.743859037199755], 0, [12.23362472371057, 8.552710046188702], 0, [12.233990240675425, 8.552758322014249], 0, [12.414545456418505, 8.796498405316765], 0, false]] + + let p = new Vector3().fromArray([13.027857377005425, 8.947146562082409, 0]); + + f.Read(); + let pl = f.ReadObject() as Polyline; + + expect(pl.PtInCurve(p)).toBeFalsy(); + }); + + test('圆弧起点切线与直线相切IKOTO', () => + { + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 4, false, 2, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2.1413261486708848, -0.2136470533821324, 0, 1], 2, 5, [376.7856405585377, -104.2720901386406], -0.9999999999999999, [378.6693614887703, -104.2720901386406], 0, [378.4693614887703, -104.2720901386406], 0.9999999999999999, [376.9856405585377, -104.2720901386406], 0, [376.7856405585377, -104.2720901386406], 0, false]] + + let p = new Vector3().fromArray([380.6106876374412, -106.61364416876692, 0]); + + f.Read(); + let pl = f.ReadObject() as Polyline; + expect(pl.PtInCurve(p)).toBeFalsy(); + }); + + + test('大圆弧', () => + { + + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 5, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 3, [5.113122171945702, 3.235294117647059], 2.037852023242286, [6.69683257918552, 0.7918552036199095], 0, [5.113122171945702, 3.235294117647059], 0, false]] + let p = new Vector3().fromArray([5.113122171945701, 0.7239819004524888, 0]); + + f.Read(); + let pl = f.ReadObject() as Polyline; + expect(pl.PtInCurve(p)).toBeTruthy(); + }); + }) diff --git a/__test__/Polyline/PtInPolylineDir.test.ts b/__test__/Polyline/PtInPolylineDir.test.ts new file mode 100644 index 000000000..b0f1a76c3 --- /dev/null +++ b/__test__/Polyline/PtInPolylineDir.test.ts @@ -0,0 +1,243 @@ +import { CADFile } from '../../src/DatabaseServices/CADFile'; +import { Polyline } from '../../src/DatabaseServices/Polyline'; +import { GetPointAtCurveDir } from '../../src/Common/CurveUtils'; +import { Vector3, Vector2 } from 'three'; +import { Factory } from '../../src/DatabaseServices/CADFactory'; + +Factory(Polyline); +test('2个大圆中间', () => +{ + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 17, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 3, [108.37693694676932, 28.257776463252547], 0.4472440944881872, [108.37693694676932, 17.68555707305448], 3.5940684866260213, [111.70676982557188, 14.089337563947739], 0.5086040192926764, false]] + f.Read(); + let pl = f.ReadObject() as Polyline; + + let isR = GetPointAtCurveDir(pl, new Vector3(110.19, 17.4)); + + expect(isR).toBeFalsy(); + + let p = new Vector3().fromArray([84.89024786541306, 11.031154321671167, 0]); + + isR = GetPointAtCurveDir(pl, p); + + expect(isR).toBeTruthy(); +}); + +test('点在端点且端点平行', () => +{ + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 16, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 43.1743189069092, -3.552713678800501e-15, 0, 1], 2, 3, [35.26780526232293, 5.878508963172803], -0.8898689681740283, [35.26780526232293, 13.326990467988661], 0, [35.26780526232293, 23.770222268555234], 0, false]] + f.Read(); + let pl = f.ReadObject() as Polyline; + + let p = new Vector3(81.8, 12.06); + let isR = GetPointAtCurveDir(pl, p); + + expect(isR).toBeTruthy(); +}); + +test('点在和端点平行', () => +{ + let pl = new Polyline(); + pl.AddVertexAt(0, new Vector2(0, 0)); + pl.AddVertexAt(1, new Vector2(0, 5)); + pl.AddVertexAt(2, new Vector2(0, 10)); + + let p = new Vector3(5, 5, 0); + + let isR = GetPointAtCurveDir(pl, p); + + expect(isR).toBeTruthy(); +}); + + +test('点在端点上且点在圆心上', () => +{ + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 31, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 5, [17.513342658829444, 9.06588623278316], 0, [19.513342658829444, 9.06588623278316], 0.9999999999999999, [19.513342658829444, 7.06588623278316], -0.9999999999999999, [19.513342658829444, 5.06588623278316], 0, [17.513342658829444, 5.06588623278316], 0, true]] + f.Read(); + let pl = f.ReadObject() as Polyline; + + let p = new Vector3(19.48, 8.1097); + let isR = GetPointAtCurveDir(pl, p); + + expect(isR).toBeFalsy(); +}); + + +test('存在精度误差,并且点在圆内', () => +{ + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 3, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 5, [0, 0], 0, [2, 0], 1, [2, 2], -1, [2, 4], 0, [0, 4], 0, true]] + f.Read(); + let pl = f.ReadObject() as Polyline; + + let p = new Vector3().fromArray([2, 3.8, 0]); + + for (let i = 0; i < 5; i++) + { + p.x -= 0.01; + + let isR = GetPointAtCurveDir(pl, p); + + expect(isR).toBeTruthy(); + } + + pl.Reverse(); + + for (let i = 0; i < 10; i++) + { + p.x += 0.01; + + let isR = GetPointAtCurveDir(pl, p); + + expect(isR).toBeFalsy(); + } +}); + + +test('大于1凸度的圆', () => +{ + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 46, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 4, [0, 0], 0, [1, 0], 3.7050811739160006, [1, 1.7149500468022372], 0, [0, 1.7149500468022374], 0, true]] + f.Read(); + let pl = f.ReadObject() as Polyline; + + let pt = new Vector3().fromArray([1.5998555184265881, 0.9127879590514852, 0]); + + let isR = GetPointAtCurveDir(pl, pt); + + expect(isR).toBeFalsy(); +}); + +test('盲区计算', () => +{ + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 65, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 3, [0, 0], 0, [1, 0], 0.6998085535161632, [1, -1], -1.9959956751332304, false]] + f.Read(); + let pl = f.ReadObject() as Polyline; + + let pt = new Vector3().fromArray([1.3789363811285338, -0.34949587695820905, 0]); + + let isR = GetPointAtCurveDir(pl, pt); + + expect(isR).toBeFalsy(); + + pl.Reverse(); + isR = GetPointAtCurveDir(pl, pt); + + expect(isR).toBeTruthy(); +}); + + +test('参数点在终点上', () => +{ + + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 38, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 9, [26.388805509867886, 5.284389892538414], 0, [28.388805509867886, 5.284389892538414], 0.9999999999999999, [28.388805509867886, 7.284389892538414], -0.9999999999999999, [28.388805509867886, 9.284389892538414], 0, [24.129449189553043, 9.284389892538414], 0, [24.129449189553043, 13.772242014802323], 0, [33.10581078647962, 13.772242014802321], 0, [33.10581078647962, 0.368763303745288], 0, [27.762738407356657, 0.36876330374528865], 0, true]] + f.Read(); + let pl = f.ReadObject() as Polyline; + let pt = new Vector3().fromArray([24.40481807693597, 4.679455426066854, 0]); + let isR = GetPointAtCurveDir(pl, pt); + expect(isR).toBeFalsy(); + + pt = new Vector3().fromArray([30.314368267468268, 10.71117601112483, 0]); + isR = GetPointAtCurveDir(pl, pt); + expect(isR).toBeTruthy(); + + pl.Reverse(); + isR = GetPointAtCurveDir(pl, pt); + expect(isR).toBeFalsy(); + + pt = new Vector3().fromArray([24.40481807693597, 4.679455426066854, 0]); + isR = GetPointAtCurveDir(pl, pt); + expect(isR).toBeTruthy(); +}); + + +test('点在小角内', () => +{ + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 27, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -47.5997122622927, 10.897558314909338, 0, 1], 2, 4, [-0.906131771640112, 6.969061295372345], 3.7897716494829594, [4.660056657223795, 6.487252124645892], 0, [20.106696883852685, 18.393201133144473], 0, [9.169563739376775, 22.583048158640217], 0, false]] + + f.Read(); + let pl = f.ReadObject() as Polyline; + let pt = new Vector3().fromArray([-45.25245903106504, 20.057300217471017, 0]); + + let isR = GetPointAtCurveDir(pl, pt); + expect(isR).toBeFalsy(); + + pl.Reverse(); + isR = GetPointAtCurveDir(pl, pt); + expect(isR).toBeTruthy(); +}); + +test('首尾点相等', () => +{ + + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 264, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 6, [0, 0], 0, [5, 0], 0.9999999999999999, [5, 5], -0.9999999999999999, [5, 10], 0, [0, 10], 0, [0, 0], 0, false]] + f.Read(); + let pl = f.ReadObject() as Polyline; + + let pt = new Vector3().fromArray([-0.4662687082606203, -0.5388413229688394, 0]); + let isR = GetPointAtCurveDir(pl, pt); + expect(isR).toBeTruthy(); + + pl.Reverse(); + isR = GetPointAtCurveDir(pl, pt); + expect(isR).toBeFalsy(); + +}); + +test('点在圆弧的弦中心上', () => +{ + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 264, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 6, [0, 0], 0, [5, 0], 0.9999999999999999, [5, 5], -0.9999999999999999, [5, 10], 0, [0, 10], 0, [0, 0], 0, false]] + f.Read(); + let pl = f.ReadObject() as Polyline; + + let pt = new Vector3().fromArray([4.999999999999999, 2.5, 0]); + let isR = GetPointAtCurveDir(pl, pt); + expect(isR).toBeFalsy(); + + pt = new Vector3().fromArray([5, 9.5, 0]); + isR = GetPointAtCurveDir(pl, pt); + expect(isR).toBeTruthy(); + pt = new Vector3().fromArray([5, 5.5, 0]); + isR = GetPointAtCurveDir(pl, pt); + expect(isR).toBeTruthy(); + + pt = new Vector3().fromArray([6, 10, 0]); + isR = GetPointAtCurveDir(pl, pt); + expect(isR).toBeTruthy(); +}); + +test('圆弧过大导致直线小角错误', () => +{ + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 3, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.028011204481791063, 0.028011204481792618, 0, 1], 2, 4, [-2241.358078364921, -214.80916327845875], -1.1401903887488165, [-2190.503423306555, -220.10318099746846], -1.1044015866963386, [-2231.667025600985, -212.01859443849355], 0, [-2241.358078364921, -214.80916327845875], 0, false]] + f.Read(); + let pl = f.ReadObject() as Polyline; + + let pt = new Vector3().fromArray([-2246.7733894227954, -220.1844621837422, 0]); + let isR = GetPointAtCurveDir(pl, pt); + expect(isR).toBeFalsy(); + + pt = new Vector3().fromArray([-2184.668414731696, -221.35723534211996, 0]); + + //[1,["Circle",1,1,4,false,7,-1,[1,0,0,0,0,1,0,0,0,0,1,0,-2184,-221.357,0,1],1,1]] + isR = GetPointAtCurveDir(pl, pt); + expect(isR).toBeFalsy(); +}); diff --git a/__test__/Polyline/__snapshots__/offset.test.ts.snap b/__test__/Polyline/__snapshots__/offset.test.ts.snap new file mode 100644 index 000000000..3d4483410 --- /dev/null +++ b/__test__/Polyline/__snapshots__/offset.test.ts.snap @@ -0,0 +1,147 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`IKKGK圆与直线补圆弧 1`] = `1`; + +exports[`IKKGK圆与直线补圆弧 2`] = `44.998097679646904`; + +exports[`IKKGK圆与直线补圆弧 3`] = `1`; + +exports[`IKKGK圆与直线补圆弧 4`] = `44.998097679647046`; + +exports[`IKKGK圆与直线补圆弧 5`] = `1`; + +exports[`IKKGK圆与直线补圆弧 6`] = `52.52605376818708`; + +exports[`中间区域需要圆裁剪 1`] = `1`; + +exports[`中间区域需要圆裁剪 2`] = `24.711300177428036`; + +exports[`圆求交错误导致的线丢失 1`] = `4148.643109243218`; + +exports[`圆求交错误导致的线丢失 2`] = `4425.268216257021`; + +exports[`圆求交错误导致的线丢失 3`] = `4021.8883370297244`; + +exports[`圆求交错误导致的线丢失 4`] = `4581.211434067452`; + +exports[`圆求交错误导致的线丢失 5`] = `3900.596094685299`; + +exports[`圆求交错误导致的线丢失 6`] = `4757.455448859379`; + +exports[`圆求交错误导致的线丢失 7`] = `3783.7370117939813`; + +exports[`圆求交错误导致的线丢失 8`] = `4971.999047743294`; + +exports[`拱门偏移 1`] = `1`; + +exports[`拱门偏移 2`] = `4.314156035548454`; + +exports[`拱门偏移 3`] = `1`; + +exports[`拱门偏移 4`] = `6.827404319936081`; + +exports[`简单图形因为点在线内算法错误导致的丢失 1`] = `8.675026988029915`; + +exports[`简单图形因为点在线内算法错误导致的丢失 2`] = `8.252659494518674`; + +exports[`简单图形因为点在线内算法错误导致的丢失 3`] = `6.802593049888034`; + +exports[`简单图形因为点在线内算法错误导致的丢失 4`] = `6.045525633131274`; + +exports[`补圆弧测试 补圆弧测试1 1`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 2`] = `202.39234999237357`; + +exports[`补圆弧测试 补圆弧测试1 3`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 4`] = `202.97100463130596`; + +exports[`补圆弧测试 补圆弧测试1 5`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 6`] = `203.6334701054305`; + +exports[`补圆弧测试 补圆弧测试1 7`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 8`] = `204.40220678622498`; + +exports[`补圆弧测试 补圆弧测试1 9`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 10`] = `205.30911616294793`; + +exports[`补圆弧测试 补圆弧测试1 11`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 12`] = `206.4013429179738`; + +exports[`补圆弧测试 补圆弧测试1 13`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 14`] = `207.75214121305123`; + +exports[`补圆弧测试 补圆弧测试1 15`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 16`] = `209.48307101962268`; + +exports[`补圆弧测试 补圆弧测试1 17`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 18`] = `211.81505991717293`; + +exports[`补圆弧测试 补圆弧测试1 19`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 20`] = `215.20865129706962`; + +exports[`补圆弧测试 补圆弧测试1 21`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 22`] = `220.89085248936073`; + +exports[`补圆弧测试 补圆弧测试1 23`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 24`] = `243.05075247271324`; + +exports[`补圆弧测试 补圆弧测试1 25`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 26`] = `236.6991306483004`; + +exports[`补圆弧测试 补圆弧测试1 27`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 28`] = `236.69932095661932`; + +exports[`补圆弧测试 补圆弧测试1 29`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 30`] = `205.62990409788847`; + +exports[`补圆弧测试 补圆弧测试1 31`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 32`] = `198.55214618601138`; + +exports[`补圆弧测试 补圆弧测试1 33`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 34`] = `195.91344487061264`; + +exports[`补圆弧测试 补圆弧测试1 35`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 36`] = `194.55183899140644`; + +exports[`补圆弧测试 补圆弧测试1 37`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 38`] = `193.73991606924534`; + +exports[`补圆弧测试 补圆弧测试1 39`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 40`] = `193.2116315246781`; + +exports[`补圆弧测试 补圆弧测试1 41`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 42`] = `192.84670578048616`; + +exports[`补圆弧测试 补圆弧测试1 43`] = `1`; + +exports[`补圆弧测试 补圆弧测试1 44`] = `192.58324938493251`; + +exports[`闭合多段线判断精度和重复交点参数导致偏移丢失 1`] = `54789.14760701891`; + +exports[`闭合多段线判断精度和重复交点参数导致偏移丢失 2`] = `54907.17933624483`; + +exports[`闭合多段线判断精度和重复交点参数导致偏移丢失 3`] = `55497.39792073307`; + +exports[`闭合多段线判断精度和重复交点参数导致偏移丢失 4`] = `56678.13326318633`; + +exports[`闭合多段线判断精度和重复交点参数导致偏移丢失 5`] = `57859.26303559798`; diff --git a/__test__/Polyline/__snapshots__/polyline.test.ts.snap b/__test__/Polyline/__snapshots__/polyline.test.ts.snap new file mode 100644 index 000000000..4c2c527e9 --- /dev/null +++ b/__test__/Polyline/__snapshots__/polyline.test.ts.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`多段线 优化参数在端点上的时候 1`] = ` +Vector3 { + "x": 0, + "y": 0, + "z": 0, +} +`; + +exports[`多段线 存在大圆弧的多段线面积 1`] = `-24.019436375469752`; + +exports[`多段线 最近点精度 1`] = ` +Vector3 { + "x": 1.987500976448074, + "y": 3.9999218841540816, + "z": 0, +} +`; diff --git a/__test__/Polyline/offset.test.ts b/__test__/Polyline/offset.test.ts new file mode 100644 index 000000000..1b37e8f54 --- /dev/null +++ b/__test__/Polyline/offset.test.ts @@ -0,0 +1,835 @@ +import { Factory } from "../../src/DatabaseServices/CADFactory"; +import { CADFile } from "../../src/DatabaseServices/CADFile"; +import { Curve } from "../../src/DatabaseServices/Curve"; +import { Polyline } from "../../src/DatabaseServices/Polyline"; +Factory(Polyline); + +function loadFile(data) +{ + let file = new CADFile(); + file.Data = data; + let cus: Curve[] = []; + let count = file.Read(); + for (let i = 0; i < count; i++) + { + cus.push(file.ReadObject(undefined) as Curve); + } + return cus; +} + +function EntityToMatchSnapshot(ens: Curve[]) +{ + expect(ens.length).toMatchSnapshot(); + for (let c of ens) + { + expect(c.Length).toMatchSnapshot(); + } +} + +test('IKKGK圆与直线补圆弧', () => +{ + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 5, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -47.808747539346534, 10.92028558763661, 0, 1], 2, 3, [-0.906131771640112, 6.969061295372345], 3.7897716494829594, [4.660056657223795, 6.487252124645892], 0, [18.466397865202012, 17.128892766521286], 0, false]] + f.Read(); + let pl = f.ReadObject() as Polyline; + + //负1 左边. 禁止补圆弧 + let cus = pl.GetOffsetCurves(-1); + + EntityToMatchSnapshot(cus); + + pl.GetOffsetCurves(-5.654208780429431); + // + + //翻转曲线后测试 + pl.Reverse(); + cus = pl.GetOffsetCurves(1); + + EntityToMatchSnapshot(cus); + + //圆丢失的问题 + cus = pl.GetOffsetCurves(-1.926388985025112); + EntityToMatchSnapshot(cus); +}); +describe("闭合多段线", () => +{ + + test('闭合多段线偏移测试1', () => + { + let data = + [1, ["Polyline", 1, 1, 3, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 4, [6.059716713881022, 3.983456090651555], 1.1217395195062214, [6.383002832861191, 4.064022662889515], 0, [4.929178470254959, 8.399433427762037], 1.2642365052895204, [5.750708215297452, 4.050991501416427], 0.7171789779484218, true]] + let cus = loadFile(data); + for (let i = 1; i < 10; i += 1) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = -0.5; i < -0.15; i += 0.05) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = 2; i < 10; i += 2) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(0); + } + }) + test('闭合多段线偏移测试2', () => + { + let data = + [1, ["Polyline", 1, 1, 734, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 4, [8.176442643449754, 4.003694129881114], 0.6482011490054378, [8.554554536583165, 4.090460859042256], 0, [7.7292787370841225, 8.733182129315965], 0, [7.8227800907745895, 4.037582052934876], 1.223388515290821, true]] + + let cus = loadFile(data); + expect(cus[0].GetOffsetCurves(-0.1799).length).toBe(2); + + for (let i = 0.2; i < 10; i += 2) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = -0.17; i < 0; i += 0.03) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = -0.2; i > -0.34; i -= 0.02) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = 0.35; i < 0.5; i += 0.05) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(0); + } + }) + test('闭合多段线偏移测试3', () => + { + let data = + [1, ["Polyline", 1, 1, 1172, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 5, [12.52535684411379, 4.512623511896158], 0.39558516940595195, [13.121416971132648, 4.487590129452889], 0, [12.414545456418505, 8.796498405316765], 0, [12.015990240675425, 8.743859037199755], 0, [12.015990240675425, 4.4951100391838885], 0.8508932598252141, true]] + + let cus = loadFile(data); + for (let i = 0.5; i < 10; i += 1) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = 0.1; i <= 0.5; i += 0.01) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + for (let i = 0.51; i <= 1; i += 0.1) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(0); + } + }) + test('闭合多段线偏移测试4', () => + { + let data = + [1, ["Polyline", 1, 1, 1453, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 5, [17.138593827490823, 4.9429359111242475], 0.6262511063576436, [18.12066066115083, 4.8347623552068075], 0, [18.12066066115083, 10.450468898178228], 0, [16.250318720818097, 10.450468898178228], 0, [16.250318720818097, 4.952028938447833], 0.8739221457690426, true]]; + let cus = loadFile(data); + for (let i = 1; i < 10; i += 2) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = 0.4; i <= 0.6; i += 0.02) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + for (let i = 1; i <= 2; i += 0.2) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(0); + } + //变换曲线顺序 + data = [1, ["Polyline", 1, 1, 1891, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 5, [22.178278439318607, -1.9155229050126357], 0, [22.83746943877066, -1.800164480108526], 0, [23.79329638797614, -6.11786552651948], -0.871731137783436, [23.348342463346004, -6.282663276382493], -0.7430788314178012, [22.903388538715866, -6.267501883395095], 0, true]]; + cus = loadFile(data); + for (let i = 0.01; i <= 0.33; i += 0.03) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = 0.34; i <= 1; i += 0.1) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(0); + } + }) + test('闭合多段线偏移测试5', () => + { + let data = + [1, ["Polyline", 1, 1, 264, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 6, [0, 0], 0, [5, 0], 0.9999999999999999, [5, 5], -0.9999999999999999, [5, 10], 0, [0, 10], 0, [0, 0], 0, false]]; + let cus = loadFile(data); + for (let i = 0.5; i < 10; i += 1.5) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + expect(cus[0].GetOffsetCurves(-1.3).length).toBe(2); + expect(cus[0].GetOffsetCurves(-1.2).length).toBe(1); + expect(cus[0].GetOffsetCurves(-2).length).toBe(1); + + for (let i = 2; i <= 2.8; i += 0.1) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + for (let i = 2.9; i <= 6; i += 0.5) + { + let len = cus[0].GetOffsetCurves(-i).length; + expect(len).toBe(0); + } + //反转曲线 + cus[0].Reverse(); + for (let i = 0.5; i < 10; i += 1.5) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + expect(cus[0].GetOffsetCurves(1.3).length).toBe(2); + expect(cus[0].GetOffsetCurves(1.2).length).toBe(1); + expect(cus[0].GetOffsetCurves(2).length).toBe(1); + + for (let i = 2; i <= 2.8; i += 0.2) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = 2.9; i <= 6; i += 1) + { + let len = cus[0].GetOffsetCurves(i).length; + expect(len).toBe(0); + } + }) + test('闭合多段线偏移测试6', () => + { + let data = + [1, ["Polyline", 1, 1, 11080, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -2.007355593658339, -15.226898894860806, 0, 1], 2, 6, [0.06393073988345588, -13.474722415885717], 0, [0.06393073988345643, -4.4950563800695225], 0, [6.069811270331949, -4.495056380069524], -0.9999999999999999, [6.0698112703319484, -9.101508437403805], 0.8261854469247998, [6.069811270331948, -13.53303193559881], 0, [0.06393073988345588, -13.474722415885717], 0, false]]; + let cus = loadFile(data); + for (let i = 0.5; i < 10; i += 0.5) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + for (let i = 0.1; i <= 2.9; i += 0.1) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = 3; i <= 6; i += 0.1) + { + let len = cus[0].GetOffsetCurves(i).length; + expect(len).toBe(0); + } + }) + test('闭合多段线偏移测试7', () => + { + let data = + [1, ["Polyline", 1, 1, 3103, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 6, [7.068575921553821, 19.385189367549113], 0, [17.866251977281493, 27.179961431447197], -0.7242773730087224, [27.449988121418478, 20.215779833374317], -0.5496546648274403, [21.06083069199382, 18.554598901723907], 0.7351204491094537, [17.802360402987244, 16.70174324719076], -1.609719383557213, [7.068575921553821, 19.385189367549113], 0, true]]; + let cus = loadFile(data); + //外偏移 + for (let i = 0.5; i < 5.5; i += 1) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + //内偏移 + for (let i = 3.6; i > 0; i -= 0.6) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + + for (let i = 3.7; i <= 4.7; i += 0.2) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(2); + } + expect(cus[0].GetOffsetCurves(5).length).toBe(1); + for (let i = 6.1; i < 10; i += 1) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(0); + } + }) + test('闭合多段线偏移测试8', () => + { + let data = + [1, ["Polyline", 1, 1, 8895, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 9, [-35.77751476623335, 275.4953263952156], 0, [220.87993293360785, 358.6317790975911], 0, [317.8724610863793, 154.43698298649332], 0, [128.26300755464567, 91.72000989522755], 0, [202.6482547094027, 193.81740795077644], 0, [96.90452100901277, 149.33211308371583], 0, [167.64343251892882, 238.30270281783706], 0, [-5.9221441755043855, 147.87357882577945], 0, [115.1361992332179, 265.2855865896607], 0, true]]; + let cus = loadFile(data); + //外偏移 - + for (let i = 20; i < 200; i += 100) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + //内偏移+ + expect(cus[0].GetOffsetCurves(15).length).toBe(1); + expect(cus[0].GetOffsetCurves(17.8).length).toBe(2); + expect(cus[0].GetOffsetCurves(19).length).toBe(3); + for (let i = 20; i <= 40; i += 10) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(3); + } + for (let i = 41.3; i <= 44.3; i += 1) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(2); + } + expect(cus[0].GetOffsetCurves(45).length).toBe(1); + for (let i = 50; i <= 250; i += 100) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(0); + } + }) + test('闭合多段线偏移测试9', () => + { + let data = + [1, ["Polyline", 1, 1, 12283, false, 2, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -2494.1602062139054, -154.37421492307953, 0, 1], 2, 4, [-835.9056689963722, -81.71614968554674], -1.404257507243489, [-2.5205246091134654, -224.3682931983378], -0.7704346688180675, [-677.9527382520764, -55.635630094036955], 0, [-835.9056689963722, -81.71614968554674], 0, false]]; + let cus = loadFile(data); + //外偏移 - + //内偏移+ + for (let i = -350; i <= 350; i += 100) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = 360; i <= 460; i += 50) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(0); + } + cus[0].Reverse(); + for (let i = -350; i <= 350; i += 100) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + for (let i = 360; i <= 460; i += 50) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(0); + } + }) + test('闭合多段线偏移测试10', () => + { + let data = + [1, ["Polyline", 1, 1, 4133, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -38.7066882611841, -36.73336766355714, 0, 1], 2, 10, [57.916496677407835, -1.0361149153581897], 0, [57.916496677407835, 9.149866136686853], 0, [63.00948720343037, 6.40233177396418], 0, [67.96845166297861, 11.696361399698116], 0, [67.96845166297861, 5.866227508067071], 0, [76.41209385085804, 8.144670638129776], -0.6438035714487224, [82.13500688930966, 1.215523001409662], -0.16133386709235895, [73.7315725213725, -7.388950466238915], -0.24328993833238707, [62.473382937533245, -7.388950466238914], 0.16864648307761998, [53.5472469103464, -6.102300228085856], -1.9523252757782097, true]]; + let cus = loadFile(data); + //外偏移 - + for (let i = 5; i < 20; i += 5) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + + //内偏移+ + expect(cus[0].GetOffsetCurves(2.3).length).toBe(1); + for (let i = 2.5; i < 4; i += 1) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(2); + } + for (let i = 4.2; i <= 7.2; i += 0.8) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = 7.5; i <= 17.5; i += 5) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(0); + } + }) + test('闭合多段线偏移测试11', () => + { + let data = + [1, ["Polyline", 1, 1, 109, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 9, [8.533519553072624, 3.044692737430166], 0, [8.198324022346368, 5.782122905027932], 0, [9.483240223463685, 4.594972067039105], 0, [10.293296089385473, 4.958100558659217], 0, [11.298882681564244, 3.50558659217877], 0, [11.648044692737429, 3.1424581005586587], 0, [9.525139664804467, 2.3324022346368705], 0, [9.748603351955305, 3.589385474860334], 0, [8.519553072625698, 1.3268156424580995], 0, true]]; + let cus = loadFile(data); + //外偏移 - + for (let i = 0.1; i <= 0.3; i += 0.1) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + + //内偏移+ + expect(cus[0].GetOffsetCurves(0.15).length).toBe(1); + + for (let i = 0.2; i <= 0.7; i += 0.1) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(2); + } + for (let i = 0.8; i <= 3.8; i++) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(0); + } + + }) + test('闭合多段线偏移测试12', () => + { + let data = + [1, ["Polyline", 1, 1, 4132, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 12, [13.549363225511495, 3.33570646745377], 0, [14.34284463241268, 4.996139781895137], 0, [14.523074259257426, 3.9557232996550056], 0, [15.838887129162556, 4.9814356943054605], 0, [15.943612070431213, 3.9627476292376005], 0, [17.33950450111622, 4.461280640196535], 0, [18.123624596131165, 2.7156799524846913], 0, [17.99903152781382, 1.6177035379380693], 0, [16.03466125512531, 2.900960499995888], 0, [15.014220997727234, 1.7794865537465168], 0, [14.332835411729478, 2.6268506799232134], 0, [13.549363225511495, 3.33570646745377], 0, false]]; + let cus = loadFile(data); + //外偏移 - + for (let i = 0.5; i <= 2; i += 0.5) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + + //内偏移+ + expect(cus[0].GetOffsetCurves(0.3).length).toBe(1); + expect(cus[0].GetOffsetCurves(0.4).length).toBe(2); + expect(cus[0].GetOffsetCurves(0.46).length).toBe(3); + expect(cus[0].GetOffsetCurves(0.5).length).toBe(2); + expect(cus[0].GetOffsetCurves(0.9).length).toBe(1); + + for (let i = 0.55; i <= 0.85; i++) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(2); + } + for (let i = 1; i <= 2; i += 0.2) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(0); + } + + }) + test('闭合多段线偏移测试13', () => + { + let data = + [1, ["Polyline", 1, 1, 2689, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 13.053732578466253, -1.845982384833612, 0, 1], 2, 12, [10.73237450059647, 5.925995426719515], 0, [14.934000173256667, 9.279899014962986], 0, [15.443475259425389, 6.338838290261696], 0, [16.902963217271246, 7.476549375511614], 0, [17.07910144494388, 5.7632047972414195], 0, [18.04054823840169, 6.106578652047782], 0, [19.469515702452366, 2.925424892792106], 0, [19.064111106000592, -0.6472031134391991], 0, [17.253277279358528, 2.956832838161076], 0, [14.95258895308757, 3.1324290048358128], 0, [13.37431523183268, 1.7225442000746034], 0, [10.73237450059647, 5.925995426719515], 0, false]]; + let cus = loadFile(data); + //外偏移 - + for (let i = 0.5; i <= 2; i += 0.5) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + //内偏移+ + expect(cus[0].GetOffsetCurves(1).length).toBe(1); + expect(cus[0].GetOffsetCurves(1.2).length).toBe(3); + }) + test('闭合多段线偏移测试14', () => + { + let data = + [1, ["Polyline", 1, 1, 1625, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 14, [340.820007357324, -3.870032999900914], 0, [431.6163856609081, 116.2815653567217], 0, [485.54806901416487, 54.84040710617603], 0, [533.3356365423672, 67.81131829240235], 0, [624.1320148459513, 40.504136847715344], 0, [656.9006325795757, 23.43714844478601], -0.783383849320176, [633.0068488154745, -74.86870475608706], 0.4462700081240285, [605.0169878346704, -106.27196341747704], -0.8416099256631104, [529.9222388617812, -128.11770857322662], 0, [510.1245323143834, -22.98506001118185], 0, [430.251026588674, -129.48306764546098], 0, [430.251026588674, -65.99387078656383], 0, [351.74287993519897, -145.86737651227318], 0, [303.2726328708796, -87.83961594231337], 0, true]]; + let cus = loadFile(data); + //外偏移 - + for (let i = 10; i <= 90; i += 10) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + //内偏移+ + for (let i = 5; i <= 25; i += 10) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + expect(cus[0].GetOffsetCurves(26.5).length).toBe(2); + expect(cus[0].GetOffsetCurves(36).length).toBe(2); + expect(cus[0].GetOffsetCurves(36.1).length).toBe(3); + expect(cus[0].GetOffsetCurves(45).length).toBe(2); + expect(cus[0].GetOffsetCurves(60).length).toBe(1); + for (let i = 71; i <= 101; i += 10) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(0); + } + + }) + test('闭合多段线偏移测试15', () => + { + let data = + [1, ["Polyline", 1, 1, 394, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 8, [-3.096429035694039, 1.977370035127478], 0, [2.646251476487261, 5.186031066288948], -0.5113280232193118, [8.158566581303122, 2.619102241359773], 0.23700604598013766, [11.103952861189804, -1.2148363240793185], -1.4842976346055852, [7.994019861756378, -4.028585228328609], 0.5402454464135219, [3.1398916351274853, -2.8932128634560885], -1.0879771453350477, [1.4615150957507161, -0.803469525212464], -0.082022192610066, [-3.096429035694039, 1.977370035127478], 0, true]]; + let cus = loadFile(data); + //外偏移 - + for (let i = 0.5; i <= 5.5; i += 1) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + //内偏移+ + for (let i = 0.1; i <= 1.8; i += 0.3) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + + for (let i = 1.9; i <= 2.2; i += 0.05) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(2); + } + for (let i = 2.3; i <= 3.7; i += 0.2) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = 3.9; i <= 6.9; i += 0.5) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(0); + } + }) + test('闭合多段线偏移测试16', () => + { + let data = + [1, ["Polyline", 1, 1, 4, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 5, [-6.203966005665723, -0.056657223796033884], 0, [-1.8834846458923415, -0.923353563739375], -1.4125691360015384, [-1.9069913201133035, -1.5932937790368253], 0, [-6.189801699716714, -2.2096317280453257], -2.3881275241710798, [-6.203966005665723, -0.056657223796033884], 0, true]]; + let cus = loadFile(data); + //外偏移 - + //内偏移+ + for (let i = -1.5; i <= 1.5; i += 0.2) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + expect(cus[0].GetOffsetCurves(1.6).length).toBe(0); + for (let i = 1.6; i <= 4.6; i++) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(0); + } + }) + +}) +describe("补圆弧测试", () => +{ + test("补圆弧测试1", () => + { + let data = + [1, ["Polyline", 1, 1, 8815, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 5, [-99.60068327971061, 80.69599512140186], 0, [-48.362234971168355, 80.69599512140186], -0.9874208089640422, [-14.972250883848176, 88.46675505445091], -1.1745224194600496, [18.29631507951812, 78.51046889023182], 0, [89.44733571649859, 78.5104688902318], 0, false]]; + let cus = loadFile(data); + // 向上- + // 向下+ + expect(cus[0].GetOffsetCurves(17.45).length).toBe(2); + for (let i = -55; i <= 55; i += 5) + { + if (i === 0) continue; + EntityToMatchSnapshot(cus[0].GetOffsetCurves(i)); + } + }) + test("补圆弧测试2", () => + { + let data = + [1, ["Polyline", 1, 1, 12266, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 5, [-869.4193524531552, -50.92359793870933], 0, [-609.861025770732, -50.92359793870939], -0.8167984427288847, [-480.7149315189898, -23.068558002059024], -1.4042575072434897, [-317.9835774160859, -50.92359793870939], 0, [-92.77019349218811, -50.92359793870939], 0, false]]; + let cus = loadFile(data); + // 向上- + // 向下+ + expect(cus[0].GetOffsetCurves(83.24).length).toBe(2); + expect(cus[0].GetOffsetCurves(86.46).length).toBe(2); + for (let i = 364; i <= 395; i++) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + }) + test("补圆弧测试3", () => + { + let data = + [1, ["Polyline", 1, 1, 12272, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 4, [-894.3165541673695, -331.80629190023], 0, [-671.3300245231958, -331.80629190023], -1.0058680701224356, [-535.7124860261546, -329.1982623137485], 0, [-214.92484688892245, -329.1982623137485], 0, false]]; + let cus = loadFile(data); + // 向上- + // 向下+ + expect(cus[0].GetOffsetCurves(-1200).length).toBe(1); + for (let i = 63; i <= 94; i++) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + }) + test("补圆弧测试3", () => + { + let data = + [1, ["Polyline", 1, 1, 195, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 23, [-25.572010711696727, -62.383195892823124], 0, [-10.312959798454724, -40.55427583637971], 0, [0.28360333574111785, -55.81332674962171], 0, [9.820510156517361, -48.18380129300071], 0, [18.721623189241864, -65.13830230771404], 0, [29.530117586121616, -60.47581452866788], 0, [39.06702440689787, -79.97349069558823], 0, [51.78290016793288, -70.64851513749588], -0.6227891115113805, [70.85671380948538, -80.60928448363998], 0.3759603135798005, [86.75155851077912, -99.68309812519247], -1.1383492019405204, [81.45327694368122, -117.6972554533254], 0.6271394112393051, [57.2931129977147, -138.2545879336653], -1.2933960333754264, [21.68866086681669, -138.2545879336653], -0.8106183277649434, [-17.73055399239181, -132.9563063665674], 0, [-43.37423677714574, -105.82910474302606], 0, [-51.003762233766736, -83.5763221612148], 0, [-48.67251834424364, -64.07864599429448], 0, [-62.65998168138215, -50.09118265715597], 0, [-86.39628310198083, -65.35023357039795], 0, [-86.39628310198083, -33.13668164244262], 0, [-73.68040734094582, -13.639005475522275], 0, [-43.16230551446182, -13.639005475522275], 0, [-33.62539869368556, -11.307761585999202], 0, false]]; + let cus = loadFile(data); + // 向外- + // 向内+ + expect(cus[0].GetOffsetCurves(18).length).toBe(3); + for (let i = 50; i <= 80; i += 5) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + //需要删除一个无效的线段 ref #IKO6L + expect(cus[0].GetOffsetCurves(27.3).length).toBe(1); + }) + test("补圆弧测试3", () => + { + let data = + [1, ["Polyline", 1, 1, 3, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 10, [-5.637393767705383, -1.586402266288952], 0, [-0.84985835694051, 1.954674220963173], -0.5207626681247078, [1.728045325779038, 1.0906515580736542], -1.2136457904456304, [4.060938624362605, 1.3838754084985838], -1.0694858869096764, [6.891142200566573, -0.047681051558073895], -1.4716493502287318, [9.178341602266286, -2.976612659490083], -1.5003222663355726, [8.09233325325779, -7.040916632294616], -0.9628743606456998, [4.53257790368272, -7.478753541076488], -1.076087427994497, [2.3229461756373926, -7.478753541076488], 0, [-5.39660056657224, -7.478753541076487], 0, false]]; + let cus = loadFile(data); + // 向外- + // 向内+ + expect(cus[0].GetOffsetCurves(1.88).length).toBe(2); + expect(cus[0].GetOffsetCurves(2.12).length).toBe(2); + expect(cus[0].GetOffsetCurves(3.2).length).toBe(1); + for (let i = 0.5; i <= 3.5; i += 0.5) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + for (let i = 3.3; i <= 5.3; i += 0.5) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(0); + } + for (let i = 2.33; i <= 3.2; i += 0.2) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + }) +}) + +describe("不规则不闭合多段线测试", () => +{ + test("test1", () => + { + let data = + [1, ["Polyline", 1, 1, 3135, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 6, [16.034190840579708, -16.545186318840567], 0, [36.985943188405784, -16.545186318840567], 0, [31.311510260869554, -12.507609043478254], 0, [48.334809043478245, -12.507609043478254], 0, [43.969860637681144, -16.6543100289855], 0, [56.84645843478259, -16.654310028985503], 0, false]]; + let cus = loadFile(data); + for (let i = 1.27; i <= 2.96; i += 0.2) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(2); + } + expect(cus[0].GetOffsetCurves(9.16).length).toBe(2); + for (let i = 0.5; i <= 7.5; i++) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + + }) + test("test2-共线线段", () => + { + let data = + [1, ["Polyline", 1, 1, 8814, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 7, [15.642409533480423, -31.01626057276902], 0, [38.129771973149346, -31.016260572769028], 0, [32.85929640135194, -26.50707591689791], 0, [47.440945483324754, -26.50707591689791], 0, [45.33685543194063, -31.016260572769028], 0, [61.52187596164093, -31.016260572769035], 0, [70.31099992504923, -35.88209931313313], 0, false]]; + let cus = loadFile(data); + for (let i = 1.82; i <= 3.37; i += 0.3) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(2); + } + expect(cus[0].GetOffsetCurves(18).length).toBe(2); + for (let i = 1; i <= 27; i += 5) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + cus[0].Reverse(); + expect(cus[0].GetOffsetCurves(-18).length).toBe(2); + data = [1, ["Polyline", 1, 1, 22696, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 7, [21.944751443449043, -31.01626057276902], 0, [38.129771973149346, -31.016260572769028], 0, [32.85929640135194, -26.50707591689791], 0, [47.440945483324754, -26.50707591689791], 0, [45.33685543194063, -31.016260572769028], 0, [61.52187596164093, -31.016260572769035], 0, [70.31099992504923, -35.88209931313313], 0, false]]; + cus = loadFile(data); + expect(cus[0].GetOffsetCurves(11.4).length).toBe(1); + data = [1, ["Polyline", 1, 1, 24383, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -56.21146708819905, -48.62279820279199, 0, 1], 2, 7, [26.333787338902418, -31.01626057276902], 0, [35.05226039203026, -31.016260572769013], 0, [32.85929640135194, -26.50707591689791], 0, [47.440945483324754, -26.50707591689791], 0, [41.54871106124796, -31.016260572769013], 0, [61.52187596164093, -31.016260572769035], 0, [70.31099992504923, -35.88209931313313], 0, false]] + cus = loadFile(data); + expect(cus[0].GetOffsetCurves(5.2).length).toBe(1); + + data = [1, ["Polyline", 1, 1, 4, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 6, [-3.158640226628895, -0.9915014164305944], 0, [-1.0906515580736538, -0.991501416430595], 0, [-1.983002832861189, 0.18413597733711037], 0, [1.0906515580736549, 0.3966005665722377], 0, [0.2974504249291776, -0.9915014164305944], 0, [3.966005665722378, -0.9915014164305953], 0, false]]; + cus = loadFile(data); + expect(cus[0].GetOffsetCurves(2.01133).length).toBe(1); + }) + test("test3", () => + { + let data = + [1, ["Polyline", 1, 1, 4308, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 5, [-41.52354973772388, -163.989590203395], 0, [-41.5235497377239, -231.3399374277236], 0, [21.016058399152634, -231.3399374277236], 0, [21.016058399152623, -294.68133541250876], 1.378670046466899, [261.5530127717545, -216.10593031745873], 0, false]]; + let cus = loadFile(data); + for (let i = -100; i <= 181; i += 30) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + expect(cus[0].GetOffsetCurves(-122.6).length).toBe(2); + }) + test("test4", () => + { + let data = + [1, ["Polyline", 1, 1, 14565, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -131.50093441946296, -806.1569163328921, 0, 1], 2, 24, [256.5228611988947, -38.705370748164505], 0, [372.1955588024039, 31.844231195771492], 0, [372.1955588024039, -31.54297461172431], 0, [450.9819163032461, 37.57414810492366], 0, [423.0485713711294, -54.10452244151095], 0, [589.216161736542, 52.97329979827009], 0, [611.0614699526845, -2.177150452319474], 0, [672.6580767260702, 61.21005535517633], 0, [678.0298738284004, 13.938240854670997], 0, [735.329042919922, 21.100636991111184], 0, [684.1179105443746, -21.515620020708024], 0, [730.6734854312358, -32.61733403219032], 0, [815.1897598412304, 38.29038771856768], 0, [835.6025888300849, -8.981426781937659], 0, [862.1034545349137, 74.4604882075907], 0, [943.4539498526012, 74.46048820759069], 0, [986.972668777612, -29.064785548515978], 0, [924.5022496755805, -66.26627108118637], 0, [1001.0109652050348, -106.27541589934134], 0, [965.9152241364777, -128.73669018321777], 0, [1006.6262837760039, -176.46689803645526], 0, [904.1467198558175, -189.8032796425069], 0, [981.3573502066429, -242.44689124534239], 0, [1004.5205393118904, -256.48518767276516], 0, false]]; + let cus = loadFile(data); + expect(cus[0].GetOffsetCurves(-100).length).toBe(1); + expect(cus[0].GetOffsetCurves(46.8).length).toBe(2); + expect(cus[0].GetOffsetCurves(49.6).length).toBe(3); + expect(cus[0].GetOffsetCurves(86.6).length).toBe(2); + expect(cus[0].GetOffsetCurves(102.3).length).toBe(2); + expect(cus[0].GetOffsetCurves(110).length).toBe(1); + + }) + test("test5", () => + { + let data = + [1, ["Polyline", 1, 1, 3, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 18, [-3.895184135977337, 0.7648725212464592], 0, [0.8215297450424939, 0.7648725212464592], 0, [-2.1246458923512743, -2.1388101983002827], 0, [5.311614730878187, -2.1388101983002827], 0, [1.89801699716714, 3.4844192634560907], 0, [6.671388101983003, 3.4844192634560907], 0, [6.671388101983002, -3.4985835694051], 0, [10.6657223796034, -0.11331444759206824], 0, [8.810198300283286, 1.161473087818697], 0, [14.674220963172807, 3.30028328611898], 0, [16.713881019830033, -2.521246458923512], 0, [11.55807365439094, -5.773371104815864], 0, [3.8441926345609083, -5.773371104815863], 0, [2.3569405099150167, -8.787535410764873], 0, [-1.5297450424929169, -6.090651558073655], 0, [-6.586402266288951, -12.674220963172807], 0, [-8.113314447592067, -8.767705382436262], 0, [-10.929178470254957, -12.436260623229463], 0, false]]; + let cus = loadFile(data); + expect(cus[0].GetOffsetCurves(0.755).length).toBe(3); + expect(cus[0].GetOffsetCurves(1.915).length).toBe(4); + expect(cus[0].GetOffsetCurves(2.36).length).toBe(4); + expect(cus[0].GetOffsetCurves(2.892).length).toBe(2); + expect(cus[0].GetOffsetCurves(3.6850).length).toBe(1); + expect(cus[0].GetOffsetCurves(-1).length).toBe(3); + for (let i = 2; i <= 4; i++) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + + }) + test("test6", () => + { + let data = + [1, ["Polyline", 1, 1, 2781, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -38.54242746474825, 6.4891615215880964, 0, 1], 2, 3, [18.466397865202012, 17.128892766521286], 0, [4.660056657223795, 6.487252124645892], -3.7897716494829594, [-0.906131771640112, 6.969061295372345], 0, false]]; + let cus = loadFile(data); + + for (let i = -2.766; i <= 4.57; i++) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + for (let i = 2.9; i <= 5.3; i += 0.5) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(2); + } + for (let i = 6.14; i <= 8; i += 0.5) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + cus[0].Reverse(); + for (let i = -2.766; i <= 4.57; i++) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = 2.9; i <= 5.3; i += 0.5) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(2); + } + for (let i = 6.14; i <= 8; i += 0.5) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + }) + test("test7", () => + { + let data = + [1, ["Polyline", 1, 1, 2347, false, 1, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -86.54515219838936, -34.782221464287886, 0, 1], 2, 5, [-51.274354719855246, 28.573003242461496], 0, [-13.931531095689206, 15.255196629685734], 3.7897716494829594, [-8.365342666825299, 14.77338745895928], 0, [5.4409985411529185, 25.415028100834675], 0.37399715496699776, [29.033295173881022, 23.38858533792915], 0, false]]; + let cus = loadFile(data); + expect(cus[0].GetOffsetCurves(-3.133).length).toBe(2); + for (let i = 10; i <= 60; i += 10) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + for (let i = 5.9; i <= 43.3; i += 10) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + cus[0].Reverse(); + expect(cus[0].GetOffsetCurves(3.133).length).toBe(2); + for (let i = 10; i <= 60; i += 10) + { + expect(cus[0].GetOffsetCurves(-i).length).toBe(1); + } + for (let i = 5.9; i <= 43.3; i += 10) + { + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + }) + test("test8", () => + { + let data = + [1, ["Polyline", 1, 1, 395, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 8, [14.442408344729744, 12.584578126613017], 0, [14.442408344729742, 4.502438174603963], 0, [20.086360825183, 4.502438174603963], 0, [20.086360825183, -0.9157562066311582], 0, [26.226981123916133, -0.9157562066311598], 0.4633692494967541, [33.496391918739924, 7.663051563657783], -0.673355221273594, [51.96340443478295, 18.996108144407916], 0.32851266965536, [64.10919017271836, 18.996108144407913], -1.9784627492130062, false]]; + let cus = loadFile(data); + for (let i = -15; i <= 15; i += 5) + { + if (i === 0) continue; + expect(cus[0].GetOffsetCurves(i).length).toBe(1); + } + }) + test("test9-2圆弧没交点", () => + { + let data = + [1, ["Polyline", 1, 1, 23321, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 3, [-245.7800398731897, -124.83507012637789], 0.47532901948537815, [-247.69480466441718, -117.62074637472406], 0.12615539092932912, [-193.96898890583213, -126.5297489543612], 0, false]] + let cus = loadFile(data); + expect(cus[0].GetOffsetCurves(11.22).length).toBe(1); + expect(cus[0].GetOffsetCurves(-11.22).length).toBe(1); + }) + test("test10-共线部分错误裁剪", () => + { + let data = + [1, ["Polyline", 1, 1, 151, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 13, [37846.488077851434, 5711.914029035103], 0, [28152.473806690192, 5711.914029035104], 0, [31338.416239942802, 10002.31650581529], 0, [39750.51284565329, 10002.316505815288], 0, [39750.51284565329, 17632.65706309083], 0, [45109.026777541825, 17632.65706309083], 0, [45109.02677754183, 10002.31650581529], 0, [54643.39210261924, 10002.31650581529], -0.8704734266845496, [55266.30970531861, 5526.15551510335], 0, [45201.90603450778, 5526.155515103216], 0, [45201.90603450778, 249.9945243911434], 0, [37846.488077851434, 435.75303832303064], 0, [37846.488077851434, 5711.914029035103], 0, false]] + let cus = loadFile(data); + expect(cus[0].GetOffsetCurves(2700).length).toBe(1); + }) +}) + + +test('提前丢失所有的线段', () => +{ + let f = new CADFile(); + + f.Data = + [1, ["Polyline", 1, 1, 12, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -17.386067452264705, 17.11283965241085, 0, 1], 2, 3, [108.37693694676932, 28.257776463252547], 0.4472440944881872, [108.37693694676932, 17.68555707305448], 3.5940684866260213, [111.70676982557188, 14.089337563947739], 0.5086040192926764, false]] + + f.Read(); + + let pl = f.ReadObject() as Polyline; + + let cus = pl.GetOffsetCurves(-7.155086190577401); + + expect(cus.length).toBe(0); +}); + + +test('中间区域需要圆裁剪', () => +{ + let f = new CADFile(); + + f.Data = + [1, ["Polyline", 1, 1, 2628, false, 1, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -1325.2800870006804, -703.3564533406019, 0, 1], 2, 5, [-51.274354719855246, 28.573003242461496], 0, [-13.931531095689206, 15.255196629685734], 3.7897716494829594, [-8.365342666825299, 14.77338745895928], 0, [5.4409985411529185, 25.415028100834675], 0.37399715496699776, [29.033295173881022, 23.38858533792915], 0, false]] + + f.Read(); + + let pl = f.ReadObject() as Polyline; + + let cus = pl.GetOffsetCurves(-35.6); + + EntityToMatchSnapshot(cus); +}); + + +test('拱门偏移', () => +{ + let f = new CADFile(); + + f.Data = + [1, ["Polyline", 1, 1, 6, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 4, [0, 0], 0, [0, 2], -1, [1, 2], 0, [1, 0], 0, false]] + + f.Read(); + + let pl = f.ReadObject() as Polyline; + + let cus = pl.GetOffsetCurves(0.4); + EntityToMatchSnapshot(cus); + + cus = pl.GetOffsetCurves(-0.4); + EntityToMatchSnapshot(cus); +}); + +test('土偏移因为点在圆弧切线上错误导致的丢失', () => +{ + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 3, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -9159.663865546216, 8361.344537815126, 0, 1], 2, 17, [28900.928792569663, -4648.336577340973], -0.9999999999999999, [28900.928792569663, 580.3540867812549], 0, [37687.5122530782, 580.3540867812552], 0, [37687.5122530782, 5568.3816889128875], 0, [27851.140930176796, 5568.381688912888], 0, [31251.043800494088, 10146.917554273516], 0, [39591.53702088006, 10146.917554273514], 0, [39591.53702088006, 17633.191417258837], -0.9999999999999999, [45238.18434134903, 17633.191417258837], 0, [45238.184341349035, 10146.917554273516], 0, [54628.482972136226, 10146.917554273516], -0.8741669928857767, [55271.13672581777, 5382.623174980927], 0, [45331.063598314984, 5382.623174981001], 0, [45331.063598314984, 1648.4655418896164], 0, [53885.44891640867, 1648.4655418896143], -0.6803347640614656, [56371.20555398866, -4648.336577340822], 0, [28900.928792569663, -4648.336577340976], 0, false]] + + f.Read(); + + let pl = f.ReadObject() as Polyline; + let cus = pl.GetOffsetCurves(-801); + + expect(cus.length).toBe(1); + cus = pl.GetOffsetCurves(801); + expect(cus.length).toBe(1); +}); + +//ISSUE #IKSMH +test('闭合多段线判断精度和重复交点参数导致偏移丢失', () => +{ + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 3, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.042016806722689815, 0.028011204481792618, 0, 1], 2, 11, [15758.920914649752, -1642.832074394305], 1.1432358165627696, [15758.920914649756, -3551.5797508523133], 0.010240353549940432, [20850.68748853413, -2761.478041111637], 0.39873042440013945, [23564.17781148077, 901.7338948663096], 0.029927117999965277, [22452.27083112051, 6973.8467189556395], 0, [31778.947378692996, 6973.846718955635], 0, [31327.636369627562, 7575.594679533537], 0, [31646.066893308867, 7787.881806674935], 0, [20955.832135358123, 7787.881806674936], 0, [21703.795934364476, 5637.485884531684], -0.559029831351755, [15758.920914649752, -1642.832074394304], 0, false]] + + f.Read(); + let pl = f.ReadObject() as Polyline; + + for (let d of [40, 50, 100, 200, 300]) + { + let cus = pl.GetOffsetCurves(d); + + expect(cus.length).toBe(1); + expect(cus[0].Length).toMatchSnapshot(); + } +}); +//ISSUE #IKSMH +test('简单图形因为点在线内算法错误导致的丢失', () => +{ + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 3, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.01400560224089542, 0.37815126050420145, 0, 1], 2, 7, [2.075848303393214, 0.09193367302761479], -3.109533447960064, [2.8332320619106284, 1.1077844311377247], 0, [3.542914171656688, 1.1077844311377247], 0, [3.542914171656688, -0.4890219560878247], 0, [2.0758483033932134, -0.4890219560878247], 0, [2.0758483033932134, -0.4890219560878247], 0, [2.0758483033932134, 0.09193367302761501], 0, false]] + + f.Read(); + let pl = f.ReadObject() as Polyline; + + for (let d of [0.2, 0.3, 0.4, 0.5]) + { + let cus = pl.GetOffsetCurves(d); + + expect(cus.length).toBe(1); + expect(cus[0].Length).toMatchSnapshot(); + } +}); + +test('圆求交错误导致的线丢失', () => +{ + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 1077, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 8, [245.62964413684085, 1116.1391538309313], -0.6953179171905909, [357.14209871457234, 1741.1251588427203], -0.6821635222887411, [892.2042303620135, 1596.040230558597], 0.37526047823909664, [1317.3934365017694, 1221.2756098839004], 0.14384173346393453, [1715.0964405621776, 1096.7424042631346], 0.026448384140957723, [1793.2064027435729, 1099.4889679050386], -0.1912121824403452, [733.4339688924879, 1000.3133228797258], -0.3304377553967397, [245.62964413684108, 1116.139153830931], 0, false]] + + f.Read(); + let pl = f.ReadObject() as Polyline; + + for (let d of [10, 20, 30, 40]) + { + let cus = pl.GetOffsetCurves(d); + + expect(cus.length).toBe(1); + expect(cus[0].Length).toMatchSnapshot(); + + cus = pl.GetOffsetCurves(-d); + expect(cus.length).toBe(1); + expect(cus[0].Length).toMatchSnapshot(); + } +}); diff --git a/__test__/Polyline/polyline.test.ts b/__test__/Polyline/polyline.test.ts index 3c9225e3d..1df42fa19 100644 --- a/__test__/Polyline/polyline.test.ts +++ b/__test__/Polyline/polyline.test.ts @@ -470,6 +470,48 @@ describe('多段线', () => pl.Reverse(); expect(pl.StartPoint).toEqual(new Vector3(0, 10, 0)) }) + + //直线和圆的求交的det精度不能为0.1 太大了.请保证这个测试是通过的 + test('最近点精度', () => + { + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 3, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 5, [0, 0], 0, [2, 0], 1, [2, 2], -1, [2, 4], 0, [0, 4], 0, true]] + f.Read(); + let pl = f.ReadObject() as Polyline; + pl.Reverse(); + + + let cp = pl.GetClosestPointTo(new Vector3(2 - 1e-2, 3.8), false); + + expect(cp).toMatchSnapshot(); + }); + + test('优化参数在端点上的时候', () => + { + let pl = new Polyline(); + pl.AddVertexAt(0, new Vector2(0, 0)); + pl.AddVertexAt(1, new Vector2(5, 0)); + pl.AddVertexAt(2, new Vector2(5, 5)); + pl.CloseMark = true; + + let p = pl.GetPointAtParam(3); + expect(p).toMatchSnapshot(); + }); + + test('存在大圆弧的多段线面积', () => + { + let f = new CADFile(); + f.Data = + [1, ["Polyline", 1, 1, 1606, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -19.17745164184445, -6.335408131680751, 0, 1], 2, 5, [11.884352072596577, -75.64730611519322], -1.9523252757782086, [16.25360183965801, -70.58112080246555], 0, [14.621427950114992, -70.9720172143345], 1.8583004340984604, [11.834326201222096, -73.95002751582643], 0, [11.884352072596577, -75.64730611519322], 0, false]] + + f.Read(); + + let pl = f.ReadObject() as Polyline; + + expect(pl.Area2).toMatchSnapshot(); + }); + }) test('最近点参数刚好在端点上', () => diff --git a/package.json b/package.json index 218e20fb3..7331db162 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,9 @@ "dev": "webpack-dev-server", "dev2": "webpack-dev-server --config web-cad-view.config.ts", "dts": "tcm src -o ./src/UI/css_dts", - "build": "npm run type && webpack --config dll.config.js &&webpack", - "build2": "webpack --config dll.config.js &&webpack --config web-cad-view.config.ts", + "buildview": "webpack --config dll.config.js &&webpack --config web-cad-view.config.ts", + "prebuild": "npm run type && webpack --config dll.config.js", + "build": "webpack", "i": "npm i && npm i -dev", "test": "jest", "ser": "node ./utils/server.js", diff --git a/src/Add-on/CopyPoint.ts b/src/Add-on/CopyPoint.ts new file mode 100644 index 000000000..63e2abfa9 --- /dev/null +++ b/src/Add-on/CopyPoint.ts @@ -0,0 +1,14 @@ +import { app } from "../ApplicationServices/Application"; +import { copyTextToClipboard } from "../Common/Utils"; +import { PromptStatus } from "../Editor/PromptResult"; + +export class Command_CopyPoint +{ + async exec() + { + let ptRes = await app.m_Editor.GetPoint(); + + if (ptRes.Status === PromptStatus.OK) + copyTextToClipboard(`new Vector3().fromArray(${JSON.stringify(ptRes.Value.toArray())});`); + } +} diff --git a/src/Add-on/DrawPolyline.ts b/src/Add-on/DrawPolyline.ts index b683b21f6..b531e55cb 100644 --- a/src/Add-on/DrawPolyline.ts +++ b/src/Add-on/DrawPolyline.ts @@ -42,7 +42,7 @@ export class DrawPolyline { ops = nextOps; ops.BasePoint = pl.EndPoint; - if (pl.NumberOfVertices > 3) + if (pl.NumberOfVertices >= 3 || (pl.NumberOfVertices === 2 && this.model === PolylineModel.Arc)) nextOps.KeyWordList = keywords2; else nextOps.KeyWordList = keywords; diff --git a/src/Add-on/Entsel.ts b/src/Add-on/Entsel.ts index 2cf49a34a..51430cfcd 100644 --- a/src/Add-on/Entsel.ts +++ b/src/Add-on/Entsel.ts @@ -5,6 +5,7 @@ import { Command } from '../Editor/CommandMachine'; import { PromptStatus } from '../Editor/PromptResult'; import { DynamicInputManage } from '../UI/DynamicPrompt/DynamicInputManage'; import { PromptBlock } from '../UI/DynamicPrompt/PromptBlock'; +import { Polyline } from '../DatabaseServices/Polyline'; //演示了如何单选图形 @@ -23,7 +24,8 @@ export class Entsel implements Command vcs.add(new Vector3(0, -20, 0)); dyn.SetPostion(vcs); - dyn.updatePrompt(res.Entity.constructor.name + ",id:" + res.Entity.Id.Index); + if (res.Entity instanceof Polyline) + dyn.updatePrompt(res.Entity.constructor.name + ",area:" + res.Entity.Area2); } else dyn.Visible = false; diff --git a/src/Add-on/Fillet.ts b/src/Add-on/Fillet.ts index 4d0942a4f..d95a7d513 100644 --- a/src/Add-on/Fillet.ts +++ b/src/Add-on/Fillet.ts @@ -1,6 +1,6 @@ import { Vector3 } from 'three'; - import { app } from '../ApplicationServices/Application'; +import { KeyWord } from '../Common/InputState'; import { KeyBoard } from '../Common/KeyEnum'; import { Arc } from '../DatabaseServices/Arc'; import { Curve } from '../DatabaseServices/Curve'; @@ -182,12 +182,12 @@ export class CommandFillet implements Command /** * 指定用户选择曲线 * - * @param {any} keyword 关键字 + * @param {KeyWord[]} keyword 关键字 * @param {Curve} [oldCurve] 第一次选择的曲线 * @returns {Promise} * @memberof CommandFillet */ - async SelectCurve(keyword, oldCurve?: Curve): Promise + async SelectCurve(keyword: KeyWord[], oldCurve?: Curve): Promise { while (true) { diff --git a/src/Add-on/Lisp.ts b/src/Add-on/Lisp.ts new file mode 100644 index 000000000..3564f81e1 --- /dev/null +++ b/src/Add-on/Lisp.ts @@ -0,0 +1,45 @@ +import { app } from "../ApplicationServices/Application"; +import { copyTextToClipboard } from "../Common/Utils"; +import { Polyline } from "../DatabaseServices/Polyline"; +import { PromptStatus } from "../Editor/PromptResult"; + +export class Command_Lisp +{ + async exec() + { + let ssRes = await app.m_Editor.GetEntity(); + if (ssRes.Status != PromptStatus.OK) return; + + let pl = ssRes.Entity as Polyline; + if (!pl) return; + + let { pts, buls } = pl.PtsBuls; + + let fun = ` +(defun cx-make-pline (pts buls closeMark) + (entmakeX + (append + (list + '(0 . "LWPOLYLINE") + '(100 . "AcDbEntity") + '(100 . "AcDbPolyline") + (cons 90 (length pts)) + (cons 70 closeMark) + ) + (apply 'append(mapcar '(lambda (pt bul) (list (cons 10 pt) (cons 42 bul))) pts buls)) + ) + ) +) + ` + + let str = fun + "(cx-make-pline '("; + pts.forEach(p => str += `(${p.x} ${p.y})`) + str += ") '("; + buls.forEach(b => str += `${b} `); + str += `)`; + str += pl.CloseMark ? "1" : "0"; + str += ")"; + + copyTextToClipboard(str); + } +} diff --git a/src/Add-on/Move.ts b/src/Add-on/Move.ts index ae614c697..b540e1543 100644 --- a/src/Add-on/Move.ts +++ b/src/Add-on/Move.ts @@ -25,13 +25,11 @@ export class Command_Move implements Command Msg: "请点击移动终点:", Callback: (p: THREE.Vector3) => { - let moveMat = new THREE.Matrix4(); let moveVec = p.clone().sub(pt1); - moveMat.makeTranslation(moveVec.x, moveVec.y, moveVec.z); - + let moveMatrix = MoveMatrix(moveVec); ens.forEach(e => { - e.ApplyMatrix(moveMat); + e.ApplyMatrix(moveMatrix); }) pt1 = p.clone(); }, diff --git a/src/Add-on/Offset.ts b/src/Add-on/Offset.ts index 3c1e08021..f2b67000f 100644 --- a/src/Add-on/Offset.ts +++ b/src/Add-on/Offset.ts @@ -1,26 +1,31 @@ import { app } from '../ApplicationServices/Application'; +import { GetPointAtCurveDir } from '../Common/CurveUtils'; import { Curve } from '../DatabaseServices/Curve'; import { Command } from '../Editor/CommandMachine'; import { PromptStatus } from '../Editor/PromptResult'; -import { Polyline } from '../DatabaseServices/Polyline'; -import { IsPointInPolyLine } from '../DatabaseServices/PointInPolyline'; export class Command_Offset implements Command { + offsetDis: number = 1; async exec() { let dis = await app.m_Editor.GetDistance({ Msg: "指定偏移距离:", - KeyWordList: [{ msg: "通过", key: "T" }] + KeyWordList: [{ msg: "通过", key: "T" }], + Default: this.offsetDis }); if (dis.Status != PromptStatus.OK) return; + this.offsetDis = dis.Value; + while (true) { let enRes = await app.m_Editor.GetEntity({ Msg: "选择要偏移的对象:" }); - if (enRes.Status != PromptStatus.OK) + if (enRes.Status === PromptStatus.Cancel) break; + else if (enRes.Status !== PromptStatus.OK) + continue; let pt = await app.m_Editor.GetPoint({ Msg: "指定要偏移的那一侧的点" }); @@ -29,17 +34,8 @@ export class Command_Offset implements Command if (cu instanceof Curve) { - let ptClose = cu.GetClosestPointTo(pt.Value, false); - let toPtVec = pt.Value.clone().sub(ptClose); //点击处向量 - let d = cu.GetFistDeriv(cu.GetParamAtPoint(ptClose));//切线。 - - let c = toPtVec.cross(d); - if (cu instanceof Polyline && cu.EndParam > 1 && cu.Area > 0) - { - c.z = (IsPointInPolyLine(cu, pt.Value) ? -1 : 1) * Math.sign(cu.Area2); - } - - let offCurs = cu.GetOffsetCurves(dis.Value * Math.sign(c.z)); + let dir = GetPointAtCurveDir(cu, pt.Value) ? 1 : -1 + let offCurs = cu.GetOffsetCurves(this.offsetDis * dir); offCurs.forEach((offCur) => { app.m_Database.ModelSpace.Append(offCur); @@ -63,7 +59,11 @@ export class Command_TestOffset implements Command Msg: "选择要偏移的对象:" }); if (enRes.Status != PromptStatus.OK) + { + //结束偏移后打开点捕捉 + app.m_Editor.m_GetpointServices.snapServices.m_Disabled = false; break; + } let cu = enRes.Entity as Curve; let lastpls: Curve[]; @@ -73,37 +73,14 @@ export class Command_TestOffset implements Command Callback: (p) => { if (lastpls) lastpls.forEach(cu => cu.Erase()); - - let dist = p.distanceTo(cu.GetClosestPointTo(p, !cu.IsClose)); - - if (cu instanceof Polyline && cu.EndParam > 1 && cu.Area > 0) - { - let pls1 = cu.GetOffsetCurves(dist); - let pls2 = cu.GetOffsetCurves(-dist); - - let dis1 = Math.min.apply(undefined, pls1.map(c => p.distanceTo(c.GetClosestPointTo(p, false)))); - let dis2 = Math.min.apply(undefined, pls2.map(c => p.distanceTo(c.GetClosestPointTo(p, false)))); - - if (dis1 < dis2) - lastpls = pls1; - else - lastpls = pls2; - } - else - { - let ptClose = cu.GetClosestPointTo(p, false); - let toPtVec = p.clone().sub(ptClose); //点击处向量 - let d = cu.GetFistDeriv(cu.GetParamAtPoint(ptClose));//切线。 - let c = toPtVec.cross(d); - lastpls = cu.GetOffsetCurves(p.distanceTo(cu.GetClosestPointTo(p, !cu.IsClose)) * Math.sign(c.z)); - } + let dir = GetPointAtCurveDir(cu, p) ? 1 : -1 + lastpls = cu.GetOffsetCurves(p.distanceTo(cu.GetClosestPointTo(p, false)) * dir); lastpls.forEach((offCur) => { app.m_Database.ModelSpace.Append(offCur); }) app.m_Viewer.m_bNeedUpdate = true; - } }); } diff --git a/src/Add-on/OffsetX.ts b/src/Add-on/OffsetX.ts new file mode 100644 index 000000000..3254461af --- /dev/null +++ b/src/Add-on/OffsetX.ts @@ -0,0 +1,69 @@ +import { app } from "../ApplicationServices/Application"; +import { Polyline } from "../DatabaseServices/Polyline"; +import { Command } from "../Editor/CommandMachine"; +import { PromptStatus } from "../Editor/PromptResult"; +import { Vector3 } from "three"; + +//无限内偏移 +export class OffsetX implements Command +{ + offsetDis: number = 1; + async exec() + { + let ssRes = await app.m_Editor.GetEntity(); + if (ssRes.Status != PromptStatus.OK) return; + let pl = ssRes.Entity as Polyline; + if (!pl) return; + + let dis: number, step: number; + + if (window["autoDis"]) + { + this.offsetDis = 1.2 * Math.max(...pl.BoundingBox.getSize(new Vector3()).toArray()); + dis = -this.offsetDis; + step = this.offsetDis / 20; + } + else + { + let disRes = await app.m_Editor.GetDistance({ + Msg: "指定偏移距离:", + KeyWordList: [{ msg: "通过", key: "T" }], + Default: this.offsetDis + }); + + if (disRes.Status != PromptStatus.OK) return; + this.offsetDis = disRes.Value; + dis = -this.offsetDis; + if (pl.IsClockWise) dis = -dis; + + let disRes1 = await app.m_Editor.GetDistance({ + Msg: "步长:", + KeyWordList: [{ msg: "通过", key: "T" }], + Default: 1 + }); + + if (disRes1.Status != PromptStatus.OK) return; + step = disRes1.Value; + } + + // let step = 0.5; + let offRes: Polyline[] = []; + // offRes.push(...pl.GetOffsetCurves(dis) as Polyline[]); + + // for (let i = 0; i < offRes.length; i++) + // { + // let offpl = offRes[i]; + // if (offpl.IsClose) + // { + // offRes.push(...offpl.GetOffsetCurves(dis) as Polyline[]); + // } + // } + for (let i = 0; i <= Math.abs(dis); i += step) + { + offRes.push(...pl.GetOffsetCurves(i) as Polyline[]); + offRes.push(...pl.GetOffsetCurves(-i) as Polyline[]); + } + + offRes.forEach(c => app.m_Database.ModelSpace.Append(c)); + } +} diff --git a/src/Add-on/Open.ts b/src/Add-on/Open.ts index 4b096fc27..32c8add4a 100644 --- a/src/Add-on/Open.ts +++ b/src/Add-on/Open.ts @@ -9,15 +9,18 @@ export class Open implements Command { let store = await IndexedDbStore.CADStore(); - let file = await store.Get(StoreName.Dwg, "1"); - - if (file) + let fid = window.sessionStorage.getItem("fid"); + if (fid) { - let cadF = new CADFile(); - cadF.Data = file; + let file = await store.Get(StoreName.Dwg, fid); - app.m_Database.FileRead(cadF); - } + if (file) + { + let cadF = new CADFile(); + cadF.Data = file; + app.m_Database.FileRead(cadF); + } + } } -} \ No newline at end of file +} diff --git a/src/Add-on/closetest.ts b/src/Add-on/closetest.ts index e67c11fcd..7c36cae95 100644 --- a/src/Add-on/closetest.ts +++ b/src/Add-on/closetest.ts @@ -1,6 +1,6 @@ import { Vector3 } from 'three'; - import { app } from '../ApplicationServices/Application'; +import { GetPointAtCurveDir } from '../Common/CurveUtils'; import { Arc } from '../DatabaseServices/Arc'; import { Circle } from '../DatabaseServices/Circle'; import { Curve } from '../DatabaseServices/Curve'; @@ -9,12 +9,15 @@ import { IsPointInBowArc, IsPointInPolyLine } from '../DatabaseServices/PointInP import { Polyline } from '../DatabaseServices/Polyline'; import { Command } from '../Editor/CommandMachine'; import { PromptStatus } from '../Editor/PromptResult'; +import { PromptBlock } from '../UI/DynamicPrompt/PromptBlock'; +import { DynamicInputManage } from '../UI/DynamicPrompt/DynamicInputManage'; export class Command_ClosePt implements Command { async exec() { + let dyn = new PromptBlock(DynamicInputManage.GetManage()); let cuRes = await app.m_Editor.GetEntity(); @@ -55,8 +58,14 @@ export class Command_ClosePt implements Command if (cu instanceof Polyline) { + dyn.Visible = true; + let vcs = app.m_Editor.m_MouseCtrl.m_CurMousePointVCS.clone(); + vcs.add(new Vector3(0, -20, 0)); + dyn.SetPostion(vcs); + + dyn.updatePrompt("点在线内外?:" + IsPointInPolyLine(cu, p)); closeCir.Center = p; - closeCir.ColorIndex = IsPointInPolyLine(cu, p) ? 1 : 2; + closeCir.ColorIndex = GetPointAtCurveDir(cu, p) ? 1 : 2; } } }) @@ -65,9 +74,10 @@ export class Command_ClosePt implements Command extend = !extend; } if (p.Status === PromptStatus.Cancel) - return; + break; } } + dyn.Destroy(); } } diff --git a/src/Add-on/test.ts b/src/Add-on/test.ts index 5dbfcf5a6..42b825c1e 100644 --- a/src/Add-on/test.ts +++ b/src/Add-on/test.ts @@ -1,10 +1,15 @@ +import { Vector3 } from 'three'; import { app } from '../ApplicationServices/Application'; +import { GetPointAtCurveDir } from '../Common/CurveUtils'; import { matrixAlignCoordSys } from '../Common/Matrix4Utils'; +import { copyTextToClipboard } from '../Common/Utils'; +import { Circle } from '../DatabaseServices/Circle'; import { Curve } from '../DatabaseServices/Curve'; +import { Polyline } from '../DatabaseServices/Polyline'; import { Region } from '../DatabaseServices/Region'; import { Command } from "../Editor/CommandMachine"; import { PromptStatus } from "../Editor/PromptResult"; -import { Polyline } from '../DatabaseServices/Polyline'; +import { IntersectOption } from '../GraphicsSystem/IntersectWith'; @@ -14,23 +19,74 @@ export class Test implements Command { app.m_Editor.m_CommandStore.Prompt("载入成功!"); } - async exec() + + async exec1() { - let ssRes = await app.m_Editor.GetSelection({ UseSelect: true }); + let e1Res = await app.m_Editor.GetEntity(); + let e2Res = await app.m_Editor.GetEntity(); + + let e1 = e1Res.Entity as Curve; + let e2 = e2Res.Entity as Curve; - if (ssRes.Status === PromptStatus.OK) + let pts = e1.IntersectWith(e2, IntersectOption.OnBothOperands); + for (let p of pts) { - for (let en of ssRes.SelectSet.SelectEntityList) - { - if (en instanceof Curve) - { - if (en.Length < 0.1) - { - console.log("....."); - } - } - } + let c = new Circle(p, 0.01); + app.m_Database.ModelSpace.Append(c); } + + } + //将多段线转换为cad图形 + async exec4() + { + // let d = [["Polyline", 1, 1, 12839, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 24, [256.5228611988947, -38.705370748164505], 0, [372.1955588024039, 31.844231195771492], 0, [372.1955588024039, -31.54297461172431], 0, [450.9819163032461, 37.57414810492366], 0, [423.0485713711294, -54.10452244151095], 0, [589.216161736542, 52.97329979827009], 0, [611.0614699526845, -2.177150452319474], 0, [672.6580767260702, 61.21005535517633], 0, [678.0298738284004, 13.938240854670997], 0, [735.329042919922, 21.100636991111184], 0, [684.1179105443746, -21.515620020708024], 0, [730.6734854312358, -32.61733403219032], 0, [815.1897598412304, 38.29038771856768], 0, [835.6025888300849, -8.981426781937659], 0, [862.1034545349137, 74.4604882075907], 0, [943.4539498526012, 74.46048820759069], 0, [986.972668777612, -29.064785548515978], 0, [924.5022496755805, -66.26627108118637], 0, [1001.0109652050348, -106.27541589934134], 0, [965.9152241364777, -128.73669018321777], 0, [1006.6262837760039, -176.46689803645526], 0, [904.1467198558175, -189.8032796425069], 0, [981.3573502066429, -242.44689124534239], 0, [1004.5205393118904, -256.48518767276516], 0, false]] + + // let cadf = new CADFile(); + // cadf.Data = d; + // let en = cadf.ReadObject() as Entity; + // if (en) + // { + // app.m_Database.ModelSpace.Append(en); + // return; + // } + + // let d = [["Polyline", 1, 1, 8987, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2, 14, [340.820007357324, -3.870032999900914], 0, [431.6163856609081, 116.2815653567217], 0, [485.54806901416487, 54.84040710617603], 0, [533.3356365423672, 67.81131829240235], 0, [624.1320148459513, 40.504136847715344], 0, [656.9006325795757, 23.43714844478601], -0.783383849320176, [633.0068488154745, -74.86870475608706], 0.4462700081240285, [605.0169878346704, -106.27196341747704], -0.8416099256631104, [529.9222388617812, -128.11770857322662], 0, [510.1245323143834, -22.98506001118185], 0, [430.251026588674, -129.48306764546098], 0, [430.251026588674, -65.99387078656383], 0, [351.74287993519897, -145.86737651227318], 0, [303.2726328708796, -87.83961594231337], 0, true]]; + // let cadf = new CADFile(); + // cadf.Data = d; + // let en = cadf.ReadObject() as Entity; + // if (en) + // { + // app.m_Database.ModelSpace.Append(en); + // return; + // } + + } + async execn() + { + let ssRes = await app.m_Editor.GetEntity(); + if (ssRes.Status != PromptStatus.OK) return; + let ptRes = await app.m_Editor.GetPoint(); + if (ptRes.Status != PromptStatus.OK) return; + + let cu = ssRes.Entity as Polyline; + let p = ptRes.Value; + + console.log(cu.PtOnCurve(p)); + console.log(cu.PtInCurve(p)) + + // if (ssRes.Status === PromptStatus.OK) + // { + // for (let en of ssRes.SelectSet.SelectEntityList) + // { + // if (en instanceof Curve) + // { + // if (en.Length < 0.1) + // { + // console.log("....."); + // } + // } + // } + // } } async exec3() @@ -70,4 +126,42 @@ export class Test implements Command r1.ShapeManager.ShapeList.forEach(s => s.Explode().forEach(c => app.m_Database.ModelSpace.Append(c))); r2.ShapeManager.ShapeList.forEach(s => s.Explode().forEach(c => app.m_Database.ModelSpace.Append(c))); } + + async exec() + { + let e = await app.m_Editor.GetEntity(); + if (e.Status === PromptStatus.OK) + { + let box = e.Entity.BoundingBox; + + if (e.Entity instanceof Polyline) + { + console.log(GetPointAtCurveDir(e.Entity, new Vector3(5, 5.5))); + this.drawPtDirTestCir(e.Entity); + } + return; + } + } + private drawPtDirTestCir(pl: Polyline) + { + let box = pl.BoundingBox; + let size = box.getSize(new Vector3()); + box.min.sub(size.clone().multiplyScalar(0.2)); + box.max.add(size.clone().multiplyScalar(0.2)); + size.multiplyScalar(0.05); + app.m_Database.ModelSpace.Append(pl); + for (let j = 0; j < 14 * 2; j++) + { + let p = box.min.clone(); + p.x += j * size.x; + for (let k = 0; k < 14 * 2; k++) + { + p.y += size.y; + let c = new Circle(p, 0.1); + c.ColorIndex = GetPointAtCurveDir(pl, p) ? 3 : 1; + app.m_Database.ModelSpace.Append(c); + } + } + } + } diff --git a/src/Add-on/test/testIntersect.ts b/src/Add-on/test/testIntersect.ts index a656d6187..7093d40ea 100644 --- a/src/Add-on/test/testIntersect.ts +++ b/src/Add-on/test/testIntersect.ts @@ -1,11 +1,11 @@ +import { Line } from "three"; import { app } from "../../ApplicationServices/Application"; +import { Arc } from "../../DatabaseServices/Arc"; import { Circle } from "../../DatabaseServices/Circle"; import { Curve } from "../../DatabaseServices/Curve"; import { Command } from "../../Editor/CommandMachine"; import { PromptStatus } from "../../Editor/PromptResult"; -import { IntersectLAndLFor3D, IntersectOption, IntersectCircleAndCircle } from "../../GraphicsSystem/IntersectWith"; -import { Line } from "three"; -import { Arc } from "../../DatabaseServices/Arc"; +import { IntersectCircleAndCircle, IntersectLAndLFor3D, IntersectOption } from "../../GraphicsSystem/IntersectWith"; export class TestIntersect implements Command { @@ -15,6 +15,7 @@ export class TestIntersect implements Command // this.testLineAndLine(); // this.testLineAndCirOrArc(); // this.testCirAndCir() + this.testInter(); app.m_Editor.UpdateScreen(); } @@ -113,4 +114,24 @@ export class TestIntersect implements Command } } } + async testInter() + { + let exRefSsRes = await app.m_Editor.GetSelection({ Msg: "请选择对象<全部选择>:", UseSelect: true }); + if (exRefSsRes.Status !== PromptStatus.OK) return; + let cus = exRefSsRes.SelectSet.SelectEntityList as Curve[]; + if (cus.length === 2) + { + let pt = cus[0].IntersectWith(cus[1], 3); + console.log('pt: ', pt); + if (pt.length) + { + pt.forEach(p => + { + let cir = new Circle(p, 10); + cir.ColorIndex = 2; + app.m_Database.ModelSpace.Append(cir); + }) + } + } + } } diff --git a/src/Add-on/testEntity/TestBoolOperation.ts b/src/Add-on/testEntity/TestBoolOperation.ts deleted file mode 100644 index a33610ef3..000000000 --- a/src/Add-on/testEntity/TestBoolOperation.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { app } from "../../ApplicationServices/Application"; -import { Circle } from "../../DatabaseServices/Circle"; -import { Curve } from "../../DatabaseServices/Curve"; -import { Polyline } from "../../DatabaseServices/Polyline"; -import { Command } from "../../Editor/CommandMachine"; -import { PromptStatus } from "../../Editor/PromptResult"; -import { isTargetCurInSourceCur } from "../../GraphicsSystem/BoolOperateUtils"; - -export class TestBoolOperationUtil implements Command -{ - async exec() - { - let ssRes = await app.m_Editor.GetSelection( - { - Msg: "选择曲线" - }, - ); - let source = ssRes.SelectSet.SelectEntityList[0] as Polyline | Circle; - ssRes = await app.m_Editor.GetSelection( - { - Msg: "选择目标曲线" - }, - ); - if (ssRes.Status === PromptStatus.OK) - { - let target = ssRes.SelectSet.SelectEntityList as Curve[]; - target.forEach(l => - { - if (!isTargetCurInSourceCur(source, l)) - { - l.ColorIndex = 1; - } else - l.ColorIndex = 2; - }) - } - } -} diff --git a/src/Add-on/testEntity/TestCurve.ts b/src/Add-on/testEntity/TestCurve.ts new file mode 100644 index 000000000..65657498e --- /dev/null +++ b/src/Add-on/testEntity/TestCurve.ts @@ -0,0 +1,130 @@ +import { Vector3 } from "three"; +import { app } from "../../ApplicationServices/Application"; +import { arrayLast, arrayRemoveIf, arraySortByNumber } from "../../Common/ArrayExt"; +import { Circle } from "../../DatabaseServices/Circle"; +import { Contour } from "../../DatabaseServices/Contour"; +import { Curve } from "../../DatabaseServices/Curve"; +import { Polyline } from "../../DatabaseServices/Polyline"; +import { Command } from "../../Editor/CommandMachine"; +import { PromptStatus } from "../../Editor/PromptResult"; +import { equaln } from "../../Geometry/GeUtils"; +import { IsPtsAllOutOrOnReg } from "../../GraphicsSystem/BoolOperateUtils"; + +export class TestTargeOnCurve implements Command +{ + async exec() + { + try + { + let ssRes = await app.m_Editor.GetSelection( + { + Msg: "选择曲线" + }, + ); + let source = ssRes.SelectSet.SelectEntityList[0] as Polyline | Circle; + ssRes = await app.m_Editor.GetSelection( + { + Msg: "选择目标曲线" + }, + ); + + if (ssRes.Status === PromptStatus.OK && source.Length) + { + let target = ssRes.SelectSet.SelectEntityList[0] as Curve; + + // let con1 = Contour.CreateContour([source]); + // let con2 = Contour.CreateContour([target]); + // let res = con1.getOperatedCurves(con2); + // testCurve(res.unionList, 1); + // testCurve(res.intersectionList, 2); + // testCurve(res.subtractList, 3); + // this.testClip(source as Polyline, target).forEach((c, i) => + // { + // c.ColorIndex = i + 1; + // app.m_Database.ModelSpace.Append(c); + // }) + this.testPtIn(source as Polyline, target); + // target.Erase(); + // source.Erase(); + + } + } catch (err) + { + console.log(err); + } + } + testClip(outline: Polyline, l: Curve) + { + let tmpCus = []; + //交点参数列表 + let iParams = l.IntersectWith(outline, 0) + .map(p => l.GetParamAtPoint(p)); + arraySortByNumber(iParams); + + //需要计算的点列表 + let needCaclPts: Vector3[] = []; + for (let i = 0; i < iParams.length - 1; i++) + { + needCaclPts.push(l.GetPointAtParam((iParams[i] + iParams[i + 1]) / 2)); + } + //如果交点不是首尾点,就加入首尾点 + if (!equaln(iParams[0], 0, 1e-6)) + needCaclPts.unshift(l.StartPoint); + if (!equaln(arrayLast(iParams), 1, 1e-6)) + needCaclPts.push(l.EndPoint); + + //切割曲线,缓存切割后曲线包围盒 + if (IsPtsAllOutOrOnReg(outline, needCaclPts)) + { + tmpCus.push(l); + } + else + { + let cus = l.GetSplitCurves(iParams); + + //移除0长度线和在轮廓内的线. + arrayRemoveIf(cus, cu => equaln(cu.Length, 0, 1e-6) + || outline.PtInCurve(cu.GetPointAtParam(0.5)) + ); + tmpCus.push(...cus); + } + return tmpCus; + } + testPtIn(outline: Polyline, l: Curve) + { + + if (outline.PtInCurve(l.EndPoint)) + { + l.ColorIndex = 3 + } else + l.ColorIndex = 1; + + } +} + +export function testPts(pts: Vector3[], color = 2) +{ + pts.forEach(p => + { + let cir = new Circle(p, 0.5); + cir.ColorIndex = color; + app.m_Database.ModelSpace.Append(cir); + }); +} +export function testContours(cons: Contour[], color = 2) +{ + cons.forEach(c => + { + c.Outline.ColorIndex = color; + app.m_Database.ModelSpace.Append(c.Outline); + }); +} +export function testCurve(cons: Curve[], color: number = 2) +{ + cons.forEach(c => + { + c.ColorIndex = color; + app.m_Database.ModelSpace.Append(c); + }); +} + diff --git a/src/Common/ArrayExt.ts b/src/Common/ArrayExt.ts index ea61f44c0..937b16946 100644 --- a/src/Common/ArrayExt.ts +++ b/src/Common/ArrayExt.ts @@ -87,18 +87,13 @@ export function arraySortByNumber(arr: Array): Array * @param {(e1, e2) => boolean} [checkFuction] 校验对象相等函数 * @returns {Array} 返回自身 */ - -export function arrayRemoveDuplicateBySort(arr: Array, checkFuction?: (e1, e2) => boolean): Array +export function arrayRemoveDuplicateBySort(arr: Array, checkFuction: (e1, e2) => boolean = checkEqual): Array { - checkFuction = checkFuction || checkEqual; + if (arr.length < 2) return arr; let j = 1; for (let i = 1, l = arr.length; i < l; i++) - { if (!checkFuction(arr[i], arr[j - 1])) - { arr[j++] = arr[i]; - } - } arr.length = j; return arr; } diff --git a/src/Common/CurveUtils.ts b/src/Common/CurveUtils.ts index 7534ead2b..f394ef1c3 100644 --- a/src/Common/CurveUtils.ts +++ b/src/Common/CurveUtils.ts @@ -3,11 +3,13 @@ import { Arc } from '../DatabaseServices/Arc'; import { Circle } from '../DatabaseServices/Circle'; import { Curve } from '../DatabaseServices/Curve'; import { Line } from '../DatabaseServices/Line'; +import { IsPointInBowArc } from '../DatabaseServices/PointInPolyline'; import { Polyline } from '../DatabaseServices/Polyline'; import { Count } from '../Geometry/Count'; import { CurveMap } from '../Geometry/CurveMap'; import { equal, equaln } from '../Geometry/GeUtils'; import { Stand } from '../Geometry/RegionParse'; +import { FixIndex } from './Utils'; //3点获取圆心 export function getCircleCenter(pt1: Vector3, pt2: Vector3, pt3: Vector3) @@ -103,10 +105,9 @@ export function curveLinkGroup(cus: Curve[]): Array> let isClose = c.IsClose; if (isClose) groupCus.push([c]); - return !isClose; }); - + if (cus.length === 0) return groupCus; //曲线节点图 let cuMap = new CurveMap(); cus.forEach(c => cuMap.addCurveToMap(c)); @@ -211,3 +212,138 @@ export function equalCurveAndCurve(cu1: Curve, cu2: Curve) } return false; } + +/** +* 计算点在曲线前进方向的方位,左边或者右边 +* +* @param {Curve} cu +* @param {Vector3} pt +* @returns {boolean} 左边为false,右边为true +*/ +export function GetPointAtCurveDir(cu: Curve, pt: Vector3): boolean +{ + //最近点 + let cp = cu.GetClosestPointTo(pt, false); + //最近点参数 + let cparam = cu.GetParamAtPoint(cp); + //归一化最近点参数 + let floorParam = Math.floor(cparam + 0.5); + + //当最近点在参数上时,可能产生退化,在这里重新计算合适的最近点和切线. + if ( + cu instanceof Polyline + && equaln(floorParam, cparam, 1e-2) + && + ( + cu.IsClose || + ( + floorParam !== 0 + && floorParam !== cu.EndParam + ) + ) + ) + { + let plVerCount = cu.NumberOfVertices; + if (equal(cu.GetPoint2dAt(0), cu.GetPoint2dAt(plVerCount - 1))) + plVerCount--; + + //分三点,本点,前一个点,后一个点 + if (floorParam === cu.EndParam) floorParam = 0; + let fIndex = FixIndex(floorParam - 1, plVerCount); + let nIndex = FixIndex(floorParam + 1, plVerCount); + + let fpInCir = PointInPolylineArc(cu, fIndex, pt); + if (fpInCir !== 0) return fpInCir < 0; + let npInCir = PointInPolylineArc(cu, floorParam, pt); + if (npInCir !== 0) return npInCir < 0; + + let p = cu.GetPointAtParam(floorParam); + let fDer = cu.GetFistDeriv(fIndex + 0.99).normalize(); + let nDer = cu.GetFistDeriv(floorParam + 0.01).normalize(); + + let l1 = new Line(p.clone().sub(fDer), p); + let l2 = new Line(p, p.clone().add(nDer)); + let cp1 = l1.GetClosestPointTo(pt, true); + let cp2 = l2.GetClosestPointTo(pt, true); + + if (equal(fDer, nDer.clone().negate())) + return true; + if (equal(fDer, nDer)) + return fDer.cross(pt.clone().sub(cp1)).z < 0; + + let adir = Math.sign(fDer.clone().cross(nDer).z); + //判断点是否在小角内. + if ( + Math.sign(fDer.cross(pt.clone().sub(cp1)).z) === adir + && Math.sign(nDer.cross(pt.clone().sub(cp2)).z) === adir + ) + return adir < 0; + else return adir > 0; + } + let dri = cu.GetFistDeriv(cparam); + let cross = dri.cross(pt.clone().sub(cp)); + + return cross.z < 0; +} + +/** + * 点在多段线的某个索引的圆弧(弓形)内 + * + * @param {Polyline} pl + * @param {number} index + * @param {Vector3} pt + * @returns {number} + */ +function PointInPolylineArc(pl: Polyline, index: number, pt: Vector3): number +{ + let bul = pl.GetBuilgeAt(index); + if (bul === 0) return 0; + + let arc = pl.GetCurveAtIndex(index) as Arc; + + if (IsPointInBowArc(arc, pt, true)) + return Math.sign(bul); + + return 0; +} + +/** + * 对多段线的圆弧进行打断,并且返回新的圆弧. + * 打断的依据是,圆弧的凸度不能大于0.5 + * + * @export + * @param {Polyline} pl + * @returns {Polyline} 返回新的多段线 + */ +export function BreakPolylineAtArc(pl: Polyline): Polyline +{ + let { pts, buls } = pl.PtsBuls; + + let npl = new Polyline(); + + for (let i = 0; i < pts.length; i++) + { + let bul = buls[i]; + npl.AddVertexAt(npl.NumberOfVertices, pts[i]); + if (i !== pts.length - 1 && Math.abs(bul) > 0.5) + { + let divCount = Math.floor(Math.abs(bul) / 0.5) + 1; + let arc = new Arc().ParseFromBul(pts[i], pts[i + 1], bul); + + let bulDiv = Math.tan(Math.atan(bul) / divCount); + npl.SetBulgeAt(npl.NumberOfVertices - 1, bulDiv); + + for (let i = 0; i < divCount - 1; i++) + { + npl.AddVertexAt(npl.NumberOfVertices, Vec3DTo2D(arc.GetPointAtParam((i + 1) / divCount))); + npl.SetBulgeAt(npl.NumberOfVertices - 1, bulDiv); + } + } + else + { + npl.SetBulgeAt(npl.NumberOfVertices - 1, 0); + } + } + + return npl; +} diff --git a/src/Common/Utils.ts b/src/Common/Utils.ts index ea02ae1f5..e73eea2a5 100644 --- a/src/Common/Utils.ts +++ b/src/Common/Utils.ts @@ -29,13 +29,13 @@ export function clamp(value, min, max) export function FixIndex(index: number, arr: Array | number) { - let cout = (arr instanceof Array) ? arr.length : arr; + let count = (arr instanceof Array) ? arr.length : arr; if (index < 0) - return cout - 1; - else if (index >= cout) - return 0 + return count - 1; + else if (index >= count) + return index - count; else - return index + return index; } //格式化日期 export function formateDate(date: Date, fmt: string) diff --git a/src/DatabaseServices/Arc.ts b/src/DatabaseServices/Arc.ts index 2142e5410..f57a9f0ee 100644 --- a/src/DatabaseServices/Arc.ts +++ b/src/DatabaseServices/Arc.ts @@ -119,6 +119,10 @@ export class Arc extends Curve { return this.m_Clockwise; } + set IsClockWise(v: boolean) + { + this.m_Clockwise = v; + } get StartAngle() { @@ -200,7 +204,7 @@ export class Arc extends Curve { if (this.m_Radius == 0 || this.AllAngle == 0 || - !equaln(pt.distanceToSquared(this.Center), this.m_Radius * this.m_Radius, 1e-6)) + !equaln(pt.distanceToSquared(this.Center), this.m_Radius * this.m_Radius, 1e-4)) return NaN; let ptTmp = pt.clone().applyMatrix4(this.OCSInv); diff --git a/src/DatabaseServices/Circle.ts b/src/DatabaseServices/Circle.ts index 3b30bf11d..8a5ada845 100644 --- a/src/DatabaseServices/Circle.ts +++ b/src/DatabaseServices/Circle.ts @@ -1,10 +1,10 @@ import * as THREE from 'three'; -import { Box3, EllipseCurve, Geometry, Matrix4, Object3D, Vector3 } from 'three'; -import { arrayFirst, arraySortByNumber, arrayRemoveDuplicateBySort, arrayLast } from '../Common/ArrayExt'; +import { Box3, EllipseCurve, Geometry, Object3D, Vector3 } from 'three'; +import { arrayLast, arrayRemoveDuplicateBySort } from '../Common/ArrayExt'; import { ColorMaterial } from '../Common/ColorPalette'; import { clamp } from '../Common/Utils'; import { Arc } from '../DatabaseServices/Arc'; -import { angle, equaln, polar, MoveMatrix } from '../Geometry/GeUtils'; +import { MoveMatrix, angle, equaln, polar } from '../Geometry/GeUtils'; import { RenderType } from '../GraphicsSystem/Enum'; import { IntersectCircleAndArc, IntersectCircleAndCircle, IntersectLineAndCircle, IntersectOption, IntersectPolylineAndCurve, reverseIntersectOption } from '../GraphicsSystem/IntersectWith'; import { Factory } from './CADFactory'; @@ -134,7 +134,7 @@ export class Circle extends Curve { let sa = anglelist[i]; let ea = anglelist[i + 1]; - if (!equaln(sa, ea)) + if (!equaln(sa, ea, 1e-6)) { let arc = new Arc(new Vector3(), this.m_Radius, sa, ea); arc.ApplyMatrix(this.OCS); @@ -155,7 +155,7 @@ export class Circle extends Curve PtOnCurve(pt: Vector3) { - return equaln(pt.distanceToSquared(this.Center), this.m_Radius * this.m_Radius, 1e-6); + return equaln(pt.distanceToSquared(this.Center), this.m_Radius * this.m_Radius, 1e-4); } GetOffsetCurves(offsetDist: number) { @@ -293,6 +293,7 @@ export class Circle extends Curve } GetClosestPointTo(pt: Vector3, extend: boolean): Vector3 { + if (pt.distanceToSquared(this.Center) == 0) return this.GetPointAtParam(0); let l = new Line(this.Center, pt); let pts = l.IntersectWith(this, IntersectOption.ExtendBoth); let ptIns = pt.distanceTo(pts[0]) < pt.distanceTo(pts[1]) ? pts[0] : pts[1]; diff --git a/src/DatabaseServices/Contour.ts b/src/DatabaseServices/Contour.ts index 8c534c5e7..e2953238b 100644 --- a/src/DatabaseServices/Contour.ts +++ b/src/DatabaseServices/Contour.ts @@ -1,14 +1,14 @@ import * as THREE from "three"; import { CreateBoardUtil } from "../ApplicationServices/mesh/createBoard"; import { Vec2DTo3D, Vec3DTo2D, curveLinkGroup, equalCurveAndCurve } from "../Common/CurveUtils"; -import { RegionParse, Route } from "../Geometry/RegionParse"; -import { isTargetCurInSourceCur } from "../GraphicsSystem/BoolOperateUtils"; +import { isTargetCurInOrOnSourceCur } from "../GraphicsSystem/BoolOperateUtils"; import { IntersectOption } from "../GraphicsSystem/IntersectWith"; import { Arc } from "./Arc"; import { Circle } from "./Circle"; import { Curve } from "./Curve"; import { Ellipse } from "./Ellipse"; import { Polyline } from "./Polyline"; +import { rotatePoint } from "../Geometry/GeUtils"; export class Contour { @@ -29,6 +29,16 @@ export class Contour } } } + static CreateContour(cus: Curve[]) + { + let con = new Contour(); + con.m_Outline = Contour.Combine(cus ? cus : [])[0] as Polyline | Circle; + if (con.m_Outline && !con.m_Outline.IsClose) + { + return undefined; + } + return con; + } get Outline(): Polyline | Circle { return this.m_Outline; @@ -67,7 +77,7 @@ export class Contour * @returns {intersectionList //交集曲线表, unionList //并集曲线表, subtractList 差集曲线表} * @memberof Contour */ - private getOperatedCurves(target: Contour) + getOperatedCurves(target: Contour) { let intersectionList: Curve[] = []; let unionList: Curve[] = []; @@ -77,6 +87,7 @@ export class Contour let targetOutline = target.Outline; let interPts = sourceOutline.IntersectWith(targetOutline, IntersectOption.OnBothOperands); + let sourceContainerTarget = this.CuInOutline(targetOutline); let targetContainerSource = target.CuInOutline(sourceOutline); @@ -105,15 +116,29 @@ export class Contour let sourceCus: Array = sourceOutline.GetSplitCurves(pars1); let targetCus: Array = targetOutline.GetSplitCurves(pars2); + //是否有相等的曲线 + let hasEqualCus = false; + //共同线段是否连接2区域 + let disLink = false; + for (let pl of sourceCus) { - //在目标分离曲线数组从若有相等的曲线,则直接加入合并和相交集合中,在目标数组中就直接跳过不在添加,在2轮廓有共线部分时需此判断 - let hasEqualCus = targetCus.some(l => equalCurveAndCurve(pl, l)); + //在目标分离曲线数组从若有相等的曲线,且相等的线段不连接2区域,则直接加入合并和相交集合中 + //在目标数组中就直接跳过不在添加,在2轮廓有共线部分时需此判断 + hasEqualCus = targetCus.some(l => equalCurveAndCurve(pl, l)); if (hasEqualCus) { - unionList.push(pl); - intersectionList.push(pl); - continue; + //获得共有线段中点2侧的点,判断是否有存在2区域外的点 + let tmpPt = pl.GetPointAtParam(0.5); + let tmpDerv = rotatePoint(pl.GetFistDeriv(0.5), Math.PI / 2); + let sidePts = [tmpPt.clone().add(tmpDerv), tmpPt.add(tmpDerv.negate())]; + disLink = sidePts.some(p => !sourceOutline.PtInCurve(p) && !targetOutline.PtInCurve(p)); + if (disLink) + { + unionList.push(pl); + intersectionList.push(pl); + continue; + } } if (target.CuInOutline(pl)) @@ -130,7 +155,7 @@ export class Contour for (let pl of targetCus) { let hasEqualCus = sourceCus.some(l => equalCurveAndCurve(pl, l)); - if (hasEqualCus) + if (hasEqualCus && disLink) { continue; } @@ -145,32 +170,22 @@ export class Contour } } } + return { intersectionList, unionList, subtractList }; } //获得所有闭合的轮廓. static GetAllContour(cus: Curve[]) { - const getSealCurByRoutes = (route: Set) => - { - let cus: Curve[] = []; - for (let r of route) - { - cus.push(r.curve); - } - return new Contour(cus); - } - let regPar = new RegionParse(cus); + let cuGroups = curveLinkGroup(cus); + let contours: Contour[] = [] - for (let routes of regPar.m_RegionsOutline) - { - contours.push(getSealCurByRoutes(routes)); - } - for (let routes of regPar.m_RegionsInternal) + + cuGroups.forEach(cus => { - contours.push(getSealCurByRoutes(routes)); - } - return contours; + contours.push(Contour.CreateContour(cus)); + }) + return contours.filter(c => c !== undefined); } /** * 合并曲线数组 @@ -234,7 +249,7 @@ export class Contour } CuInOutline(targetCur: Curve) { - return isTargetCurInSourceCur(this.m_Outline, targetCur); + return isTargetCurInOrOnSourceCur(this.m_Outline, targetCur); } Equal(tar: Contour) { diff --git a/src/DatabaseServices/Curve.ts b/src/DatabaseServices/Curve.ts index 11d329525..5ba41097e 100644 --- a/src/DatabaseServices/Curve.ts +++ b/src/DatabaseServices/Curve.ts @@ -43,8 +43,10 @@ export abstract class Curve extends Entity super(); } get StartPoint(): Vector3 { return; } + set StartPoint(v: Vector3) { return; } get StartParam(): number { return; } get EndPoint(): Vector3 { return; } + set EndPoint(v: Vector3) { return; } get EndParam(): number { return; } get Area(): number { return 0; } //获得曲线的面积,逆时针为正,顺时针为负. @@ -83,6 +85,12 @@ export abstract class Curve extends Entity * @memberof Curve */ GetSplitCurves(param: number[] | number): Array { return; } + GetSplitCurvesByPts(pt: Vector3[] | Vector3): Array + { + let pts = Array.isArray(pt) ? pt : [pt]; + let pars = pts.map(p => this.GetParamAtPoint(p)) + return this.GetSplitCurves(pars); + } protected SplitParamSort(param: number[] | number): number[] { //切割参数列表 @@ -92,7 +100,7 @@ export abstract class Curve extends Entity params = param.filter(param => this.ParamOnCurve(param)); params.push(0, this.EndParam); arraySortByNumber(params); - arrayRemoveDuplicateBySort(params, equaln); + arrayRemoveDuplicateBySort(params, (e1, e2) => equaln(e1, e2, 1e-6)); } else params = [0, param, 1]; @@ -116,8 +124,8 @@ export abstract class Curve extends Entity return equal(this.StartPoint, pt) || equal(this.EndPoint, pt) || this.ParamOnCurve(this.GetParamAtPoint(pt)); } - //参数在曲线上 - ParamOnCurve(param: number): boolean { return !isNaN(param) && param >= -1e-8 && param <= this.EndParam + 1e-8; } + //参数在曲线上,增加误差范围1e-6 + ParamOnCurve(param: number): boolean { return !isNaN(param) && param >= -1e-6 && param <= this.EndParam + 1e-6; } GetOffsetCurves(offsetDist: number): Array { return; } GetClosestPointTo(pt: Vector3, extend: boolean): Vector3 { return; } diff --git a/src/DatabaseServices/Line.ts b/src/DatabaseServices/Line.ts index 08fc62ef4..e761ff17a 100644 --- a/src/DatabaseServices/Line.ts +++ b/src/DatabaseServices/Line.ts @@ -183,7 +183,7 @@ export class Line extends Curve if (param >= 0 && param <= 1) return pt_int; if (param < 0) return this.StartPoint; if (param > 0) return this.EndPoint; - return; + return this.StartPoint; } Extend(newParam: number) @@ -239,11 +239,7 @@ export class Line extends Curve } get BoundingBox(): Box3 { - let pts = [ - this.m_StartPoint, - this.m_EndPoint - ]; - return new Box3().setFromPoints(pts); + return new Box3().setFromPoints([this.StartPoint, this.EndPoint]); } get StartParam() diff --git a/src/DatabaseServices/PointInPolyline.ts b/src/DatabaseServices/PointInPolyline.ts index 044102e79..669e8b3c8 100644 --- a/src/DatabaseServices/PointInPolyline.ts +++ b/src/DatabaseServices/PointInPolyline.ts @@ -1,6 +1,5 @@ import { Vector2, Vector3 } from 'three'; - -import { angle, equal, equaln } from '../Geometry/GeUtils'; +import { angle, equaln, equal } from '../Geometry/GeUtils'; import { IntersectOption } from '../GraphicsSystem/IntersectWith'; import { Arc } from './Arc'; import { Line } from './Line'; @@ -29,9 +28,10 @@ function IsPointInCircularSector(arc: Arc, pt: Vector3): boolean * * @param {Arc} arc 二维圆弧 * @param {Vector3} pt 点 + * @param {boolean} isInChrodIsTrue 当点在弦上也认为在弓形内部 * @returns {boolean} 点在内部 */ -export function IsPointInBowArc(arc: Arc, pt: Vector3): boolean +export function IsPointInBowArc(arc: Arc, pt: Vector3, isInChrodIsTrue = false): boolean { let pv = pt.clone().sub(arc.StartPoint); let av = arc.EndPoint.sub(arc.StartPoint); @@ -47,7 +47,7 @@ export function IsPointInBowArc(arc: Arc, pt: Vector3): boolean // return arc.Center.distanceToSquared(pt) < arc.Radius * arc.Radius; //简化的代码 - if ((pv.z > 0) !== arc.IsClockWise) + if ((pv.z > 0) !== arc.IsClockWise || (isInChrodIsTrue && equaln(pv.z, 0))) { return arc.Center.distanceToSquared(pt) < arc.Radius * arc.Radius; } @@ -70,29 +70,84 @@ export function IsPointInPolyLine(pl: Polyline, pt: Vector3): boolean for (let i = 0; i < pl.EndParam; i++) { - let cu = pl.GetCurveAtIndex(i); - let inpts = cu.IntersectWith(insLine, IntersectOption.ExtendArg); + if (equaln(pl.GetBuilgeAt(i), 0))//直线 + { + let sp = pl.GetPointAtParam(i); + let ep = pl.GetPointAtParam(i + 1); + //点位于线上面 + if (pt.y > Math.max(sp.y, ep.y)) + continue; + //线垂直Y轴 + let derX = ep.x - sp.x; + if (equaln(derX, 0)) + continue; + + //起点 + if (equaln(sp.x, pt.x)) + { + if (sp.y > pt.y && derX < 0) crossings++; + continue; + } + //终点 + if (equaln(ep.x, pt.x)) + { + if (ep.y > pt.y && derX > 0) crossings++; + continue; + } - for (let pti of inpts) + //快速求交,只验证有没有交点 + let [x1, x2] = sp.x > ep.x ? [ep.x, sp.x] : [sp.x, ep.x]; + if (pt.x > x1 && pt.x < x2) + { + let derY = ep.y - sp.y; + let k = derY / derX; + + if ((pt.x - sp.x) * k + sp.y > pt.y) + crossings++; + } + } + else //圆弧 { - if (pti.y < pt.y) + let arc = pl.GetCurveAtIndex(i) as Arc; + let sp = arc.StartPoint; + let ep = arc.EndPoint; + + //如果相切 + if (equaln(Math.abs(pt.x - arc.Center.x), arc.Radius)) + { + //当点和起点或者终点和点相切时 + if (equaln(sp.x, pt.x) && sp.y > pt.y) + { + if (ep.x - sp.x < -1e-5) + crossings++; + } + else if (equaln(ep.x, pt.x) && ep.y > pt.y) + { + if (ep.x - sp.x > 1e-5) + crossings++; + } continue; + } - if (equal(pti, cu.StartPoint)) + if (equaln(sp.x, pt.x) && sp.y > pt.y) { - let der = cu.GetFistDeriv(0); - if (der.x < -1e-3) //左边+ 右边0 + let der = arc.GetFistDeriv(0); + if (der.x < -1e-5) crossings++; } - else if (equal(pti, cu.EndPoint)) + if (equaln(ep.x, pt.x) && ep.y > pt.y) { - let der = cu.GetFistDeriv(1); - if (der.x > 1e-3) //左边+ 右边0 + let der = arc.GetFistDeriv(1); + if (der.x > 1e-5) crossings++; } - else + + for (let pti of arc.IntersectWith(insLine, IntersectOption.ExtendArg)) { - let der = cu.GetFistDeriv(pti); + if (pti.y < pt.y || equal(sp, pti) || equal(ep, pti)) + continue; + + let der = arc.GetFistDeriv(pti); if (!equaln(der.x, 0)) //相切. crossings++; } diff --git a/src/DatabaseServices/Polyline.ts b/src/DatabaseServices/Polyline.ts index faa4edc3c..1a04f492c 100644 --- a/src/DatabaseServices/Polyline.ts +++ b/src/DatabaseServices/Polyline.ts @@ -3,22 +3,20 @@ import { Box3, Geometry, Matrix4, Object3D, Vector2, Vector3 } from 'three'; import { CreateBoardUtil } from '../ApplicationServices/mesh/createBoard'; import { arrayLast, arrayRemoveDuplicateBySort } from '../Common/ArrayExt'; import { ColorMaterial } from '../Common/ColorPalette'; -import { Vec2DTo3D, Vec3DTo2D, getDeterminantFor2V } from '../Common/CurveUtils'; +import { getDeterminantFor2V, Vec2DTo3D, Vec3DTo2D } from '../Common/CurveUtils'; import { matrixAlignCoordSys } from '../Common/Matrix4Utils'; import { FixIndex } from '../Common/Utils'; -import { equal, equaln, rotatePoint, updateGeometry } from '../Geometry/GeUtils'; +import { equal, equaln, updateGeometry } from '../Geometry/GeUtils'; import { RenderType } from '../GraphicsSystem/Enum'; import { IntersectOption, IntersectPolylineAndCurve } from '../GraphicsSystem/IntersectWith'; -import { angleTo } from './../Geometry/GeUtils'; +import { PolyOffsetUtil } from '../GraphicsSystem/OffsetPolyline'; import { Arc } from './Arc'; import { Factory } from './CADFactory'; import { CADFile } from './CADFile'; -import { Circle } from './Circle'; import { Curve, ExtendType } from './Curve'; import { Line } from './Line'; import { IsPointInPolyLine } from './PointInPolyline'; - export interface PolylineProps { pt: Vector2, @@ -155,6 +153,18 @@ export class Polyline extends Curve return Vec2DTo3D(this.m_LineData[0].pt).applyMatrix4(this.OCS); return new Vector3(); } + set StartPoint(v: Vector3) + { + this.SetPointAt(0, Vec3DTo2D(v)); + let bul = this.GetBuilgeAt(0) + if (bul !== 0) + { + let arc = this.GetCurveAtParam(0) as Arc; + arc.StartPoint = v; + //前面线的凸度调整 + this.SetBulgeAt(0, Math.tan(arc.AllAngle / 4) * Math.sign(bul)); + } + } get EndPoint() { if (this.m_ClosedMark) return this.StartPoint; @@ -162,6 +172,18 @@ export class Polyline extends Curve return Vec2DTo3D(this.m_LineData[this.EndParam].pt).applyMatrix4(this.OCS); return new Vector3(); } + set EndPoint(v: Vector3) + { + this.SetPointAt(this.EndParam, Vec3DTo2D(v)); + let bul = this.GetBuilgeAt(this.EndParam - 1) + if (bul !== 0) + { + let arc = this.GetCurveAtParam(0) as Arc; + arc.EndPoint = v; + //前面线的凸度调整 + this.SetBulgeAt(this.EndParam - 1, Math.tan(arc.AllAngle / 4) * Math.sign(bul)); + } + } get StartParam() { return 0; @@ -187,27 +209,27 @@ export class Polyline extends Curve } get Area2() { + let { pts, buls } = this.PtsBuls; let area = 0; - for (let i = 0; i < this.m_LineData.length - 1; i++) + for (let i = 0; i < pts.length - 1; i++) { - let startV = this.m_LineData[i]; - let endV = this.m_LineData[i + 1]; + let startV = pts[i]; + let endV = pts[i + 1]; + let det = getDeterminantFor2V(startV, endV); - area += getDeterminantFor2V(startV.pt, endV.pt) / 2; - - if (startV.bul != 0) + let bul = buls[i]; + if (bul != 0) { - let arc = new Arc().ParseFromBul(startV.pt, endV.pt, startV.bul); - area += arc.Area * Math.sign(startV.bul); + let arc = new Arc().ParseFromBul(startV, endV, bul); + let rSq = arc.Radius ** 2; + //弓形面积 + let arcArea = rSq * arc.AllAngle * 0.5 - 0.5 * rSq * Math.sin(arc.AllAngle); + //我们可以知道 正的凸度总是增加逆时针的面积, 负的凸度总是增加顺时针的面积 + det += arcArea * Math.sign(bul) * 2; } + area += det; } - if (!this.IsClose) - { - let startV = arrayLast(this.m_LineData); - let endV = this.m_LineData[0]; - area += getDeterminantFor2V(startV.pt, endV.pt) / 2 - } - return area; + return area / 2; } get Area() { @@ -221,7 +243,7 @@ export class Polyline extends Curve //曲线是否闭合 get IsClose(): boolean { - return this.CloseMark || equal(this.StartPoint, this.EndPoint); + return this.CloseMark || equal(this.StartPoint, this.EndPoint, 1e-5); } set CloseMark(v: boolean) { @@ -245,11 +267,11 @@ export class Polyline extends Curve */ GetPointAtParam(param: number): Vector3 { + if (param === Math.floor(param) && this.ParamOnCurve(param)) + return Vec2DTo3D(this.GetPoint2dAt(FixIndex(param, this.NumberOfVertices))).applyMatrix4(this.OCS); let cu: Curve = this.GetCurveAtParam(param); if (cu) - { return cu.GetPointAtParam(this.GetCurveParamAtParam(param)); - } return undefined; } @@ -392,7 +414,6 @@ export class Polyline extends Curve return pl; } - GetSplitCurves(param: number[] | number): Array { //参数需要转化为参数数组 @@ -419,7 +440,7 @@ export class Polyline extends Curve return []; //判断是否存在0参数 - let hasZeroParam = params[0] == 0; + let hasZeroParam = equaln(params[0], 0, 1e-6); if (hasZeroParam) params.shift(); @@ -446,7 +467,7 @@ export class Polyline extends Curve //添加点 for (let i = 0; i < pafloor; i++) { - if (i == 0 && buls[0] != 0) + if (i == 0 && !equaln(buls[0], 0, 1e-6)) { buls[0] = Math.tan((1 - prePa) * Math.atan(buls[0])); } @@ -462,7 +483,7 @@ export class Polyline extends Curve else //在曲线上 { let bul: number = buls[0]; - if (bul != 0) + if (!equaln(bul, 0, 1e-6)) bul = Math.tan((pa - pafloor - (0 === pafloor ? prePa : 0)) * Math.atan(buls[0])); //->凸度 //加入顶点+凸度 @@ -475,7 +496,8 @@ export class Polyline extends Curve } prePa = pa - pafloor; - pls.push(new Polyline(plData).ApplyMatrix(this.OCS)); + if (plData.length > 1) + pls.push(new Polyline(plData).ApplyMatrix(this.OCS)); } //当曲线为闭合曲线,并且不存在0切割参数时,首尾连接曲线 @@ -684,7 +706,7 @@ export class Polyline extends Curve //最近点 let ptC = this.StartPoint; - //最近点的距离 + //最近点的距离\ let ptCDist = ptC.distanceToSquared(pt); for (let i = 0; i < this.EndParam; i++) @@ -730,220 +752,11 @@ export class Polyline extends Curve return ptC; } - - //偏移 GetOffsetCurves(offsetDist: number): Array { - let plList: Array = []; - for (let i = 0; i < this.EndParam; i++) - { - let pl = this.offestCurve(i, offsetDist); - pl && plList.push(pl); - } - //连接全部连接点 - let newPlList: Array = this.linkPolylineGroup(plList, offsetDist); - // //分离出自交的线 - let outputPls = this.removeSelfIntersect(newPlList, offsetDist); - this.linkPLine(newPlList, offsetDist); - this.cuttingPolyLine(newPlList, offsetDist); - outputPls.push(...this.arrangePlineList(newPlList)); - return outputPls; - } - //偏移曲线 - private offestCurve(param: number, dist: number) - { - let startV: PolylineProps = { - pt: this.m_LineData[param].pt, - bul: this.m_LineData[param].bul - } - let endV: PolylineProps = { - pt: this.m_LineData[param + 1].pt, - bul: this.m_LineData[param + 1].bul - } - if (equaln(startV.pt.distanceTo(endV.pt), 0)) return; - let tarLine = this.GetFistDeriv(param).normalize(); - //偏移向量 - let moveVec = rotatePoint(tarLine, Math.PI / 2).multiplyScalar(-dist); - //偏移矩阵 - let mat = new Matrix4().makeTranslation(moveVec.x, moveVec.y, moveVec.z); - let originLine = Vec2DTo3D(endV.pt.clone().sub(startV.pt)); - - startV.pt = Vec3DTo2D(Vec2DTo3D(startV.pt).applyMatrix4(mat)); - if (startV.bul !== 0) - { - moveVec = rotatePoint(moveVec, Math.atan(startV.bul) * 4); - } - mat.makeTranslation(moveVec.x, moveVec.y, moveVec.z); - endV.pt = Vec3DTo2D(Vec2DTo3D(endV.pt).applyMatrix4(mat)); - - let finalLine = Vec2DTo3D(endV.pt.clone().sub(startV.pt)); - if (equaln(finalLine.angleTo(originLine), Math.PI)) - { - return; - } - let pl = this.copyPolyline([startV, endV]); - pl.CloseMark = false; - return pl; - } - /** - * 尝试相邻多段线段收尾相连 - * @private - * @param {Polyline[]} pls - * @memberof Polyline - */ - private linkPLine(pls: Polyline[], offset: number) - { - for (let i = 0; i < pls.length - 1; i++) - { - let frontLine = pls[i]; - let laterLine = pls[i + 1]; - let interPts = frontLine.IntersectWith(laterLine, IntersectOption.ExtendBoth); - if (interPts.length > 0) - { - let pt = this.selectPlInterPt(interPts, laterLine.StartPoint); - //调整前后两条多段线的首尾线 - this.adjustFrontLine(frontLine.LineData[1], pt, frontLine.LineData[0]); - frontLine.LineData[1].pt = Vec3DTo2D(pt); - this.adjustLaterLine(laterLine.LineData[1], pt, laterLine.LineData[0]); - } - } - } - /** - * 删除自交多段线 - * 遍历多段线数组,分离出自交的部分,包括起始和终止被切割的部分 - * @private - * @param {Array} plList - * @param {number} offsetDist - * @returns - * @memberof Polyline - */ - private removeSelfIntersect(plList: Polyline[], offsetDist: number) - { - let plLists: Polyline[] = []; - let deletePlList: Polyline[] = []; - let deleteCount = 0; - for (let i = 0; i < plList.length - 1; i++) - { - //前一根线 - let frontLine = plList[i]; - //要删除自交线的索引 - let deleteIndex: number; - //截取到的自交线交点 - let insertPt = new Vector3(); - for (let j = i + 1; j < plList.length; j++) - { - // 后一根线 - let laterLine = plList[j]; - let intPts = frontLine.IntersectWith(laterLine, IntersectOption.OnBothOperands); - if (intPts.length === 2) - { - intPts[0].distanceTo(frontLine.StartPoint) < intPts[1].distanceTo(frontLine.StartPoint) ? intPts.pop() : intPts.shift(); - } - if (intPts.length > 0 && !equal(intPts[0], laterLine.StartPoint) && !equal(intPts[0], laterLine.EndPoint)) - { - deleteIndex = j; - insertPt.copy(intPts[0]); - } - } - if (deleteIndex) - { - // 删除的自交线 - let deleteLine = plList[deleteIndex]; - let clonedeleteLine = deleteLine.Clone() as Polyline; - let cloneStartLine = frontLine.Clone() as Polyline - - this.adjustFrontLine(frontLine.LineData[1], insertPt, frontLine.LineData[0]); - frontLine.LineData[1].pt = Vec3DTo2D(insertPt); - //留下剩余的 - this.adjustLaterLine(cloneStartLine.LineData[1], insertPt, cloneStartLine.LineData[0]); - - this.adjustLaterLine(deleteLine.LineData[1], insertPt, deleteLine.LineData[0]); - //留下剩余的 - this.adjustFrontLine(clonedeleteLine.LineData[1], insertPt, clonedeleteLine.LineData[0]); - clonedeleteLine.LineData[1].pt = Vec3DTo2D(insertPt); - - //分离出的线 - deletePlList = plList.splice(i + 1 - deleteCount, deleteIndex - i - 1); - if (deletePlList.length >= 2) - { - //添加自交开始结束的部分 - deletePlList.unshift(cloneStartLine); - deletePlList.push(clonedeleteLine); - plLists.push(this.AssemblePolyline(this.handleRemoveLine(deletePlList))); - } - //已删除元素数量 - deleteCount += (deleteIndex - i - 1); - i = deleteIndex - 1; - } - } - return plLists; - } - //处理分离出的多段线,把相交的部分提取出来 - private handleRemoveLine(plList: Polyline[]) - { - let startIndex: number; - let endIndex: number; - let insertPt = new Vector3(); - for (let i = 0; i < plList.length - 1; i++) - { - //前一根线 - let frontLine = plList[i]; - //截取到的自交线交点 - for (let j = i + 1; j < plList.length; j++) - { - // 后一根线 - let laterLine = plList[j]; - let intPts = frontLine.IntersectWith(laterLine, IntersectOption.OnBothOperands); - if (intPts.length === 2) - { - intPts[0].distanceTo(laterLine.StartPoint) < intPts[1].distanceTo(laterLine.StartPoint) ? intPts.pop() : intPts.shift(); - } - if (intPts.length > 0 && !equal(intPts[0], laterLine.StartPoint) && !equal(intPts[0], laterLine.EndPoint)) - { - startIndex = i; - endIndex = j; - insertPt.copy(intPts[0]); - } - } - } - if (startIndex !== undefined) - { - // 删除的自交线 - let startLine = plList[startIndex]; - let endLine = plList[endIndex]; - - this.adjustFrontLine(endLine.LineData[1], insertPt, endLine.LineData[0]); - endLine.LineData[1].pt = Vec3DTo2D(insertPt); - this.adjustLaterLine(startLine.LineData[1], insertPt, startLine.LineData[0]); - - return plList.slice(startIndex, endIndex + 1); - } else return plList - } - // 根据交点调整前面线段的凸度 - private adjustFrontLine(endV: PolylineProps, insertPt: Vector3, startV: PolylineProps) - { - if (startV.bul !== 0) - { - let arc = new Arc().ParseFromBul(startV.pt, endV.pt, startV.bul); - arc.EndPoint = insertPt; - //前面线的凸度调整 - startV.bul = Math.tan(arc.AllAngle / 4) * Math.sign(startV.bul); - } - } - // 根据交点调整后面线段的凸度和起始点 - private adjustLaterLine(endV: PolylineProps, interPt: Vector3, startV: PolylineProps) - { - let bul = 0; - if (startV.bul !== 0) - { - let arc = new Arc().ParseFromBul(startV.pt, endV.pt, startV.bul); - arc.StartPoint = interPt; - bul = Math.tan(arc.AllAngle * 0.25) * Math.sign(startV.bul); - } - // 调整后面线起始点和凸度 - startV.pt = Vec3DTo2D(interPt); - startV.bul = bul; + let polyOffestUtil = new PolyOffsetUtil(this, offsetDist); + return polyOffestUtil.GetOffsetCurves(); } /** * 分解 @@ -1018,388 +831,6 @@ export class Polyline extends Curve return new Arc().ParseFromBul(d1.pt, d2.pt, d1.bul).ApplyMatrix(this.OCS); } } - // 组合多段线数组组合为一多段线 - AssemblePolyline(pls: Array) - { - if (pls.length === 0) return; - let plDataList: PolylineProps[] = []; - plDataList.push(pls[0].LineData[0]); - for (let i = 0; i < pls.length - 1; i++) - { - let frontLine = pls[i]; - let laterLine = pls[i + 1]; - if (equal(frontLine.EndPoint, laterLine.StartPoint)) - { - plDataList.push(laterLine.LineData[0]); - } - } - plDataList.push(pls[pls.length - 1].LineData[1]); - - return new Polyline(plDataList); - } - //整理多段线数组,把相连的组合成多段线,返回多段线数组 - private arrangePlineList(pls: Array) - { - if (pls.length === 0) return []; - let plDataList: PolylineProps[] = []; - let plList: Polyline[] = []; - plDataList.push(pls[0].LineData[0]); - for (let i = 0; i < pls.length - 1; i++) - { - let frontLine = pls[i]; - let laterLine = pls[i + 1]; - if (equal(frontLine.EndPoint, laterLine.StartPoint)) - { - plDataList.push(laterLine.LineData[0]); - } - else - { - plDataList.push(frontLine.LineData[1]); - plList.push(new Polyline(plDataList)); - plDataList = [laterLine.LineData[0]]; - } - } - plDataList.push(pls[pls.length - 1].LineData[1]); - plList.push(new Polyline(plDataList)); - - return plList; - } - /** - * 连接多段线数组 - * 两两取交点,去除斜率相反线 - * 不相交添加圆弧连接 - * @private - * @param {Array} plList - * @param {number} [offsetDist] - * @returns - * @memberof Polyline - */ - private linkPolylineGroup(plList: Array, offsetDist?: number) - { - if (plList.length === 1) - { - return plList; - } - let newPlList: Array = []; - - let startV = plList[0].LineData[0]; - //原先的斜率 - let oldSlope: Vector3; - //新斜率 - let newSlope: Vector3; - for (let i = 0; i < plList.length; i++) - { - //前面线 - let frontLine = plList[i]; - //原先的斜率 - oldSlope = frontLine.GetFistDeriv(0); - //后面线 - let laterLine = plList[i + 1]; - - if (i === plList.length - 1) - { - if (this.IsClose) - laterLine = plList[0]; - else break; - } - //终点data - let endV = this.clonePlData(laterLine.LineData[0]); - //交点 - let interPt = Vec2DTo3D(endV.pt); - // 如果两线结合点不相等 - if (!equal(frontLine.EndPoint, laterLine.StartPoint)) - { - // 多段线交点数组 - let interPts = frontLine.IntersectWith(laterLine, IntersectOption.ExtendBoth); - - //如果没有交点,则用圆弧连接 - if (interPts.length === 0) - { - let centerPt; - //取圆心,圆心离后面线start点距离等于偏移距离 - for (let j = i; j < this.LineData.length; j++) - { - let pt = Vec2DTo3D(this.LineData[j].pt); - if (equaln(pt.distanceTo(laterLine.StartPoint), Math.abs(offsetDist))) - { - centerPt = pt; - break; - } - } - if (!centerPt) throw new Error("找不到圆心"); - // 圆心到前面线end点向量 - let startLine = frontLine.EndPoint.clone().sub(centerPt); - // 圆心到后面线start点向量 - let endLine = laterLine.StartPoint.clone().sub(centerPt); - let circleAngle; - //补圆弧的起始交点 - let intPt = Vec2DTo3D(frontLine.LineData[1].pt); - // 如果起始向量和终止向量相等,则都为半径 - if (equaln(startLine.lengthSq(), endLine.lengthSq())) - { - circleAngle = angleTo(startLine, endLine); - } - else - { - // 以centerPt为圆心,偏移距离为为半径画圆 - let tmpCir = new Circle(centerPt, Math.abs(offsetDist)); - //只会和前面线相交,因为圆心选取是根据后面线startPoint偏移距离处的点,只会与后面线相切,允许前面线延伸 - let pts = tmpCir.IntersectWith(frontLine, IntersectOption.OnBothOperands); - if (pts.length > 0) - { - //选取交点,如果有多个选离前面线start点近的 - intPt = this.selectPlInterPt(pts, frontLine.StartPoint); - let insLine = intPt.clone().sub(centerPt); - circleAngle = angleTo(insLine, endLine); - this.adjustFrontLine(frontLine.LineData[1], intPt, startV); - } - else - { - // 与补线圆无交点,放弃补圆弧 - newPlList.push(new Polyline([ - startV, this.clonePlData(frontLine.LineData[1]) - ])) - startV = this.clonePlData(laterLine.LineData[0]); - continue; - } - } - let tmpPlData = { - pt: Vec3DTo2D(intPt), - bul: Math.tan(circleAngle / 4) - }; - newPlList.push(new Polyline([ - startV, tmpPlData - ])) - startV = this.clonePlData(tmpPlData); - } - else - { - let pts = frontLine.IntersectWith(laterLine, IntersectOption.OnBothOperands); - - interPt = pts.length > 0 ? pts[0] : this.selectPlInterPt(interPts, laterLine.StartPoint) - this.adjustFrontLine(frontLine.LineData[1], interPt, startV); - this.adjustLaterLine(laterLine.LineData[1], interPt, endV); - } - } - - let pl = new Polyline([startV, endV]) - newSlope = pl.GetFistDeriv(0); - startV = this.clonePlData(endV); - if (i === plList.length - 1) - { - this.adjustLaterLine(newPlList[0].LineData[1], interPt, newPlList[0].LineData[0]); - } - if (equal(pl.StartPoint, pl.EndPoint) || (pl.LineData[0].bul === 0 && equaln(newSlope.angleTo(oldSlope), Math.PI, 1e-7))) - { - continue; - } - - newPlList.push(pl); - } - if (!this.IsClose) - { - let frontLine = plList[plList.length - 1]; - let endV = this.clonePlData(frontLine.LineData[1]); - if (plList.length >= 3) - { - let laterLine = newPlList[0]; - let pts = frontLine.IntersectWith(laterLine, IntersectOption.OnBothOperands); - if (pts.length === 1) - { - this.adjustFrontLine(frontLine.LineData[1], pts[0], startV); - endV.pt = Vec3DTo2D(pts[0]); - if (newPlList.length > 0) - this.adjustLaterLine(newPlList[0].LineData[1], pts[0], newPlList[0].LineData[0]); - } - } - let pl = new Polyline([startV, endV]); - newSlope = pl.GetFistDeriv(0); - if (pl.LineData[0].bul !== 0 || !equaln(newSlope.angleTo(oldSlope), Math.PI, 1e-7)) - newPlList.push(pl); - } - return newPlList; - } - /** - * 优化连接多段数组,去除无用线 - * //TODO:暂保留,待优化 - * @private - * @param {Array} plList - * @returns - * @memberof Polyline - */ - private linkPolyline(plList: Array) - { - let isLine = false; - if (plList.length === 1) return plList; - for (let i = 0; i < plList.length - 1; i++) - { - let frontLine = plList[i]; - let laterLine = plList[i + 1]; - let interPt: Vector3; - if (equal(frontLine.EndPoint, laterLine.StartPoint)) - { - continue; - } - else - { - if (!isLine) - { - let oldSlope = frontLine.GetFistDeriv(0); - let newSlope; - let interpts = frontLine.IntersectWith(laterLine, IntersectOption.ExtendBoth) - do - { - interPt = this.selectPlInterPt(interpts, laterLine.StartPoint); - - while (!interPt) - { - plList.splice(i, 1); - i--; - if (i < 0) break; - frontLine = plList[i]; - oldSlope = frontLine.GetFistDeriv(0); - } - if (i === -1) break; - - this.adjustFrontLine(frontLine.LineData[1], interPt, frontLine.LineData[0]); - frontLine.LineData[1].pt = Vec3DTo2D(interPt); - newSlope = frontLine.GetFistDeriv(0); - if (!equaln(oldSlope.angleTo(newSlope), 0, 1e-7)) - { - plList.splice(i, 1); - i--; - if (i < 0) break; - frontLine = plList[i]; - oldSlope = frontLine.GetFistDeriv(0); - } - else - { - this.adjustLaterLine(laterLine.LineData[1], interPt, laterLine.LineData[0]); - break; - } - } while (true); - } - else - { - plList.splice(i - 1); - break; - } - - isLine = true; - } - } - return plList.length > 1 ? plList : []; - } - /** - * 选取离spt近的点作为交点 - * - * @private - * @param {Vector3[]} pts - * @param {Vector3} spt - * @returns - * @memberof Polyline - */ - private selectPlInterPt(pts: Vector3[], spt: Vector3) - { - let pt: Vector3; - // 多段线交点数组 - if (pts.length === 0) return; - else if (pts.length === 1) - { - pt = pts[0]; - } - else - { - // 在当前线上不存在交点,取离后面线startPt最近的交点; - pt = pts[0].distanceTo(spt) < pts[1].distanceTo(spt) ? pts[0] : pts[1]; - } - return pt; - } - /** - * 优化切割无效多段线 - * 源线段中各点画圆,半径为偏移距离 - * 若偏移线段在圆内,则切除 - * @private - * @param {Polyline[]} plList - * @param {number} rad - * @returns - * @memberof Polyline - */ - private cuttingPolyLine(plList: Polyline[], rad: number) - { - rad = Math.abs(rad); - // 每个原线段点画圆分割多段线 - for (let i = 0; i < this.LineData.length; i++) - { - //圆心点 - let centerPt = Vec2DTo3D(this.LineData[i].pt); - let circle = new Circle(centerPt, rad); - for (let j = 0; j < plList.length; j++) - { - let pl = plList[j]; - let sDist = pl.StartPoint.distanceTo(centerPt); - let eDist = pl.EndPoint.distanceTo(centerPt); - // 如果线段在圆内,则为无效线 - if (sDist - rad <= 1e-7 && eDist - rad <= 1e-7) - { - //该段多段线和切割圆弧是否重合 - if (pl.LineData[0].bul !== 0) - { - let center = (pl.GetCurveAtIndex(0) as Arc).Center; - if (equal(center, centerPt)) continue; - } - plList.splice(j, 1); - j--; - continue; - } - // 跟圆弧相交除非相切,否则都为2个交点 - let pts = pl.IntersectWith(circle, IntersectOption.ExtendBoth); - if (pts.length <= 1) continue; - // 过滤掉不在线上的点 - pts = pts.filter(p => plList[j].PtOnCurve(p)); - - if (pts.length === 0) continue; - else if (pts.length === 1) - { - // 线段一端在圆上,另一端在圆外而且没有其他交点 - if ((equaln(sDist, rad) && eDist > rad) || (equaln(eDist, rad) && sDist > rad)) - { - continue; - } - if (sDist < rad) - this.adjustLaterLine(pl.LineData[1], pts[0], pl.LineData[0]) - else - { - this.adjustFrontLine(pl.LineData[1], pts[0], pl.LineData[0]) - pl.LineData[1].pt = Vec3DTo2D(pts[0]); - } - } - else - { - //有可能误差错误漏掉切点 - if (pts[0].distanceToSquared(pts[1]) <= 1e-8) continue; - let dist = pl.StartPoint.distanceTo(pts[0]); - let dist1 = pl.StartPoint.distanceTo(pts[1]); - //区分到pl的startPT近的和远的交点 - let [pt, pt1] = dist < dist1 ? [pts[0], pts[1]] : [pts[1], pts[0]] - let clonePl = pl.Clone() as Polyline; - - this.adjustFrontLine(pl.LineData[1], pt, pl.LineData[0]); - pl.LineData[1].pt = Vec3DTo2D(pt) - this.adjustLaterLine(clonePl.LineData[1], pt1, clonePl.LineData[0]); - plList.splice(j + 1, 0, clonePl); - j++; - } - } - } - } - private clonePlData(pl: PolylineProps) - { - return { - pt: pl.pt, - bul: pl.bul - } - } IntersectWith(curve: Curve, intType: IntersectOption): Vector3[] { @@ -1471,15 +902,18 @@ export class Polyline extends Curve GetSnapPoints(): Array { - let plList: Vector3[] = []; + let ptList: Vector3[] = []; if (this.m_LineData.length < 2) - return plList; - for (let i = 0; i < this.EndParam + 0.5; i += 0.5) + return ptList; + + let enParam = this.EndParam; + if (this.CloseMark) enParam -= 0.5; + for (let i = 0; i < enParam + 0.5; i += 0.5) { let p = this.GetPointAtParam(i); - plList.push(p); + ptList.push(p); } - return plList; + return ptList; } MoveSnapPoints(indexList: number[], moveVec: Vector3) { @@ -1515,12 +949,12 @@ export class Polyline extends Curve GetStretchPoints(): Array { let ocs = this.OCS; - let plList: Vector3[] = []; + let ptList: Vector3[] = []; for (let data of this.m_LineData) { - plList.push(Vec2DTo3D(data.pt).applyMatrix4(ocs)); + ptList.push(Vec2DTo3D(data.pt).applyMatrix4(ocs)); } - return plList; + return ptList; } /** diff --git a/src/DatabaseServices/Shape.ts b/src/DatabaseServices/Shape.ts index dc3700b20..a606b6225 100644 --- a/src/DatabaseServices/Shape.ts +++ b/src/DatabaseServices/Shape.ts @@ -63,7 +63,7 @@ export class Shape IntersectionBoolOperation(targetShape: Shape): Shape[] { let resOutlines = this.m_Outline.IntersectionBoolOperation(targetShape.m_Outline); - let cus = this.targetOutlineSubHoleOutline(resOutlines, this.mergeHoles([...this.m_Holes, ...targetShape.m_Holes])) + let cus = this.targetOutlineSubHoleOutline(resOutlines, Shape.mergeContours([...this.m_Holes, ...targetShape.m_Holes])) return this.pairHoleAndOutline(cus); } @@ -121,7 +121,7 @@ export class Shape shapes.push(...this.pairHoleAndOutline(this.targetOutlineSubHoleOutline(tmpInterList, this.m_Holes))) }) - let fCus = this.targetOutlineSubHoleOutline(resOutlines, this.mergeHoles([...this.m_Holes, ...targetShape.m_Holes])); + let fCus = this.targetOutlineSubHoleOutline(resOutlines, Shape.mergeContours([...this.m_Holes, ...targetShape.m_Holes])); shapes.push(...this.pairHoleAndOutline(fCus)); return shapes; @@ -206,7 +206,7 @@ export class Shape * @returns * @memberof Shape */ - private mergeHoles(holes: Contour[]): Contour[] + static mergeContours(holes: Contour[]): Contour[] { if (holes.length <= 1) return holes; let rets: Contour[] = [];//返回的合并轮廓 diff --git a/src/DatabaseServices/Spline.ts b/src/DatabaseServices/Spline.ts index 2f726a914..f9b2d3b1a 100644 --- a/src/DatabaseServices/Spline.ts +++ b/src/DatabaseServices/Spline.ts @@ -51,4 +51,4 @@ export class Spline extends Entity geo.computeBoundingSphere(); } } -} \ No newline at end of file +} diff --git a/src/Editor/CameraControls.ts b/src/Editor/CameraControls.ts index be9d01b9e..77c8d15db 100644 --- a/src/Editor/CameraControls.ts +++ b/src/Editor/CameraControls.ts @@ -141,6 +141,9 @@ export class CameraControls this.m_State = CameraControlState.Rotate; this.m_Viewer.UpdateLockTarget(); } + + //最后一次按中键的时间 + lastMiddleClickTime = 0; //鼠标 onMouseDown = (event: MouseEvent) => { @@ -161,6 +164,15 @@ export class CameraControls } case MouseKey.Middle: { + let curTime = Date.now(); + let t = curTime - this.lastMiddleClickTime; + this.lastMiddleClickTime = curTime; + if (t < 350) + { + this.m_Viewer.ZoomAll(); + return; + } + if (this.m_KeyDown.get(KeyBoard.Control)) { this.beginRotate(); diff --git a/src/Editor/CommandRegister.ts b/src/Editor/CommandRegister.ts index 708d84056..7d8c08360 100644 --- a/src/Editor/CommandRegister.ts +++ b/src/Editor/CommandRegister.ts @@ -1,9 +1,12 @@ import { DrawAxis } from '../Add-on/AddAxis'; import { Command_Array } from '../Add-on/Array'; import { IntersectionOperation, SubsractOperation, UnionOperation } from '../Add-on/BoolOperation'; -import { Union } from '../Add-on/CSGUnion'; +import { Command_ClosePt } from '../Add-on/closetest'; import { Command_Copy } from '../Add-on/Copy'; +import { CopyClip } from '../Add-on/CopyClip'; +import { Command_CopyPoint } from '../Add-on/CopyPoint'; import { CustomUcs } from '../Add-on/CostumUCS'; +import { Union } from '../Add-on/CSGUnion'; import { DrawArc } from '../Add-on/DrawArc'; import { Command_DrawBoard } from '../Add-on/DrawBoard'; import { DrawAlignedDimension } from '../Add-on/DrawDim/DrawAlignedDimension'; @@ -14,6 +17,7 @@ import { DrawFloor } from '../Add-on/DrawFloor'; import { DrawGripStretch } from '../Add-on/DrawGripStretch'; import { DrawCircle, DrawLine, DrawSphere, DrawTest, ZoomE } from '../Add-on/DrawLine'; import { DrawPolyline } from '../Add-on/DrawPolyline'; +import { DrawRect } from '../Add-on/DrawRec'; import { DrawRegion } from '../Add-on/DrawRegion'; import { DrawSky } from '../Add-on/DrawSky'; import { DrawSpline } from '../Add-on/DrawSpline'; @@ -26,41 +30,40 @@ import { Command_Explode } from '../Add-on/Explode'; import { Command_Extend } from '../Add-on/Extends'; import { CommandFillet } from '../Add-on/Fillet'; import { Command_GenerateCode } from '../Add-on/GenerateCode'; +import { Command_INsTest } from '../Add-on/instest'; import { Command_Join } from '../Add-on/Join'; +import { Command_Lisp } from '../Add-on/Lisp'; +import { Fbx } from '../Add-on/loadfbx'; import { LoadImg } from '../Add-on/LoadImg'; import { Command_Move } from '../Add-on/Move'; import { Command_Offset, Command_TestOffset } from '../Add-on/Offset'; +import { OffsetX } from '../Add-on/OffsetX'; import { Open } from '../Add-on/Open'; +import { PasteClip } from '../Add-on/PasteClip'; import { Pedit } from '../Add-on/Pedit'; +import { Command_PLTest } from '../Add-on/polytest'; +import { Command_PtInCu } from '../Add-on/ptincu'; import { Command_RevPl } from '../Add-on/RevPl'; import { Command_Rotate } from '../Add-on/Rotate'; import { Save } from '../Add-on/Save'; +import { Command_Ssget } from '../Add-on/ssget'; import { Stretch } from '../Add-on/Stretch'; import { Command_SwitchCamera } from '../Add-on/SwitchCamera'; import { Command_SwitchPass } from '../Add-on/SwitchPass'; +// import { DrawFloor } from '../Add-on/DrawFloor'; +// import { RevTarget, SaveTarget } from '../Add-on/RenderTarget'; +import { TestIntersect } from '../Add-on/test/testIntersect'; import { Command_TestBox } from '../Add-on/TestBox'; import { Command_DrawBoard2 } from '../Add-on/TestDrawBoard'; import { TestDrawDirectionalLight } from '../Add-on/TestDrawDirectionalLight'; import { TestDrawPointLight } from '../Add-on/TestDrawPointLight'; import { TestDrawSpotLight } from '../Add-on/TestDrawSpotLight'; +import { TestTargeOnCurve } from '../Add-on/testEntity/TestCurve'; import { Command_Trim } from '../Add-on/Trim'; import { Redo, Undo } from '../Add-on/Undo'; import { ViewToFront, ViewToRight, ViewToTop } from '../Add-on/ViewChange'; -import { Command_ClosePt } from '../Add-on/closetest'; -import { Command_INsTest } from '../Add-on/instest'; -import { Fbx } from '../Add-on/loadfbx'; -import { Command_PLTest } from '../Add-on/polytest'; -import { Command_PtInCu } from '../Add-on/ptincu'; -import { Command_Ssget } from '../Add-on/ssget'; -// import { DrawFloor } from '../Add-on/DrawFloor'; -// import { RevTarget, SaveTarget } from '../Add-on/RenderTarget'; -import { TestIntersect } from '../Add-on/test/testIntersect'; -import { TestBoolOperationUtil } from '../Add-on/testEntity/TestBoolOperation'; import { Command_DimTest } from '../DatabaseServices/Dimension/dimTest'; import { commandMachine } from './CommandMachine'; -import { DrawRect } from '../Add-on/DrawRec'; -import { CopyClip } from '../Add-on/CopyClip'; -import { PasteClip } from '../Add-on/PasteClip'; export function registerCommand() { @@ -126,6 +129,8 @@ export function registerCommand() commandMachine.RegisterCommand("copy", new Command_Copy()); + commandMachine.RegisterCommand("lisp", new Command_Lisp()); + commandMachine.RegisterCommand("rev", new Command_RevPl()); commandMachine.RegisterCommand("ex", new Command_Extend()); @@ -142,8 +147,12 @@ export function registerCommand() commandMachine.RegisterCommand("ptl", new TestDrawPointLight()); + commandMachine.RegisterCommand("pt", new Command_CopyPoint()); + commandMachine.RegisterCommand("dl", new TestDrawDirectionalLight()); + commandMachine.RegisterCommand("oo", new OffsetX()); + commandMachine.RegisterCommand("sl", new TestDrawSpotLight()); commandMachine.RegisterCommand("dal", new DrawAlignedDimension()); commandMachine.RegisterCommand("dli", new DrawLinearDimension()); @@ -171,7 +180,7 @@ export function registerCommand() commandMachine.RegisterCommand("incu", new Command_PtInCu()); commandMachine.RegisterCommand("code", new Command_GenerateCode()); - commandMachine.RegisterCommand("testbool", new TestBoolOperationUtil()); + commandMachine.RegisterCommand("outcur", new TestTargeOnCurve()); commandMachine.RegisterCommand("join", new Command_Join()); commandMachine.RegisterCommand("testInt", new TestIntersect()); diff --git a/src/Editor/KeyBoardControls.ts b/src/Editor/KeyBoardControls.ts index a24c27d88..5dbd2ac13 100644 --- a/src/Editor/KeyBoardControls.ts +++ b/src/Editor/KeyBoardControls.ts @@ -1,7 +1,6 @@ import { KeyBoard } from '../Common/KeyEnum'; export class KeyBoardControls { - private m_KeyDownMap = new Map(); constructor() { diff --git a/src/Editor/SnapServices.ts b/src/Editor/SnapServices.ts index ff62ede16..9c9449591 100644 --- a/src/Editor/SnapServices.ts +++ b/src/Editor/SnapServices.ts @@ -151,8 +151,10 @@ export class SnapServices let vcsP = app.m_Editor.m_MouseCtrl.m_CurMousePointVCS; for (let obj of app.m_Viewer.Scene.children) { - if (!this.notSnapEntity.has(obj.userData) && obj.userData && obj.userData instanceof Entity) + if (obj.userData && obj.userData instanceof Entity) { + if (this.notSnapEntity.has(obj.userData)) continue; + if (obj.userData.IsErase) continue; for (let p of obj.userData.GetSnapPoints()) { let pv = p.clone(); diff --git a/src/Geometry/CurveIntersection.ts b/src/Geometry/CurveIntersection.ts new file mode 100644 index 000000000..8d6e2ddd5 --- /dev/null +++ b/src/Geometry/CurveIntersection.ts @@ -0,0 +1,88 @@ +import { Box3, Vector3 } from "three"; +import { Curve } from "../DatabaseServices/Curve"; +import { IntersectOption } from "../GraphicsSystem/IntersectWith"; +import { greater } from "./GeUtils"; + +/** + * + * 简化优化版本的曲线求交, 优化版本可以参考(算法导论33.2 确定任意一对线段是否相交 p599) + * + * @class CurveIntersection + */ +export class CurveIntersection +{ + //用来缓存的曲线包围盒 + private boxMap: Map = new Map(); + + /** + * 交点数据集,key 为曲线 value 为和它相交的(曲线和交点的Map) + * + * @type {Map>} + * @memberof CurveIntersection + */ + intersect: Map> = new Map(); + + /** + * @param {Curve[]} cus 请注意数组的顺序会被更改,如果你在意数组的顺序,请拷贝数组后传进来 + * @memberof CurveIntersection + */ + constructor(cus: Curve[]) + { + this.genBox(cus); + //按x排序 + this.sortCurve(cus); + + let count = cus.length; + for (let i = 0; i < count; i++) + { + let c1 = cus[i]; + let c1d = this.GetIntersect(c1); + + let c1b = this.boxMap.get(c1); + for (let j = i + 1; j < count; j++) + { + let c2 = cus[j]; + + //过滤掉不需要计算的曲线 + let c2b = this.boxMap.get(c2); + + if (greater(c2b.min.x, c1b.max.x, 1e-6)) + break; + if (greater(c2b.min.y, c1b.max.y, 1e-6)) + continue; + + let pts = c1.IntersectWith(c2, IntersectOption.OnBothOperands); + if (pts.length > 0) + { + c1d.set(c2, pts); + this.GetIntersect(c2).set(c1, pts); + } + } + } + } + private genBox(cus: Curve[]) + { + cus.forEach(c => + { + this.boxMap.set(c, c.BoundingBox); + }); + } + + private sortCurve(cus: Curve[]) + { + cus.sort((c1, c2) => + { + return this.boxMap.get(c1).min.x - this.boxMap.get(c2).min.x; + }); + } + + GetIntersect(cu: Curve): Map + { + if (this.intersect.has(cu)) + return this.intersect.get(cu); + + let m = new Map(); + this.intersect.set(cu, m); + return m; + } +} diff --git a/src/Geometry/CurveMap.ts b/src/Geometry/CurveMap.ts index 71a9c8bda..6c43cb2ea 100644 --- a/src/Geometry/CurveMap.ts +++ b/src/Geometry/CurveMap.ts @@ -72,7 +72,7 @@ export class CurveMap */ private GenerateP(v: Vector3): Vector3 { - let str = v.toArray().map(v => v.toFixed(4)).join(","); + let str = v.toArray().map(v => v.toFixed(3)).join(","); if (this.m_vecMap.has(str)) return this.m_vecMap.get(str); this.m_vecMap.set(str, v); diff --git a/src/Geometry/GeUtils.ts b/src/Geometry/GeUtils.ts index 92d076e09..e4bd69367 100644 --- a/src/Geometry/GeUtils.ts +++ b/src/Geometry/GeUtils.ts @@ -26,11 +26,18 @@ export function equaln(v1: number, v2: number, fuzz = 1e-3) { return Math.abs(v1 - v2) < fuzz; } -export function equal(v1: T, v2: T) +export function equal(v1: T, v2: T, fuzzSq = 1e-8) { - return v1.distanceToSquared(v2) < 1e-8; + return v1.distanceToSquared(v2) < fuzzSq; +} +export function less(v1, v2, fuzz = 0) +{ + return v1 - v2 < fuzz; +} +export function greater(v1, v2, fuzz = 0) +{ + return v1 - v2 > fuzz; } - export function fixAngle(an: number, fixAngle: number, fuzz: number = 0.1) { if (an < 0) diff --git a/src/Geometry/SortEntityByBox.ts b/src/Geometry/SortEntityByBox.ts new file mode 100644 index 000000000..3a18a1385 --- /dev/null +++ b/src/Geometry/SortEntityByBox.ts @@ -0,0 +1,34 @@ +import { Box3 } from "three"; +import { equaln } from "./GeUtils"; + +export interface EBox +{ + BoundingBox: Box3; +} + +/** + * 根据盒子x排序盒子 + * + * @export + * @param {EBox[]} arr + */ +export function SortEntityByBox(arr: EBox[], sort: boolean = true) +{ + let boxMap: Map = new Map(); + arr.forEach(e => boxMap.set(e, e.BoundingBox)); + + if (sort) + arr.sort((e1, e2) => + { + let b1 = boxMap.get(e1); + let b2 = boxMap.get(e2); + if (!equaln(b1.min.x, b2.min.x)) + return b1.min.x - b2.min.x; + else + { + return b2.min.y - b1.min.y; + } + }) + + return boxMap; +} diff --git a/src/GraphicsSystem/BoolOperateUtils.ts b/src/GraphicsSystem/BoolOperateUtils.ts index 398697465..e55999af5 100644 --- a/src/GraphicsSystem/BoolOperateUtils.ts +++ b/src/GraphicsSystem/BoolOperateUtils.ts @@ -13,14 +13,15 @@ export enum BoolOpeartionType } //判断曲线是否在源封闭曲线内 -export function isTargetCurInSourceCur(sourceCur: Polyline | Circle | Ellipse, targetCur: Curve) +export function isTargetCurInOrOnSourceCur(sourceCur: Polyline | Circle | Ellipse, targetCur: Curve) { let pts = []; getIntPtContextPts(sourceCur, targetCur, pts); - if (pts.length === 0) + if (pts.length <= 1) { - pts.push(targetCur.StartPoint); + pts.push(targetCur.StartPoint, targetCur.EndPoint); } + return IsPtsAllInOrOnReg(sourceCur, pts); } @@ -37,7 +38,6 @@ function getIntPtContextPts(sourceCur: Curve, cu: Curve, pts: Vector3[]) par <= (cu.EndParam - 0.02) && pts.push(cu.GetPointAtParam(par + 0.01)); }) } - } //判断点点是否全部都在封闭区域内或者在曲线上 function IsPtsAllInOrOnReg(sourceReg: Polyline | Circle | Ellipse, pts: Vector3[]) @@ -45,6 +45,16 @@ function IsPtsAllInOrOnReg(sourceReg: Polyline | Circle | Ellipse, pts: Vector3[ return pts.every(pt => { //是否点在封闭曲线内 - return sourceReg.PtInCurve(pt) || sourceReg.PtOnCurve(pt); + return sourceReg.PtOnCurve(pt) || sourceReg.PtInCurve(pt); + }) +} + +//判断点是否全部都在封闭区域外或者在曲线上 +export function IsPtsAllOutOrOnReg(sourceReg: Polyline | Circle, pts: Vector3[]) +{ + return pts.every(pt => + { + //是否点在封闭曲线内 + return sourceReg.PtOnCurve(pt) || !sourceReg.PtInCurve(pt); }) } diff --git a/src/GraphicsSystem/CameraUpdate.ts b/src/GraphicsSystem/CameraUpdate.ts index fba645d6d..772054e53 100644 --- a/src/GraphicsSystem/CameraUpdate.ts +++ b/src/GraphicsSystem/CameraUpdate.ts @@ -2,6 +2,11 @@ import * as THREE from 'three'; import { Vector3 } from 'three'; import { Orbit } from '../Geometry/Orbit'; +const ViewScopeSize = 4e4; +//相机活动范围 +const ViewScopeMin = new Vector3(-ViewScopeSize, -ViewScopeSize * 0.7, -ViewScopeSize); +const ViewScopeMax = ViewScopeMin.clone().negate(); + /** * * 相机的控制. @@ -30,12 +35,16 @@ export class CameraUpdate //观察的轨道. private m_Orbit: Orbit = new Orbit(); + //最大最小视口高度 + m_MinViewHeight = 1e-3; + m_MaxViewHeight = 3e4; + constructor() { this.m_CameraArray.set(THREE.OrthographicCamera, new THREE.OrthographicCamera(-2, 2, 2, -2, - -10000, 10000)); + -ViewScopeSize, ViewScopeSize)); - this.m_CameraArray.set(THREE.PerspectiveCamera, new THREE.PerspectiveCamera(50, 1, 0.01, 10000)); + this.m_CameraArray.set(THREE.PerspectiveCamera, new THREE.PerspectiveCamera(50, 1, 0.01, ViewScopeSize)); this.m_CurCamera = this.m_CameraArray.get(THREE.OrthographicCamera); @@ -60,7 +69,7 @@ export class CameraUpdate } set ViewHeight(height) { - this.m_ViewHeight = THREE.Math.clamp(height, 1e-8, 1e8); + this.m_ViewHeight = THREE.Math.clamp(height, this.m_MinViewHeight, this.m_MaxViewHeight); } SetSize(width: number, height: number) @@ -81,6 +90,7 @@ export class CameraUpdate mouseMove.multiplyScalar(-this.m_ViewHeight / this.m_Height); mouseMove.applyQuaternion(this.Camera.quaternion); this.m_Target.add(mouseMove); + this.m_Target.clamp(ViewScopeMin, ViewScopeMax); this.Update(); } Rotate(mouseMove: THREE.Vector3, target: THREE.Vector3) @@ -114,7 +124,7 @@ export class CameraUpdate if (this.Camera instanceof THREE.OrthographicCamera) { this.ViewHeight *= scale; - if (scaleCenter) + if (scaleCenter && this.m_ViewHeight < this.m_MaxViewHeight) { this.m_Target.sub(scaleCenter); this.m_Target.multiplyScalar(scale); @@ -131,7 +141,7 @@ export class CameraUpdate } ZoomExtensBox3(box3: THREE.Box3) { - if (!box3) return; + if (!box3 || box3.isEmpty()) return; this.Camera.updateMatrixWorld(false); //变换到相机坐标系 box3.applyMatrix4(this.Camera.matrixWorldInverse); @@ -148,11 +158,11 @@ export class CameraUpdate // if (aspectRatio > viewAspectRatio) { - this.m_ViewHeight = size.x / viewAspectRatio; + this.ViewHeight = size.x / viewAspectRatio; } else { - this.m_ViewHeight = size.y; + this.ViewHeight = size.y; } this.Update(); } diff --git a/src/GraphicsSystem/IntersectWith.ts b/src/GraphicsSystem/IntersectWith.ts index cc67cb2bc..890e2bff4 100644 --- a/src/GraphicsSystem/IntersectWith.ts +++ b/src/GraphicsSystem/IntersectWith.ts @@ -76,8 +76,10 @@ export function IntersectCircleAndCircle(cu1: Circle | Arc, cu2: Circle | Arc) let pts: Vector3[] = []; let dist = center2.distanceTo(center1); - if (dist > (radius1 + radius2 + 1e-3)) + if (dist < Math.abs(radius1 - radius2) + || dist > (radius1 + radius2 + 1e-3)) return pts; + if (equaln(dist, 0, 1e-6)) return pts; let dstsqr = dist * dist; let r1sqr = radius1 * radius1; @@ -155,6 +157,8 @@ function IntersectLineAndCircleOrArc(line: Line, circle: Circle | Arc) let radius = circle.Radius; let a = endPoint.distanceToSquared(startPoint); + if (a == 0) + return []; let lineV = endPoint.clone().sub(startPoint); let lineToCir = startPoint.clone().sub(center); @@ -163,7 +167,7 @@ function IntersectLineAndCircleOrArc(line: Line, circle: Circle | Arc) let c = center.lengthSq() + startPoint.lengthSq() - 2 * center.dot(startPoint) - radius * radius; let det = b * b - 4 * a * c; - if (equaln(det, 0, 1e-5)) + if (equaln(det, 0, 1e-4)) { let delta = -b / (2 * a); return [startPoint.add(lineV.multiplyScalar(delta))]; @@ -177,7 +181,10 @@ function IntersectLineAndCircleOrArc(line: Line, circle: Circle | Arc) delta = (-b - sqrt_det) / (2 * a); let p3 = startPoint.clone().add(lineV.multiplyScalar(delta)); - return [p2, p3]; + if (!equal(p2, p3, 1e-5)) + return [p2, p3]; + else + return [p2]; } return []; } @@ -238,15 +245,18 @@ export function IntersectLAndLFor3D(p1: Vector3, p2: Vector3, p3: Vector3, p4: V let v1 = p2.clone().sub(p1).normalize(); let v2 = p4.clone().sub(p3).normalize(); - let w = p3.clone().sub(p1); - if (Math.abs(v1.dot(v2)) === 1) + let w = p3.clone().sub(p1).normalize(); + + if (equaln(Math.abs(v1.dot(v2)), 1, 1e-6) && (equal(w, new Vector3()) || equaln(Math.abs(v1.dot(w)), 1, 1e-6))) //平行共线 { - let tmpLine = new Line(p1, p2); - let par = tmpLine.GetParamAtPoint(p3); - if (par) - { - pt = midPoint(midPoint(p1, p2), midPoint(p3, p4)); - } + let pts = [p1, p2, p3, p4]; + pts.sort(comparePoint('xyz')); + pt = midPoint(pts[1], pts[2]); + + } + else if (equaln(Math.abs(v1.dot(v2)), 1, 1e-6)) //平行不共线 + { + return undefined; } else if (equaln(getDeterminantFor3V(v1, v2, w), 0, 0.01)) { @@ -267,6 +277,59 @@ export function IntersectLAndLFor3D(p1: Vector3, p2: Vector3, p3: Vector3, p4: V } return pt; } +export function IntersectLAndLFor3D1(p1: Vector3, p2: Vector3, p3: Vector3, p4: Vector3) +{ + let x12 = p1.x - p2.x; + let x43 = p4.x - p3.x; + let x42 = p4.x - p2.x; + let y12 = p1.y - p2.y; + let y43 = p4.y - p3.y; + let y42 = p4.y - p2.y; + let z12 = p1.z - p2.z; + let z43 = p4.z - p3.z; + let z42 = p4.z - p2.z; + + let pts: Vector3[] = []; + let v1 = p2.clone().sub(p1).normalize(); + let v2 = p4.clone().sub(p3).normalize(); + let w = p3.clone().sub(p1).normalize(); + + if (equaln(Math.abs(v1.dot(v2)), 1, 1e-6) && equaln(Math.abs(v1.dot(w)), 1, 1e-6)) //平行共线 + { + let tmpPts = [p1, p2, p3, p4]; + tmpPts.sort(comparePoint('xyz')); + if (equal(tmpPts[1], p3) || equal(tmpPts[1], p4)) + { + pts = [tmpPts[1], tmpPts[2]]; + } + else + { + pts = [midPoint(tmpPts[1], tmpPts[2])] + } + } + else if (equaln(Math.abs(v1.dot(v2)), 1, 1e-6)) //平行不共线 + { + return []; + } + else if (equaln(getDeterminantFor3V(v1, v2, w), 0, 0.01)) + { + let t: number; + if (x12 * y43 - y12 * x43) + { + t = (x42 * y43 - x43 * y42) / (x12 * y43 - y12 * x43); + } + else if (x12 * z43 - x43 * z12) + { + t = (x42 * z43 - x43 * z42) / (x12 * z43 - x43 * z12); + } + else + { + t = (y42 * z43 - y43 * z42) / (y12 * z43 - y43 * z12); + } + pts = [new Vector3(x12 * t + p2.x, y12 * t + p2.y, z12 * t + p2.z)] + } + return pts; +} //直线和直线 export function IntersectLineAndLine(l1: Line, l2: Line, extType: IntersectOption) { @@ -307,7 +370,7 @@ export function IntersectPolylineAndCurve(pl: Polyline, cu: Curve, extType: Inte if ((cu instanceof Polyline && cu.CloseMark) || !(isStart2 || isEnd2)) ext = ext & ~IntersectOption.ExtendArg; - let ipts = cu1.IntersectWith(cu2, ext); + let ipts = cu1.IntersectWith(cu2, ext).filter(p1 => pts.every(p2 => !equal(p1, p2))); //校验延伸 if (IntersectOption.ExtendThis & ext) diff --git a/src/GraphicsSystem/LinkSelft.ts b/src/GraphicsSystem/LinkSelft.ts new file mode 100644 index 000000000..5b8ec26c7 --- /dev/null +++ b/src/GraphicsSystem/LinkSelft.ts @@ -0,0 +1,293 @@ +import { Vector3 } from "three"; +import { Curve } from "../DatabaseServices/Curve"; +import { CurveIntersection } from "../Geometry/CurveIntersection"; +import { CurveMap } from "../Geometry/CurveMap"; +import { equal, equaln } from "../Geometry/GeUtils"; +import { Stand } from "../Geometry/RegionParse"; + +export class LinkSelf +{ + private curveUseData: WeakMap = new WeakMap(); + private curveIndexData: WeakMap = new WeakMap(); + sealCus: Set[] = []; + noSealCus: Curve[][] = []; + private m_Count; + private cuMap: CurveMap; + constructor(cus: Curve[]) + { + this.m_Count = cus.length; + //打断曲线 + let breakCus: Curve[] = this.BreakCurve(cus); + // breakCus.forEach(c => + // { + // c.ColorIndex = 3; + // app.m_Database.ModelSpace.Append(c); + // }) + + //曲线图 用来快速搜索求交 + this.cuMap = this.GenerateCurveMap(breakCus); + + //自交曲线表 + this.CalCloseCurve(breakCus, this.cuMap); + + //非自交曲线连接表 + this.CalOrderLink(breakCus, this.cuMap); + } + + //打断曲线 并且保存了曲线的索引 + private BreakCurve(cus: Curve[]): Curve[] + { + let insMap = new CurveIntersection(cus.concat()); + //打断后存储的曲线列表 + let breakCus: Curve[] = []; + // let count = cus.length; + for (let i = 0; i < this.m_Count; i++) + { + let cu = cus[i]; + //交点数据 + let insData = insMap.GetIntersect(cu); + let insPts: Vector3[] = []; + for (let [, pts] of insData) + { + insPts.push(...pts); + } + + let params = insPts.map(p => cu.GetParamAtPoint(p)); + let spCus = cu.GetSplitCurves(params); + if (spCus.length === 0) + { + this.SetCurveIndex(cu, i); + breakCus.push(cu); + } + else + { + for (let c of spCus) + { + if (!equaln(c.Length, 0, 1e-6)) + { + this.SetCurveIndex(c, i); + breakCus.push(c); + } + } + } + } + return breakCus; + } + + //计算闭合的区域 + private CalCloseCurve(breakCus: Curve[], cuMap: CurveMap) + { + let breakCount = breakCus.length; + + //搜索闭合自交 + for (let i = 0; i < breakCount; i++) + { + let cu = breakCus[i]; + if (this.GetCurveUse(cu)) continue; + let cuIndex = this.GetCurveIndex(cu); + let stand = cuMap.GetStand(cu.EndPoint); + + let routeCus = new Set(); + + for (let route of stand.routes) + { + //缓存走过的节点 + let ways: Stand[] = []; + ways.push(cuMap.GetStand(cu.StartPoint)); + + if (equal(route.curve.EndPoint, stand.position)) + continue; //如果方向相反,那么退出计算 (不符合) + + let routeIndex = this.GetCurveIndex(route.curve); + if (routeIndex === cuIndex) + continue;//自己不和自己计算 + if (routeIndex < cuIndex) + continue;//不和小于自己索引的计算 + + //如果存在闭合区域 + if (this.FindCloseCurve(route.to, cuIndex, ways, routeCus, cuMap)) + { + routeCus.add(route.curve); + routeCus.add(cu); + //提取闭合区域 + for (let c of routeCus) + { + this.SetCurveUse(c); + } + this.sealCus.push(routeCus); + break; + } + } + } + + } + + + //顺序连接. + private CalOrderLink(breakCus: Curve[], cuMap: CurveMap) + { + let breakCount = breakCus.length; + //剩下没有被自交 + for (let i = 0; i < breakCount; i++) + { + let cu = breakCus[i]; + if (this.GetCurveUse(cu)) continue; + // let cuIndex = this.GetCurveIndex(cu); + this.SetCurveUse(cu); + let cs = [cu]; + //顺序连接 + this.linkCurve(cu, cuMap, cs); + //拿出第一条进行倒序连接 + cu = cs[0]; + this.linkCurve(cu, cuMap, cs, true); + } + } + linkCurve(originCu: Curve, cuMap: CurveMap, cs: Curve[], isInv: boolean = false) + { + //FIXME:两线共线时连接有问题 + let cuIndex = this.GetCurveIndex(originCu); + while (true) + { + let oldCount = cs.length; + let routes = cuMap.GetStand(isInv ? originCu.StartPoint : originCu.EndPoint).routes; + + //把索引相等的站点最后遍历 + for (let i = 0; i < routes.length; i++) + { + let index = this.GetCurveIndex(routes[i].curve); + if (index === cuIndex) + { + routes.unshift(routes.splice(i, 1)[0]); + } + } + + for (let j = routes.length; j--;) //按照索引从小到大搜索 + { + let cu2 = routes[j].curve; + if (this.GetCurveUse(cu2)) continue; + + //能够连接 + let isLink = isInv ? equal(cu2.EndPoint, originCu.StartPoint) : equal(cu2.StartPoint, originCu.EndPoint); + if (isLink) + { + this.SetCurveUse(cu2); + isInv ? cs.unshift(cu2) : cs.push(cu2); + originCu = cu2; + cuIndex = this.GetCurveIndex(cu2); + break; + } + } + + //如果没找到 则退出循环 + if (oldCount === cs.length) + { + !isInv && this.noSealCus.push(cs); + break; + } + } + } + //寻找最小索引的路线 + private FindMinRoute(nowStand: Stand, nowIndex: number, cs: Curve[], routes: Curve[]): { minIndex: number, routes: Curve[] } + { + //和当前索引一样的 搜索结果 + let curIndex: { minIndex: number, routes: Curve[] } = undefined; + for (let j = nowStand.routes.length; j--;) //按照索引从小到大搜索 + { + let cu = nowStand.routes[j].curve; + let cuIndex = this.GetCurveIndex(cu); + if (this.GetCurveUse(cu)) continue; + if (!equal(cu.StartPoint, nowStand.position)) continue; + if (nowIndex === cuIndex) + { + let routes2 = routes.concat();//复制数组保证函数有唯一结果 + routes2.push(cu); + curIndex = this.FindMinRoute(this.cuMap.GetStand(cu.EndPoint), nowIndex, cs, routes2); + continue; + } + + if (cuIndex < nowIndex) //小于 回归自身 + if (cs.findIndex((c) => this.GetCurveIndex(c) === cuIndex) === -1) + continue; + + if (curIndex && curIndex.minIndex < cuIndex) + return curIndex; + + //这里不复制数组,因为数组不会再被改变 + routes.push(cu); + return { minIndex: cuIndex, routes }; + } + + return { minIndex: Infinity, routes }; + } + + GetCurveUse(curve: Curve): boolean + { + return this.curveUseData.get(curve); + } + SetCurveUse(curve: Curve, use: boolean = true) + { + this.curveUseData.set(curve, use); + } + GetCurveIndex(curve: Curve): number + { + return this.curveIndexData.get(curve); + } + SetCurveIndex(curve: Curve, index: number) + { + this.curveIndexData.set(curve, index); + } + + //搜索闭合的区域 + private FindCloseCurve(nowStand: Stand, cuIndex: number, ways: Stand[], routes: Set, cuMap: CurveMap) + { + for (let route of nowStand.routes) + { + if (equal(route.curve.EndPoint, nowStand.position)) + continue; //如果方向相反,那么退出计算 (不符合) + + if (this.GetCurveUse(route.curve)) continue; + + let routeIndex = this.GetCurveIndex(route.curve); + if (routeIndex === cuIndex) + continue;//自己不和自己计算 + if (routeIndex < cuIndex) + continue;//不和小于自己索引的计算 + + //验证通过,可以连接. + let toStand = route.to; + + routes.add(route.curve); + if (ways[0] === toStand)//自交闭合 + return true; + else if (ways.indexOf(toStand) !== -1)//中途回路 + return false; + else + { + ways.push(toStand); + if (this.FindCloseCurve(toStand, routeIndex, ways, routes, cuMap)) + return true; + else + { + routes.delete(route.curve); + return false; + } + } + } + return false; + } + + GenerateCurveMap(breakCus: Curve[]) + { + let cuMap = new CurveMap(); + breakCus.forEach(c => cuMap.addCurveToMap(c)); + //所有的站点 逆序排序. + for (let [, stand] of cuMap.m_NodeMap) + { + stand.routes.sort((r1, r2) => + { + return this.GetCurveIndex(r2.curve) - this.GetCurveIndex(r1.curve); //从大到小 + }); + } + return cuMap; + } +} diff --git a/src/GraphicsSystem/OffsetPolyline.ts b/src/GraphicsSystem/OffsetPolyline.ts new file mode 100644 index 000000000..2ecd4032d --- /dev/null +++ b/src/GraphicsSystem/OffsetPolyline.ts @@ -0,0 +1,750 @@ +import { Box3, Vector3 } from "three"; +import { arrayLast, arrayRemoveDuplicateBySort, arrayRemoveIf, arraySortByNumber } from "../Common/ArrayExt"; +import { curveLinkGroup, GetPointAtCurveDir } from "../Common/CurveUtils"; +import { FixIndex } from "../Common/Utils"; +import { Arc } from "../DatabaseServices/Arc"; +import { Circle } from "../DatabaseServices/Circle"; +import { Contour } from '../DatabaseServices/Contour'; +import { Curve } from "../DatabaseServices/Curve"; +import { Line } from "../DatabaseServices/Line"; +import { Polyline } from '../DatabaseServices/Polyline'; +import { equal, equaln } from "../Geometry/GeUtils"; +import { EBox, SortEntityByBox } from "../Geometry/SortEntityByBox"; +import { IsPtsAllOutOrOnReg } from "./BoolOperateUtils"; +import { IntersectOption } from "./IntersectWith"; + +interface offsetRes +{ + index: number; + curve: Curve; +} + +export class PolyOffsetUtil +{ + private m_Polyline: Polyline; + private m_OffsetDist: number; + private m_Contours: Contour[] = [];//构建的轮廓 + private m_RetCurves: Curve[] = [];//第一步修剪的数组 + //偏移距离平方值 + private m_dist2: number; + //偏移距离绝对值 + private m_AbsDist: number; + //不用裁剪的线段 + private unNeedCutCus: Curve[] = []; + //源线段点数量 + private m_PtCount: number; + + constructor(pl: Polyline, offset: number) + { + this.m_Polyline = pl; + this.m_OffsetDist = offset; + this.m_dist2 = Math.pow(offset, 2); + this.m_AbsDist = Math.abs(this.m_OffsetDist); + this.m_PtCount = pl.EndParam; + } + GetOffsetCurves(): Curve[] + { + let expCus = this.m_Polyline.Explode(); + let offres = this.OffestCurve(expCus); + + //不闭合曲线传入首尾点构建圆轮廓 + if (!this.m_Polyline.IsClose) + { + let cir1 = new Circle(this.m_Polyline.StartPoint, this.m_AbsDist); + let cir2 = new Circle(this.m_Polyline.EndPoint, this.m_AbsDist); + this.m_Contours.push(Contour.CreateContour([cir1])); + this.m_Contours.push(Contour.CreateContour([cir2])); + } + + //连接修剪并构建轮廓 + this.TrimAndBuildContour(offres); + // 裁剪并优化的曲线 + let { boxCurves, outputCus } = this.trimByContours(this.m_RetCurves); + + //最后也加入优化数组,避免出错 + this.unNeedCutCus.forEach(cu => + { + boxCurves.set(cu, cu.BoundingBox); + outputCus.push(cu); + }) + + this.m_RetCurves = this.optimizeCus(boxCurves, outputCus); + + + this.m_RetCurves = this.linkCurves(this.m_RetCurves); + + // 如果源线段闭合只保留闭合的部分 + if (this.m_Polyline.IsClose) + return this.m_RetCurves.filter(c => c.IsClose); + + return this.m_RetCurves; + } + private CheckPointDir(pt: Vector3) + { + let dir = GetPointAtCurveDir(this.m_Polyline, pt) ? 1 : -1; + return dir === Math.sign(this.m_OffsetDist); + } + + /** + * + *优化裁剪后的曲线 + * @private + * @param {Map} boxCurves 曲线对应的包围盒列表 + * @param {Curve[]} outputCus 待优化曲线 + * @returns + * @memberof PolyOffestUtil + */ + private optimizeCus(boxCurves: Map, outputCus: Curve[]) + { + //过滤掉无效的线段 + outputCus = outputCus.filter(c => + { + //与源线段自交 + if (c.IntersectWith(this.m_Polyline, IntersectOption.OnBothOperands).length !== 0) + return false; + //删除在反方向的无效线段 + return this.CheckPointDir(c.StartPoint) || this.CheckPointDir(c.EndPoint); + }); + + //处理自交的线段,先根据包围盒排序 + outputCus.sort((e1, e2) => + { + let b1 = boxCurves.get(e1); + let b2 = boxCurves.get(e2); + if (!equaln(b1.min.x, b2.min.x)) + return b1.min.x - b2.min.x; + else + { + return b2.min.y - b1.min.y; + } + }) + + // 寻找自交线段 + for (let i = 0; i < outputCus.length; i++) + { + let c1 = outputCus[i]; + let c1b = boxCurves.get(c1); + + for (let j = i + 1; j < outputCus.length; j++) + { + let c2 = outputCus[j]; + let c2b = boxCurves.get(c2); + + //过滤掉不需要计算的曲线 + if (c2b.min.x - 1e-6 > c1b.max.x) + break; + if (c2b.min.y - 1e-6 > c1b.max.y) + continue; + + let c1StartPt = c1.StartPoint; + let c1EndPt = c1.EndPoint; + let c2StartPt = c2.StartPoint; + let c2EndPt = c2.EndPoint; + + //被包含的直线删掉 + if (c1 instanceof Line && c2 instanceof Line) + { + //c1完全在c2内 + if (c2.PtOnCurve(c1StartPt) && c2.PtOnCurve(c1EndPt)) + { + outputCus.splice(i, 1); + i--; + break; + } + else if (c1.PtOnCurve(c2StartPt) && c1.PtOnCurve(c2EndPt)) + { + // c2完全在c1内 + outputCus.splice(j, 1); + j--; + continue; + } + } + + //如果线段端点相连,跳过 + let isLink = [c1StartPt, c1EndPt].some(p => equal(p, c2StartPt) || equal(p, c2EndPt)); + if (isLink) continue; + + let pts = c1.IntersectWith(c2, IntersectOption.OnBothOperands); + if (pts.length > 0) + { + //按照从左到右,从上到下,分别取前后部分,即去掉中间部分 + [c1, c2].forEach((c, index) => + { + let cs = c.GetSplitCurvesByPts(pts); + if (cs.length === 2) + { + let box = SortEntityByBox(cs, true); + outputCus[index === 0 ? i : j] = cs[index] + boxCurves.set(cs[index], box.get(cs[index])); + } + }) + + } + } + } + return outputCus; + } + //偏移曲线 + private OffestCurve(pls: Curve[]): offsetRes[] + { + return pls.map((cu, index) => + { + let curve = cu.GetOffsetCurves(this.m_OffsetDist)[0]; + return { curve, index }; + }); + } + + /** + * 根据2曲线或者一曲线一点构造轮廓 + * @private + * @param {Curve} cu + * @param {Curve} [cuOrPt] + * @returns + * @memberof PolyOffestUtil + */ + private BuildContour(cu: Curve, cuOrPt: Curve | Vector3): Contour + { + if (cuOrPt instanceof Curve) + { + let l1 = new Line(cu.StartPoint, cuOrPt.StartPoint); + let l2 = new Line(cu.EndPoint, cuOrPt.EndPoint); + + //若2曲线都是圆弧,构建轮廓可能裁剪掉圆弧,把被裁剪掉的圆弧存入数组跳过后续运算 + if (cu instanceof Arc && cuOrPt instanceof Arc) + { + //被起点连线裁剪放入前面那段,被终点连线裁剪放入后面那段 + [l1, l2].forEach((l, i) => + { + let pts = l.IntersectWith(cuOrPt, 0); + if (pts.length === 2) + { + let splitCus = cuOrPt.GetSplitCurvesByPts(pts); + this.unNeedCutCus.push(splitCus[i]) + } + }) + } + //防止轮廓自交 + if (l1.IntersectWith(l2, 0).length > 0) + { + l1 = new Line(cu.StartPoint, cuOrPt.EndPoint); + l2 = new Line(cu.EndPoint, cuOrPt.StartPoint); + } + return Contour.CreateContour([ + cu.Clone() as Curve, + cuOrPt.Clone() as Curve, + l1, + l2 + ]); + } + else + { + let l1 = new Line(cu.StartPoint, cuOrPt); + let l2 = new Line(cu.EndPoint, cuOrPt); + return Contour.CreateContour([ + cu, + l1, + l2 + ]); + } + } + + /** + *通过源线段索引或者点构建圆轮廓 + * @private + * @param {(number | Vector3)} iV 源线段索引或者圆心 + * @param {Contour[]} cons + * @memberof PolyOffestUtil + */ + private BuildCircleContour(iV: number | Vector3, cons: Contour[]) + { + let center: Vector3; + if (iV instanceof Vector3) + center = iV; + else + center = this.m_Polyline.GetPointAtParam(iV); + + let cir = new Circle(center, this.m_AbsDist) + cons.push(Contour.CreateContour([cir])); + } + /** + * 连接修剪相邻曲线,并构造修剪轮廓 + * + * @private + * @param {offsetRes[]} offResList + * @memberof PolyOffestUtil + */ + private TrimAndBuildContour(offResList: offsetRes[]) + { + arrayRemoveIf(offResList, r => !r.curve || equaln(r.curve.Length, 0, 1e-6)); + + if (offResList.length <= 1) + { + this.m_RetCurves = offResList.map(r => r.curve); + return; + } + let cirContours: Contour[] = []; + //下一线段起始点 + let nextStartPt: Vector3 = offResList[0].curve.StartPoint; + for (let i = 0; i < offResList.length; i++) + { + //源线段对应索引 + let startIndex = offResList[i].index; + //后面线对应源线段索引 + let endIndex = offResList[FixIndex(i + 1, offResList)].index; + + //前面线 + let frontLine = offResList[i].curve; + //后面线 + let laterLine: Curve; + + if (i === 0 && !this.m_Polyline.IsClose) + { + if (startIndex !== 0) + { + this.BuildCircleContour(1, cirContours); + } + } + + //3.如果曲线不不闭合,那么最后一段时候退出,如果最后一段丢失,添加圆轮廓 + if (i === offResList.length - 1 && !this.m_Polyline.IsClose) + { + if (startIndex !== this.m_PtCount - 1) + { + this.BuildCircleContour(startIndex + 1, cirContours); + } + this.appendNewCuAndContour(frontLine, nextStartPt, frontLine.EndPoint, startIndex); + break; + } + laterLine = offResList[FixIndex(i + 1, offResList)].curve; + + + //#region 1.中间丢失线段的情况下且循环到尾部,补圆弧. + let isFillArc = FixIndex(startIndex + 1, this.m_PtCount) !== endIndex; + + if (isFillArc) + { + //在丢失圆的地方构建圆轮廓 + for (let i = startIndex + 1; ; i++) + { + let index = FixIndex(i, this.m_PtCount); + this.BuildCircleContour(index, cirContours); + if (index === endIndex) break; + } + //丢失圆弧时在丢失圆弧的起点构造一个圆 + if (startIndex > endIndex) + { + this.fillArc(startIndex, endIndex, nextStartPt, frontLine, laterLine); + continue; + } + } + //#endregion + + //#region 2.修剪延伸,根据真假交点 + + let iPts: Vector3[]; //延伸交点 + let tPts: Vector3[]; //都在两条线上的为真交点 + if (equal(frontLine.EndPoint, laterLine.StartPoint)) + tPts = [frontLine.EndPoint]; + else + { + iPts = frontLine.IntersectWith(laterLine, IntersectOption.ExtendBoth); + //过滤掉交点为laterline终点的交点 + tPts = iPts.filter(p => + frontLine.PtOnCurve(p) + && laterLine.PtOnCurve(p) + ); + } + + if (tPts.length > 0)//存在真交点情况下直接修剪 + { + let iPt = this.selectFitInterPt(tPts, frontLine.EndPoint); + if (isFillArc) + { + //用丢失圆弧和交点构建扇形轮廓 + for (let i = startIndex + 1; ; i++) + { + let index = FixIndex(i, this.m_PtCount); + let cu = this.m_Polyline.GetCurveAtParam(index); + cirContours.push(this.BuildContour(cu, iPt)); + + if (index === endIndex - 1) break; + } + } + this.appendNewCuAndContour(frontLine, nextStartPt, iPt, startIndex); + } + else if (isFillArc || iPts.length === 0)//连交点都没或者圆弧丢失补圆弧 + { + this.fillArc(startIndex, endIndex, nextStartPt, frontLine, laterLine); + } + else //iPts.length > 0 有交点,但是都是假交点. + { + let iPt = this.selectFitInterPt(iPts, frontLine.EndPoint); + //通过真假交点连接 + this.checkCuAndAppendList(frontLine, laterLine, iPt, nextStartPt, startIndex, endIndex); + } + //#endregion + } + + //曲线闭合的时,修改第一条的起点,更新第一个轮廓 + if (this.m_Polyline.IsClose) + { + this.m_RetCurves[0].StartPoint = nextStartPt; + this.m_Contours[0] = this.BuildContour( + this.m_Polyline.GetCurveAtParam(offResList[0].index), + this.m_RetCurves[0]); + }; + this.m_Contours.push(...cirContours); + } + + /** + * 将线段拷贝并修改,加入到结果数组里面 + * 构建轮廓加入轮廓数组. + * + * @param {Curve} offsetedCurve 偏移后的曲线 + * @param {Vector3} startPoint 修改曲线的起点,修改后将起点设置为终点 + * @param {Vector3} endPoint 修改曲线的中点 + * @param {number} index 偏移前该线段的索引 + * @memberof PolyOffestUtil + */ + appendNewCuAndContour(offsetedCurve: Curve, + startPoint: Vector3, + endPoint: Vector3, + index: number + ) + { + //复制一条新曲线,修改起始点和终止点,并修改下一段的起始点 + let newCu = offsetedCurve.Clone() as Curve; + newCu.StartPoint = startPoint; + newCu.EndPoint = endPoint; + startPoint.copy(endPoint); + this.m_RetCurves.push(newCu); + let originCu = this.m_Polyline.GetCurveAtParam(index); + this.m_Contours.push(this.BuildContour(originCu, newCu)); + } + + /** + * 检测偏移线段是否有效并加入结果曲线数组 + * 已经排除点都是真交点的情况 + * @private + * @param {Curve} frontLine + * @param {Curve} laterLine + * @param {Vector3} intPt + * @param {Vector3} nextPt + * @param {number} endIndex + * @memberof PolyOffestUtil + */ + private checkCuAndAppendList(frontLine: Curve, laterLine: Curve, intPt: Vector3, nextPt: Vector3, startIndex: number, endIndex: number) + { + let par1 = frontLine.GetParamAtPoint(intPt); + + //2段都是圆弧和其他情况分开判断 + if (frontLine instanceof Arc && laterLine instanceof Arc) + { + let isOnFline = frontLine.PtOnCurve(intPt); + let isOnLline = laterLine.PtOnCurve(intPt); + //交点均不在2圆弧上,直接加入结果数组,否则补圆弧连接 + if (!isOnFline && !isOnLline) //可能 isOnFline || isOnLline + { + this.appendNewCuAndContour(frontLine, nextPt, intPt, startIndex); + } + else + { + this.fillArc(startIndex, endIndex, nextPt, frontLine, laterLine); + } + } + else + { + let par2 = laterLine.GetParamAtPoint(intPt); + if (par1 > 1) + { + //laterline是圆弧且都是正假交点,补圆弧,否则连接 + if (par2 > 0 && laterLine instanceof Arc) + { + this.fillArc(startIndex, endIndex, nextPt, frontLine, laterLine); + } + else + { + this.appendNewCuAndContour(frontLine, nextPt, intPt, startIndex); + } + } + else if (frontLine instanceof Arc) + { + //其余情况如果frontline是Arc,补圆弧,否则补直线 + this.fillArc(startIndex, endIndex, nextPt, frontLine, laterLine); + } + else//frontLine is Line + { + this.appendNewCuAndContour(frontLine, nextPt, frontLine.EndPoint, startIndex); + this.m_RetCurves.push(new Line(frontLine.EndPoint, laterLine.StartPoint)); + nextPt.copy(laterLine.StartPoint); + } + } + } + /** + * 补圆弧 + * + * @param {number} startIndex 源线段索引 + * @param {number} endIndex 源线段索引 + * @param {Vector3} nextPt 修改曲线的起点,修改后将起点设置为终点 + * @param {Curve} frontLine + * @param {Curve} laterLine + * @returns + * @memberof PolyOffestUtil3 + */ + fillArc(startIndex: number, endIndex: number, + nextPt: Vector3, + frontLine: Curve, laterLine: Curve + ) + { + let cirs: Circle[] = []; //需要补的圆列表 + for (let i = startIndex + 1; ; i++) + { + let index = FixIndex(i, this.m_PtCount); + let center = this.m_Polyline.GetPointAtParam(index); + cirs.push(new Circle(center, this.m_AbsDist)); + + if (index === endIndex) break; + } + //如果和第二个圆(消失圆弧终点构建圆)相交,则直接连接,否则从第一个圆开始,圆数量要大于1 + let iPts = cirs.length > 1 ? frontLine.IntersectWith(cirs[1], 0) : []; + if (iPts.length === 0) + { + //第一个圆肯定和frontline相切,切点为frontline的终点 + iPts = [frontLine.EndPoint]; + } + else + { + cirs.shift(); + } + + //#region 避免错误块 + //假设我们认为连接一定能成功,那么下面的if不是必要的 + // if (!iPts) + // { + // console.error("补圆失败,距离为", this.m_OffestDist); + // } + //#endregion + + let iPt = this.selectFitInterPt(iPts, frontLine.EndPoint); + + this.appendNewCuAndContour(frontLine, nextPt, iPt, startIndex); + + //连接剩下的圆. + //可能的情况 (|)()(|)() 竖线为直线 + for (let index = 0; index < cirs.length; index++) + { + let c1 = cirs[index];//已经计算过的圆 + + let iPtsLater: Vector3[]; + if (index === cirs.length - 1) + iPtsLater = [laterLine.StartPoint]; + else + { + //和前面圆的交点需要大于偏移距离才能提前相交 + iPtsLater = c1.IntersectWith(laterLine, 0).filter(p => + { + let dist = p.distanceToSquared(this.m_Polyline.GetClosestPointTo(p, false)); + return dist + 1e-3 > this.m_dist2; + }); + } + + if (iPtsLater.length > 0)//直接和最后一条连接 + { + let iPt = this.selectFitInterPt(iPtsLater, nextPt); + this.buildArcJoinList(c1, nextPt, iPt, FixIndex(startIndex + 1, this.m_PtCount) === endIndex); + return; + } + + let c2 = cirs[index + 1]; + + let iPts = c1.IntersectWith(c2, 0); + let iPt = this.selectFitInterPt(iPts, nextPt); + + this.buildArcJoinList(c1, nextPt, iPt, FixIndex(startIndex + 1, this.m_PtCount) === endIndex); + } + } + + /** + * 构建补圆弧 + * @param {Circle} cir 补圆 + * @param {Vector3} startPt 补圆的切割点1 + * @param {Vector3} endPt 补圆的切割点2 + * @param {boolean} isbuildCir 是否构建圆轮廓 + * @returns + * @memberof PolyOffestUtil + */ + buildArcJoinList(cir: Circle, startPt: Vector3, endPt: Vector3, isbuildCir: boolean) + { + let splitCus = cir.GetSplitCurvesByPts([startPt, endPt]); + + startPt.copy(endPt); + + if (splitCus.length === 2) //2圆相交应该有2段,否则相切 + { + let arc1 = splitCus[0] as Arc; + let arc2 = splitCus[1] as Arc; + + let tmpPts = cir.IntersectWith(this.m_Polyline, IntersectOption.OnBothOperands); + let onCu0Pts = tmpPts.filter(p => arc1.PtOnCurve(p)); + let onCu1Pts = tmpPts.filter(p => arc2.PtOnCurve(p)); + + let lastCu = arrayLast(this.m_RetCurves); + + //让圆弧保持和最后一段首尾相连 + if (!equal(arc1.StartPoint, lastCu.EndPoint)) + arc1.Reverse(); + if (!equal(arc2.StartPoint, lastCu.EndPoint)) + arc2.Reverse(); + + //优先选择和源线段不想交的圆弧,如果都相交或者都不相交,选择和最后一段切线接近的圆弧 + let cu: Arc; + if (onCu0Pts.length === onCu1Pts.length) + { + let derv = lastCu.GetFistDeriv(1); + let derv1 = arc1.GetFistDeriv(0); + let derv2 = arc2.GetFistDeriv(0); + cu = derv.angleTo(derv1) < derv.angleTo(derv2) ? arc1 : arc2; + } + else + { + cu = onCu0Pts.length < onCu1Pts.length ? arc1 : arc2; + } + + this.m_RetCurves.push(cu); + + isbuildCir && this.BuildCircleContour(cu.Center, this.m_Contours); + } + } + // 通过构建的轮廓对偏移曲线进行裁剪 + private trimByContours(needCutCus: Curve[]) + { + let boxContours = SortEntityByBox(this.m_Contours, false); + let boxCurves = SortEntityByBox(needCutCus, false); + this.m_Contours.forEach(c => + { + let tmpCus: Curve[] = []; + let outline = c.Outline; + for (let l of needCutCus) + { + if (boxCurves.get(l).min.x > boxContours.get(c).max.x + || boxCurves.get(l).max.x < boxContours.get(c).min.x) + { + tmpCus.push(l); + continue; + } + + //交点参数列表 + let iParams = l.IntersectWith(outline, IntersectOption.OnBothOperands) + .map(p => l.GetParamAtPoint(p)); + arraySortByNumber(iParams); + arrayRemoveDuplicateBySort(iParams, equaln); + + //需要计算的点列表 + let needCaclPts: Vector3[] = []; + if (iParams.length === 0) + needCaclPts = [l.StartPoint] + else + { + for (let i = 0; i < iParams.length - 1; i++) + { + needCaclPts.push(l.GetPointAtParam((iParams[i] + iParams[i + 1]) / 2)); + } + //如果交点不是首尾点,就加入首尾点 + if (!equaln(iParams[0], 0, 1e-6)) + needCaclPts.unshift(l.StartPoint); + if (!equaln(arrayLast(iParams), 1, 1e-6)) + needCaclPts.push(l.EndPoint); + } + + //切割曲线,缓存切割后曲线包围盒 + if (IsPtsAllOutOrOnReg(outline, needCaclPts)) + { + tmpCus.push(l); + } + else + { + let cus = l.GetSplitCurves(iParams); + + //移除0长度线和在轮廓内的线. + arrayRemoveIf(cus, cu => + equaln(cu.Length, 0, 1e-6) + || outline.PtInCurve(cu.GetPointAtParam(0.5)) + ); + cus.forEach(c => boxCurves.set(c, c.BoundingBox)) + tmpCus.push(...cus); + } + } + needCutCus = tmpCus; + }) + + return { boxCurves, outputCus: needCutCus }; + } + + /** + * 连接最终曲线 + * @private + * @param {Curve[]} cus + * @returns {Polyline[]} + * @memberof PolyOffestUtil + */ + private linkCurves(cus: Curve[]): Polyline[] + { + let groups = curveLinkGroup(cus); + let resultPls: Polyline[] = []; + for (let g of groups) + { + let pl = new Polyline(); + for (let cu of g) + { + pl.Join(cu); + } + resultPls.push(pl) + } + return resultPls; + } + + /** + * 选择合适的交点 + * 优先选择交点在偏移方向上同一侧的 + * 其次选择交点距离源线段为偏移距离的,若都是或者都不是,选择离传入点近的 + * @private + * @param {Vector3[]} pts + * @param {Vector3} refPt + * @returns + * @memberof PolyOffestUtil + */ + private selectFitInterPt(pts: Vector3[], refPt: Vector3) + { + if (pts.length > 1) + { + let rad2 = Math.pow(this.m_OffsetDist, 2); + + //优先选择交点在偏移方向上同一侧的 + let pt1Dir = this.CheckPointDir(pts[0]); + let pt2Dir = this.CheckPointDir(pts[1]); + if (pt1Dir !== pt2Dir) + { + return pt1Dir ? pts[0] : pts[1]; + } + + //计算到曲线的距离大于偏移距离 + let [ptGtRad1, ptGtRad2] = pts.map(p => + { + let dis = this.m_Polyline.GetClosestPointTo(p, false).distanceToSquared(p); + return dis + 1e-3 > rad2; + }); + + //都大于或者都小于`偏移距离` + if (ptGtRad1 === ptGtRad2) + { + let dist1 = refPt.distanceToSquared(pts[0]); + let dist2 = refPt.distanceToSquared(pts[1]); + return dist1 <= dist2 ? pts[0] : pts[1]; + } + else + return ptGtRad1 ? pts[0] : pts[1]; + } + + return pts[0]; + } +} diff --git a/src/GraphicsSystem/Viewer.ts b/src/GraphicsSystem/Viewer.ts index 2a728c3ca..18375ccd1 100644 --- a/src/GraphicsSystem/Viewer.ts +++ b/src/GraphicsSystem/Viewer.ts @@ -129,8 +129,8 @@ export class Viewer onSize = (width?, height?) => { - this._Width = width ? width : this.m_DomEl.scrollWidth; - this._Height = height ? height : this.m_DomEl.scrollHeight; + this._Width = width ? width : this.m_DomEl.clientWidth; + this._Height = height ? height : this.m_DomEl.clientHeight; //校验.成为2的倍数 避免外轮廓错误. if (this._Width % 2 == 1) diff --git a/src/Loader/FBXLoader.js b/src/Loader/FBXLoader.js index 32e90abd6..94cd5c319 100644 --- a/src/Loader/FBXLoader.js +++ b/src/Loader/FBXLoader.js @@ -4365,11 +4365,11 @@ if ( window.Zlib === undefined ) { - // throw new Error( 'THREE.FBXLoader: External library Inflate.min.js required, obtain or import from https://github.com/imaya/zlib.js' ); + throw new Error( 'THREE.FBXLoader: External library Inflate.min.js required, obtain or import from https://github.com/imaya/zlib.js' ); } - var inflate = new threeZlib.Zlib.Inflate( new Uint8Array( reader.getArrayBuffer( compressedLength ) ) ); + var inflate = new Zlib.Inflate( new Uint8Array( reader.getArrayBuffer( compressedLength ) ) ); var reader2 = new BinaryReader( inflate.decompress().buffer ); switch ( type ) { diff --git a/src/Loader/FBXLoader.ts b/src/Loader/FBXLoader.ts index 727d88215..6ca175a55 100644 --- a/src/Loader/FBXLoader.ts +++ b/src/Loader/FBXLoader.ts @@ -2,8 +2,6 @@ import * as THREE from "three"; require('three-FBXLoader'); -window["threeZlib"] = require("zlib") - export interface LoaderState { State: boolean, @@ -54,4 +52,4 @@ export function loadFBX(fileName: string): Promise } ) }); -} \ No newline at end of file +} diff --git a/src/UI/Components/SourceManage/FilePanel.tsx b/src/UI/Components/SourceManage/FilePanel.tsx index 515924036..86213cf72 100644 --- a/src/UI/Components/SourceManage/FilePanel.tsx +++ b/src/UI/Components/SourceManage/FilePanel.tsx @@ -105,6 +105,10 @@ export class FilePanel extends React.Component fileData.title = fname; //切换当前图纸id store.m_CurrentFileId = fid; + + //用于打开上一次打开的图 + window.sessionStorage.setItem("fid", fid); + let file = await store.Get(StoreName.Dwg, fid); if (fileData.isNew || !file) diff --git a/src/UI/Components/commandLineInput/InputHint.css b/src/UI/Components/commandLineInput/InputHint.css index 3c5f1c00c..e12c9f97a 100644 --- a/src/UI/Components/commandLineInput/InputHint.css +++ b/src/UI/Components/commandLineInput/InputHint.css @@ -71,9 +71,9 @@ a { } #input-hint ul { - position: absolute; + position: fixed; left: -1rem; - bottom: 2rem; + bottom: 3.3rem; list-style: none; width: 10rem; background: #fff; diff --git a/src/UI/Components/signComponent/login.tsx b/src/UI/Components/signComponent/login.tsx index 196c09dda..eba285052 100644 --- a/src/UI/Components/signComponent/login.tsx +++ b/src/UI/Components/signComponent/login.tsx @@ -26,7 +26,7 @@ export default class Login extends React.Component { onClose={() => this.props.toggleDialog()} title="请登录" > -
+
e.stopPropagation()}>