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

334 lines
9.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as DrawShare from "./drawShare";
import {useAddSignal, useDispatchSignal, useRemoveSignal} from "@/hooks";
import OffScreenCanvasWorker from './offScreenCanvas.worker.ts?worker&url';
import App from "@/core/app/App.ts";
/** ----------------- 离屏渲染中的事件处理--------------------------- **/
const mouseEventHandler = makeSendPropertiesHandler( [
'ctrlKey',
'metaKey',
'shiftKey',
'button',
'pointerType',
'clientX',
'clientY',
'pageX',
'pageY',
] );
const wheelEventHandlerImpl = makeSendPropertiesHandler( [
'deltaX',
'deltaY',
] );
const keydownEventHandler = makeSendPropertiesHandler( [
'ctrlKey',
'metaKey',
'shiftKey',
'keyCode',
] );
function wheelEventHandler( event, sendFn ) {
event.preventDefault();
wheelEventHandlerImpl( event, sendFn );
}
function preventDefaultHandler( event ) {
event.preventDefault();
}
function copyProperties(src, properties, dst) {
for ( const name of properties ) {
dst[ name ] = src[ name ];
}
}
function makeSendPropertiesHandler( properties ) {
return function sendProperties(event, sendFn) {
const data = { type: event.type };
copyProperties( event, properties, data );
sendFn( data );
};
}
function touchEventHandler(event, sendFn) {
const touches: { pageX:number,pageY:number }[] = [];
const data = { type: event.type, touches };
for ( let i = 0; i < event.touches.length; ++ i ) {
const touch = event.touches[ i ];
touches.push({
pageX: touch.pageX,
pageY: touch.pageY,
});
}
sendFn( data );
}
// 四个方向键
const orbitKeys = {
'37': true, // left
'38': true, // up
'39': true, // right
'40': true, // down
};
function filteredKeydownEventHandler(event, sendFn) {
const { keyCode } = event;
if (orbitKeys[keyCode]) {
event.preventDefault();
keydownEventHandler( event, sendFn );
}
}
/** ----------------- 离屏渲染中的事件end --------------------------- **/
let nextProxyId = 0;
/**
* 转发 DOM 事件给Worker中的 ElementProxyReceiver
*/
class ElementProxy {
readonly id: number;
private worker: Worker;
private element: HTMLElement;
constructor(element, worker, eventHandlers) {
this.id = nextProxyId++;
this.worker = worker;
this.element = element;
// 注册一个响应元素id
worker.postMessage({
type: 'makeProxy',
data:{
id: this.id,
}
});
this.sendSize();
// 添加相应事件的监听
for (const [eventName, handler] of Object.entries(eventHandlers)) {
element.addEventListener(eventName, (event) => {
// @ts-ignore
handler(event, this.sendEvent.bind(this));
});
}
}
sendEvent(data){
this.worker.postMessage({
type: 'event',
data:{
id: this.id,
data,
}
});
};
sendSize() {
const rect = this.element.getBoundingClientRect();
this.sendEvent({
type: 'size',
left: rect.left,
top: rect.top,
width: this.element.clientWidth,
height: this.element.clientHeight,
});
}
}
let cadDialogMoveFn;
/**
* dxf对象的查看器类
* @param {any} data - dxf对象
* @param {HTMLCanvasElement} canvas - three 画布
* @param {Number} width - 渲染画布的宽度,以像素为单位
* @param {Number} height - 渲染画布的高度,以像素为单位
* @constructor
*/
class DxfViewer {
private worker:Worker | undefined;
private resizeObserver: ResizeObserver;
private proxy: ElementProxy | undefined;
private middleObject:any = new Proxy({},{
set(target, p: string | symbol, newValue: any): boolean {
target[p] = newValue;
// switch (p) {
// case "markList":
// break;
// }
return true;
}
})
constructor(data: any, canvas: HTMLCanvasElement, width: number, height: number, onComplete?:()=>void) {
//console.log('dxf data:', data);
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
const cadFontUrl = new URL(import.meta.env.BASE_URL + 'resource/fonts/Alimama_DongFangDaKai_Regular.ttf', import.meta.url).href;
// 检查是否支持离屏canvas
if (canvas.transferControlToOffscreen) {
// console.log('OffscreenCanvas supported');
// 创建Worker进行离屏渲染
this.worker = new Worker(OffScreenCanvasWorker, {type: 'module'});
const offscreen = canvas.transferControlToOffscreen();
offscreen.width = width;
offscreen.height = height;
// 添加鼠标事件的监听
const eventHandlers = {
contextmenu: preventDefaultHandler,
mousedown: mouseEventHandler,
mousemove: mouseEventHandler,
mouseup: mouseEventHandler,
pointerdown: mouseEventHandler,
pointermove: mouseEventHandler,
pointerup: mouseEventHandler,
touchstart: touchEventHandler,
//touchmove: touchEventHandler,
touchend: touchEventHandler,
wheel: wheelEventHandler,
keydown: filteredKeydownEventHandler,
};
this.proxy = new ElementProxy(canvas, this.worker, eventHandlers);
this.worker.postMessage({
type: 'start', data: {
canvas: offscreen,
canvasId: this.proxy.id,
data:data,
options: {
bgColor:0x000000,
contrastColor:0xffffff,
fontUrl:cadFontUrl
},
middleObject:{
markList: JSON.stringify(App.project.getKey("drawing.markList")),
selectedRectIndex: App.project.getKey("drawing.selectedRectIndex"),
}
}
}, [offscreen]);
this.worker.onmessage = (event) => {
const data = event.data;
switch (data.type) {
case 'complete':
onComplete && onComplete();
break;
case "signal":
this.handleSignal(data.data)
break;
case "style":
canvas.style[data.data.key] = data.data.value;
break;
case "middle":
this.middleObject[data.data.key] = data.data.value;
break;
}
}
cadDialogMoveFn = this.proxy.sendSize.bind(this.proxy);
useAddSignal("cadViewerResize",cadDialogMoveFn)
}else{
canvas.width = width;
canvas.height = height;
//不支持离屏渲染
DrawShare.main({
canvas,
inputElement:canvas,
data:data,
onComplete:onComplete,
signal:this.handleSignal,
options: {
bgColor:0x000000,
contrastColor:0xffffff,
fontUrl:cadFontUrl
},
middleObject:this.middleObject
});
}
// 监听窗口变化
this.resizeObserver = new ResizeObserver(entries => {
canvas.style.width = `${entries[0].contentRect.width}px`;
canvas.style.height = `${entries[0].contentRect.height}px`;
const data = {
width:entries[0].contentRect.width,
height:entries[0].contentRect.height
}
if(this.worker){
this.worker.postMessage({
type:"resize",
data
})
this.proxy?.sendSize();
}else{
DrawShare.resize(data)
}
});
this.resizeObserver.observe(canvas.parentElement as HTMLDivElement);
}
// 触发signal
handleSignal(args){
const {type,name,data} = args;
switch (type) {
case "dispatch":
useDispatchSignal(name,...data)
break;
}
}
// 调用drawShare中的方法,data为传入的参数对象展示
callMethod(methodName:string, data:any = {}){
if(this.worker){
this.worker.postMessage({
type:methodName,
data,
})
}else{
DrawShare[methodName](data);
}
}
dispose() {
if(this.worker){
this.worker.terminate();
useRemoveSignal("cadViewerResize",cadDialogMoveFn)
}else{
DrawShare.dispose();
}
this.resizeObserver.disconnect();
}
/* ------------------- 需要共同实现的标记相关方法 ------------------------- */
// 获取选中的标记
get selectRectIndex(){
return this.middleObject.selectRectIndex;
}
// 删除选中的标记
deleteRect(){
const id = this.middleObject.selectRect?.id;
if(!id) return;
const elementId = this.middleObject.selectRect?.elementId;
this.callMethod('callModuleMethod',{
moduleName: "drawRect", methodName: "deleteRect",elementId:elementId
})
}
// 根据模型选中对应标记
selectRect(uuid:string){
this.callMethod('callModuleMethod',{
moduleName: "drawRect", methodName: "setSelect",elementId:uuid
})
}
}
export {DxfViewer}