mirror of https://gitee.com/cf-fz/WebCAD.git
parent
72f4d04b36
commit
63003a6f6e
@ -0,0 +1,225 @@
|
|||||||
|
import { Matrix4, Vector3 } from "three";
|
||||||
|
import { arrayLast } from "../../Common/ArrayExt";
|
||||||
|
import { Board } from "../../DatabaseServices/Entity/Board";
|
||||||
|
import { Curve } from "../../DatabaseServices/Entity/Curve";
|
||||||
|
import { ExtrudeSolid } from "../../DatabaseServices/Entity/Extrude";
|
||||||
|
import { Line } from "../../DatabaseServices/Entity/Line";
|
||||||
|
import { Polyline } from "../../DatabaseServices/Entity/Polyline";
|
||||||
|
import { Box3Ext } from "../../Geometry/Box";
|
||||||
|
import { AsVector2, equaln } from "../../Geometry/GeUtils";
|
||||||
|
import { GetBoardSealingCurves, SubsectionCurvesOfHightSeal } from "../../GraphicsSystem/CalcEdgeSealing";
|
||||||
|
import { IntersectOption } from "../../GraphicsSystem/IntersectWith";
|
||||||
|
import { FixIndex } from "../../Nest/Common/Util";
|
||||||
|
import { SplitPolyline } from "./SplitPolyline";
|
||||||
|
|
||||||
|
//侧面造型分裂
|
||||||
|
export class SplitBoardSideModelUtil
|
||||||
|
{
|
||||||
|
//备份原始二维刀路在世界坐标系中
|
||||||
|
private OrgBoardOCS: Matrix4 = new Matrix4();
|
||||||
|
private CacheSideModel: Map<number, ExtrudeSolid[]> = new Map();
|
||||||
|
private OldSealCurves: Curve[] = [];
|
||||||
|
|
||||||
|
constructor(br: Board)
|
||||||
|
{
|
||||||
|
this.Init(br);
|
||||||
|
}
|
||||||
|
|
||||||
|
Init(br: Board, isSpecialShape = false)
|
||||||
|
{
|
||||||
|
this.OrgBoardOCS = br.OCS;
|
||||||
|
let curves = GetBoardSealingCurves(br);
|
||||||
|
|
||||||
|
//取消异型操作会提前令isSpecialShape = false
|
||||||
|
if (isSpecialShape)
|
||||||
|
SubsectionCurvesOfHightSeal(curves);
|
||||||
|
|
||||||
|
let oldSealCurves: Curve[] = [];
|
||||||
|
|
||||||
|
for (let c of curves)
|
||||||
|
if (c instanceof Polyline)
|
||||||
|
oldSealCurves.push(...c.Explode());
|
||||||
|
else
|
||||||
|
oldSealCurves.push(c);
|
||||||
|
|
||||||
|
this.OldSealCurves = oldSealCurves;
|
||||||
|
|
||||||
|
const sideModelMap: Map<number, ExtrudeSolid[]> = new Map();
|
||||||
|
|
||||||
|
for (let [num, soilds] of br.SideModelingMap)
|
||||||
|
sideModelMap.set(num, soilds);
|
||||||
|
|
||||||
|
this.CacheSideModel = sideModelMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckSideModel(): boolean
|
||||||
|
{
|
||||||
|
let maxSideIndex = 0;
|
||||||
|
for (let [num, soilds] of this.CacheSideModel)
|
||||||
|
maxSideIndex = Math.max(num, maxSideIndex);
|
||||||
|
|
||||||
|
return this.OldSealCurves.length >= maxSideIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetBoardSideModel(br: Board)
|
||||||
|
{
|
||||||
|
if (!this.CacheSideModel.size) return;
|
||||||
|
|
||||||
|
if (this.CheckSideModel())
|
||||||
|
{
|
||||||
|
this.SpiltSideModelOfBrContour(br);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//新轮廓切割原始轮廓
|
||||||
|
SpiltSideModelOfBrContour(br: Board)
|
||||||
|
{
|
||||||
|
let curves = GetBoardSealingCurves(br);
|
||||||
|
|
||||||
|
let newSealCurves: Curve[] = [];
|
||||||
|
|
||||||
|
for (let c of curves)
|
||||||
|
if (c instanceof Polyline)
|
||||||
|
newSealCurves.push(...c.Explode());
|
||||||
|
else
|
||||||
|
newSealCurves.push(c);
|
||||||
|
|
||||||
|
let sideMadelMap: Map<number, ExtrudeSolid[]> = new Map();
|
||||||
|
for (let [nmu, soilds] of this.CacheSideModel)
|
||||||
|
{
|
||||||
|
if (soilds?.length)
|
||||||
|
{
|
||||||
|
let oldCu = this.OldSealCurves[nmu]?.Clone();
|
||||||
|
if (!oldCu) continue;
|
||||||
|
|
||||||
|
oldCu.ApplyMatrix(this.OrgBoardOCS);
|
||||||
|
for (let i = 0; i < newSealCurves.length; i++)
|
||||||
|
{
|
||||||
|
let newCu = newSealCurves[i].Clone().ApplyMatrix(br.OCSNoClone) as Line;
|
||||||
|
let p = newCu.GetPointAtParam(newCu.EndParam * 0.5);
|
||||||
|
|
||||||
|
let spliteEnts: ExtrudeSolid[] = [];
|
||||||
|
|
||||||
|
if (oldCu.PtOnCurve(p))
|
||||||
|
{
|
||||||
|
let startX = oldCu.GetDistAtPoint(newCu.StartPoint);
|
||||||
|
let endX = oldCu.GetDistAtPoint(newCu.EndPoint);
|
||||||
|
let box = new Box3Ext(new Vector3(startX), new Vector3(endX, br.Thickness));
|
||||||
|
|
||||||
|
let knifePls: Polyline[] = [];
|
||||||
|
|
||||||
|
for (let soild of soilds)
|
||||||
|
{
|
||||||
|
if (soild.Thickness <= 0) continue;
|
||||||
|
|
||||||
|
let sCon = soild.ContourCurve.Clone().ApplyMatrix(soild.OCSNoClone) as Polyline;
|
||||||
|
let thickness = soild.Thickness;
|
||||||
|
|
||||||
|
let newNeighborCus: Curve[] = [];
|
||||||
|
newNeighborCus.push(newSealCurves[FixIndex(i - 1, newSealCurves)].Clone().ApplyMatrix(br.OCSNoClone));
|
||||||
|
newNeighborCus.push(newSealCurves[FixIndex(i + 1, newSealCurves)].Clone().ApplyMatrix(br.OCSNoClone));
|
||||||
|
|
||||||
|
const offsetCus = newCu.GetOffsetCurves(-thickness);
|
||||||
|
const xPtList: number[] = [startX, endX];
|
||||||
|
|
||||||
|
for (let cu of offsetCus)
|
||||||
|
{
|
||||||
|
for (let nbCu of newNeighborCus)
|
||||||
|
{
|
||||||
|
const intersectPts = cu.IntersectWith(nbCu, IntersectOption.ExtendThis);
|
||||||
|
for (let pt of intersectPts)
|
||||||
|
{
|
||||||
|
let { closestPt } = newCu.GetClosestAtPoint(pt, true);
|
||||||
|
let ptX = oldCu.GetDistAtPoint(closestPt);
|
||||||
|
xPtList.push(ptX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xPtList.sort((a, b) => a - b);
|
||||||
|
|
||||||
|
for (let x of [xPtList[0], arrayLast(xPtList)])
|
||||||
|
knifePls.push(new Polyline([{ pt: AsVector2({ x, y: 0 }), bul: 0 }, { pt: AsVector2({ x, y: 1 }), bul: 0 }]));
|
||||||
|
|
||||||
|
let splitSideModelCons = SplitPolyline(sCon, knifePls);
|
||||||
|
|
||||||
|
if (!splitSideModelCons.length)
|
||||||
|
splitSideModelCons.push(sCon);
|
||||||
|
|
||||||
|
for (let j = 0; j < splitSideModelCons.length; j++)
|
||||||
|
{
|
||||||
|
let intersectBox = box.clone().intersect(splitSideModelCons[j].BoundingBox);
|
||||||
|
if (box.intersectsBox(splitSideModelCons[j].BoundingBox, 1) && !equaln(intersectBox.max.x, intersectBox.min.x))
|
||||||
|
{
|
||||||
|
let soildClone = soild.Clone();
|
||||||
|
soildClone.ContourCurve = splitSideModelCons[j].ApplyMatrix(soild.OCSInv);
|
||||||
|
soildClone.GrooveCheckAllAutoSplit();
|
||||||
|
soildClone.ApplyMatrix(new Matrix4().setPosition(-startX, 0, 0));
|
||||||
|
spliteEnts.push(soildClone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spliteEnts.length)
|
||||||
|
sideMadelMap.set(i, spliteEnts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
br.SideModelingMap = sideMadelMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
//修改板厚度时裁剪侧面造型
|
||||||
|
SpiltSideModelOfBrThickness(br: Board, thickness: number)
|
||||||
|
{
|
||||||
|
this.Init(br);
|
||||||
|
|
||||||
|
let sideMadelMap: Map<number, ExtrudeSolid[]> = new Map();
|
||||||
|
for (let [nmu, soilds] of this.CacheSideModel)
|
||||||
|
{
|
||||||
|
if (soilds?.length)
|
||||||
|
{
|
||||||
|
let cu = this.OldSealCurves[nmu]?.Clone();
|
||||||
|
if (!cu) continue;
|
||||||
|
cu.ApplyMatrix(this.OrgBoardOCS);
|
||||||
|
|
||||||
|
let spliteEnts: ExtrudeSolid[] = [];
|
||||||
|
let knifePls: Polyline[] = [];
|
||||||
|
let box = new Box3Ext(new Vector3(), new Vector3(cu.Length, thickness));
|
||||||
|
|
||||||
|
for (let soild of soilds)
|
||||||
|
{
|
||||||
|
if (soild.Thickness <= 0) continue;
|
||||||
|
|
||||||
|
let sCon = soild.ContourCurve.Clone().ApplyMatrix(soild.OCSNoClone) as Polyline;
|
||||||
|
|
||||||
|
knifePls.push(new Polyline([{ pt: AsVector2({ x: 0, y: 0 }), bul: 0 }, { pt: AsVector2({ x: 1, y: 0 }), bul: 0 }]));
|
||||||
|
knifePls.push(new Polyline([{ pt: AsVector2({ x: 0, y: thickness }), bul: 0 }, { pt: AsVector2({ x: 1, y: thickness }), bul: 0 }]));
|
||||||
|
|
||||||
|
let splitSideModelCons = SplitPolyline(sCon, knifePls);
|
||||||
|
|
||||||
|
if (!splitSideModelCons.length)
|
||||||
|
splitSideModelCons.push(sCon);
|
||||||
|
|
||||||
|
for (let con of splitSideModelCons)
|
||||||
|
{
|
||||||
|
let intersectBox = box.clone().intersect(con.BoundingBox);
|
||||||
|
if (box.intersectsBox(con.BoundingBox, 1) && !equaln(intersectBox.max.y, intersectBox.min.y))
|
||||||
|
{
|
||||||
|
let soildClone = soild.Clone();
|
||||||
|
soildClone.ContourCurve = con.ApplyMatrix(soild.OCSInv);
|
||||||
|
soildClone.GrooveCheckAllAutoSplit();
|
||||||
|
spliteEnts.push(soildClone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spliteEnts.length)
|
||||||
|
sideMadelMap.set(nmu, spliteEnts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
br.SideModelingMap = sideMadelMap;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
import { Matrix4, Vector3 } from "three";
|
||||||
|
import { app } from "../ApplicationServices/Application";
|
||||||
|
import { Intent, Toaster } from "../Common/Toaster";
|
||||||
|
import { Board } from "../DatabaseServices/Entity/Board";
|
||||||
|
import { Entity } from "../DatabaseServices/Entity/Entity";
|
||||||
|
import { Polyline } from "../DatabaseServices/Entity/Polyline";
|
||||||
|
import { Text, TextAligen } from "../DatabaseServices/Text/Text";
|
||||||
|
import { Command } from "../Editor/CommandMachine";
|
||||||
|
import { PromptStatus } from "../Editor/PromptResult";
|
||||||
|
import { ParseBoardSideFace } from "../Geometry/DrillParse/ParseBoardSideFace";
|
||||||
|
import { MoveMatrix } from "../Geometry/GeUtils";
|
||||||
|
import { FeedingToolPath } from "../GraphicsSystem/ToolPath/FeedingToolPath";
|
||||||
|
|
||||||
|
export class SideModelFeedingCommand implements Command
|
||||||
|
{
|
||||||
|
async exec()
|
||||||
|
{
|
||||||
|
let enRes = await app.Editor.GetEntity({ Filter: { filterTypes: [Entity] } });
|
||||||
|
if (enRes.Status !== PromptStatus.OK) return;
|
||||||
|
let br = enRes.Entity as Board;
|
||||||
|
|
||||||
|
if (!br.SideModelingMap?.size) return;
|
||||||
|
|
||||||
|
const tool = FeedingToolPath.GetInstance();
|
||||||
|
|
||||||
|
let faces = new ParseBoardSideFace(br);
|
||||||
|
|
||||||
|
let ptRes = await app.Editor.GetPoint({ Msg: "点取位置" });
|
||||||
|
|
||||||
|
if (ptRes.Status === PromptStatus.OK)
|
||||||
|
{
|
||||||
|
let pos = ptRes.Point;;
|
||||||
|
let width = 0;
|
||||||
|
|
||||||
|
let sideModelList = Array.from(br.SideModelingMap);
|
||||||
|
sideModelList.sort((a, b) => a[0] - b[0]);
|
||||||
|
|
||||||
|
for (let [n, soilds] of sideModelList)
|
||||||
|
{
|
||||||
|
let tMtx = MoveMatrix(pos);
|
||||||
|
let faceContour = faces.Faces[n].Region.ShapeManager.ShapeList[0].Outline.Curve as Polyline;
|
||||||
|
|
||||||
|
for (let soild of soilds)
|
||||||
|
{
|
||||||
|
let con = soild.ContourCurve.Clone().ApplyMatrix(soild.OCSNoClone).ApplyMatrix(tMtx);
|
||||||
|
con.ColorIndex = 8;
|
||||||
|
app.Database.ModelSpace.Append(con);
|
||||||
|
|
||||||
|
let paths = tool.GetSideModelFeedPath(soild, faceContour);//走刀路径
|
||||||
|
|
||||||
|
if (paths.length)
|
||||||
|
for (let pl of paths)
|
||||||
|
{
|
||||||
|
pl.ApplyMatrix(tMtx);
|
||||||
|
pl.ColorIndex = 1;
|
||||||
|
app.Database.ModelSpace.Append(pl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Toaster({
|
||||||
|
message: "板件有侧面造型无法加工,请运行造型检测命令<checkmodeing>确认",
|
||||||
|
timeout: 5000,
|
||||||
|
intent: Intent.DANGER,
|
||||||
|
key: "造型加工错误"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let hole of soild.Shape.Holes)
|
||||||
|
{
|
||||||
|
let cu = hole.Curve.ApplyMatrix(soild.OCSNoClone).ApplyMatrix(tMtx);
|
||||||
|
cu.ColorIndex = 8;
|
||||||
|
app.Database.ModelSpace.Append(cu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
faceContour.ApplyMatrix(tMtx);
|
||||||
|
faceContour.ColorIndex = 3;
|
||||||
|
app.Database.ModelSpace.Append(faceContour);
|
||||||
|
|
||||||
|
width = faces.Faces[n].Length;
|
||||||
|
|
||||||
|
this.OrderText((n + 1).toString(), tMtx, width);
|
||||||
|
|
||||||
|
pos.add(new Vector3(width + 100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OrderText(order: string, tMtx: Matrix4, width: number)
|
||||||
|
{
|
||||||
|
//文本上下间隔 i
|
||||||
|
let i = -100;
|
||||||
|
|
||||||
|
let text = new Text(new Vector3, order);
|
||||||
|
text.Height = 60;
|
||||||
|
text.TextAligen = TextAligen.Mid;
|
||||||
|
text.ApplyMatrix(tMtx);
|
||||||
|
text.Position = text.Position.add(new Vector3(width * 0.5, i));
|
||||||
|
app.Database.ModelSpace.Append(text);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
import { Matrix4, Vector3 } from "three";
|
||||||
|
import { Line } from "../../DatabaseServices/Entity/Line";
|
||||||
|
import { Point } from "../../DatabaseServices/Entity/Point";
|
||||||
|
import { TestDraw } from "../test/TestUtil";
|
||||||
|
|
||||||
|
export async function TestDrawUCS(mat4: Matrix4)
|
||||||
|
{
|
||||||
|
let pt = new Vector3().applyMatrix4(mat4);
|
||||||
|
let l1 = new Line(pt, pt.clone().add(new Vector3().setFromMatrixColumn(mat4, 0).normalize().multiplyScalar(100)));
|
||||||
|
let l2 = new Line(pt, pt.clone().add(new Vector3().setFromMatrixColumn(mat4, 1).normalize().multiplyScalar(100)));
|
||||||
|
let l3 = new Line(pt, pt.clone().add(new Vector3().setFromMatrixColumn(mat4, 2).normalize().multiplyScalar(100)));
|
||||||
|
TestDraw(new Point(pt));
|
||||||
|
TestDraw(l1, 1);
|
||||||
|
TestDraw(l2, 3);
|
||||||
|
TestDraw(l3, 5);
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
import { Geom3, transform } from "@jscad/modeling/src/geometries/geom3";
|
||||||
|
import { Mat4 } from "@jscad/modeling/src/maths/mat4";
|
||||||
|
import { Matrix4 } from "three";
|
||||||
|
import { MakeMirrorMtx } from "../../Common/Matrix4Utils";
|
||||||
|
import { Board } from "../../DatabaseServices/Entity/Board";
|
||||||
|
import { Curve } from "../../DatabaseServices/Entity/Curve";
|
||||||
|
import { GetBoardContour } from "../../GraphicsSystem/CalcEdgeSealing";
|
||||||
|
import { Geometry2CSG2 } from "../../csg/core/Geometry2CSG";
|
||||||
|
import { ZAxis } from "../GeUtils";
|
||||||
|
|
||||||
|
export function BoardSideModelCSGBuilder(board: Board): Geom3[]
|
||||||
|
{
|
||||||
|
const sideModeingCsgs: Geom3[] = [];
|
||||||
|
|
||||||
|
if (!board.SideModelingMap.size)
|
||||||
|
return sideModeingCsgs;
|
||||||
|
|
||||||
|
let con = GetBoardContour(board);
|
||||||
|
let inverseZ = con.Area2 < 0;
|
||||||
|
let cus = con.Explode() as Curve[];
|
||||||
|
const mirrorMtxZ = MakeMirrorMtx(ZAxis);
|
||||||
|
|
||||||
|
for (let [index, soilds] of board.SideModelingMap)
|
||||||
|
{
|
||||||
|
let cu = cus[index];
|
||||||
|
if (!cu) continue;
|
||||||
|
|
||||||
|
let mt4 = GetSideCuFaceMtx(cus[index], inverseZ);
|
||||||
|
|
||||||
|
for (let soild of soilds)
|
||||||
|
{
|
||||||
|
let s = soild.Clone();
|
||||||
|
let geom3 = Geometry2CSG2(s.MeshGeometry);
|
||||||
|
geom3 = transform(mirrorMtxZ.elements as Mat4, geom3);
|
||||||
|
geom3 = transform(mt4.clone().multiply(soild.OCS).elements as Mat4, geom3);
|
||||||
|
sideModeingCsgs.push(geom3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sideModeingCsgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GetSideCuFaceMtx(cu: Curve, inverseZ = false): Matrix4
|
||||||
|
{
|
||||||
|
let x = cu.GetFirstDeriv(0).normalize();
|
||||||
|
let y = ZAxis;
|
||||||
|
let z = x.clone().cross(y);
|
||||||
|
if (inverseZ) z.negate();
|
||||||
|
|
||||||
|
let basePt = cu.StartPoint;
|
||||||
|
|
||||||
|
return new Matrix4().makeBasis(x, y, z).setPosition(basePt);
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
import { Matrix4 } from "three";
|
||||||
|
import { Arc } from "../../DatabaseServices/Entity/Arc";
|
||||||
|
import { Board } from "../../DatabaseServices/Entity/Board";
|
||||||
|
import { Curve } from "../../DatabaseServices/Entity/Curve";
|
||||||
|
import { GetBoardContour } from "../../GraphicsSystem/CalcEdgeSealing";
|
||||||
|
import { GetSideCuFaceMtx } from "../Board2DModelCSG/BoardSideModelCSGBuilder";
|
||||||
|
import { equaln } from "../GeUtils";
|
||||||
|
import { BoardFaceType, BoardGetFace } from "./BoardGetFace";
|
||||||
|
import { Face } from "./Face";
|
||||||
|
|
||||||
|
export class ParseBoardSideFace extends BoardGetFace
|
||||||
|
{
|
||||||
|
constructor(public Board: Board)
|
||||||
|
{
|
||||||
|
super(Board);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseFaces()
|
||||||
|
{
|
||||||
|
this.GetSideFaces();
|
||||||
|
}
|
||||||
|
|
||||||
|
GetSideFaces()
|
||||||
|
{
|
||||||
|
let con = GetBoardContour(this.Board);
|
||||||
|
let inverseZ = con.Area2 < 0;
|
||||||
|
let cus = con.Explode() as Curve[];
|
||||||
|
for (let cu of cus)
|
||||||
|
{
|
||||||
|
let type = BoardFaceType.Side;
|
||||||
|
|
||||||
|
let length = cu.Length;
|
||||||
|
if (equaln(length, 0) || cu instanceof Arc)
|
||||||
|
type = BoardFaceType.NoSide;
|
||||||
|
|
||||||
|
let mtx = GetSideCuFaceMtx(cu, inverseZ);
|
||||||
|
let face = new Face({
|
||||||
|
type,
|
||||||
|
localBoard: this.Board,
|
||||||
|
matrix4: new Matrix4().multiplyMatrices(this.Board.OCS.clone(), mtx),
|
||||||
|
length,
|
||||||
|
width: this.Board.Thickness,
|
||||||
|
});
|
||||||
|
this.Faces.push(face);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,622 @@
|
|||||||
|
import { Button, Intent } from "@blueprintjs/core";
|
||||||
|
import React from "react";
|
||||||
|
import { Box3, LineSegments, Matrix4, Mesh, Object3D, Vector3 } from "three";
|
||||||
|
import { FaceDirection } from "../../../Add-on/DrawDrilling/DrillType";
|
||||||
|
import { app } from "../../../ApplicationServices/Application";
|
||||||
|
import { ColorMaterial } from "../../../Common/ColorPalette";
|
||||||
|
import { curveLinkGroup } from "../../../Common/CurveUtils";
|
||||||
|
import { GroupEntitysByBox } from "../../../Common/GroupEntitysByBox";
|
||||||
|
import { Log } from "../../../Common/Log";
|
||||||
|
import { MakeMirrorMtx } from "../../../Common/Matrix4Utils";
|
||||||
|
import { UpdateDraw } from "../../../Common/Status";
|
||||||
|
import { GetEntity } from "../../../Common/Utils";
|
||||||
|
import { CADFiler } from "../../../DatabaseServices/CADFiler";
|
||||||
|
import { Contour } from "../../../DatabaseServices/Contour";
|
||||||
|
import { Board, IModeling } from "../../../DatabaseServices/Entity/Board";
|
||||||
|
import { Curve } from "../../../DatabaseServices/Entity/Curve";
|
||||||
|
import { Ellipse } from "../../../DatabaseServices/Entity/Ellipse";
|
||||||
|
import { ExtrudeSolid } from "../../../DatabaseServices/Entity/Extrude";
|
||||||
|
import { Polyline } from "../../../DatabaseServices/Entity/Polyline";
|
||||||
|
import { Region } from "../../../DatabaseServices/Entity/Region";
|
||||||
|
import { Spline } from "../../../DatabaseServices/Spline";
|
||||||
|
import { CommandWrap } from "../../../Editor/CommandMachine";
|
||||||
|
import { PromptStatus } from "../../../Editor/PromptResult";
|
||||||
|
import { TempEditor } from "../../../Editor/TempEditor";
|
||||||
|
import { userConfig } from "../../../Editor/UserConfig";
|
||||||
|
import { CreateContours } from "../../../Geometry/CreateContour2";
|
||||||
|
import { BoardFaceType } from "../../../Geometry/DrillParse/BoardGetFace";
|
||||||
|
import { Face } from "../../../Geometry/DrillParse/Face";
|
||||||
|
import { ParseBoardSideFace } from "../../../Geometry/DrillParse/ParseBoardSideFace";
|
||||||
|
import { ContourTreeNode } from "../../../Geometry/ExtrudeMeshGeomBuilder/ExtrudeEdgeGeometry2";
|
||||||
|
import { ZAxis, angleTo, equalv3 } from "../../../Geometry/GeUtils";
|
||||||
|
import { CameraUpdate } from "../../../GraphicsSystem/CameraUpdate";
|
||||||
|
import { RenderType } from "../../../GraphicsSystem/RenderType";
|
||||||
|
import { Board_Editor_Key } from "../../Store/RightPanelStore/BoardEdgesEditor";
|
||||||
|
import { RightPanelStore } from "../../Store/RightPanelStore/RightPanelStore";
|
||||||
|
import { AppConfirm } from "../Common/Confirm";
|
||||||
|
import { AppToaster } from "../Toaster";
|
||||||
|
|
||||||
|
export class SideModelingEditor
|
||||||
|
{
|
||||||
|
private Board: Board;
|
||||||
|
private SideFaces: Face[] = [];
|
||||||
|
private AroundBoardMesh: Object3D[] = []; //周围板件
|
||||||
|
private SideModelEdgeGeom: Object3D[] = []; //已存在侧面造型边线
|
||||||
|
private CurrentFace: Face; //选择编辑的侧面
|
||||||
|
private CurrentFaceIndex: number;
|
||||||
|
private CurrentFaceOCS: Matrix4;
|
||||||
|
private CurrentFaceOCSInv: Matrix4;
|
||||||
|
private CurrentSideContour: Polyline; //选择编辑的侧面轮廓
|
||||||
|
private Editoring = false;
|
||||||
|
private CameraFiler: CADFiler;
|
||||||
|
private OpacityBak: number;
|
||||||
|
private BackUCSMatrix: Matrix4;
|
||||||
|
private BackVisibleUCS: boolean;
|
||||||
|
private RenderTypeBak: RenderType;
|
||||||
|
private ShowSaveCancleButton: boolean = false;
|
||||||
|
|
||||||
|
constructor(br: Board)
|
||||||
|
{
|
||||||
|
this.Board = br;
|
||||||
|
}
|
||||||
|
|
||||||
|
StartEditor = async () =>
|
||||||
|
{
|
||||||
|
if (this.Editoring)
|
||||||
|
throw "重复进入编辑模式!";
|
||||||
|
|
||||||
|
this.Editoring = true;
|
||||||
|
|
||||||
|
this.Init();
|
||||||
|
this.ShowToaster(); //保存或取消
|
||||||
|
this.Start();
|
||||||
|
await this.SelectSideFace(); //选择侧面
|
||||||
|
if (this.Editoring)
|
||||||
|
this.AddModelingCurve(); //添加自身已存在的测槽
|
||||||
|
};
|
||||||
|
AddModelingCurve()
|
||||||
|
{
|
||||||
|
CommandWrap(async () =>
|
||||||
|
{
|
||||||
|
for (const face of this.SideFaces)
|
||||||
|
{
|
||||||
|
let region = face.Region;
|
||||||
|
region.Erase();
|
||||||
|
}
|
||||||
|
//添加最外圈轮廓
|
||||||
|
this.CurrentSideContour = this.CurrentFace.Region.ShapeManager.ShapeList[0].Outline.Curve as Polyline;
|
||||||
|
app.Database.ModelSpace.Append(this.CurrentSideContour);
|
||||||
|
|
||||||
|
//添加该面已有造型轮廓
|
||||||
|
const SideModelingList = this.Board.SideModelingMap.get(this.CurrentFaceIndex);
|
||||||
|
|
||||||
|
if (SideModelingList?.length)
|
||||||
|
{
|
||||||
|
const rightPanelStore = RightPanelStore.GetInstance();
|
||||||
|
let dataColorMap = new Map<string, number>();//data->color
|
||||||
|
let canUseColor: number[] = [];//可使用的颜色(无高度)
|
||||||
|
let onUseColor: number[] = [];//已经使用的颜色
|
||||||
|
let colorIndex = 1;
|
||||||
|
for (let item of rightPanelStore.modelingStore.modelingItems)
|
||||||
|
{
|
||||||
|
let str = [item.height, item.knifeRad, item.addLen, item.addWidth, item.addDepth].join("-");
|
||||||
|
|
||||||
|
if (dataColorMap.has(str) || item.height === 0)//重复的相同设定一样没有意义,所以可以覆盖
|
||||||
|
canUseColor.push(item.color);//可以被覆盖
|
||||||
|
else
|
||||||
|
dataColorMap.set(str, item.color);//记录颜色
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let solid of SideModelingList)
|
||||||
|
{
|
||||||
|
let str = [solid.Thickness, solid.KnifeRadius, solid.GroovesAddLength, solid.GroovesAddWidth, solid.GroovesAddDepth].join("-");
|
||||||
|
let color: number;
|
||||||
|
if (dataColorMap.has(str))
|
||||||
|
color = dataColorMap.get(str);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
color = canUseColor[0];
|
||||||
|
if (!color)
|
||||||
|
{
|
||||||
|
while (colorIndex < 24)
|
||||||
|
{
|
||||||
|
if (!onUseColor.includes(colorIndex)) break;
|
||||||
|
colorIndex++;
|
||||||
|
}
|
||||||
|
color = colorIndex;
|
||||||
|
}
|
||||||
|
canUseColor.shift();
|
||||||
|
dataColorMap.set(str, color);
|
||||||
|
|
||||||
|
const data: IModeling = {
|
||||||
|
shape: undefined,
|
||||||
|
thickness: solid.Thickness,
|
||||||
|
dir: FaceDirection.Front,
|
||||||
|
knifeRadius: solid.KnifeRadius,
|
||||||
|
addLen: solid.GroovesAddLength,
|
||||||
|
addWidth: solid.GroovesAddWidth,
|
||||||
|
addDepth: solid.GroovesAddDepth
|
||||||
|
};
|
||||||
|
|
||||||
|
rightPanelStore.modelingStore.ChangeModelingValue(color - 1, data);
|
||||||
|
}
|
||||||
|
onUseColor.push(color);
|
||||||
|
let cu = solid.ContourCurve.Clone().ApplyMatrix(new Matrix4().multiplyMatrices(this.CurrentFace.OCS, solid.OCS));
|
||||||
|
cu.ColorIndex = color;
|
||||||
|
app.Database.ModelSpace.Append(cu);
|
||||||
|
|
||||||
|
//添加子集
|
||||||
|
for (let c of solid.Shape.Holes)
|
||||||
|
{
|
||||||
|
cu = c.Curve.Clone().ApplyMatrix(new Matrix4().multiplyMatrices(this.CurrentFace.OCS, solid.OCS));
|
||||||
|
cu.ColorIndex = color;
|
||||||
|
app.Database.ModelSpace.Append(cu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "选择侧面");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Init()
|
||||||
|
{
|
||||||
|
let br = this.Board;
|
||||||
|
if (
|
||||||
|
this.Board.BoardProcessOption.spliteHeight ||
|
||||||
|
this.Board.BoardProcessOption.spliteWidth ||
|
||||||
|
this.Board.BoardProcessOption.spliteThickness
|
||||||
|
)
|
||||||
|
{
|
||||||
|
br = br.Clone();
|
||||||
|
br.BoardProcessOption.spliteHeight = "";
|
||||||
|
br.BoardProcessOption.spliteWidth = "";
|
||||||
|
br.BoardProcessOption.spliteThickness = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
let bgf = new ParseBoardSideFace(br);
|
||||||
|
this.SideFaces = bgf.Faces;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ShowToaster()
|
||||||
|
{
|
||||||
|
AppToaster.show({
|
||||||
|
message: this.RenderToasterMessage(),
|
||||||
|
intent: Intent.PRIMARY,
|
||||||
|
timeout: 0,
|
||||||
|
onDismiss: () => this.EndEditor()
|
||||||
|
}, Board_Editor_Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RenderToasterMessage = () =>
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<div className="flex-between toaster-message">
|
||||||
|
<span>正在编辑侧面造型</span>
|
||||||
|
{
|
||||||
|
<div>
|
||||||
|
<Button text="保存" minimal onClick={async () => { await this.ParseCurveLinkGroup(); }} />
|
||||||
|
<Button text="取消" minimal onClick={() => { AppToaster.dismiss(Board_Editor_Key); }} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//分析绘画造型曲线轮廓
|
||||||
|
private async ParseCurveLinkGroup()
|
||||||
|
{
|
||||||
|
if (!this.ShowSaveCancleButton) return;
|
||||||
|
|
||||||
|
let contourMap = new Map<number, Contour[]>();
|
||||||
|
let hasDrawNoHeight = false;
|
||||||
|
|
||||||
|
await CommandWrap(async () =>
|
||||||
|
{
|
||||||
|
const Curves: Set<Curve> = new Set();
|
||||||
|
|
||||||
|
for (let obj of app.Viewer.Scene.children)
|
||||||
|
{
|
||||||
|
if (obj.visible)
|
||||||
|
{
|
||||||
|
let ent = GetEntity(obj);
|
||||||
|
if (ent && ent.Id?.Object && ent.Visible && !ent.IsErase && ent instanceof Curve)
|
||||||
|
{
|
||||||
|
if (ent instanceof Ellipse || ent instanceof Spline)
|
||||||
|
{
|
||||||
|
let temp = ent.Convert2Polyline();
|
||||||
|
temp.__CachePolyline__ = ent;
|
||||||
|
Curves.add(temp);
|
||||||
|
}
|
||||||
|
else if (ent !== this.CurrentSideContour)
|
||||||
|
Curves.add(ent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const curGroups = curveLinkGroup(Array.from(Curves));
|
||||||
|
const rightStore = RightPanelStore.GetInstance();
|
||||||
|
|
||||||
|
if (curGroups.length)
|
||||||
|
{
|
||||||
|
let box = this.CurrentSideContour.BoundingBox;
|
||||||
|
|
||||||
|
for (let g of curGroups)
|
||||||
|
{
|
||||||
|
for (let c of g)
|
||||||
|
{
|
||||||
|
let cd = rightStore.modelingStore.modelingItems[c.ColorIndex - 1];
|
||||||
|
if (!cd || cd.height <= 0)
|
||||||
|
{
|
||||||
|
hasDrawNoHeight = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (box.intersectsBox(c.BoundingBox))
|
||||||
|
{
|
||||||
|
//在创建内部造型时,如果存在封闭的轮廓或者圆,那么自动分裂将失效(需要用户自己裂开)
|
||||||
|
let contours = CreateContours(g);
|
||||||
|
|
||||||
|
if (!contourMap.has(c.ColorIndex))
|
||||||
|
contourMap.set(c.ColorIndex, contours);
|
||||||
|
else
|
||||||
|
contourMap.get(c.ColorIndex).push(...contours);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "解析侧面造型");
|
||||||
|
|
||||||
|
if (hasDrawNoHeight)
|
||||||
|
{
|
||||||
|
let status = await AppConfirm.show({
|
||||||
|
message: "检查到部分造型轮廓未设置高度,是否继续?",
|
||||||
|
confirmButtonText: "确定",
|
||||||
|
intent: Intent.WARNING
|
||||||
|
});
|
||||||
|
if (!status)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppToaster.dismiss(Board_Editor_Key);
|
||||||
|
await this.AddModeling(contourMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async AddModeling(contourMap: Map<number, Contour[]>)
|
||||||
|
{
|
||||||
|
const rightStore = RightPanelStore.GetInstance();
|
||||||
|
const SideModelingSolidList: ExtrudeSolid[] = [];
|
||||||
|
|
||||||
|
for (let [color, contours] of contourMap)
|
||||||
|
{
|
||||||
|
for (let con of contours)
|
||||||
|
con.Curve.ApplyMatrix(this.CurrentFaceOCSInv);
|
||||||
|
|
||||||
|
let cd = rightStore.modelingStore.modelingItems[color - 1];
|
||||||
|
|
||||||
|
//分析包含关系
|
||||||
|
let contourNodes = contours.map(contour => new ContourTreeNode(contour));
|
||||||
|
ContourTreeNode.ParseContourTree(contourNodes);
|
||||||
|
|
||||||
|
for (let contourNode of contourNodes)
|
||||||
|
{
|
||||||
|
if (contourNode.IsHole) continue;
|
||||||
|
|
||||||
|
let solid = new ExtrudeSolid();
|
||||||
|
solid.Thickness = cd.height;
|
||||||
|
solid.ContourCurve = contourNode.contour.Curve;
|
||||||
|
solid.KnifeRadius = cd.knifeRad;
|
||||||
|
solid.GroovesAddLength = cd.addLen;
|
||||||
|
solid.GroovesAddWidth = cd.addWidth;
|
||||||
|
solid.GroovesAddDepth = cd.addDepth;
|
||||||
|
for (let hole of contourNode.children)
|
||||||
|
{
|
||||||
|
let holeSolid = new ExtrudeSolid();
|
||||||
|
holeSolid.Thickness = cd.height;
|
||||||
|
holeSolid.ContourCurve = hole.contour.Curve;
|
||||||
|
solid.AppendGroove(holeSolid);
|
||||||
|
}
|
||||||
|
|
||||||
|
SideModelingSolidList.push(solid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandWrap(() =>
|
||||||
|
{
|
||||||
|
this.Board.WriteAllObjectRecord();
|
||||||
|
|
||||||
|
if (SideModelingSolidList.length)
|
||||||
|
this.Board.SideModelingMap.set(this.CurrentFaceIndex, SideModelingSolidList);
|
||||||
|
else
|
||||||
|
this.Board.SideModelingMap.delete(this.CurrentFaceIndex);
|
||||||
|
|
||||||
|
this.Board.ClearSideModelingCache();
|
||||||
|
this.Board.Update(UpdateDraw.Geometry);
|
||||||
|
}, "应用侧面造型");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Start()
|
||||||
|
{
|
||||||
|
app.Editor.ModalManage.ToggleShow();
|
||||||
|
app.Editor.MaskManage.Clear();
|
||||||
|
|
||||||
|
TempEditor.Start();
|
||||||
|
|
||||||
|
//记录初始
|
||||||
|
this.BackUCSMatrix = app.Editor.UCSMatrix;
|
||||||
|
this.BackVisibleUCS = app.Editor.UcsServices.Visible;
|
||||||
|
this.OpacityBak = userConfig.ConceptualOpacity;
|
||||||
|
this.RenderTypeBak = userConfig.RenderType;
|
||||||
|
|
||||||
|
this.CameraFiler = new CADFiler;
|
||||||
|
app.Viewer.CameraCtrl.WriteFile(this.CameraFiler);
|
||||||
|
|
||||||
|
//初始视角 俯视图
|
||||||
|
// app.Viewer.CameraCtrl.LookAt(ZAxisN);
|
||||||
|
// app.Viewer.CameraCtrl.ZoomExtentsBox3(this.Board.BoundingBoxInOCS);
|
||||||
|
// app.Viewer.CameraCtrl.Zoom(1.3);
|
||||||
|
// app.Editor.UcsServices.Visible = false;
|
||||||
|
userConfig.RenderType = RenderType.Conceptual;
|
||||||
|
|
||||||
|
//获取周围板件
|
||||||
|
let aroundBoard = app.Database.ModelSpace.Entitys.filter((ent) => ent && ent.IsVisible && ent instanceof Board) as Board[];
|
||||||
|
let groupEntMap = GroupEntitysByBox(aroundBoard);
|
||||||
|
|
||||||
|
CommandWrap(() =>
|
||||||
|
{
|
||||||
|
const MirrorMtxZ = MakeMirrorMtx(ZAxis);
|
||||||
|
let b = new Board();
|
||||||
|
b.Thickness = this.Board.Thickness;
|
||||||
|
b.ContourCurve = this.Board.ContourCurve;
|
||||||
|
|
||||||
|
let cloneBrMesh = new Mesh(b.MeshGeometry, ColorMaterial.GetBasicMaterialTransparent(this.Board.ColorIndex, 0.3));
|
||||||
|
cloneBrMesh["freeze"] = true;
|
||||||
|
cloneBrMesh.applyMatrix4(this.Board.OCSNoClone);
|
||||||
|
TempEditor.editorScene.add(cloneBrMesh);
|
||||||
|
this.AroundBoardMesh.push(cloneBrMesh);
|
||||||
|
|
||||||
|
for (let [index, soilds] of this.Board.SideModelingMap)
|
||||||
|
{
|
||||||
|
let face = this.SideFaces[index];
|
||||||
|
if (!face) continue;
|
||||||
|
|
||||||
|
for (let soild of soilds)
|
||||||
|
{
|
||||||
|
//添加原板件网格
|
||||||
|
let line = new LineSegments(soild.EdgeGeometry, ColorMaterial.GetConceptualEdgeMaterial());
|
||||||
|
line.applyMatrix4(MirrorMtxZ);
|
||||||
|
line.applyMatrix4(soild.OCS);
|
||||||
|
line.applyMatrix4(face.OCS);
|
||||||
|
TempEditor.editorScene.add(line);
|
||||||
|
this.SideModelEdgeGeom.push(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let [box, brs] of groupEntMap)
|
||||||
|
{
|
||||||
|
if (brs.find((br) => br === this.Board))
|
||||||
|
{
|
||||||
|
for (let br of brs)
|
||||||
|
{
|
||||||
|
if (br === this.Board) continue;
|
||||||
|
|
||||||
|
let geo = br.MeshGeometry;
|
||||||
|
let aroundMesh = new Mesh(geo, ColorMaterial.GetBasicMaterialTransparent(br.ColorIndex, 0.1));
|
||||||
|
aroundMesh["freeze"] = true;
|
||||||
|
aroundMesh.applyMatrix4(br.OCSNoClone);
|
||||||
|
TempEditor.editorScene.add(aroundMesh);
|
||||||
|
this.AroundBoardMesh.push(aroundMesh);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//侧面面域
|
||||||
|
for (let face of this.SideFaces)
|
||||||
|
{
|
||||||
|
if (face.type === BoardFaceType.Side)
|
||||||
|
{
|
||||||
|
let region = face.Region;
|
||||||
|
region.ShapeApplyMatrix(face.OCS);
|
||||||
|
region.ColorIndex = this.Board.ColorIndex;
|
||||||
|
app.Database.ModelSpace.Append(region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}, "编辑侧面造型");
|
||||||
|
|
||||||
|
app.Database.hm.lockIndex++;//禁止初始化动作被撤销
|
||||||
|
}
|
||||||
|
|
||||||
|
private async EndEditor()
|
||||||
|
{
|
||||||
|
if (!this.Editoring) return;
|
||||||
|
|
||||||
|
//马上标记为否,避免重入
|
||||||
|
this.Editoring = false;
|
||||||
|
await app.Editor.ModalManage.EndExecingCmd();
|
||||||
|
app.Editor.UcsServices.Visible = this.BackVisibleUCS;
|
||||||
|
app.Editor.UCSMatrix = this.BackUCSMatrix;
|
||||||
|
userConfig.ConceptualOpacity = this.OpacityBak;
|
||||||
|
app.Viewer.CameraCtrl.ReadFile(this.CameraFiler);
|
||||||
|
userConfig.RenderType = this.RenderTypeBak;
|
||||||
|
|
||||||
|
TempEditor.End();
|
||||||
|
|
||||||
|
app.Editor.Cancel();
|
||||||
|
this.SideFaces = [];
|
||||||
|
this.AroundBoardMesh = [];
|
||||||
|
this.SideModelEdgeGeom = [];
|
||||||
|
this.CurrentSideContour = undefined;
|
||||||
|
this.CameraFiler = undefined;
|
||||||
|
this.BackUCSMatrix = undefined;
|
||||||
|
this.ShowSaveCancleButton = false;
|
||||||
|
app.Editor.ModalManage.ToggleShow();
|
||||||
|
app.Editor.MaskManage.OnFocusEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private SelectSideFace = async () =>
|
||||||
|
{
|
||||||
|
let enRes = await app.Editor.GetEntity({
|
||||||
|
Msg: "选择该板件的一个侧面:",
|
||||||
|
Filter: { filterTypes: [Region] },
|
||||||
|
NotNone: true,
|
||||||
|
Callback: (res) =>
|
||||||
|
{
|
||||||
|
if (res.Status === PromptStatus.OK)
|
||||||
|
{
|
||||||
|
if (app.Viewer.OutlinePass.selectedObjects[0] !== res.Entity.DrawObject)
|
||||||
|
{
|
||||||
|
app.Viewer.OutlinePass.selectedObjects = [res.Entity.DrawObject];
|
||||||
|
app.Viewer.UpdateRender();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.Viewer.OutlinePass.selectedObjects = [];
|
||||||
|
app.Viewer.UpdateRender();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (enRes.Status !== PromptStatus.OK)
|
||||||
|
{
|
||||||
|
AppToaster.dismiss(Board_Editor_Key);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
app.Editor.SelectCtrl.Cancel();
|
||||||
|
|
||||||
|
const firstMesh = this.AroundBoardMesh.shift();
|
||||||
|
|
||||||
|
for (const Mesh of this.AroundBoardMesh)
|
||||||
|
TempEditor.editorScene.remove(Mesh);
|
||||||
|
|
||||||
|
let index = this.SideFaces.findIndex((face) => face.Region === enRes.Entity);
|
||||||
|
if (index === -1)
|
||||||
|
{
|
||||||
|
Log("出错了,找不到选择面位置");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.CurrentFaceIndex = index;
|
||||||
|
this.CurrentFace = this.SideFaces[index];
|
||||||
|
this.CurrentFaceOCS = this.CurrentFace.OCS;
|
||||||
|
this.CurrentFaceOCSInv = new Matrix4().getInverse(this.CurrentFaceOCS);
|
||||||
|
|
||||||
|
this.ShowSaveCancleButton = true;
|
||||||
|
|
||||||
|
const CameraUpDir = new Vector3().setFromMatrixColumn(this.CurrentFaceOCS, 2).normalize().multiplyScalar(-1);
|
||||||
|
//旋转至选择面
|
||||||
|
await RotateCamera(this.Board.OCS, this.Board.BoundingBox, CameraUpDir, 70, this.Board.Normal);
|
||||||
|
|
||||||
|
//设置世界坐标系
|
||||||
|
app.Editor.SetUCSLookAt(CameraUpDir);
|
||||||
|
app.Editor.UCSMatrix = this.CurrentFaceOCS;
|
||||||
|
|
||||||
|
TempEditor.editorScene.remove(firstMesh);
|
||||||
|
|
||||||
|
for (const obj of this.SideModelEdgeGeom)
|
||||||
|
TempEditor.editorScene.remove(obj);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//旋转至面视角
|
||||||
|
export async function RotateCamera(UCSMatrix: Matrix4, box: Box3, lookAtDir: Vector3, totalSegments: number = 100, cameraUpDir = new Vector3(0, 0, 1)): Promise<void>
|
||||||
|
{
|
||||||
|
return new Promise<void>((res) =>
|
||||||
|
{
|
||||||
|
const cameraControl = app.Viewer.CameraControl;
|
||||||
|
const currentCameraUp = cameraControl.Camera.up;
|
||||||
|
const currentDir = app.Viewer.CameraControl.Direction;
|
||||||
|
const currentViewHeight = app.Viewer.CameraControl.ViewHeight;
|
||||||
|
|
||||||
|
//最终观察视角
|
||||||
|
const cameraControlClone = new CameraUpdate();
|
||||||
|
cameraControlClone.SetSize(cameraControl.Width, cameraControl.Height);
|
||||||
|
cameraControlClone.LookAt(lookAtDir);
|
||||||
|
cameraControlClone.ZoomExtentsBox3(box.clone());
|
||||||
|
cameraControlClone.Zoom(1.2);
|
||||||
|
const EndViewHeight = cameraControlClone.ViewHeight;
|
||||||
|
|
||||||
|
// ----获取相机曲线控制点 begin------
|
||||||
|
let controlMidPt = lookAtDir.clone();
|
||||||
|
|
||||||
|
const OCS = UCSMatrix.clone();
|
||||||
|
let norZ = new Vector3().setFromMatrixColumn(OCS, 2);
|
||||||
|
controlMidPt = currentDir.clone().sub(norZ.clone().multiplyScalar(currentDir.dot(norZ)));
|
||||||
|
let an = angleTo(controlMidPt, lookAtDir, norZ);
|
||||||
|
|
||||||
|
let normalV = controlMidPt.clone().normalize();
|
||||||
|
|
||||||
|
if (!normalV.length())
|
||||||
|
{
|
||||||
|
controlMidPt = new Vector3(0, 1).applyMatrix4(app.Editor.UCSMatrix).add(lookAtDir).multiplyScalar(0.5);
|
||||||
|
if (equalv3(controlMidPt, new Vector3))
|
||||||
|
an = Math.PI / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
//相机朝向角度较大时 使用中点过渡
|
||||||
|
if (Math.abs(an) >= Math.PI / 2)
|
||||||
|
controlMidPt = lookAtDir.clone().applyMatrix4(new Matrix4().makeRotationAxis(norZ, Math.sign(-an) * Math.PI / 2));
|
||||||
|
else
|
||||||
|
controlMidPt = currentDir.clone().add(lookAtDir).multiplyScalar(0.5);
|
||||||
|
|
||||||
|
const ControlPoints = [
|
||||||
|
currentDir, // 起始点
|
||||||
|
controlMidPt, // 控制中点
|
||||||
|
lookAtDir // 结束点
|
||||||
|
];
|
||||||
|
// ----获取相机曲线控制点 end------
|
||||||
|
|
||||||
|
let index = 0;
|
||||||
|
let time = setInterval(() =>
|
||||||
|
{
|
||||||
|
// 当前分段参数
|
||||||
|
const totalSeg = index / totalSegments;
|
||||||
|
// 计算贝塞尔曲线上对应 t 值的点
|
||||||
|
const dirPt = EvaluateBezierCurve(ControlPoints, totalSeg);
|
||||||
|
// 相机的上方向向量
|
||||||
|
const cameraUp = new Vector3().lerpVectors(currentCameraUp, cameraUpDir, totalSeg);
|
||||||
|
const traget = box.getCenter(new Vector3);
|
||||||
|
|
||||||
|
cameraControl.LookAt(dirPt);
|
||||||
|
cameraControl.Camera.up = cameraUp;
|
||||||
|
cameraControl.Target = traget;
|
||||||
|
cameraControl.ViewHeight = currentViewHeight - (currentViewHeight - EndViewHeight) * totalSeg;
|
||||||
|
cameraControl.Update();
|
||||||
|
|
||||||
|
app.Viewer.UpdateRender();
|
||||||
|
|
||||||
|
if (index === totalSegments)
|
||||||
|
{
|
||||||
|
clearInterval(time);
|
||||||
|
return res();
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 贝塞尔曲线求值函数
|
||||||
|
function EvaluateBezierCurve(controlPoints: Vector3[], t: number): Vector3
|
||||||
|
{
|
||||||
|
let n = controlPoints.length - 1;
|
||||||
|
let result = new Vector3(0, 0, 0);
|
||||||
|
for (let i = 0; i <= n; i++)
|
||||||
|
{
|
||||||
|
let binomialCoefficient = BinomialCoefficient(n, i);
|
||||||
|
let powerTerm = Math.pow(1 - t, n - i) * Math.pow(t, i);
|
||||||
|
result.addScaledVector(controlPoints[i], binomialCoefficient * powerTerm);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算二项式系数
|
||||||
|
function BinomialCoefficient(n: number, k: number): number
|
||||||
|
{
|
||||||
|
let result = 1;
|
||||||
|
for (let i = 1; i <= k; i++)
|
||||||
|
{
|
||||||
|
result *= (n - i + 1) / i;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
Loading…
Reference in new issue