TkAstral3D/packages/sdk/lib/dxf/drawRect.ts
2025-10-04 23:36:07 +08:00

400 lines
13 KiB
TypeScript

import * as THREE from "three";
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls.js";
import {stopRender, renderLoop, render} from "./drawShare"
import {DragControls} from './DragControls.js';
let cpmFn, cpdFn, cpuFn;
function throttle(func, wait) {
let timer: NodeJS.Timeout | null = null;
return function (e) {
if (timer) return;
timer = setTimeout(function () {
func(e);
timer = null;
}, wait)
}
}
/**
* three中绘制矩形
* @param {THREE.Scene} scene 场景
**/
export class DrawRect {
static RectShapeMaterial = new THREE.MeshBasicMaterial({
transparent: true,
opacity: 0.1,
color: 0xccebff,
side: THREE.FrontSide
});
static LineMaterial = new THREE.LineBasicMaterial({
color: 0x15FF00,
linewidth: 10,
linecap: 'round',
linejoin: 'round'
})
static HoverLineMaterial = new THREE.LineBasicMaterial({
color: 0xff0015,
linewidth: 10,
linecap: 'round',
linejoin: 'round'
})
private canvas: HTMLCanvasElement;
private scene: THREE.Scene;
private camera: THREE.OrthographicCamera;
private controls: OrbitControls | undefined;
private signal: (args: { type: 'add' | 'remove' | 'dispatch', name: string, data?: any }) => void;
private middleObject: any;
private readonly group: THREE.Group;
private readonly dragObjects: THREE.Group[] = [];
private readonly dragControls: DragControls;
private isDrag: boolean = false;
private tempModelUuid: string | undefined;
private tempRect: THREE.Group | undefined;
private downPoint: THREE.Vector2 = new THREE.Vector2();
constructor(canvas: HTMLCanvasElement, scene: THREE.Scene, camera: THREE.OrthographicCamera, signal, middleObject) {
this.canvas = canvas;
this.scene = scene;
this.camera = camera;
this.signal = signal;
this.middleObject = middleObject;
middleObject.markList || (middleObject.markList = []); // 矩形标记数组
middleObject.selectRectIndex || (middleObject.selectRectIndex = -1); // 当前选中的矩形下标
middleObject.selectRect = {}; // 当前选中的矩形数据
middleObject.hoverRectIndex = -1; // 当前鼠标经过的矩形的下标
this.group = new THREE.Group();
this.group.name = "cadDrawRect";
this.scene.add(this.group);
this.dragControls = new DragControls(this.dragObjects, camera, canvas);
this.dragControls.addEventListener("hoveron", (event) => {
if(this.isDrag) return;
this.isDrag = true;
this.controls && (this.controls.enabled = false);
this.group.children.forEach((child, index) => {
if (child.userData.rect.modelUuid !== event.object.userData.rect.modelUuid) return;
this.middleObject.hoverRectIndex = index;
child.children.forEach((c:any) => {
if(c.isLine){
c.material = DrawRect.HoverLineMaterial;
}
})
});
});
this.dragControls.addEventListener("dragstart", () => {
// 有上一次选中
if(this.middleObject.selectRectIndex !== -1){
this.group.children[this.middleObject.selectRectIndex]?.children.forEach((c:any) => {
if(c.isLine){
c.material = DrawRect.LineMaterial;
}
})
// 两次选中同一个则取消
if(this.middleObject.selectRectIndex === this.middleObject.hoverRectIndex){
this.middleObject.selectRectIndex = -1;
this.middleObject.selectRect = {};
this.signal({
type: "dispatch",
name: "objectFocusByUuid",
data: [undefined]
})
return;
}
}
this.middleObject.selectRectIndex = this.middleObject.hoverRectIndex;
this.middleObject.selectRect = this.group.children[this.middleObject.selectRectIndex]?.userData.rect;
this.signal({
type: "dispatch",
name: "objectFocusByUuid",
data: [this.middleObject.selectRect.modelUuid]
})
});
this.dragControls.addEventListener("drag", throttle(render,50));
this.dragControls.addEventListener("dragend", (event) => {
if (!event.object) return;
const rectItem = event.object.userData.rect;
rectItem.x += event.object.position.x;
rectItem.y += event.object.position.y;
this.signal({
type: "dispatch",
name: "drawingMarkDone",
data: ["update", rectItem]
})
});
this.dragControls.addEventListener("hoveroff", () => {
if(!this.isDrag) return;
this.isDrag = false;
this.controls && (this.controls.enabled = true);
// 经过的模型未被选择
if(this.middleObject.selectRectIndex !== this.middleObject.hoverRectIndex){
this.group.children[this.middleObject.hoverRectIndex].children.forEach((c:any) => {
if(c.isLine){
c.material = DrawRect.LineMaterial;
}
})
}
this.middleObject.hoverRectIndex = -1;
render();
});
this.dragControls.enabled = true;
cpmFn = throttle(this.onpointermove.bind(this), 16);
cpdFn = this.onpointerdown.bind(this);
cpuFn = this.onpointerup.bind(this);
this.init();
}
init() {
// 若list长度不为0, 则显示已标记框
if (this.middleObject.markList.length !== 0) {
this.middleObject.markList.forEach((item: IDrawingMark) => {
const rectObject = this.draw(item);
this.dragObjects.push(rectObject);
});
}
}
setList({list}) {
this.middleObject.markList = list;
this.init();
}
// 设置controls
setControls(controls: OrbitControls) {
this.controls = controls;
}
// 设置选中
setSelect({modelUuid}){
// 先去除上一次选中
this.group.children[this.middleObject.selectRectIndex]?.children.forEach((c:any) => {
if(c.isLine){
c.material = DrawRect.LineMaterial;
}
})
this.middleObject.selectRectIndex = -1;
this.middleObject.selectRect = {};
if(modelUuid === undefined) return;
this.group.children.forEach((child, index) => {
if (child.userData.rect?.modelUuid !== modelUuid) return;
this.middleObject.selectRectIndex = index;
this.middleObject.selectRect = child.userData.rect;
child.children.forEach((c:any) => {
if(c.isLine){
c.material = DrawRect.HoverLineMaterial;
}
})
});
}
// 还原rect
restoreRect({rect}) {
this.group.children.forEach((child, index) => {
if (child.userData.rect.modelUuid !== rect.modelUuid) return;
const re = JSON.parse(JSON.stringify(child.userData.rect));
this.middleObject.markList.splice(index, 1, re);
this.group.remove(child);
const rectObject = this.draw(re);
this.dragObjects.splice(this.dragObjects.findIndex(item => item.userData.rect.modelUuid === rect.modelUuid), 1, rectObject);
})
}
/**
* 准备开始画矩形标记框
*/
addRect({modelUuid}) {
if (!this.controls) return;
this.tempModelUuid = modelUuid;
this.controls.enabled = false;
this.dragControls.enabled = false;
this.canvas.style.cursor = "crosshair";
stopRender();
this.canvas.addEventListener("pointerdown", cpdFn);
}
// 删除当前选中的模型
deleteRect({modelUuid}){
this.group.children.forEach((child, index) => {
if (child.userData.rect.modelUuid !== modelUuid) return;
this.middleObject.markList.splice(index, 1);
this.group.remove(child);
this.dragObjects.splice(this.dragObjects.findIndex(item => item.userData.rect.modelUuid === modelUuid), 1);
})
}
// 鼠标按下事件
onpointerdown(event: PointerEvent) {
event.stopPropagation();
if (this.isDrag || event.button !== 0) return;
const wp = this.screenToScenePosition(event);
this.downPoint = new THREE.Vector2(wp.x, wp.y);
this.canvas.addEventListener("pointermove", cpmFn);
this.canvas.addEventListener("pointerup", cpuFn);
}
// 鼠标移动事件
onpointermove(event: PointerEvent) {
event.stopPropagation();
if (this.isDrag) return;
const wp = this.screenToScenePosition(event);
this.tempRect && this.group.remove(this.tempRect);
const item: IDrawingMark = {
x: this.downPoint.x,
y: this.downPoint.y,
w: wp.x - this.downPoint.x,
h: wp.y - this.downPoint.y,
modelUuid: this.tempModelUuid
};
this.tempRect = this.draw(item);
}
// 鼠标抬起事件
onpointerup(event: PointerEvent) {
event.stopPropagation();
if (this.isDrag) return;
const wp = this.screenToScenePosition(event);
const item: IDrawingMark = {
x: this.downPoint.x,
y: this.downPoint.y,
w: wp.x - this.downPoint.x,
h: wp.y - this.downPoint.y,
modelUuid: this.tempModelUuid
};
this.drawDone(item)
this.signal({
type: "dispatch",
name: "drawingMarkDone",
data: ["add", item]
})
this.canvas.style.cursor = "default";
this.tempModelUuid = undefined;
this.canvas.removeEventListener("pointermove", cpmFn);
this.canvas.removeEventListener("pointerup", cpuFn);
}
// 取消设置名称去除对应绘制的方法
clearTemp() {
this.tempRect && this.group.remove(this.tempRect);
this.exit();
}
// 名称设置完成后调用的绘制结束方法
drawDone(rect:IDrawingMark) {
this.tempRect && this.group.remove(this.tempRect);
this.middleObject.markList.push(rect);
const rectObject = this.draw(rect);
this.dragObjects.push(rectObject);
this.exit();
}
exit() {
this.controls && (this.controls.enabled = true);
this.dragControls.enabled = true;
renderLoop();
this.canvas.removeEventListener("pointerdown", cpdFn);
}
// 绘制
draw(item: IDrawingMark) {
/* 第二种方式 */
const g = new THREE.Group();
g.name = "mark-rect"
const rectShape = new THREE.Shape();
rectShape.moveTo(item.x, item.y);
rectShape.lineTo(item.x + item.w, item.y);
rectShape.lineTo(item.x + item.w, item.y + item.h);
rectShape.lineTo(item.x, item.y + item.h);
rectShape.lineTo(item.x, item.y);
const geometry = new THREE.ShapeGeometry(rectShape);
const material = DrawRect.RectShapeMaterial;
const mesh = new THREE.Mesh(geometry, material);
g.add(mesh);
//绘制边框线
const lineGeom = new THREE.EdgesGeometry(geometry)
const lineMaterial = DrawRect.LineMaterial;
const line = new THREE.LineSegments(lineGeom, lineMaterial)
line.scale.copy(mesh.scale)
line.rotation.copy(mesh.rotation)
line.position.copy(mesh.position)
g.add(line)
g.userData.rect = item;
this.group.add(g);
// helpRender();
render();
return g;
}
screenToScenePosition(event) {
const offsetX = event.clientX - this.canvas.offsetLeft;
const offsetY = event.clientY - this.canvas.offsetTop;
const screenPosition = new THREE.Vector3(offsetX, offsetY, 0);
//const worldPosition = new THREE.Vector3();
screenPosition.x = (screenPosition.x / this.canvas.clientWidth) * 2 - 1;
screenPosition.y = -(screenPosition.y / this.canvas.clientHeight) * 2 + 1;
screenPosition.z = 0.5;
let p = screenPosition.unproject(this.camera);
return new THREE.Vector2(p.x, p.y);
// screenPosition.sub(this.camera.position).normalize();
// const distance = -this.camera.position.z / screenPosition.z;
// worldPosition.copy(this.camera.position).add(screenPosition.multiplyScalar(distance));
//
// return worldPosition;
}
}