|
|
|
@ -1,6 +1,8 @@
|
|
|
|
|
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 { Arc } from '../DatabaseServices/Entity/Arc';
|
|
|
|
|
import { Circle } from '../DatabaseServices/Entity/Circle';
|
|
|
|
|
import { Line } from '../DatabaseServices/Entity/Line';
|
|
|
|
@ -11,7 +13,8 @@ import { PromptStatus } from '../Editor/PromptResult';
|
|
|
|
|
import { SelectPick } from '../Editor/SelectPick';
|
|
|
|
|
import { UCSUtils } from '../Editor/UCSRAII';
|
|
|
|
|
import { userConfig } from '../Editor/UserConfig';
|
|
|
|
|
import { equalv3, ZeroVec } from '../Geometry/GeUtils';
|
|
|
|
|
import { ZeroVec, equalv3 } from '../Geometry/GeUtils';
|
|
|
|
|
import { AppToaster } from '../UI/Components/Toaster';
|
|
|
|
|
|
|
|
|
|
export class DrawLine implements Command
|
|
|
|
|
{
|
|
|
|
@ -24,18 +27,20 @@ export class DrawLine implements Command
|
|
|
|
|
UCSUtils.SetUCSFromPointRes(ptRes);
|
|
|
|
|
|
|
|
|
|
let ptLast = ptRes.Point;
|
|
|
|
|
let isTan = ptRes.SnaoMode === ObjectSnapMode.Tan;
|
|
|
|
|
let cir: Circle | Arc;
|
|
|
|
|
if (isTan)
|
|
|
|
|
let basePoint = ptRes.Point;
|
|
|
|
|
let firstSnapModeIsTan = ptRes.SnaoMode === ObjectSnapMode.Tan;
|
|
|
|
|
let secondSnapModelIsTan = false;
|
|
|
|
|
|
|
|
|
|
let firstTangeCircle: Circle | Arc;
|
|
|
|
|
if (firstSnapModeIsTan)
|
|
|
|
|
{
|
|
|
|
|
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[];
|
|
|
|
|
for (let e of cirs)
|
|
|
|
|
{
|
|
|
|
|
if (e.PtOnCurve(ptRes.Point))
|
|
|
|
|
cir = e;
|
|
|
|
|
firstTangeCircle = e;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -49,34 +54,62 @@ export class DrawLine implements Command
|
|
|
|
|
|
|
|
|
|
let drawLines: Line[] = [];
|
|
|
|
|
let pts: Vector3[] = [ptRes.Point];
|
|
|
|
|
let snapMode: ObjectSnapMode;
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
let line = new Line();
|
|
|
|
|
let ucsMatrix = app.Editor.UCSMatrix;
|
|
|
|
|
line.ApplyMatrix(ucsMatrix);
|
|
|
|
|
line.StartPoint = ptLast;
|
|
|
|
|
|
|
|
|
|
let isFirstAndTan = (isTan && drawLines.length === 0 && cir);
|
|
|
|
|
if (isFirstAndTan)
|
|
|
|
|
if (firstSnapModeIsTan || secondSnapModelIsTan)
|
|
|
|
|
JigUtils.Draw(line);
|
|
|
|
|
line.StartPoint = ptLast;
|
|
|
|
|
|
|
|
|
|
let updateEndPt = (p: Vector3) =>
|
|
|
|
|
const UpdateEndPt = (p: Vector3) =>
|
|
|
|
|
{
|
|
|
|
|
if (isFirstAndTan)
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
let tanP = cir.GetObjectSnapPoints(ObjectSnapMode.Tan, ptLast, p)[0];
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
//计算离鼠标最近的一个点
|
|
|
|
|
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];
|
|
|
|
|
else
|
|
|
|
|
tanP = firstTangeCircle.GetObjectSnapPoints(ObjectSnapMode.Tan, basePoint, p)[1];
|
|
|
|
|
if (tanP)
|
|
|
|
|
{
|
|
|
|
|
line.StartPoint = tanP;
|
|
|
|
|
ptLast.copy(tanP);
|
|
|
|
|
basePoint.copy(tanP);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
line.EndPoint = p;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ptRes = await app.Editor.GetPoint({
|
|
|
|
|
Msg: "请输入点2:",
|
|
|
|
|
BasePoint: isFirstAndTan ? undefined : ptLast,
|
|
|
|
|
BasePoint: (firstSnapModeIsTan || secondSnapModelIsTan) ? undefined : basePoint,
|
|
|
|
|
AllowDrawRubberBand: true,
|
|
|
|
|
AllowNone: true,
|
|
|
|
|
SupportSnapPoints: pts,
|
|
|
|
@ -84,16 +117,126 @@ export class DrawLine implements Command
|
|
|
|
|
{ msg: "放弃", key: "U" },
|
|
|
|
|
{ msg: "闭合", key: "C" },
|
|
|
|
|
],
|
|
|
|
|
Callback: updateEndPt,
|
|
|
|
|
Callback: UpdateEndPt,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let secondTangeCircle: Circle;
|
|
|
|
|
if (ptRes.Status === PromptStatus.OK)
|
|
|
|
|
{
|
|
|
|
|
updateEndPt(ptRes.Point);
|
|
|
|
|
//点选圆切线
|
|
|
|
|
if (ptRes.SnaoMode === ObjectSnapMode.Tan)
|
|
|
|
|
{
|
|
|
|
|
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[];
|
|
|
|
|
|
|
|
|
|
for (let e of cirs)
|
|
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
AppToaster.show({
|
|
|
|
|
message: "选择的点位无法构成切线",
|
|
|
|
|
timeout: 3000,
|
|
|
|
|
intent: Intent.WARNING,
|
|
|
|
|
});
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let minDistance = Infinity;
|
|
|
|
|
let distance = Infinity;
|
|
|
|
|
let nearestObjectIndex = -1;
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (distance < minDistance)
|
|
|
|
|
{
|
|
|
|
|
minDistance = distance;
|
|
|
|
|
nearestObjectIndex = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
JigUtils.Destroy();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//正常画线
|
|
|
|
|
app.LayoutTool.AppendDatabaseSpace(line);
|
|
|
|
|
drawLines.push(line);
|
|
|
|
|
basePoint = ptRes.Point;
|
|
|
|
|
ptLast = ptRes.Point;
|
|
|
|
|
pts.push(ptLast);
|
|
|
|
|
pts.push(basePoint);
|
|
|
|
|
snapMode = ObjectSnapMode.All;
|
|
|
|
|
|
|
|
|
|
if (firstSnapModeIsTan)
|
|
|
|
|
firstSnapModeIsTan = false;
|
|
|
|
|
|
|
|
|
|
if (firstTangeCircle)
|
|
|
|
|
firstTangeCircle = undefined;
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else if (ptRes.Status === PromptStatus.Keyword)
|
|
|
|
@ -105,9 +248,8 @@ export class DrawLine implements Command
|
|
|
|
|
let lastLine = drawLines[drawLines.length - 1];
|
|
|
|
|
app.LayoutTool.CurrentSpace.Remove(lastLine);
|
|
|
|
|
drawLines.pop();
|
|
|
|
|
|
|
|
|
|
ptLast = lastLine.StartPoint;
|
|
|
|
|
|
|
|
|
|
basePoint = lastLine.StartPoint;
|
|
|
|
|
pts.pop();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -120,11 +262,15 @@ export class DrawLine implements Command
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (ptRes.StringResult === "R")
|
|
|
|
|
{
|
|
|
|
|
JigUtils.Destroy();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hotkeys.unbind("Control+Z", hotkeys.getScope(), TempUndo);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|