!2692 功能:新增切线绘画功能

pull/2865/MERGE
彭泽 3 months ago committed by ChenX
parent afea2b423c
commit 3bcc3fcaf9

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

@ -1,8 +1,8 @@
import { Command } from "../Editor/CommandMachine";
import { app } from "../ApplicationServices/Application";
import { Circle } from "../DatabaseServices/Entity/Circle";
import { Arc } from "../DatabaseServices/Entity/Arc";
import { CircleInternalTangentLines, CircleOuterTangentLines } from "../Common/CurveUtils";
import { Arc } from "../DatabaseServices/Entity/Arc";
import { Circle } from "../DatabaseServices/Entity/Circle";
import { Command } from "../Editor/CommandMachine";
import { PromptStatus } from "../Editor/PromptResult";
export class DrawTangentLine implements Command

@ -14,6 +14,7 @@ export enum CommandNames
FBXImport = "FBX",
Insert = "INSERT",
Line = "LINE", //直线
Tangent = "TANGENT", //切线
XLine = "XLINE",
Undo = "UNDO", //撤销
Redo = "REDO", //重做

@ -393,7 +393,6 @@ export function CircleInternalTangentLines(cir0: Circle, cir1: Circle): Line[]
let [c0p0, c0p1] = GetTanPtsOnArcOrCircle(cir0, i);
let [c1p0, c1p1] = GetTanPtsOnArcOrCircle(cir1, i);
return [
new Line(c0p0, c1p0),
new Line(c0p1, c1p1),

@ -506,7 +506,7 @@ export function registerCommand()
commandMachine.RegisterCommand("ptcopy", new Command_CopyPoint());
commandMachine.RegisterCommand("tan", new DrawTangentLine());
commandMachine.RegisterCommand(CommandNames.Tangent, new DrawTangentLine());
commandMachine.RegisterCommand(CommandNames.Divide, new CMD_Divide());
commandMachine.RegisterCommand(CommandNames.Point, new CMD_DrawPoint());

@ -6,10 +6,10 @@ import { app } from '../ApplicationServices/Application';
import { HostApplicationServices } from '../ApplicationServices/HostApplicationServices';
import { ColorMaterial } from '../Common/ColorPalette';
import { DisposeThreeObj } from '../Common/Dispose';
import { safeEval } from '../Common/eval';
import { InputState, KeyWord, MenuDividerKWD } from '../Common/InputState';
import { KeyBoard, MouseKey } from '../Common/KeyEnum';
import { LogType } from '../Common/Log';
import { safeEval } from '../Common/eval';
import { Entity } from '../DatabaseServices/Entity/Entity';
import { BufferGeometryUtils } from '../Geometry/BufferGeometryUtils';
import { isParallelTo } from '../Geometry/GeUtils';
@ -26,7 +26,7 @@ import { GenerateRaycaster, Raycast } from './PointPick';
import { GetPointPrompt } from "./PromptOptions";
import { PromptPointResult, PromptStatus } from './PromptResult';
import { Filter } from './SelectFilter';
import { SnapMenuKW, SNAPMODE } from './ShowSnapMenu';
import { SNAPMODE, SnapMenuKW } from './ShowSnapMenu';
import { SnapServices } from './SnapServices';
const MinP = new Vector3(-2e6, -2e6, -2e6);
@ -48,6 +48,13 @@ export class GetPointServices implements EditorService
return;
}
this.UpdateCurPointEvent();//触摸的时候会因为没有鼠标移动导致这个位置错误!
if (this.snapServices.SnapModeEnable === ObjectSnapMode.Tan && !this.curPointIsSnap)//切线捕捉时 必须在圆上
{
app.Editor.Prompt("点无效!切点必须在圆上!", LogType.Error);
return;
}
this.ReturnPoint(this.curPoint, this.curPointIsSnap ? this.snapServices.SnapType : undefined);
return true;
}

@ -53,7 +53,6 @@ export interface GetPointPrompt extends PromptOptions
RaycastFilter?: Filter;
RaycastPreExecFunc?: Function;
DisableAngleDynInput?: boolean;
SupportSnapPoints?: Vector3[];
}

@ -195,7 +195,15 @@ export const CommandList: ICommand[] = [
chName: "计算封闭线段的面积",
chDes: "计算封闭线段的面积",
},
{
typeId: "i2d",
link: `#`,
defaultCustom: CommandNames.Tangent,
command: CommandNames.Tangent,
type: "二维",
chName: "圆与圆之间相切的线",
chDes: "圆与圆之间相切的线",
},
//三维命令
{
icon: IconEnum.UCS,

Loading…
Cancel
Save