diff --git a/__test__/Geometry/__snapshots__/intersect.test.ts.snap b/__test__/Geometry/__snapshots__/intersect.test.ts.snap index 3547efee7..518443fbd 100644 --- a/__test__/Geometry/__snapshots__/intersect.test.ts.snap +++ b/__test__/Geometry/__snapshots__/intersect.test.ts.snap @@ -1,5 +1,151 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`三维空间圆圆相交测试 1`] = ` +Array [ + Vector3 { + "x": 3.8465654731285666, + "y": -0.4898400510314999, + "z": 0.3777515851723181, + }, + Vector3 { + "x": 4.518686772594937, + "y": -2.2643579789591692, + "z": -0.09066385360916451, + }, +] +`; + +exports[`三维空间圆圆相交测试 2`] = ` +Array [ + Vector3 { + "x": 1.25, + "y": -4.841229182759271, + "z": 0, + }, + Vector3 { + "x": 1.25, + "y": 4.841229182759271, + "z": 0, + }, +] +`; + +exports[`三维空间圆圆相交测试 3`] = ` +Array [ + Vector3 { + "x": 1.25, + "y": 0, + "z": -4.841229182759271, + }, + Vector3 { + "x": 1.25, + "y": 0, + "z": 4.841229182759271, + }, +] +`; + +exports[`三维空间圆圆相交测试 4`] = ` +Array [ + Vector3 { + "x": 0, + "y": -11.313088368701884, + "z": -9.556275638274851, + }, + Vector3 { + "x": 0, + "y": -9.579281670837858, + "z": -2.8986776905615708, + }, +] +`; + +exports[`三维空间圆圆相交测试 5`] = `Array []`; + +exports[`三维空间直线和圆相交测试 1`] = ` +Array [ + Vector3 { + "x": 7.265045456946408, + "y": 0, + "z": 1.514708484572001, + }, + Vector3 { + "x": 3.93574588263578, + "y": 0, + "z": 4.789429377336553, + }, +] +`; + +exports[`三维空间直线和圆相交测试 2`] = ` +Array [ + Vector3 { + "x": -3.622627029705465, + "y": -3.4462404738565997, + "z": 0, + }, + Vector3 { + "x": -3.622627029705464, + "y": 3.4462404738566006, + "z": 0, + }, +] +`; + +exports[`三维空间直线和圆相交测试 3`] = ` +Array [ + Vector3 { + "x": 9.796270630192112, + "y": -13.16305455920182, + "z": 4.841809080303142, + }, + Vector3 { + "x": 9.796270630192113, + "y": -6.270573611488618, + "z": 4.841809080303142, + }, +] +`; + +exports[`三维空间直线和圆相交测试 4`] = ` +Array [ + Vector3 { + "x": 12.384261138056765, + "y": -13.163054559201814, + "z": 6.346016491847222, + }, + Vector3 { + "x": 12.384261138056765, + "y": -6.270573611488626, + "z": 6.346016491847221, + }, +] +`; + +exports[`三维空间直线相交测试 1`] = ` +Vector3 { + "x": 5, + "y": 5, + "z": 5, +} +`; + +exports[`三维空间直线相交测试 2`] = ` +Vector3 { + "x": 4.75, + "y": 4.75, + "z": 4.75, +} +`; + +exports[`三维空间直线相交测试 3`] = ` +Vector3 { + "x": 5, + "y": 5, + "z": 5, +} +`; + exports[`相交测试 1`] = ` Vector3 { "x": 0.5, diff --git a/__test__/Geometry/intersect.test.ts b/__test__/Geometry/intersect.test.ts index 43f34b545..d618982c3 100644 --- a/__test__/Geometry/intersect.test.ts +++ b/__test__/Geometry/intersect.test.ts @@ -1,6 +1,12 @@ import * as THREE from 'three'; -import { IntersectLAndL } from '../../src/GraphicsSystem/IntersectWith'; +import { IntersectLAndLFor3D, IntersectCircleAndCircle, IntersectOption } from '../../src/GraphicsSystem/IntersectWith'; +import { Vector3 } from 'three'; +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 { Line } from '../../src/DatabaseServices/Line'; test('相交测试', () => { @@ -12,19 +18,102 @@ test('相交测试', () => let p5 = new THREE.Vector3(3, 0, 0); let p6 = new THREE.Vector3(6, 0, 0); - let res = IntersectLAndL(p1, p2, p3, p4);/*?*/ + let res = IntersectLAndLFor3D(p1, p2, p3, p4);/*?*/ expect(res).toMatchSnapshot(); - res = IntersectLAndL(p1, p2, p5, p6);/*?*/ + res = IntersectLAndLFor3D(p1, p2, p5, p6);/*?*/ expect(res).toMatchSnapshot(); - let ins = IntersectLAndL( + let ins = IntersectLAndLFor3D( new THREE.Vector3(0, 5), new THREE.Vector3(5, 5), new THREE.Vector3(0.5, 1), new THREE.Vector3(0.5, 8)); expect(ins).toMatchSnapshot(); +}) +test('三维空间直线相交测试', () => +{ + let p1 = new THREE.Vector3(); + let p2 = new THREE.Vector3(10, 10, 10); + let p3 = new THREE.Vector3(5, 5, 5); + let p4 = new THREE.Vector3(5, 10, 5); + + let p5 = new THREE.Vector3(3, 3, 3); + let p6 = new THREE.Vector3(6, 6, 6); + + let res = IntersectLAndLFor3D(p1, p2, p3, p4);/*?*/ + expect(res).toMatchSnapshot(); + + res = IntersectLAndLFor3D(p1, p2, p5, p6);/*?*/ + expect(res).toMatchSnapshot(); + res = IntersectLAndLFor3D(p2, p6, p3, p4);/*?*/ + expect(res).toMatchSnapshot(); + + res = IntersectLAndLFor3D( + new Vector3(1, 1, 1), + new Vector3(1, 1, 4), + new Vector3(2, 2, 2), + new Vector3(2, 2, 10) + ) + expect(res).toBeUndefined(); + res = IntersectLAndLFor3D( + new Vector3(1, 0, 1), + new Vector3(1, 0, 4), + new Vector3(0.5, 0, 3), + new Vector3(0.5, 0, 10) + ) + expect(res).toBeUndefined(); +}) +test('三维空间圆圆相交测试', () => +{ + let data = [["Circle", 1, 1, 3, false, 7, -1, [-0.1485675258840154, -0.98346692600856, 0.10353982663678746, 0, 0.8068528994682419, -0.18108783903750572, -0.5623127183093208, 0, 0.5717657639911589, 0, 0.8204169129946106, 0, 5.585923427922135, -0.5874251796993211, -0.834443475807827, 1], 1, 2.1223349919617873], ["Circle", 1, 1, 9, false, 7, -1, [-0.1485675258840154, -0.98346692600856, 0.10353982663678746, 0, 0.8068528994682419, -0.18108783903750572, -0.5623127183093208, 0, 0.5717657639911589, 0, 0.8204169129946106, 0, 3.2540542870196947, -1.8996318241962369, 0.7906850652516981, 1], 1, 1.5840127550993974]] + testCirAndCirIntersect(data); + data = [["Circle", 1, 1, 3, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 1, 5], ["Circle", 1, 1, 4, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2.5, 0, 0, 1], 1, 5]]; + testCirAndCirIntersect(data); + data = [["Circle", 1, 1, 5, false, 7, -1, [1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1], 1, 5], ["Circle", 1, 1, 6, false, 7, -1, [1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 2.5, 0, 0, 1], 1, 5]]; + testCirAndCirIntersect(data); + data = [["Arc", 1, 1, 10, false, 7, -1, [0, -1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, -7.223901135674274, -7.066640793063616, 1], 2, 4.787455889013074, 2.4468553886273967, 0.7203768859081977, true], ["Arc", 1, 1, 11, false, 7, -1, [0, -1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, -12.887397630639091, -5.59172322768673, 1], 2, 4.265691675276281, 3.020045238916937, 0.9631807673370143, true]] + testCirAndCirIntersect(data); + data = [["Circle", 1, 1, 4, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2.5, 0, 0, 1], 1, 5], ["Circle", 1, 1, 6, false, 7, -1, [1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 2.5, 0, 0, 1], 1, 5]]; + testCirAndCirIntersect(data); +}) +test('三维空间直线和圆相交测试', () => +{ + let data = [["Circle", 1, 1, 6, false, 7, -1, [1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 2.5, 0, 0, 1], 1, 5], ["Line", 1, 1, 12, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 1, [2.4219301113484804, 0, 6.278428496635537], [9.19486541121855, 0, -0.38347507700715716]]] + testLineAndCirIntersect(data); + data = [["Circle", 1, 1, 3, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 1, 5], ["Line", 1, 1, 13, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 1, [-3.6226270297054635, 5.273119429622456, 0], [-3.6226270297054652, -5.297100907223948, 0]]]; + testLineAndCirIntersect(data); + data = [["Circle", 1, 1, 3, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 13.418897659897576, -9.71681408534522, 4.841809080303142, 1], 1, 5], ["Line", 1, 1, 13, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 1, [9.796270630192113, -4.443694655722764, 4.841809080303142], [9.796270630192112, -15.013914992569168, 4.841809080303142]]]; + testLineAndCirIntersect(data); + data = [["Circle", 1, 1, 3, false, 7, -1, [0.8517840265050032, 0, -0.5238930923298417, 0, 0, 1, 0, 0, 0.5238930923298417, 0, 0.8517840265050032, 0, 15.469956975945145, -9.71681408534522, 4.4481472148971575, 1], 1, 5], ["Line", 1, 1, 13, false, 7, -1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 1, [12.384261138056765, -4.443694655722764, 6.346016491847221], [12.384261138056765, -15.013914992569168, 6.346016491847222]]]; + testLineAndCirIntersect(data); +}) + + +function loadFile(data) +{ + let file = new CADFile(); + file.Data = data; + let cus: Curve[] = []; + for (let i = 0; i < file.Data.length; i++) + { + cus.push(file.ReadObject(undefined) as Curve); + } + return cus; } -) +function testCirAndCirIntersect(data) +{ + let cus = loadFile(data) as Array; + let pts = IntersectCircleAndCircle(cus[0], cus[1]); + expect(pts).toMatchSnapshot(); +} +function testLineAndCirIntersect(data) +{ + let cus = loadFile(data); + let [cu1, cu2] = cus[0] instanceof Line ? cus : cus.reverse(); + let pts = cu1.IntersectWith(cu2, IntersectOption.ExtendBoth); + expect(pts).toMatchSnapshot(); +} + diff --git a/__test__/Line/__snapshots__/line.test.ts.snap b/__test__/Line/__snapshots__/line.test.ts.snap index e1c8f30d1..892a51696 100644 --- a/__test__/Line/__snapshots__/line.test.ts.snap +++ b/__test__/Line/__snapshots__/line.test.ts.snap @@ -34,7 +34,7 @@ Vector3 { exports[`最近点 1`] = ` Vector3 { - "x": -2.5, + "x": -2.499999999999999, "y": -2.499999999999999, "z": 0, } diff --git a/__test__/Polyline/__snapshots__/split.test.ts.snap b/__test__/Polyline/__snapshots__/split.test.ts.snap index d8c0a23b1..cdf059072 100644 --- a/__test__/Polyline/__snapshots__/split.test.ts.snap +++ b/__test__/Polyline/__snapshots__/split.test.ts.snap @@ -277,131 +277,6 @@ Array [ ] `; -exports[`单刀 6`] = ` -Array [ - Object { - "bul": 0, - "pt": Vector2 { - "x": 0, - "y": 0, - }, - }, - Object { - "bul": 1, - "pt": Vector2 { - "x": 5, - "y": 0, - }, - }, - Object { - "bul": -0.41421356237309503, - "pt": Vector2 { - "x": 5, - "y": 5, - }, - }, - Object { - "bul": 0, - "pt": Vector2 { - "x": 2.5, - "y": 7.5, - }, - }, -] -`; - -exports[`单刀 7`] = ` -Array [ - Object { - "bul": -0.41421356237309503, - "pt": Vector2 { - "x": 2.5, - "y": 7.5, - }, - }, - Object { - "bul": 0, - "pt": Vector2 { - "x": 5, - "y": 10, - }, - }, - Object { - "bul": 0, - "pt": Vector2 { - "x": 0, - "y": 10, - }, - }, - Object { - "bul": 0, - "pt": Vector2 { - "x": 0, - "y": 0, - }, - }, -] -`; - -exports[`单刀 8`] = ` -Array [ - Object { - "bul": 0, - "pt": Vector2 { - "x": 0, - "y": 0, - }, - }, - Object { - "bul": 1, - "pt": Vector2 { - "x": 5, - "y": 0, - }, - }, - Object { - "bul": -1, - "pt": Vector2 { - "x": 5, - "y": 5, - }, - }, - Object { - "bul": 0, - "pt": Vector2 { - "x": 5, - "y": 10, - }, - }, -] -`; - -exports[`单刀 9`] = ` -Array [ - Object { - "bul": 0, - "pt": Vector2 { - "x": 5, - "y": 10, - }, - }, - Object { - "bul": 0, - "pt": Vector2 { - "x": 0, - "y": 10, - }, - }, - Object { - "bul": 0, - "pt": Vector2 { - "x": 0, - "y": 0, - }, - }, -] -`; - exports[`单刀闭合 1`] = ` Array [ Object { diff --git a/src/Add-on/DrawArc.ts b/src/Add-on/DrawArc.ts index e97f427fe..2e209a5fc 100644 --- a/src/Add-on/DrawArc.ts +++ b/src/Add-on/DrawArc.ts @@ -1,55 +1,44 @@ -import * as THREE from 'three'; -import { Vector3, Vector2 } from 'three'; - import { app } from '../ApplicationServices/Application'; +import { Arc } from '../DatabaseServices/Arc'; import { Command } from '../Editor/CommandMachine'; import { PromptStatus } from '../Editor/PromptResult'; -import { RenderType } from '../GraphicsSystem/Enum'; -import { Arc } from '../DatabaseServices/Arc'; export class DrawArc implements Command { async exec() { - let pt1 = app.m_Editor.m_MouseCtrl.m_CurMousePointWCS.clone(); - let pt2 = pt1; - let pt3 = pt2; - - app.m_Editor.UpdateScreen(); let ptRes = await app.m_Editor.GetPoint({ Msg: "请输入第一个点:" }); if (ptRes.Status != PromptStatus.OK) - { return; - } - pt1 = ptRes.Value; + let pt1 = ptRes.Value; let ptRes2 = await app.m_Editor.GetPoint({ Msg: "请输入第二个点:", BasePoint: pt1, AllowDrawRubberBand: true }); if (ptRes2.Status != PromptStatus.OK) - { return; - } - pt2 = ptRes2.Value; + let pt2 = ptRes2.Value; let arc = new Arc(); + arc.ApplyMatrix(app.m_Editor.UCSMatrix); + + app.m_Database.ModelSpace.Append(arc); app.m_Editor.AddNoSnapEntity(arc); - let ptRes3 = await app.m_Editor.GetPoint({ - Msg: "请输入第三个点:", Callback: (p) => + const updateArc = (p) => + { + if (!p.equals(pt2) && !p.equals(pt1)) { - if (!p.equals(pt2) && !p.equals(pt1)) - { - arc.FromThreePoint(pt1, pt2, p); - arc.Update(); - if (!arc.Id) - { - app.m_Database.ModelSpace.Append(arc); - } - } + arc.FromThreePoint(pt1, pt2, p); + arc.Update(); } - }); - if (ptRes.Status != PromptStatus.OK) - { - return; } - app.m_Database.hm.EndCmd(); + + let ptRes3 = await app.m_Editor.GetPoint({ + Msg: "请输入第三个点:", + Callback: updateArc + }); + + if (ptRes3.Status != PromptStatus.OK) + arc.Erase(); + else + updateArc(ptRes3.Value); } } diff --git a/src/Add-on/DrawLine.ts b/src/Add-on/DrawLine.ts index c2fbfeb6d..2725e395b 100644 --- a/src/Add-on/DrawLine.ts +++ b/src/Add-on/DrawLine.ts @@ -1,11 +1,9 @@ import * as THREE from 'three'; -import { Box3, Vector3 } from 'three'; +import { Vector3 } from 'three'; import { app } from '../ApplicationServices/Application'; -import { Vec3DTo2D } from '../Common/CurveUtils'; import { Arc } from '../DatabaseServices/Arc'; import { Circle } from '../DatabaseServices/Circle'; import { Line } from '../DatabaseServices/Line'; -import { Polyline } from '../DatabaseServices/Polyline'; import { Command } from '../Editor/CommandMachine'; import { PromptStatus } from '../Editor/PromptResult'; import { midPoint } from '../Geometry/GeUtils'; @@ -134,56 +132,6 @@ void main() { } */ -export class DrawRect implements Command -{ - async exec() - { - let ptRes = await app.m_Editor.GetPoint(); - if (ptRes.Status != PromptStatus.OK) - { - return; - } - - let p1: THREE.Vector3; - let p2: THREE.Vector3; - p1 = ptRes.Value; - - let rec = new Polyline(); - rec.CloseMark = true; - app.m_Database.ModelSpace.Append(rec); - - app.m_Editor.AddNoSnapEntity(rec); - - let box = new Box3(); - let updateRect = (p1, p2) => - { - box.setFromPoints([p2, p1]); - - let px1 = Vec3DTo2D(box.min); - let px3 = Vec3DTo2D(box.max); - let px2 = new THREE.Vector2(px3.x, px1.y); - let px4 = new THREE.Vector2(px1.x, px3.y); - - rec.LineData = [{ pt: px1, bul: 0 }, - { pt: px2, bul: 0 }, - { pt: px3, bul: 0 }, - { pt: px4, bul: 0 }]; - } - - ptRes = await app.m_Editor.GetPoint({ - Callback: (p) => - { - updateRect(p, p1); - } - }); - - if (ptRes.Status == PromptStatus.OK) - updateRect(ptRes.Value, p1); - else - rec.Erase(); - } -} - export class DrawCircle implements Command { async exec() @@ -192,7 +140,6 @@ export class DrawCircle implements Command Msg: "指定圆的圆心", KeyWordList: [{ key: "3P", msg: "三点" }, { key: "2P", msg: "二点" }, { key: "T", msg: "切点、切点、半径" }] }); - switch (ptRes.Status) { case PromptStatus.Cancel: @@ -228,7 +175,10 @@ export class DrawCircle implements Command async DrawCircleUseRadious(val: Vector3) { - let cir = new Circle(val, 1e-3); + let cir = new Circle(); + cir.ApplyMatrix(app.m_Editor.UCSMatrix); + cir.Center = val; + app.m_Database.ModelSpace.Append(cir); app.m_Editor.AddNoSnapEntity(cir); @@ -246,22 +196,25 @@ export class DrawCircle implements Command async DrawCicleUseTwoPoint() { - let cir = new Circle(app.m_Editor.m_MouseCtrl.m_CurMousePointWCS.clone(), 1e-3); let ptRes1 = await app.m_Editor.GetPoint({ Msg: "指定圆直径的第一个端点:", }); if (ptRes1.Status != PromptStatus.OK) return; + let cir = new Circle(); app.m_Database.ModelSpace.Append(cir); - app.m_Editor.AddNoSnapEntity(cir); let ptRes2 = await app.m_Editor.GetPoint({ Msg: "指定圆直径的第二个端点:", BasePoint: ptRes1.Value, AllowDrawRubberBand: true, - Callback: (v) => { cir.Radius = v.distanceTo(ptRes1.Value) / 2; cir.Center = midPoint(v, ptRes1.Value) }, + Callback: (v) => + { + cir.Radius = v.distanceTo(ptRes1.Value) / 2; + cir.Center = midPoint(v, ptRes1.Value) + }, }); if (ptRes2.Status === PromptStatus.OK) { @@ -269,22 +222,19 @@ export class DrawCircle implements Command cir.Center = midPoint(ptRes2.Value, ptRes1.Value); } else - { cir.Erase(); - } - - app.m_Editor.ClearSnapEntity(); } async DrawCicleUseThreePoint() { - let cir = new Circle(app.m_Editor.m_MouseCtrl.m_CurMousePointWCS.clone(), 1e-3); - let ar = new Arc(); + let ptRes1 = await app.m_Editor.GetPoint({ Msg: "指定圆上第一个点:" }); if (ptRes1.Status != PromptStatus.OK) return; + let cir = new Circle(); + let ar = new Arc(); app.m_Database.ModelSpace.Append(cir); let ptRes2 = await app.m_Editor.GetPoint({ @@ -300,7 +250,12 @@ export class DrawCircle implements Command Msg: "指定圆上第三个点:", BasePoint: ptRes2.Value, AllowDrawRubberBand: true, - Callback: (v) => { ar.FromThreePoint(ptRes1.Value, ptRes2.Value, v); cir.Radius = ar.Radius; cir.Center = ar.Center }, + Callback: (v) => + { + ar.FromThreePoint(ptRes1.Value, ptRes2.Value, v); + cir.Radius = ar.Radius; + cir.Center = ar.Center + }, }); if (ptRes3.Status === PromptStatus.OK) { @@ -309,11 +264,7 @@ export class DrawCircle implements Command cir.Center = ar.Center; } else - { cir.Erase(); - } - - app.m_Editor.ClearSnapEntity(); } async DrawCicleUseCutoffPointAndRadious() { diff --git a/src/Add-on/DrawPolyline.ts b/src/Add-on/DrawPolyline.ts index e0308618d..b683b21f6 100644 --- a/src/Add-on/DrawPolyline.ts +++ b/src/Add-on/DrawPolyline.ts @@ -1,9 +1,11 @@ -import { Vector3 } from 'three'; +import { Matrix4, Vector3, Vector2 } from 'three'; import { app } from '../ApplicationServices/Application'; -import { Vec2DTo3D, Vec3DTo2D, getCirAngleByChordAndTangent, rotateLine } from '../Common/CurveUtils'; +import { Vec2DTo3D, Vec3DTo2D, getCirAngleByChordAndTangent } from '../Common/CurveUtils'; import { Polyline, PolylineProps } from '../DatabaseServices/Polyline'; import { Command } from '../Editor/CommandMachine'; -import { PromptStatus } from '../Editor/PromptResult'; +import { PromptStatus, PromptSsgetResult } from '../Editor/PromptResult'; +import { rotatePoint } from '../Geometry/GeUtils'; +import { GetPointPrompt } from '../Common/InputState'; enum PolylineModel { @@ -11,157 +13,121 @@ enum PolylineModel Line = 1 } -export class DrawPolyline implements Command +export class DrawPolyline { - private m_Model; + model: PolylineModel = PolylineModel.Line; async exec() { - app.m_Editor.m_CommandStore.Prompt("请输入一个点:"); - let ptRes = await app.m_Editor.GetPoint({ Msg: "请输入第一个点:" }); - if (ptRes.Status != PromptStatus.OK) - { - return; - } - - this.m_Model = PolylineModel.Line; + this.model = PolylineModel.Line; - let basePt = ptRes.Value; - //存储基点 - let BasePts = [basePt]; - //多段线最终点 - let data: PolylineProps = { - pt: Vec3DTo2D(basePt), - bul: 0 - } - //储存多段线数据 - let lineProps = [data] + let pl = new Polyline(); + pl.ApplyMatrix(app.m_Editor.UCSMatrix); + app.m_Database.ModelSpace.Append(pl); + app.m_Editor.AddNoSnapEntity(pl); - let polyline = new Polyline(lineProps); + let Callback = (p: Vector3) => + this.UpdatePoint(pl, p); - app.m_Database.ModelSpace.Append(polyline); - // 切线 - let tangentLine: Vector3; - // 弦 - let chord: Vector3; - // 圆心角 - let cirAng: number; - // 圆弧方向 - let dir: Vector3; + let firstOps: GetPointPrompt = { Msg: "请输入第一个点:", Callback }; + let keywords = [{ msg: "圆弧", key: "A" }, { msg: "直线", key: "L" }, { msg: "放弃", key: "U" }]; + let keywords2 = keywords.concat([{ msg: "闭合", key: "C" }]); + let nextOps: GetPointPrompt = { Msg: "请点击下一个点或", Callback }; - let KeyWordList = [{ msg: "圆弧", key: "A" }, { msg: "直线", key: "L" }, { msg: "放弃", key: "U" }]; - //返回一步 - let restore = () => - { - lineProps.pop(); - polyline.LineData = lineProps; - } - // 更新多段线凸度 - let updateBul = (endPt: Vector3, startPt: Vector3) => - { - // 弦长矢量 - chord = endPt.clone().sub(startPt); - cirAng = getCirAngleByChordAndTangent(chord, tangentLine); - if (cirAng) - { - //凸度 - let bul = Math.tan(cirAng / 4); - lineProps[lineProps.length - 2].bul = bul - } - } while (true) { - data = { - pt: Vec3DTo2D(basePt), - bul: 0 - } - if (lineProps.length === 2) + let ops: GetPointPrompt; + if (pl.NumberOfVertices === 0) + ops = firstOps; + else { - KeyWordList.push({ msg: "闭合", key: "C" }) + ops = nextOps; + ops.BasePoint = pl.EndPoint; + if (pl.NumberOfVertices > 3) + nextOps.KeyWordList = keywords2; + else + nextOps.KeyWordList = keywords; } - lineProps.push(data); - app.m_Editor.m_CommandStore.Prompt("请输入点2:"); - ptRes = await app.m_Editor.GetPoint({ - Msg: "请输入点2:", - BasePoint: basePt, - AllowDrawRubberBand: true, - KeyWordList, - Callback: (v) => - { - if (this.m_Model === 0) - { - if (!tangentLine) tangentLine = new Vector3(1, 0, 0); - data.pt.set(v.x, v.y); - updateBul(v, basePt); - polyline.LineData = lineProps; - } - } - }); - if (ptRes.Status == PromptStatus.OK) + pl.AddVertexAt(pl.NumberOfVertices, new Vector2()); + let p = await app.m_Editor.GetPoint(ops); + + if (p.Status === PromptStatus.OK) + this.UpdatePoint(pl, p.Value); + else if (p.Status === PromptStatus.Keyword) { - data.pt.set(ptRes.Value.x, ptRes.Value.y) - if (this.m_Model === 1) + this.RemoveLastVertex(pl); + if (p.StringResult === "A") { - //切线 - tangentLine = ptRes.Value.clone().sub(basePt).normalize(); + this.model = PolylineModel.Arc; } - else + else if (p.StringResult === "L") { - updateBul(ptRes.Value, basePt); - tangentLine = rotateLine(tangentLine, cirAng) - + this.model = PolylineModel.Line; + if (pl.NumberOfVertices > 0) + pl.SetBulgeAt(pl.NumberOfVertices - 1, 0); } - polyline.LineData = lineProps; - BasePts.push(basePt); - basePt = ptRes.Value; - - continue; - } - else if (ptRes.Status == PromptStatus.Keyword) - { - //是否闭合曲线 - if (ptRes.StringResult == "C") + else if (p.StringResult === "U") { - data.pt = lineProps[0].pt; - let endPt = Vec2DTo3D(data.pt); - if (this.m_Model === PolylineModel.Arc) + this.RemoveLastVertex(pl); + if (pl.NumberOfVertices > 0) { - polyline.CloseMark = true; - updateBul(endPt, basePt); + this.model = pl.GetBuilgeAt(pl.NumberOfVertices - 1) !== 0 ? + PolylineModel.Arc : PolylineModel.Line; } - polyline.LineData = lineProps; - break; } - - if (ptRes.StringResult == "U") + else if (p.StringResult === "C") { - basePt = BasePts.pop(); - lineProps.pop(); - if (this.m_Model === 1) - tangentLine = basePt.clone().sub(BasePts[BasePts.length - 1]).normalize(); - else + if (this.model === PolylineModel.Arc) { - data = lineProps[lineProps.length - 2]; - cirAng = Math.atan(data.bul) * 4; - tangentLine = rotateLine(tangentLine, -cirAng) + pl.AddVertexAt(pl.NumberOfVertices, new Vector2()); + this.UpdatePoint(pl, pl.StartPoint); + this.RemoveLastVertex(pl); } + pl.CloseMark = true; + break; } - else if (ptRes.StringResult == "A") - { - this.m_Model = PolylineModel.Arc; - } - else if (ptRes.StringResult == "L") - { - lineProps[lineProps.length - 2].bul = 0; - this.m_Model = PolylineModel.Line; - } - restore(); } else { - restore(); + this.RemoveLastVertex(pl); break; } } + if (pl.NumberOfVertices < 2) + pl.Erase(); + } + + UpdatePoint(pl: Polyline, pt: Vector3) + { + let ptCout = pl.NumberOfVertices; + + let p = pt.clone().applyMatrix4(pl.OCSInv); + pl.SetPointAt(ptCout - 1, Vec3DTo2D(p)); + + if (this.model === PolylineModel.Arc && ptCout > 1) + { + let pLast = pl.GetPoint2dAt(ptCout - 2); + //弦长 + let chord = p.clone().sub(Vec2DTo3D(pLast)); + //切线 + let tangent: Vector3; + if (ptCout > 2) + tangent = pl.GetCurveAtIndex(ptCout - 3).GetFistDeriv(1).applyMatrix4(pl.OCSInv); + else + tangent = new Vector3(1, 0, 0); + + let cirAng = getCirAngleByChordAndTangent(chord, tangent); + if (cirAng) + pl.SetBulgeAt(ptCout - 2, Math.tan(cirAng * 0.25)); + } + + app.m_Editor.UpdateScreen(); + } + + RemoveLastVertex(pl: Polyline) + { + if (pl.NumberOfVertices > 0) + pl.RemoveVertexAt(pl.NumberOfVertices - 1); } } + diff --git a/src/Add-on/DrawRec.ts b/src/Add-on/DrawRec.ts new file mode 100644 index 000000000..c0ea20d4c --- /dev/null +++ b/src/Add-on/DrawRec.ts @@ -0,0 +1,53 @@ +import * as THREE from 'three'; +import { Box3, Vector3 } from 'three'; +import { app } from '../ApplicationServices/Application'; +import { Vec3DTo2D } from '../Common/CurveUtils'; +import { Polyline } from '../DatabaseServices/Polyline'; +import { Command } from '../Editor/CommandMachine'; +import { PromptStatus } from '../Editor/PromptResult'; +export class DrawRect implements Command +{ + async exec() + { + let ptRes = await app.m_Editor.GetPoint(); + if (ptRes.Status != PromptStatus.OK) + return; + + let p1: THREE.Vector3; + let p2: THREE.Vector3; + p1 = ptRes.Value; + + let rec = new Polyline(); + rec.CloseMark = true; + rec.ApplyMatrix(app.m_Editor.UCSMatrix); + + app.m_Database.ModelSpace.Append(rec); + app.m_Editor.AddNoSnapEntity(rec); + + let box = new Box3(); + let updateRect = (p1, p2) => + { + box.setFromPoints([p2, p1].map((p: Vector3) => p.clone().applyMatrix4(rec.OCSInv))); + + let px1 = Vec3DTo2D(box.min); + let px3 = Vec3DTo2D(box.max); + let px2 = new THREE.Vector2(px3.x, px1.y); + let px4 = new THREE.Vector2(px1.x, px3.y); + + rec.LineData = [ + { pt: px1, bul: 0 }, + { pt: px2, bul: 0 }, + { pt: px3, bul: 0 }, + { pt: px4, bul: 0 }]; + } + + ptRes = await app.m_Editor.GetPoint({ + Callback: (p) => updateRect(p, p1) + }); + + if (ptRes.Status == PromptStatus.OK) + updateRect(ptRes.Value, p1); + else + rec.Erase(); + } +} diff --git a/src/Add-on/Stretch.ts b/src/Add-on/Stretch.ts index d72f315b0..b4e893792 100644 --- a/src/Add-on/Stretch.ts +++ b/src/Add-on/Stretch.ts @@ -1,6 +1,4 @@ -import { Callback } from 'awesome-typescript-loader/dist/paths-plugin'; import * as THREE from 'three'; - import { app } from '../ApplicationServices/Application'; import { CADFile } from '../DatabaseServices/CADFile'; import { Entity } from '../DatabaseServices/Entity'; @@ -11,33 +9,33 @@ import { SelectPick } from '../Editor/SelectPick'; import { SelectSet, SelectType } from '../Editor/SelectSet'; import { MoveMatrix } from '../Geometry/GeUtils'; -export class Stretch implements Command +//拉伸的解析数据 +interface StretchData { + moveEntityList: Entity[];//被移动的图元(所有点都被选中) + stretchEntityMap: Map>; //被拉伸的图元,对照被拉伸的点索引. +} +export class Stretch implements Command +{ m_CacheEntity: Map = new Map(); - async exec(ss: SelectSet) + async exec() { - if (ss.SelectObjectList.length == 0) - { - let ssRes = await app.m_Editor.GetSelection(); - if (ssRes.Status != PromptStatus.OK) - return; + let ssRes = await app.m_Editor.GetSelection({ UseSelect: true, Msg: "请选择拉伸对象:" }); + if (ssRes.Status != PromptStatus.OK) return; + let ss = ssRes.SelectSet; - ss == ssRes.SelectSet; - if (ss.SelectEntityList.length === 0) - return; - } - let p1 = await app.m_Editor.GetPoint({ Msg: "请选择第一个拉伸点:" }); + let p1 = await app.m_Editor.GetPoint({ Msg: "指定基点:" });//, KeyWordList: [{ msg: "位移", key: "D" }] if (p1.Status != PromptStatus.OK) return; let data = this.parse(ss); - for (let [obj] of data.str) - app.m_Editor.AddNoSnapEntity(obj.userData); + for (let [e] of data.stretchEntityMap) + app.m_Editor.AddNoSnapEntity(e); let lastP = p1.Value.clone(); let p2 = await app.m_Editor.GetPoint( { BasePoint: p1.Value, - Msg: "请选择第二个拉伸点:", + Msg: "指定第二个点:",//或 <使用第一个点作为位移> Callback: (p) => { let v = p.clone().sub(lastP); @@ -52,42 +50,37 @@ export class Stretch implements Command this.m_CacheEntity.clear(); } - parse(ss: SelectSet): { str: Map>, move: Array } + parse(ss: SelectSet): StretchData { - let stretchPtsIndexMap = new Map>(); - let move = []; + let data: StretchData = + { + stretchEntityMap: new Map>(), + moveEntityList: [] + }; for (let set of ss.SelectSetList) { if (set instanceof SelectPick) { for (let obj of set.m_SelectList) - { - move.push(obj); - } + data.moveEntityList.push(obj.userData); } else if (set instanceof SelectBox) { if (set.m_SelectType == SelectType.W) { for (let obj of set.m_SelectList) - { - move.push(obj); - } + data.moveEntityList.push(obj.userData); } else { for (let obj of set.m_SelectList) { let indexArr = []; - stretchPtsIndexMap.set(obj, indexArr); - let en = obj.userData; if (en && en instanceof Entity) { - let f = new CADFile(); - en.WriteFile(f); - this.m_CacheEntity.set(en, f); - + this.CacheEntity(en); + data.stretchEntityMap.set(en, indexArr); let pts = en.GetStretchPoints(); for (let i = pts.length; i--;) { @@ -103,27 +96,37 @@ export class Stretch implements Command } } } - return { - str: stretchPtsIndexMap, - move: move - } + + for (let e of data.moveEntityList) + this.CacheEntity(e); + + return data; } - s(d: { str: Map>, move: Array }, vec: THREE.Vector3) + s(d: StretchData, vec: THREE.Vector3) { let moveMat = MoveMatrix(vec); - for (let obj of d.move) + for (let e of d.moveEntityList) { - obj.applyMatrix(moveMat); + this.RestoreEntity(e); + e.ApplyMatrix(moveMat); } - for (let [obj, arr] of d.str) + for (let [e, arr] of d.stretchEntityMap) { - if (obj.userData instanceof Entity) - { - let f = this.m_CacheEntity.get(obj.userData); - f.Reset(); - obj.userData.ReadFile(f); - obj.userData.MoveStretchPoints(arr, vec); - } + this.RestoreEntity(e); + e.MoveStretchPoints(arr, vec); } } + + CacheEntity(e: Entity) + { + let f = new CADFile(); + e.WriteFile(f); + this.m_CacheEntity.set(e, f); + } + RestoreEntity(e: Entity) + { + let f = this.m_CacheEntity.get(e); + f.Reset(); + e.ReadFile(f); + } } diff --git a/src/Add-on/Trim.ts b/src/Add-on/Trim.ts index 25680d62c..7a982cf37 100644 --- a/src/Add-on/Trim.ts +++ b/src/Add-on/Trim.ts @@ -70,7 +70,7 @@ export class Command_Trim implements Command if (s instanceof SelectBox) { let set = s; - splitCus = splitCus.filter(cu => { return set.IntersectObject(cu.Draw(RenderType.Wireframe)) }); + splitCus = splitCus.filter(c => set.IntersectObject(c.Draw(RenderType.Wireframe))); let needBreakCus: Curve[] = []; while (splitCus.length > 0) diff --git a/src/Add-on/test/testIntersect.ts b/src/Add-on/test/testIntersect.ts new file mode 100644 index 000000000..cc812415d --- /dev/null +++ b/src/Add-on/test/testIntersect.ts @@ -0,0 +1,89 @@ +import { app } from "../../ApplicationServices/Application"; +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"; + +export class TestIntersect implements Command +{ + async exec() + { + // this.testLineAndLine(); + // this.testLineAndCirOrArc(); + this.testCirAndCir() + app.m_Editor.UpdateScreen(); + } + async testLineAndLine() + { + let exRefSsRes = await app.m_Editor.GetSelection({ Msg: "请选择对象<全部选择>:", UseSelect: true }); + if (exRefSsRes.Status !== PromptStatus.OK) return; + let cus = exRefSsRes.SelectSet.SelectEntityList as Curve[]; + let pt = IntersectLAndLFor3D(cus[0].StartPoint, cus[0].EndPoint, cus[1].StartPoint, cus[1].EndPoint); + console.log('pt: ', pt); + if (pt) + { + cus[0].ColorIndex = 7; + cus[1].ColorIndex = 7; + let cir = new Circle(pt, 0.1); + cir.ColorIndex = 2; + app.m_Database.ModelSpace.Append(cir); + } else + { + cus[0].ColorIndex = 1; + cus[1].ColorIndex = 1; + } + } + async testLineAndCirOrArc() + { + 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 > 1) + { + let [cu1, cu2] = cus[0] instanceof Line ? cus : cus.reverse(); + let pts = cu1.IntersectWith(cu2, IntersectOption.ExtendBoth); + if (pts.length) + { + pts.forEach(p => + { + let cir = new Circle(p, 0.2); + cir.ColorIndex = 2; + app.m_Database.ModelSpace.Append(cir); + }) + } else + { + cu1.ColorIndex = 1; + cu2.ColorIndex = 1; + } + } + } + async testCirAndCir() + { + let exRefSsRes = await app.m_Editor.GetSelection({ Msg: "请选择对象<全部选择>:", UseSelect: true }); + if (exRefSsRes.Status !== PromptStatus.OK) return; + let cus = exRefSsRes.SelectSet.SelectEntityList as Array; + if (cus.length > 1) + { + let pt = IntersectCircleAndCircle(cus[0], cus[1]); + console.log('pt: ', pt); + if (pt.length) + { + cus[0].ColorIndex = 7; + cus[1].ColorIndex = 7; + pt.forEach(p => + { + let cir = new Circle(p, 0.1); + cir.ColorIndex = 2; + app.m_Database.ModelSpace.Append(cir); + }) + } else + { + cus[0].ColorIndex = 1; + cus[1].ColorIndex = 1; + } + } + } +} diff --git a/src/Common/ArrayExt.ts b/src/Common/ArrayExt.ts index b121fefcd..ea61f44c0 100644 --- a/src/Common/ArrayExt.ts +++ b/src/Common/ArrayExt.ts @@ -67,6 +67,14 @@ export function arrayLast(arr: Array): T return arr[arr.length - 1]; } +/** + * 根据数值从小到大排序数组 + * + * @export + * @template T + * @param {Array} arr + * @returns {Array} 返回自身 + */ export function arraySortByNumber(arr: Array): Array { arr.sort(sortNumberCompart); @@ -77,6 +85,7 @@ export function arraySortByNumber(arr: Array): Array * 对排序好的数组进行去重操作 * * @param {(e1, e2) => boolean} [checkFuction] 校验对象相等函数 + * @returns {Array} 返回自身 */ export function arrayRemoveDuplicateBySort(arr: Array, checkFuction?: (e1, e2) => boolean): Array diff --git a/src/Common/CurveUtils.ts b/src/Common/CurveUtils.ts index 17914349f..7534ead2b 100644 --- a/src/Common/CurveUtils.ts +++ b/src/Common/CurveUtils.ts @@ -1,21 +1,13 @@ -import { Vector2, Vector3 } from 'three'; +import { Matrix3, Vector2, Vector3 } from 'three'; import { Arc } from '../DatabaseServices/Arc'; import { Circle } from '../DatabaseServices/Circle'; import { Curve } from '../DatabaseServices/Curve'; import { Line } from '../DatabaseServices/Line'; import { Polyline } from '../DatabaseServices/Polyline'; -import { equal, equaln } from '../Geometry/GeUtils'; -import { Matrix2 } from '../Geometry/Matrix2'; +import { Count } from '../Geometry/Count'; import { CurveMap } from '../Geometry/CurveMap'; +import { equal, equaln } from '../Geometry/GeUtils'; import { Stand } from '../Geometry/RegionParse'; -import { Count } from '../Geometry/Count'; - -//旋转矢量 -export function rotateLine(l: Vector3, ang: number) -{ - new Matrix2().setRotate(ang).applyVector(l); - return l; -} //3点获取圆心 export function getCircleCenter(pt1: Vector3, pt2: Vector3, pt3: Vector3) @@ -65,10 +57,16 @@ export function getCirAngleByChordAndTangent(chord: Vector3, tangentLine: Vector return cirAng *= dir.z; } //行列式 -export function getDeterminant(v1: Vector2, v2: Vector2): number +export function getDeterminantFor2V(v1: Vector2, v2: Vector2): number { return v1.x * v2.y - v1.y * v2.x; } +export function getDeterminantFor3V(v1: Vector3, v2: Vector3, v3: Vector3) +{ + let mat = new Matrix3(); + mat.set(v1.x, v1.y, v1.z, v2.x, v2.y, v2.z, v3.x, v3.y, v3.z); + return mat.determinant(); +} // 2D 3D点转换 export function Vec2DTo3D(pt: Vector2) { diff --git a/src/Common/Matrix4Utils.ts b/src/Common/Matrix4Utils.ts index acb316b08..abc02da65 100644 --- a/src/Common/Matrix4Utils.ts +++ b/src/Common/Matrix4Utils.ts @@ -1,4 +1,5 @@ import { Matrix4, Vector3 } from 'three'; +import { equaln, isParallelTo } from '../Geometry/GeUtils'; /** * 设置矩阵的某列的向量 @@ -23,8 +24,9 @@ export function matrixSetVector(mat: Matrix4, col: number, v: Vector3) * Y轴坐标轴以及Z轴坐标系统之间的坐标系统坐标系统的原点坐标系和原点坐标系统坐标轴的坐标系分别设置为XAxis,YAxis和ZAxis * * @export + * @returns {Matrix4} 返回新的矩阵 */ -export function matrixAlignCoordSys(matrixFrom: Matrix4, matrixTo: Matrix4) +export function matrixAlignCoordSys(matrixFrom: Matrix4, matrixTo: Matrix4): Matrix4 { let matrix = new Matrix4().getInverse(matrixFrom); @@ -32,3 +34,28 @@ export function matrixAlignCoordSys(matrixFrom: Matrix4, matrixTo: Matrix4) return matrix; } + +/** + * 判断2个矩形共面 + * + * @export + * @param {Matrix4} matrixFrom + * @param {Matrix4} matrixTo + * @returns {boolean} 2个矩阵共面 + */ +export function matrixIsCoplane(matrixFrom: Matrix4, matrixTo: Matrix4): boolean +{ + let nor1 = new Vector3().setFromMatrixColumn(matrixFrom, 2); + let nor2 = new Vector3().setFromMatrixColumn(matrixFrom, 2); + + //法线共面 + if (!isParallelTo(nor1, nor2)) + return false; + + //高共面 + let pt = new Vector3().setFromMatrixPosition(matrixTo); + //变换到自身对象坐标系. + pt.applyMatrix4(new Matrix4().getInverse(matrixFrom)); + + return equaln(pt.z, 0); +} diff --git a/src/DatabaseServices/Arc.ts b/src/DatabaseServices/Arc.ts index 3c8d546d4..2142e5410 100644 --- a/src/DatabaseServices/Arc.ts +++ b/src/DatabaseServices/Arc.ts @@ -3,7 +3,7 @@ import { Box3, Matrix4, Object3D, ShapeGeometry, Vector2, Vector3 } from 'three' import { ColorMaterial } from '../Common/ColorPalette'; import { Vec2DTo3D, getCircleCenter } from '../Common/CurveUtils'; import { matrixSetVector } from '../Common/Matrix4Utils'; -import { angle, angleTo2Pi, equal, equaln, midPoint, polar } from '../Geometry/GeUtils'; +import { angle, angleTo2Pi, equal, equaln, midPoint, polar, MoveMatrix } from '../Geometry/GeUtils'; import { RenderType } from '../GraphicsSystem/Enum'; import { IntersectArcAndArc, IntersectCircleAndArc, IntersectLineAndArc, IntersectOption, IntersectPolylineAndCurve, reverseIntersectOption } from '../GraphicsSystem/IntersectWith'; import { Factory } from './CADFactory'; @@ -548,22 +548,18 @@ export class Arc extends Curve this.UpdateGeometry(geo); } GetSnapPoints(): Array - { - return this.GetStretchPoints(); - } - GetStretchPoints(): Array { return [ this.StartPoint, this.GetPointAtParam(0.5), this.EndPoint, this.Center.clone(), - ] + ]; } - - MoveStretchPoints(indexList: Array, vec: Vector3) + MoveSnapPoints(indexList: Array, vec: Vector3) { - let ptsArr = this.GetStretchPoints(); + this.WriteAllObjectRecord(); + let ptsArr = this.GetSnapPoints(); if (indexList.length > 0) { let index = indexList[0]; @@ -583,6 +579,28 @@ export class Arc extends Curve } } } + GetStretchPoints(): Array + { + return [this.StartPoint, this.EndPoint]; + } + + MoveStretchPoints(indexList: Array, vec: Vector3) + { + if (indexList.length === 0) + return; + + this.WriteAllObjectRecord(); + + if (indexList.length === 2) + this.ApplyMatrix(MoveMatrix(vec)); + else + for (let index of indexList) + { + if (index !== 0) + index = 2; + this.MoveSnapPoints([index], vec); + } + } GetParamAtDist(d: number) { @@ -597,7 +615,9 @@ export class Arc extends Curve an = angle(pt.clone().applyMatrix4(this.OCSInv)); an += Math.PI * 0.5 * (this.m_Clockwise ? -1 : 1); - return polar(new Vector3(), an, this.m_Radius) as Vector3; + + let ocs = new Matrix4().extractRotation(this.OCS); + return polar(new Vector3(), an, this.m_Radius).applyMatrix4(ocs); } GetClosestPointTo(pt: Vector3, extend: boolean): Vector3 { diff --git a/src/DatabaseServices/Circle.ts b/src/DatabaseServices/Circle.ts index 442e217f5..3b30bf11d 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 } from '../Common/ArrayExt'; +import { arrayFirst, arraySortByNumber, arrayRemoveDuplicateBySort, arrayLast } from '../Common/ArrayExt'; import { ColorMaterial } from '../Common/ColorPalette'; import { clamp } from '../Common/Utils'; import { Arc } from '../DatabaseServices/Arc'; -import { angle, equaln, polar } from '../Geometry/GeUtils'; +import { angle, equaln, polar, MoveMatrix } from '../Geometry/GeUtils'; import { RenderType } from '../GraphicsSystem/Enum'; import { IntersectCircleAndArc, IntersectCircleAndCircle, IntersectLineAndCircle, IntersectOption, IntersectPolylineAndCurve, reverseIntersectOption } from '../GraphicsSystem/IntersectWith'; import { Factory } from './CADFactory'; @@ -113,12 +113,19 @@ export class Circle extends Curve GetSplitCurves(param: number[] | number) { - let params = this.SplitParamSort(param); - params.shift(); - params.pop(); - if (params.length < 2) return []; - params.push(arrayFirst(params)); - params.reverse(); + let params: number[]; + if (param instanceof Array) + { + params = param.filter(p => this.ParamOnCurve(p)); + params.sort((a1, a2) => a2 - a1);//从大到小 + arrayRemoveDuplicateBySort(params); + if (params.length < 2) return []; + } + else //圆不能被单个参数切割 + return []; + + //补上最后一个到第一个的弧 + params.unshift(arrayLast(params)); let anglelist = params.map(param => Math.PI * 2 * param); @@ -129,7 +136,8 @@ export class Circle extends Curve let ea = anglelist[i + 1]; if (!equaln(sa, ea)) { - let arc = new Arc(this.Center, this.m_Radius, sa, ea); + let arc = new Arc(new Vector3(), this.m_Radius, sa, ea); + arc.ApplyMatrix(this.OCS); curvelist.push(arc); } } @@ -142,7 +150,7 @@ export class Circle extends Curve { return NaN; } - return angle(pt.clone().sub(this.Center)) / (Math.PI * 2); + return angle(pt.clone().applyMatrix4(this.OCSInv)) / (Math.PI * 2); } PtOnCurve(pt: Vector3) @@ -220,10 +228,6 @@ export class Circle extends Curve } GetSnapPoints(): Array - { - return this.GetStretchPoints(); - } - GetStretchPoints(): Array { let pts = [ new Vector3(), @@ -237,14 +241,10 @@ export class Circle extends Curve pts.forEach(p => p.applyMatrix4(ocs)); return pts; } - MoveStretchPoints(indexList: Array, vec: Vector3) + MoveSnapPoints(indexList: Array, vec: Vector3) { - let ocs = this.OCS; - ocs.setPosition(new Vector3()); - let ocsInv = new Matrix4().getInverse(ocs); - vec = vec.clone().applyMatrix4(ocsInv); - let pts = this.GetStretchPoints(); + let pts = this.GetSnapPoints(); if (indexList.length > 0) { let index = indexList[0]; @@ -263,12 +263,25 @@ export class Circle extends Curve } } } + GetStretchPoints(): Array + { + let pts = [new Vector3()]; + let ocs = this.OCS; + pts.forEach(p => p.applyMatrix4(ocs)); + return pts; + } + MoveStretchPoints(indexList: Array, vec: Vector3) + { + if (indexList.length > 0) + { + let mat = MoveMatrix(vec); + this.ApplyMatrix(mat); + } + } GetFistDeriv(pt: number | Vector3) { if (typeof pt === "number") - { pt = this.GetPointAtParam(pt); - } else pt = pt.clone(); diff --git a/src/DatabaseServices/Curve.ts b/src/DatabaseServices/Curve.ts index 71493f15b..7080c32ff 100644 --- a/src/DatabaseServices/Curve.ts +++ b/src/DatabaseServices/Curve.ts @@ -87,7 +87,7 @@ export abstract class Curve extends Entity { //切割参数列表 let params: number[]; - if (param instanceof Array) + if (Array.isArray(param)) { params = param.filter(param => this.ParamOnCurve(param)); params.push(0, this.EndParam); diff --git a/src/DatabaseServices/Dimension/Dim.ts b/src/DatabaseServices/Dimension/Dim.ts index a43835e98..0965f6ede 100644 --- a/src/DatabaseServices/Dimension/Dim.ts +++ b/src/DatabaseServices/Dimension/Dim.ts @@ -2,10 +2,10 @@ import { Geometry, Mesh, Object3D, Quaternion, ShapeGeometry, Vector3 } from 'th import * as THREE from 'three'; import { ColorMaterial } from '../../Common/ColorPalette'; -import { rotateLine } from '../../Common/CurveUtils'; import { Factory } from '../CADFactory'; import { Entity } from '../Entity'; import { Text } from '../Text/Text'; +import { rotatePoint } from '../../Geometry/GeUtils'; /** * 标注实体基类 @@ -124,14 +124,14 @@ export class Dimension extends Entity } reviseTextPosition(v: Vector3) { - let reviseLine = rotateLine(new Vector3(1), this.TextRotation + Math.PI / 2).multiplyScalar(this.m_Text.Height / 2 + this.FONTTODIMDIST); + let reviseLine = rotatePoint(new Vector3(1), this.TextRotation + Math.PI / 2).multiplyScalar(this.m_Text.Height / 2 + this.FONTTODIMDIST); v.add(reviseLine); } updateText(text: THREE.Mesh, pos: Vector3, ro: number) { this.reviseTextPosition(pos); - let moveVecX = rotateLine(new Vector3(1), this.TextRotation).negate().multiplyScalar(this.m_Text.Width / 2); - let moveVecY = rotateLine(new Vector3(0, -1), this.TextRotation).multiplyScalar(this.m_Text.Height / 2); + let moveVecX = rotatePoint(new Vector3(1), this.TextRotation).negate().multiplyScalar(this.m_Text.Width / 2); + let moveVecY = rotatePoint(new Vector3(0, -1), this.TextRotation).multiplyScalar(this.m_Text.Height / 2); let p = pos.clone().add(moveVecX); p.add(moveVecY); diff --git a/src/DatabaseServices/Dimension/LineAngularDimension.ts b/src/DatabaseServices/Dimension/LineAngularDimension.ts index b5ee6a73b..c5eace2b6 100644 --- a/src/DatabaseServices/Dimension/LineAngularDimension.ts +++ b/src/DatabaseServices/Dimension/LineAngularDimension.ts @@ -1,19 +1,17 @@ import * as THREE from 'three'; -import { Geometry, Group, Object3D, Shape, Vector3, Matrix4 } from 'three'; - +import { Geometry, Group, Object3D, Shape, Vector3 } from 'three'; import { ColorMaterial } from '../../Common/ColorPalette'; -import { rotateLine } from '../../Common/CurveUtils'; -import { angleAndX, angleTo, getPtPostion, updateGeometry } from '../../Geometry/GeUtils'; +import { angleAndX, angleTo, getPtPostion, updateGeometry, rotatePoint } from '../../Geometry/GeUtils'; import { RenderType } from '../../GraphicsSystem/Enum'; -import { IntersectLAndL } from '../../GraphicsSystem/IntersectWith'; +import { IntersectLAndLFor3D } from '../../GraphicsSystem/IntersectWith'; import { Arc } from '../Arc'; import { Factory } from '../CADFactory'; +import { CADFile } from '../CADFile'; +import { Entity } from '../Entity'; import { Line } from '../Line'; -import { Dimension } from './Dim'; -import { Curve } from '../Curve'; import { Text } from '../Text/Text'; -import { Entity } from '../Entity'; -import { CADFile } from '../CADFile'; +import { Dimension } from './Dim'; + @Factory export class LineAngularDimension extends Dimension @@ -118,7 +116,7 @@ export class LineAngularDimension extends Dimension private getCaclSPtAndEPt() { // 2线交点即为圆心 - let center = IntersectLAndL(this.m_StartPoint1, this.m_EndPoint1, this.m_StartPoint2, this.m_EndPoint2); + let center = IntersectLAndLFor3D(this.m_StartPoint1, this.m_EndPoint1, this.m_StartPoint2, this.m_EndPoint2); // 获取实际的首尾点,离圆心近的的为起始点 let [spt1, ept1] = this.StartPoint1.distanceTo(center) < this.EndPoint1.distanceTo(center) ? [this.StartPoint1, this.EndPoint1] : [this.EndPoint1, this.StartPoint1]; let [spt2, ept2] = this.StartPoint2.distanceTo(center) < this.EndPoint2.distanceTo(center) ? [this.StartPoint2, this.EndPoint2] : [this.EndPoint2, this.StartPoint2]; @@ -128,7 +126,7 @@ export class LineAngularDimension extends Dimension private getDimArcData() { // 2线交点即为圆心 - let center = IntersectLAndL(this.m_StartPoint1, this.m_EndPoint1, this.m_StartPoint2, this.m_EndPoint2); + let center = IntersectLAndLFor3D(this.m_StartPoint1, this.m_EndPoint1, this.m_StartPoint2, this.m_EndPoint2); let rad = this.m_ArcPoint.distanceTo(center); let { ept1, ept2 } = this.getCaclSPtAndEPt(); //标注线段的起始端点 @@ -229,8 +227,8 @@ export class LineAngularDimension extends Dimension let line1 = ept1.clone().sub(spt1); let line2 = ept2.clone().sub(spt2); - rotateLine(line1, -Math.PI / 2); - rotateLine(line2, Math.PI / 2); + rotatePoint(line1, -Math.PI / 2); + rotatePoint(line2, Math.PI / 2); let angle = angleTo(originLine, line1); let angle1 = angleTo(originLine.negate(), line2); if (Math.abs(angle) >= Math.PI / 2) diff --git a/src/DatabaseServices/Ellipse.ts b/src/DatabaseServices/Ellipse.ts index ba42c2cf4..0c1561555 100644 --- a/src/DatabaseServices/Ellipse.ts +++ b/src/DatabaseServices/Ellipse.ts @@ -6,7 +6,7 @@ import { RenderType } from '../GraphicsSystem/Enum'; import { Factory } from './CADFactory'; import { CADFile } from './CADFile'; import { Curve } from './Curve'; -import { rotateLine } from '../Common/CurveUtils'; +import { rotatePoint } from '../Geometry/GeUtils'; @Factory export class Ellipse extends Curve @@ -65,7 +65,7 @@ export class Ellipse extends Curve } PtInCurve(pt: Vector3) { - let p = rotateLine(pt.clone().sub(this.Center), -this.Angle); + let p = rotatePoint(pt.clone().sub(this.Center), -this.Angle); let a = this.RadX; let b = this.RadY; return Math.pow(p.x, 2) / Math.pow(a, 2) + Math.pow(p.y, 2) / Math.pow(b, 2) < 1 diff --git a/src/DatabaseServices/Entity.ts b/src/DatabaseServices/Entity.ts index 886fbeb9d..e22f80d39 100644 --- a/src/DatabaseServices/Entity.ts +++ b/src/DatabaseServices/Entity.ts @@ -1,5 +1,6 @@ import * as THREE from 'three'; import { Box3, Geometry, Matrix4, Object3D, Vector3 } from 'three'; +import { matrixIsCoplane } from '../Common/Matrix4Utils'; import { RenderType } from '../GraphicsSystem/Enum'; import { IntersectOption } from '../GraphicsSystem/IntersectWith'; import { Factory } from './CADFactory'; @@ -7,7 +8,6 @@ import { CADFile } from './CADFile'; import { CADObject } from './CADObject'; import { ObjectId } from './ObjectId'; - /** * Entity 是所有图元的基类,绘制的实体都集成该类. * @@ -41,7 +41,6 @@ export class Entity extends CADObject { return this.m_Color; } - /** * 炸开实体 * @@ -80,6 +79,18 @@ export class Entity extends CADObject return new Matrix4().getInverse(this.m_Matrix); } + /** + * 与指定实体是否共面. + * + * @param {Entity} e 实体 + * @returns {boolean} 是否共面 + * @memberof Entity + */ + IsCoplaneTo(e: Entity): boolean + { + return matrixIsCoplane(this.m_Matrix, e.OCS); + } + /** * 测试两个实体的包围盒是否相交. * @@ -150,7 +161,6 @@ export class Entity extends CADObject */ Update() { - let ocs = this.OCS; for (let [type, en] of this.m_DrawEntity) { diff --git a/src/DatabaseServices/Line.ts b/src/DatabaseServices/Line.ts index a3eb7f9ca..a03dcf6f3 100644 --- a/src/DatabaseServices/Line.ts +++ b/src/DatabaseServices/Line.ts @@ -1,4 +1,3 @@ -//直线对象 import * as THREE from 'three'; import { Box3, Geometry, Object3D, Vector3 } from 'three'; import { arraySortByNumber } from '../Common/ArrayExt'; diff --git a/src/DatabaseServices/Polyline.ts b/src/DatabaseServices/Polyline.ts index c40708732..b00f62e45 100644 --- a/src/DatabaseServices/Polyline.ts +++ b/src/DatabaseServices/Polyline.ts @@ -1,11 +1,11 @@ import * as THREE from 'three'; import { Box3, Geometry, Matrix4, Object3D, Vector2, Vector3 } from 'three'; import { CreateBoardUtil } from '../ApplicationServices/mesh/createBoard'; -import { arrayLast } from '../Common/ArrayExt'; +import { arrayLast, arrayRemove } from '../Common/ArrayExt'; import { ColorMaterial } from '../Common/ColorPalette'; -import { Vec2DTo3D, Vec3DTo2D, getDeterminant, rotateLine } from '../Common/CurveUtils'; +import { Vec2DTo3D, Vec3DTo2D, getDeterminantFor2V } from '../Common/CurveUtils'; import { FixIndex } from '../Common/Utils'; -import { equal, equaln, updateGeometry } from '../Geometry/GeUtils'; +import { equal, equaln, updateGeometry, rotatePoint } from '../Geometry/GeUtils'; import { RenderType } from '../GraphicsSystem/Enum'; import { IntersectOption, IntersectPolylineAndCurve } from '../GraphicsSystem/IntersectWith'; import { angleTo } from './../Geometry/GeUtils'; @@ -75,6 +75,78 @@ export class Polyline extends Curve { return this.m_LineData; } + + get NumberOfVertices(): number + { + return this.m_LineData.length; + } + + /** + * 在指定位置插入点. + * 例如: + * pl.AddVertexAt(pl.NumberOfVerticesk,p);//在末尾插入一个点 + * + * @param {number} index 索引位置 + * @param {Vector2} pt 点 + * @returns {this} + * @memberof Polyline + */ + AddVertexAt(index: number, pt: Vector2): this + { + this.WriteAllObjectRecord(); + this.m_LineData.splice(index, 0, { pt: pt.clone(), bul: 0 }); + this.Update(); + return this; + } + RemoveVertexAt(index: number): this + { + if (index < this.m_LineData.length) + { + this.m_LineData.splice(index, 1); + this.Update(); + } + return this; + } + GetPoint2dAt(index: number): Vector2 | undefined + { + if (index >= 0 && this.m_LineData.length > index) + return this.m_LineData[index].pt.clone(); + } + + /** + * 设置指定点的位置 + * + * @param {number} index + * @param {Vector2} pt + * @memberof Polyline + */ + SetPointAt(index: number, pt: Vector2): this + { + let d = this.m_LineData[index]; + if (d) + { + this.WriteAllObjectRecord(); + d.pt.copy(pt); + this.Update(); + } + return this; + } + SetBulgeAt(index: number, bul: number): this + { + let d = this.m_LineData[index]; + if (d) + { + this.WriteAllObjectRecord(); + d.bul = bul; + this.Update(); + } + return this; + } + GetBuilgeAt(index: number): number + { + return this.m_LineData[index].bul; + } + //多段线起点 get StartPoint() { @@ -120,7 +192,7 @@ export class Polyline extends Curve let startV = this.m_LineData[i]; let endV = this.m_LineData[i + 1]; - area += getDeterminant(startV.pt, endV.pt) / 2; + area += getDeterminantFor2V(startV.pt, endV.pt) / 2; if (startV.bul != 0) { @@ -132,7 +204,7 @@ export class Polyline extends Curve { let startV = arrayLast(this.m_LineData); let endV = this.m_LineData[0]; - area += getDeterminant(startV.pt, endV.pt) / 2 + area += getDeterminantFor2V(startV.pt, endV.pt) / 2 } return area; } @@ -670,7 +742,7 @@ export class Polyline extends Curve if (equaln(startV.pt.distanceTo(endV.pt), 0)) return; let tarLine = this.GetFistDeriv(param).normalize(); //偏移向量 - let moveVec = rotateLine(tarLine, Math.PI / 2).multiplyScalar(-dist); + 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)); @@ -678,7 +750,7 @@ export class Polyline extends Curve startV.pt = Vec3DTo2D(Vec2DTo3D(startV.pt).applyMatrix4(mat)); if (startV.bul !== 0) { - moveVec = rotateLine(moveVec, Math.atan(startV.bul) * 4); + moveVec = rotatePoint(moveVec, Math.atan(startV.bul) * 4); } mat.makeTranslation(moveVec.x, moveVec.y, moveVec.z); endV.pt = Vec3DTo2D(Vec2DTo3D(endV.pt).applyMatrix4(mat)); @@ -1352,7 +1424,7 @@ export class Polyline extends Curve return { pts, buls }; } - CreateCurve() + private CreateCurve() { let { pts, buls } = this.PtsBuls; let curve = CreateBoardUtil.createPath(pts, buls); @@ -1391,7 +1463,7 @@ export class Polyline extends Curve { this.WriteAllObjectRecord(); - let vLoc = Vec3DTo2D(moveVec.clone().applyMatrix4(this.OCSInv)); + let moveVLoc = Vec3DTo2D(moveVec.clone().applyMatrix4(new Matrix4().extractRotation(this.OCSInv))); for (let index of indexList) { @@ -1404,18 +1476,14 @@ export class Polyline extends Curve let d = this.m_LineData[ptIndex]; if (d.bul == 0) { - this.m_LineData[ptIndex].pt.add(vLoc); - this.m_LineData[nextIndex].pt.add(vLoc); + this.m_LineData[ptIndex].pt.add(moveVLoc); + this.m_LineData[nextIndex].pt.add(moveVLoc); } else { - // 以下代码保证了中心点不变 - let arcStartPoint = this.GetPointAtParam(ptIndex); - let arcMidP = this.GetPointAtParam(ptIndex + 0.5).add(moveVec); - let arcEndPoint = this.GetPointAtParam(nextIndex); - - let arc = new Arc().FromThreePoint(arcStartPoint, arcMidP, arcEndPoint); - this.m_LineData[ptIndex].bul = Math.tan(arc.AllAngle * 0.25) * (equal(arc.StartPoint, arcStartPoint) && arc.IsClockWise ? -1 : 1); + let arc = this.GetCurveAtIndex(ptIndex) as Arc; + arc.MoveSnapPoints([1], moveVec); + this.m_LineData[ptIndex].bul = Math.tan(arc.AllAngle * 0.25) * (arc.IsClockWise ? -1 : 1); } } } @@ -1444,7 +1512,9 @@ export class Polyline extends Curve MoveStretchPoints(indexList: Array, vec: Vector3) { this.WriteAllObjectRecord(); - let moveVc = new Vector2(vec.x, vec.y); + + //本地坐标系移动向量 + let moveVLoc = vec.clone().applyMatrix4(new Matrix4().extractRotation(this.OCSInv)); let ptCout = this.m_LineData.length; @@ -1495,7 +1565,7 @@ export class Polyline extends Curve ChangeBul(nextIndex, index); //修改顶点 - this.m_LineData[index].pt.add(Vec3DTo2D(vec)); + this.m_LineData[index].pt.add(Vec3DTo2D(moveVLoc)); } this.Update(); } diff --git a/src/DatabaseServices/Region.ts b/src/DatabaseServices/Region.ts index 57b2a2745..72b7bff4a 100644 --- a/src/DatabaseServices/Region.ts +++ b/src/DatabaseServices/Region.ts @@ -59,28 +59,12 @@ export class Region extends Entity } BooleanOper(otherRegion: Region, boolType: BoolOpeartionType) { - this.WriteAllObjectRecord(); - - //法线共面 - if (!isParallelTo(this.Normal, otherRegion.Normal)) + if (this.IsCoplaneTo(otherRegion)) { - console.log("对象不共面1"); - return; + this.WriteAllObjectRecord(); + this.m_ShapeManager.BoolOper(otherRegion.m_ShapeManager, boolType); + this.Update(); } - - //高共面 - let pt = otherRegion.Position; - //变换到自身对象坐标系. - pt.applyMatrix4(this.OCSInv); - - if (!equaln(pt.z, 0)) - { - console.log("对象不共面2"); - return; - } - - this.m_ShapeManager.BoolOper(otherRegion.m_ShapeManager, boolType); - this.Update(); } InitDrawObject(renderType: RenderType = RenderType.Wireframe): Object3D { diff --git a/src/DatabaseServices/Text/Text.ts b/src/DatabaseServices/Text/Text.ts index daefd32c1..2a8874d0c 100644 --- a/src/DatabaseServices/Text/Text.ts +++ b/src/DatabaseServices/Text/Text.ts @@ -32,7 +32,6 @@ export class Text extends Entity protected FONTSIZE = 0.2; //字体粗细 protected PROVISITION = -0.1; - Normal: Vector3 = new Vector3(0, 0, 1); m_fontWidth: number; textArea: HTMLTextAreaElement; constructor(pos?: Vector3, str?: string, h?: number, ro?: number) diff --git a/src/Editor/CommandRegister.ts b/src/Editor/CommandRegister.ts index f9ecb9442..b4310bba9 100644 --- a/src/Editor/CommandRegister.ts +++ b/src/Editor/CommandRegister.ts @@ -12,7 +12,7 @@ import { DrawLinearDimension } from '../Add-on/DrawDim/DrawLinearDimension'; import { DrawEllipse } from '../Add-on/DrawEllipse'; import { DrawFloor } from '../Add-on/DrawFloor'; import { DrawGripStretch } from '../Add-on/DrawGripStretch'; -import { DrawCircle, DrawLine, DrawRect, DrawSphere, DrawTest, ZoomE } from '../Add-on/DrawLine'; +import { DrawCircle, DrawLine, DrawSphere, DrawTest, ZoomE } from '../Add-on/DrawLine'; import { DrawPolyline } from '../Add-on/DrawPolyline'; import { DrawRegion } from '../Add-on/DrawRegion'; import { DrawSky } from '../Add-on/DrawSky'; @@ -52,12 +52,14 @@ 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 { TestBoolOperationUtil } from '../Add-on/testEntity/TestBoolOperation'; +import { DrawRect } from '../Add-on/DrawRec'; -// import { DrawFloor } from '../Add-on/DrawFloor'; -// import { RevTarget, SaveTarget } from '../Add-on/RenderTarget'; export function registerCommand() { commandMachine.RegisterCommand("l", new DrawLine()) @@ -170,6 +172,8 @@ export function registerCommand() commandMachine.RegisterCommand("testbool", new TestBoolOperationUtil()); commandMachine.RegisterCommand("join", new Command_Join()); + commandMachine.RegisterCommand("testInt", new TestIntersect()); + // commandMachine.RegisterCommand("st", new SaveTarget()) // commandMachine.RegisterCommand("rt", new RevTarget()) diff --git a/src/Editor/Editor.ts b/src/Editor/Editor.ts index dcf173e22..a6e1ea218 100644 --- a/src/Editor/Editor.ts +++ b/src/Editor/Editor.ts @@ -40,7 +40,6 @@ export class Editor m_KeywordsServices: GetKeyWordsServices; //用户坐标系 - private m_UCSMatrix = new THREE.Matrix4(); constructor(app: ApplicationService) { this.m_App = app; diff --git a/src/Editor/SnapDragServices.ts b/src/Editor/SnapDragServices.ts index f138bd07f..8c7bf2c7c 100644 --- a/src/Editor/SnapDragServices.ts +++ b/src/Editor/SnapDragServices.ts @@ -11,7 +11,6 @@ import { Editor, EditorService } from './Editor'; import { MouseControls } from './MouseControls'; import { commandMachine } from './CommandMachine'; - export class SnapDragServices implements EditorService { diff --git a/src/Editor/SnapServices.ts b/src/Editor/SnapServices.ts index 65108bc1f..ff62ede16 100644 --- a/src/Editor/SnapServices.ts +++ b/src/Editor/SnapServices.ts @@ -1,14 +1,14 @@ import * as THREE from 'three'; - import { app } from '../ApplicationServices/Application'; import { ColorMaterial } from '../Common/ColorPalette'; import { GetPointPrompt } from '../Common/InputState'; import { Entity } from '../DatabaseServices/Entity'; import { equaln, fixAngle, polar } from '../Geometry/GeUtils'; -import { IntersectLAndL } from '../GraphicsSystem/IntersectWith'; +import { IntersectLAndLFor3D } from '../GraphicsSystem/IntersectWith'; import { PreViewer } from '../GraphicsSystem/PreViewer'; + //捕捉轴 interface SnapAxis { @@ -104,7 +104,7 @@ export class SnapServices for (let j = i + 1; j < snapAxisList.length; j++) { let axis2 = snapAxisList[j]; - let insP = IntersectLAndL(axis1.BasePoint, axis1.SnapPoint, axis2.BasePoint, axis2.SnapPoint); + let insP = IntersectLAndLFor3D(axis1.BasePoint, axis1.SnapPoint, axis2.BasePoint, axis2.SnapPoint); if (insP) axisIntersectList.push({ IntersectPoint: insP, diff --git a/src/Geometry/GeUtils.ts b/src/Geometry/GeUtils.ts index d861a2a82..6ba89dacd 100644 --- a/src/Geometry/GeUtils.ts +++ b/src/Geometry/GeUtils.ts @@ -1,6 +1,6 @@ import * as THREE from 'three'; import { Geometry, Vector, Vector2, Vector3 } from 'three'; -import { rotateLine } from '../Common/CurveUtils'; +import { Matrix2 } from './Matrix2'; export const cZeroVec = new THREE.Vector3(); @@ -8,6 +8,20 @@ export const cXAxis = new THREE.Vector3(1, 0, 0); export const cYAxis = new THREE.Vector3(0, 1, 0); export const cZAxis = new THREE.Vector3(0, 0, 1); +/** + * 旋转一个点,旋转中心在原点 + * + * @export + * @param {Vector3} pt 点 + * @param {number} ang 角度. + * @returns {Vector3} 返回pt不拷贝. + */ +export function rotatePoint(pt: Vector3, ang: number): Vector3 +{ + new Matrix2().setRotate(ang).applyVector(pt); + return pt; +} + export function equaln(v1: number, v2: number, fuzz = 1e-3) { return Math.abs(v1 - v2) < fuzz; @@ -150,7 +164,7 @@ export function midPtCir(v1: THREE.Vector3, v2: THREE.Vector3) let baseline = new Vector3(1, 0, 0); let outLine = v2.clone().sub(v1); let ang = angleTo(baseline, outLine) / 2; - let midLine = rotateLine(outLine, -ang); + let midLine = rotatePoint(outLine, -ang); return v1.clone().add(midLine); } diff --git a/src/GraphicsSystem/IntersectWith.ts b/src/GraphicsSystem/IntersectWith.ts index c43350f54..369c918df 100644 --- a/src/GraphicsSystem/IntersectWith.ts +++ b/src/GraphicsSystem/IntersectWith.ts @@ -1,6 +1,5 @@ import { Vector3 } from 'three'; - -import { Vec2DTo3D, Vec3DTo2D } from '../Common/CurveUtils'; +import { getDeterminantFor3V } from '../Common/CurveUtils'; import { Arc } from '../DatabaseServices/Arc'; import { Circle } from '../DatabaseServices/Circle'; import { Curve } from '../DatabaseServices/Curve'; @@ -8,13 +7,14 @@ import { Line } from '../DatabaseServices/Line'; import { Polyline } from '../DatabaseServices/Polyline'; import { equal, equaln, midPoint } from '../Geometry/GeUtils'; + /** * 相交延伸选项. - * + * * @export * @enum {number} */ -export enum IntersectOption +export enum IntersectOption { /** * 两者都不延伸 @@ -45,7 +45,7 @@ export function reverseIntersectOption(intType: IntersectOption) /** * 校验相交点是否满足延伸选项 * 算法会计算无限延伸状态下的曲线交点,调用该方法进行校验返回校验后的点表 - * + * * @param {Vector3[]} intPts 相交点.曲线当作完全状态下的相交点 * @param {Curve} c1 曲线1 由this参数传入 * @param {Curve} c2 曲线2 由arg 参数传入 @@ -60,28 +60,23 @@ function CheckPointOnCurve(intPts: Vector3[], c1: Curve, c2: Curve, extType: Int }) } -/** - * 计算圆与圆的交点,如果参数传入圆弧,那么也按照圆计算. - * - * @export - * @param {(Circle | Arc)} c1 圆或圆弧 - * @param {(Circle | Arc)} c2 圆或圆弧 - * @returns 交点集合 - */ -export function IntersectCircleAndCircle(c1: Circle | Arc, c2: Circle | Arc) +export function IntersectCircleAndCircle(cu1: Circle | Arc, cu2: Circle | Arc) { - let center1 = c1.Center; - let center2 = c2.Center; - let radius1 = c1.Radius; - let radius2 = c2.Radius; + if (!cu1.IsCoplaneTo(cu2)) return []; + + let c1OcsInv = cu1.OCSInv; + let c1Ocs = cu1.OCS; + + let center1 = cu1.Center.applyMatrix4(c1OcsInv); + let center2 = cu2.Center.applyMatrix4(c1OcsInv); + let radius1 = cu1.Radius; + let radius2 = cu2.Radius; let pts: Vector3[] = []; let dist = center2.distanceTo(center1); if (dist > (radius1 + radius2 + 1e-3)) - { return pts; - } let dstsqr = dist * dist; let r1sqr = radius1 * radius1; @@ -99,24 +94,23 @@ export function IntersectCircleAndCircle(c1: Circle | Arc, c2: Circle | Arc) let phix = center1.x + (ratio_a * dx); let phiy = center1.y + (ratio_a * dy); - dx = dx * ratio_h; - dy = dy * ratio_h; + dx *= ratio_h; + dy *= ratio_h; - let pt = new Vector3(phix + dy, phiy - dx); + let p1 = new Vector3(phix + dy, phiy - dx); let p2 = new Vector3(phix - dy, phiy + dx); + p1.applyMatrix4(c1Ocs); + p2.applyMatrix4(c1Ocs); - pts.push(pt); - if (!equal(pt, p2))//防止点重复 - { + pts.push(p1); + if (!equal(p1, p2))//防止点重复 pts.push(p2); - } return pts; } - /** * 计算圆与圆弧的交点. - * + * * @export * @param {Circle} circle 圆 * @param {Arc} arc 圆弧 @@ -131,7 +125,7 @@ export function IntersectCircleAndArc(circle: Circle, arc: Arc, extType: Interse /** * 计算圆弧与圆弧的交点 - * + * * @export * @param {Arc} arc1 圆弧 * @param {Arc} arc2 圆弧 @@ -146,7 +140,7 @@ export function IntersectArcAndArc(arc1: Arc, arc2: Arc, extType: IntersectOptio /** * 通用方法:计算直线与圆的交点,默认延伸全部 - * + * * @export * @param {Line} line 直线 * @param {(Circle | Arc)} circle 圆或圆弧 @@ -154,9 +148,9 @@ export function IntersectArcAndArc(arc1: Arc, arc2: Arc, extType: IntersectOptio */ function IntersectLineAndCircleOrArc(line: Line, circle: Circle | Arc) { - let startPoint = Vec3DTo2D(line.StartPoint); - let endPoint = Vec3DTo2D(line.EndPoint); - let center = Vec3DTo2D(circle.Center); + let startPoint = line.StartPoint; + let endPoint = line.EndPoint; + let center = circle.Center; let radius = circle.Radius; let a = endPoint.distanceToSquared(startPoint); @@ -171,16 +165,16 @@ function IntersectLineAndCircleOrArc(line: Line, circle: Circle | Arc) if (equaln(det, 0, 1e-5)) { let delta = -b / (2 * a); - return [Vec2DTo3D(startPoint.add(lineV.multiplyScalar(delta)))]; + return [startPoint.add(lineV.multiplyScalar(delta))]; } else if (det > 0) { let sqrt_det = Math.sqrt(det); let delta = (-b + sqrt_det) / (2 * a); - let p2 = Vec2DTo3D(startPoint.clone().add(lineV.clone().multiplyScalar(delta))); + let p2 = startPoint.clone().add(lineV.clone().multiplyScalar(delta)); delta = (-b - sqrt_det) / (2 * a); - let p3 = Vec2DTo3D(startPoint.clone().add(lineV.multiplyScalar(delta))); + let p3 = startPoint.clone().add(lineV.multiplyScalar(delta)); return [p2, p3]; } @@ -200,7 +194,7 @@ export function IntersectLineAndArc(line: Line, arc: Arc, extType: IntersectOpti return CheckPointOnCurve(ptArr, line, arc, extType); } //直线和直线 -export function IntersectLAndL(p1: Vector3, p2: Vector3, p3: Vector3, p4: Vector3): Vector3 +export function IntersectLAndLFor2D(p1: Vector3, p2: Vector3, p3: Vector3, p4: Vector3): Vector3 { let dx1 = p1.x - p2.x; let dx2 = p3.x - p4.x; @@ -227,11 +221,55 @@ export function IntersectLAndL(p1: Vector3, p2: Vector3, p3: Vector3, p4: Vector return pt; } - +export function IntersectLAndLFor3D(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 pt: Vector3; + + 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 tmpLine = new Line(p1, p2); + let par = tmpLine.GetParamAtPoint(p3); + if (par) + { + pt = midPoint(midPoint(p1, p2), midPoint(p3, p4)); + } + } + 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); + } + pt = new Vector3(x12 * t + p2.x, y12 * t + p2.y, z12 * t + p2.z); + } + return pt; +} //直线和直线 export function IntersectLineAndLine(l1: Line, l2: Line, extType: IntersectOption) { - let pt = IntersectLAndL(l1.StartPoint, l1.EndPoint, l2.StartPoint, l2.EndPoint); + let pt = IntersectLAndLFor3D(l1.StartPoint, l1.EndPoint, l2.StartPoint, l2.EndPoint); return pt ? CheckPointOnCurve([pt], l1, l2, extType) : []; }