|
|
|
@ -2,32 +2,89 @@ import { Intent } from '@blueprintjs/core';
|
|
|
|
|
import hotkeys from 'hotkeys-js-ext';
|
|
|
|
|
import { Vector3 } from 'three';
|
|
|
|
|
import { app } from '../ApplicationServices/Application';
|
|
|
|
|
import { CircleInternalTangentLines, CircleOuterTangentLines, GetTanPtsOnArcOrCircle } from '../Common/CurveUtils';
|
|
|
|
|
import { CircleInternalTangentLines, CircleOuterTangentLines } from '../Common/CurveUtils';
|
|
|
|
|
import { Arc } from '../DatabaseServices/Entity/Arc';
|
|
|
|
|
import { Circle } from '../DatabaseServices/Entity/Circle';
|
|
|
|
|
import { Line } from '../DatabaseServices/Entity/Line';
|
|
|
|
|
import { Command } from '../Editor/CommandMachine';
|
|
|
|
|
import { JigUtils } from '../Editor/JigUtils';
|
|
|
|
|
import { ObjectSnapMode } from '../Editor/ObjectSnapMode';
|
|
|
|
|
import { PromptStatus } from '../Editor/PromptResult';
|
|
|
|
|
import { PromptPointResult, PromptStatus } from '../Editor/PromptResult';
|
|
|
|
|
import { SelectPick } from '../Editor/SelectPick';
|
|
|
|
|
import { UCSUtils } from '../Editor/UCSRAII';
|
|
|
|
|
import { userConfig } from '../Editor/UserConfig';
|
|
|
|
|
import { ZeroVec, equalv3 } from '../Geometry/GeUtils';
|
|
|
|
|
import { AppToaster } from '../UI/Components/Toaster';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 测试用例:
|
|
|
|
|
*
|
|
|
|
|
* 1:圆切 圆切 结束
|
|
|
|
|
* 1.1: 圆切 圆切 正常画线
|
|
|
|
|
* 1.2: 圆切 圆切 圆切 正常画
|
|
|
|
|
*
|
|
|
|
|
* 2.从圆外一点开始,捕捉到圆上切线
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export class DrawLine implements Command
|
|
|
|
|
{
|
|
|
|
|
async exec()
|
|
|
|
|
{
|
|
|
|
|
let ptRes = await app.Editor.GetPoint({ Msg: "请输入第一个点:" });
|
|
|
|
|
if (ptRes.Status !== PromptStatus.OK)
|
|
|
|
|
return;
|
|
|
|
|
let ptRes: PromptPointResult;
|
|
|
|
|
let tangeMode = false;//切线模式
|
|
|
|
|
let bakSnap = app.Editor.GetPointServices.snapModeCurrent;
|
|
|
|
|
|
|
|
|
|
function resetSnap(): Disposable
|
|
|
|
|
{
|
|
|
|
|
return {
|
|
|
|
|
[Symbol.dispose]()
|
|
|
|
|
{
|
|
|
|
|
app.Editor.GetPointServices.snapModeCurrent = bakSnap;
|
|
|
|
|
app.Editor.GetPointServices.snapServices.SnapModeEnable = bakSnap;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
using r = resetSnap();
|
|
|
|
|
|
|
|
|
|
let GenKeyWord = () =>
|
|
|
|
|
{
|
|
|
|
|
return [{
|
|
|
|
|
key: "G", msg: tangeMode ? "关闭切线模式" : "切线模式"
|
|
|
|
|
}];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
ptRes = await app.Editor.GetPoint({
|
|
|
|
|
Msg: "请输入第一个点:", KeyWordList: GenKeyWord()
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (ptRes.Status === PromptStatus.Keyword)
|
|
|
|
|
{
|
|
|
|
|
if (ptRes.StringResult === "G")
|
|
|
|
|
{
|
|
|
|
|
tangeMode = !tangeMode;
|
|
|
|
|
|
|
|
|
|
if (tangeMode) app.Editor.GetPointServices.snapModeCurrent = ObjectSnapMode.Tan;
|
|
|
|
|
else app.Editor.GetPointServices.snapModeCurrent = bakSnap;
|
|
|
|
|
|
|
|
|
|
app.Editor.GetPointServices.snapServices.SnapModeEnable = app.Editor.GetPointServices.snapModeCurrent;
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ptRes.Status !== PromptStatus.OK)
|
|
|
|
|
return;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UCSUtils.SetUCSFromPointRes(ptRes);
|
|
|
|
|
|
|
|
|
|
let ptLast = ptRes.Point;
|
|
|
|
|
let basePoint = ptRes.Point;
|
|
|
|
|
let firstSnapModeIsTan = ptRes.SnaoMode === ObjectSnapMode.Tan;
|
|
|
|
|
let secondSnapModelIsTan = false;
|
|
|
|
|
|
|
|
|
@ -54,40 +111,18 @@ export class DrawLine implements Command
|
|
|
|
|
|
|
|
|
|
let drawLines: Line[] = [];
|
|
|
|
|
let pts: Vector3[] = [ptRes.Point];
|
|
|
|
|
let snapMode: ObjectSnapMode;
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
let line = new Line();
|
|
|
|
|
if (firstSnapModeIsTan || secondSnapModelIsTan)
|
|
|
|
|
JigUtils.Draw(line);
|
|
|
|
|
line.StartPoint = ptLast;
|
|
|
|
|
let line = new Line(ptLast.clone());
|
|
|
|
|
if ((pts.length === 1 && firstSnapModeIsTan) || secondSnapModelIsTan)
|
|
|
|
|
JigUtils.Draw(line);//我们用这个来代替橡皮筋
|
|
|
|
|
|
|
|
|
|
const UpdateEndPt = (p: Vector3) =>
|
|
|
|
|
const UpdateEndPt = (p: Vector3, i, snapMode: ObjectSnapMode) =>
|
|
|
|
|
{
|
|
|
|
|
if (snapMode)
|
|
|
|
|
{
|
|
|
|
|
app.Editor.GetPointServices.snapServices.SnapModeEnable = snapMode;
|
|
|
|
|
snapMode = undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (app.Editor.GetPointServices.snapServices.SnapModeEnable === ObjectSnapMode.Tan && !secondSnapModelIsTan)
|
|
|
|
|
{
|
|
|
|
|
secondSnapModelIsTan = true;
|
|
|
|
|
snapMode = ObjectSnapMode.Tan;
|
|
|
|
|
app.Editor.GetPointServices.ReturnKeyword("R");//重画
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else if (app.Editor.GetPointServices.snapServices.SnapModeEnable !== ObjectSnapMode.Tan && secondSnapModelIsTan)
|
|
|
|
|
//第一个是圆切点时,实时更新切点
|
|
|
|
|
if ((pts.length === 1 && firstSnapModeIsTan) && snapMode !== ObjectSnapMode.Tan)
|
|
|
|
|
{
|
|
|
|
|
secondSnapModelIsTan = false;
|
|
|
|
|
app.Editor.GetPointServices.ReturnKeyword("R");//重画
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (firstSnapModeIsTan)
|
|
|
|
|
{
|
|
|
|
|
//第一个是圆切点时 时实更新切点
|
|
|
|
|
const cirCenter = firstTangeCircle.Center;
|
|
|
|
|
let circlePointFirst = p.clone().sub(cirCenter);
|
|
|
|
|
let circlePoinSecond = ptLast.clone().sub(cirCenter);
|
|
|
|
@ -96,49 +131,41 @@ export class DrawLine implements Command
|
|
|
|
|
const crossProduct = new Vector3().crossVectors(circlePointFirst, circlePoinSecond).applyMatrix4(app.Editor.UCSMatrixInv);
|
|
|
|
|
let tanP: Vector3;
|
|
|
|
|
if (crossProduct.z > 0)
|
|
|
|
|
tanP = firstTangeCircle.GetObjectSnapPoints(ObjectSnapMode.Tan, basePoint, p)[0];
|
|
|
|
|
tanP = firstTangeCircle.GetObjectSnapPoints(ObjectSnapMode.Tan, ptLast, p)[0];
|
|
|
|
|
else
|
|
|
|
|
tanP = firstTangeCircle.GetObjectSnapPoints(ObjectSnapMode.Tan, basePoint, p)[1];
|
|
|
|
|
tanP = firstTangeCircle.GetObjectSnapPoints(ObjectSnapMode.Tan, ptLast, p)[1];
|
|
|
|
|
if (tanP)
|
|
|
|
|
{
|
|
|
|
|
line.StartPoint = tanP;
|
|
|
|
|
basePoint.copy(tanP);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
line.EndPoint = p;
|
|
|
|
|
};
|
|
|
|
|
ptRes = await app.Editor.GetPoint({
|
|
|
|
|
Msg: "请输入点2:",
|
|
|
|
|
BasePoint: (firstSnapModeIsTan || secondSnapModelIsTan) ? undefined : basePoint,
|
|
|
|
|
AllowDrawRubberBand: true,
|
|
|
|
|
BasePoint: ptLast,
|
|
|
|
|
AllowDrawRubberBand: !((pts.length === 1 && firstSnapModeIsTan) || secondSnapModelIsTan),
|
|
|
|
|
AllowNone: true,
|
|
|
|
|
SupportSnapPoints: pts,
|
|
|
|
|
KeyWordList: [
|
|
|
|
|
{ msg: "放弃", key: "U" },
|
|
|
|
|
{ msg: "闭合", key: "C" },
|
|
|
|
|
...GenKeyWord()
|
|
|
|
|
],
|
|
|
|
|
Callback: UpdateEndPt,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let secondTangeCircle: Circle;
|
|
|
|
|
if (ptRes.Status === PromptStatus.OK)
|
|
|
|
|
{
|
|
|
|
|
//点选圆切线
|
|
|
|
|
if (ptRes.SnaoMode === ObjectSnapMode.Tan)
|
|
|
|
|
//第一个是切点 第二个也是选切点
|
|
|
|
|
if (ptRes.SnaoMode === ObjectSnapMode.Tan && firstTangeCircle && pts.length === 1)
|
|
|
|
|
{
|
|
|
|
|
let pick = new SelectPick(app.Viewer, app.Viewer.WorldToScreen(ptRes.Point));
|
|
|
|
|
pick.Select(app.Viewer.VisibleObjects, { filterTypes: [Circle, Arc] });
|
|
|
|
|
let cirs = pick.SelectEntityList as Circle[];
|
|
|
|
|
let secondTangeCircle: Circle = (pick.SelectEntityList as Circle[]).find(c => c.PtInCurve(ptRes.Point));
|
|
|
|
|
|
|
|
|
|
for (let e of cirs)
|
|
|
|
|
if (secondTangeCircle)
|
|
|
|
|
{
|
|
|
|
|
if (e.PtOnCurve(ptRes.Point))
|
|
|
|
|
secondTangeCircle = e;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (firstTangeCircle)
|
|
|
|
|
{
|
|
|
|
|
//第一个是切点 第二个也是选切点
|
|
|
|
|
//计算最优切线
|
|
|
|
|
let lines = CircleInternalTangentLines(firstTangeCircle as Circle, secondTangeCircle as Circle).concat(CircleOuterTangentLines(firstTangeCircle as Circle, secondTangeCircle as Circle));
|
|
|
|
|
if (!lines?.length)
|
|
|
|
@ -157,12 +184,7 @@ export class DrawLine implements Command
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < lines.length; i++)
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
distance = Math.sqrt(
|
|
|
|
|
Math.pow(lines[i].StartPoint.x - line.StartPoint.x, 2) +
|
|
|
|
|
Math.pow(lines[i].StartPoint.y - line.StartPoint.y, 2)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
distance = line.StartPoint.distanceTo(lines[i].StartPoint);
|
|
|
|
|
if (distance < minDistance)
|
|
|
|
|
{
|
|
|
|
|
minDistance = distance;
|
|
|
|
@ -172,53 +194,12 @@ export class DrawLine implements Command
|
|
|
|
|
|
|
|
|
|
line = lines[nearestObjectIndex];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//第一个是固定点点 第二个是选切点
|
|
|
|
|
let tanPts = GetTanPtsOnArcOrCircle(secondTangeCircle, ptLast);
|
|
|
|
|
let tanP: Vector3;
|
|
|
|
|
|
|
|
|
|
if (tanPts?.length)
|
|
|
|
|
{
|
|
|
|
|
if (tanPts.length > 1)
|
|
|
|
|
{
|
|
|
|
|
const cirCenter = secondTangeCircle.Center;
|
|
|
|
|
let firstPt = ptLast.clone().sub(cirCenter);
|
|
|
|
|
let secondPt = ptRes.Point.clone().sub(cirCenter);
|
|
|
|
|
|
|
|
|
|
//计算离选点最近的一个点 如果没有切点就重新选
|
|
|
|
|
const crossProduct = new Vector3().crossVectors(firstPt, secondPt).applyMatrix4(app.Editor.UCSMatrixInv);
|
|
|
|
|
|
|
|
|
|
if (crossProduct.z > 0)
|
|
|
|
|
tanP = tanPts[0];
|
|
|
|
|
else if (tanPts.length > 1)
|
|
|
|
|
tanP = tanPts[1];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
tanP = tanPts[0];
|
|
|
|
|
|
|
|
|
|
line.EndPoint = tanP;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
AppToaster.show({
|
|
|
|
|
message: "选择的点位无法构成切线",
|
|
|
|
|
timeout: 3000,
|
|
|
|
|
intent: Intent.WARNING,
|
|
|
|
|
});
|
|
|
|
|
JigUtils.Destroy();
|
|
|
|
|
snapMode = ObjectSnapMode.Tan;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
app.LayoutTool.AppendDatabaseSpace(line);
|
|
|
|
|
|
|
|
|
|
drawLines.push(line);
|
|
|
|
|
basePoint = line.EndPoint;
|
|
|
|
|
ptLast = line.EndPoint;
|
|
|
|
|
firstSnapModeIsTan = false;
|
|
|
|
|
secondSnapModelIsTan = false;
|
|
|
|
|
pts.push(ptLast);
|
|
|
|
|
JigUtils.Destroy();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
@ -226,22 +207,14 @@ export class DrawLine implements Command
|
|
|
|
|
//正常画线
|
|
|
|
|
app.LayoutTool.AppendDatabaseSpace(line);
|
|
|
|
|
drawLines.push(line);
|
|
|
|
|
basePoint = ptRes.Point;
|
|
|
|
|
ptLast = ptRes.Point;
|
|
|
|
|
pts.push(basePoint);
|
|
|
|
|
snapMode = ObjectSnapMode.All;
|
|
|
|
|
|
|
|
|
|
if (firstSnapModeIsTan)
|
|
|
|
|
firstSnapModeIsTan = false;
|
|
|
|
|
|
|
|
|
|
if (firstTangeCircle)
|
|
|
|
|
firstTangeCircle = undefined;
|
|
|
|
|
pts.push(ptLast);
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else if (ptRes.Status === PromptStatus.Keyword)
|
|
|
|
|
{
|
|
|
|
|
if (ptRes.StringResult === "U")
|
|
|
|
|
if (ptRes.StringResult === "U")//撤销
|
|
|
|
|
{
|
|
|
|
|
if (drawLines.length > 0)
|
|
|
|
|
{
|
|
|
|
@ -249,11 +222,10 @@ export class DrawLine implements Command
|
|
|
|
|
app.LayoutTool.CurrentSpace.Remove(lastLine);
|
|
|
|
|
drawLines.pop();
|
|
|
|
|
ptLast = lastLine.StartPoint;
|
|
|
|
|
basePoint = lastLine.StartPoint;
|
|
|
|
|
pts.pop();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (ptRes.StringResult === "C")
|
|
|
|
|
else if (ptRes.StringResult === "C")//闭合
|
|
|
|
|
{
|
|
|
|
|
if (drawLines.length > 1)
|
|
|
|
|
{
|
|
|
|
@ -262,15 +234,20 @@ export class DrawLine implements Command
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (ptRes.StringResult === "R")
|
|
|
|
|
else if (ptRes.StringResult === "G")//切换切线
|
|
|
|
|
{
|
|
|
|
|
JigUtils.Destroy();
|
|
|
|
|
continue;
|
|
|
|
|
tangeMode = !tangeMode;
|
|
|
|
|
|
|
|
|
|
if (tangeMode) app.Editor.GetPointServices.snapModeCurrent = ObjectSnapMode.Tan;
|
|
|
|
|
else app.Editor.GetPointServices.snapModeCurrent = bakSnap;
|
|
|
|
|
|
|
|
|
|
app.Editor.GetPointServices.snapServices.SnapModeEnable = app.Editor.GetPointServices.snapModeCurrent;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hotkeys.unbind("Control+Z", hotkeys.getScope(), TempUndo);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|