重构:画线功能,增加切线模式

pull/2561/MERGE
ChenX 3 months ago
parent 45f461767b
commit dc05a1b579

@ -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);
}
}

@ -363,7 +363,7 @@ export function GetTanPtsOnArcOrCircle(cu: Arc | Circle, lastPoint?: Vector3)
),
];
for (let p of pts)
p.applyMatrix4(cu.OCS);
p.applyMatrix4(cu.OCSNoClone);
return pts;
}
}
@ -389,7 +389,7 @@ export function CircleInternalTangentLines(cir0: Circle, cir1: Circle): Line[]
let i = new Vector3(
(h1 * c0.x + h0 * c1.x) / dist,
(h1 * c0.y + h0 * c1.y) / dist
).applyMatrix4(cir0.OCS);
).applyMatrix4(cir0.OCSNoClone);
let [c0p0, c0p1] = GetTanPtsOnArcOrCircle(cir0, i);
let [c1p0, c1p1] = GetTanPtsOnArcOrCircle(cir1, i);
@ -451,7 +451,7 @@ export function CircleOuterTangentLines(circle0: Circle, circle1: Circle): Line[
p.x /= diff;
p.y /= diff;
p.applyMatrix4(circle0.OCS);
p.applyMatrix4(circle0.OCSNoClone);
let [c0p0, c0p1] = GetTanPtsOnArcOrCircle(circle0, p);
let [c1p0, c1p1] = GetTanPtsOnArcOrCircle(circle1, p);

@ -343,6 +343,7 @@ export class Circle extends Curve
let pts = GetTanPtsOnArcOrCircle(this, lastPoint);
if (pts)
return pts;
break;
case ObjectSnapMode.End:
{
let pts = this.GetGripPoints();

@ -49,7 +49,7 @@ export class GetPointServices implements EditorService
}
this.UpdateCurPointEvent();//触摸的时候会因为没有鼠标移动导致这个位置错误!
if (this.snapServices.SnapModeEnable === ObjectSnapMode.Tan && !this.curPointIsSnap)//切线捕捉时 必须在圆上
if (this.snapServices.SnapModeEnable === ObjectSnapMode.Tan && (!this.curPointIsSnap || this.snapServices.SnapType !== ObjectSnapMode.Tan))//切线捕捉时 必须在圆上
{
app.Editor.Prompt("点无效!切点必须在圆上!", LogType.Error);
return;
@ -257,7 +257,7 @@ export class GetPointServices implements EditorService
{
if (this.curPoint)
{
prompt.Callback(this.curPoint, this.intersection);
prompt.Callback(this.curPoint, this.intersection, this.curPointIsSnap ? this.snapServices.SnapType : undefined);
app.Editor.UpdateScreen();
}
}

@ -1,5 +1,6 @@
import { Intersection, Matrix4, Vector3 } from 'three';
import { KeyWord } from '../Common/InputState';
import { ObjectSnapMode } from './ObjectSnapMode';
import { PromptEntityResult, PromptSsgetResult } from './PromptResult';
import { SelectType } from './SelectBox';
import { Filter } from './SelectFilter';
@ -35,7 +36,7 @@ export interface GetPointPrompt extends PromptOptions
//相对坐标参考基点
RelativeBasePoint?: Vector3;
//当鼠标移动时,回调该函数.
Callback?: (pt: Vector3, intersection: Intersection) => void;
Callback?: (pt: Vector3, intersection: Intersection, snapMode?: ObjectSnapMode) => void;
//绘制橡皮筋(必须指定基点)
AllowDrawRubberBand?: Boolean;
//不显示动态提示

@ -174,6 +174,9 @@ export class SnapServices
this.UpdateCursort();
}
/**
*
*/
get SnapType(): ObjectSnapMode
{
return this._SnapType;

Loading…
Cancel
Save