feat(All):Initial
This commit is contained in:
@@ -0,0 +1,400 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user