289 lines
10 KiB
TypeScript
289 lines
10 KiB
TypeScript
declare const __APP_VERSION__: string;
|
|
import './bim-engine.css';
|
|
import { DialogManager } from './managers/dialog-manager';
|
|
import { EngineManager } from './managers/engine-manager';
|
|
import { RightKeyManager } from './managers/right-key-manager';
|
|
import { RadialToolbarManager } from './managers/radial-toolbar-manager';
|
|
import { BottomDockManager } from './managers/bottom-dock-manager';
|
|
import { MeasureDockManager } from './managers/measure-dock-manager';
|
|
import { SectionDockManager } from './managers/section-dock-manager';
|
|
import { WalkDockManager } from './managers/walk-dock-manager';
|
|
|
|
import { MeasureDialogManager } from './managers/measure-dialog-manager';
|
|
import { SectionPlaneDialogManager } from './managers/section-plane-dialog-manager';
|
|
import { SectionAxisDialogManager } from './managers/section-axis-dialog-manager';
|
|
import { SectionBoxDialogManager } from './managers/section-box-dialog-manager';
|
|
import { WalkControlManager } from './managers/walk-control-manager';
|
|
import { EngineInfoDialogManager } from './managers/engine-info-dialog-manager';
|
|
import { SettingDialogManager } from './managers/setting-dialog-manager';
|
|
import { ComponentDetailManager } from './managers/component-detail-manager';
|
|
import { AiChatManager } from './managers/ai-chat-manager';
|
|
import type { EngineOptions, ModelLoadOptions } from './components/engine';
|
|
import { localeManager } from './services/locale';
|
|
import { themeManager } from './services/theme';
|
|
import type { LocaleType } from './locales/types';
|
|
import type { ThemeType, ThemeConfig } from './themes/types';
|
|
import { ManagerRegistry } from './core/manager-registry';
|
|
import { EngineEvents } from './types/events';
|
|
|
|
export type { EngineOptions, ModelLoadOptions };
|
|
|
|
/**
|
|
* CusBimEngine - 定制版 BIM 引擎
|
|
* 移除了 ButtonGroupManager、ConstructTreeManagerBtn 和 ToolbarManager
|
|
*/
|
|
export class CusBimEngine {
|
|
public container: HTMLElement;
|
|
private wrapper: HTMLElement | null = null;
|
|
private sizeEl: HTMLElement | null = null;
|
|
private resizeObserver: ResizeObserver | null = null;
|
|
private lastSyncedWidth = -1;
|
|
private lastSyncedHeight = -1;
|
|
private unsubscribeTheme: (() => void) | null = null;
|
|
private registry: ManagerRegistry;
|
|
|
|
public dialog: DialogManager | null = null;
|
|
public engine: EngineManager | null = null;
|
|
public rightKey: RightKeyManager | null = null;
|
|
public radialToolbar: RadialToolbarManager | null = null;
|
|
public bottomDock: BottomDockManager | null = null;
|
|
public measureDock: MeasureDockManager | null = null;
|
|
public sectionDock: SectionDockManager | null = null;
|
|
public walkDock: WalkDockManager | null = null;
|
|
|
|
public measure: MeasureDialogManager | null = null;
|
|
public sectionPlane: SectionPlaneDialogManager | null = null;
|
|
public sectionAxis: SectionAxisDialogManager | null = null;
|
|
public sectionBox: SectionBoxDialogManager | null = null;
|
|
public walkControl: WalkControlManager | null = null;
|
|
public engineInfo: EngineInfoDialogManager | null = null;
|
|
public componentDetail: ComponentDetailManager | null = null;
|
|
public aiChat: AiChatManager | null = null;
|
|
public setting: SettingDialogManager | null = null;
|
|
|
|
private readonly handleWindowResize = () => {
|
|
this.updateClientSizeDisplay();
|
|
};
|
|
|
|
constructor(
|
|
container: HTMLElement | string,
|
|
options?: {
|
|
locale?: LocaleType;
|
|
theme?: ThemeType;
|
|
}
|
|
) {
|
|
const el = typeof container === 'string' ? document.getElementById(container) : container;
|
|
if (!el) throw new Error('Container not found');
|
|
this.container = el;
|
|
|
|
this.registry = new ManagerRegistry();
|
|
|
|
if (options?.locale) localeManager.setLocale(options.locale);
|
|
if (options?.theme) {
|
|
if (options.theme === 'custom') {
|
|
console.warn('Custom theme should be set via setCustomTheme().');
|
|
} else {
|
|
themeManager.setTheme(options.theme);
|
|
}
|
|
}
|
|
|
|
this.init();
|
|
}
|
|
|
|
public emit<K extends keyof EngineEvents>(event: K, payload: EngineEvents[K]) {
|
|
this.registry.emit(event, payload);
|
|
}
|
|
|
|
/**
|
|
* 订阅事件
|
|
* @param event 事件名称
|
|
* @param listener 事件监听器
|
|
* @returns 取消订阅函数
|
|
*/
|
|
public on<K extends keyof EngineEvents>(event: K, listener: (payload: EngineEvents[K]) => void): () => void {
|
|
return this.registry.on(event, listener);
|
|
}
|
|
|
|
public setLocale(locale: LocaleType) {
|
|
localeManager.setLocale(locale);
|
|
}
|
|
|
|
public getLocale(): LocaleType {
|
|
return localeManager.getLocale();
|
|
}
|
|
|
|
public setTheme(theme: 'dark' | 'light') {
|
|
themeManager.setTheme(theme);
|
|
}
|
|
|
|
public setCustomTheme(theme: ThemeConfig) {
|
|
themeManager.setCustomTheme(theme);
|
|
}
|
|
|
|
private init() {
|
|
this.container.innerHTML = '';
|
|
this.wrapper = document.createElement('div');
|
|
this.wrapper.className = 'bim-engine-wrapper';
|
|
this.container.appendChild(this.wrapper);
|
|
|
|
const versionEl = document.createElement('div');
|
|
versionEl.className = 'bim-engine-version';
|
|
versionEl.textContent = `v${__APP_VERSION__}`;
|
|
this.wrapper.appendChild(versionEl);
|
|
|
|
this.sizeEl = document.createElement('div');
|
|
this.sizeEl.className = 'bim-engine-size';
|
|
this.wrapper.appendChild(this.sizeEl);
|
|
this.updateClientSizeDisplay();
|
|
this.bindSizeObserver();
|
|
|
|
this.registry.container = this.container;
|
|
this.registry.wrapper = this.wrapper;
|
|
|
|
this.engine = new EngineManager(this.wrapper, this.registry);
|
|
this.dialog = new DialogManager(this.wrapper, this.registry);
|
|
this.rightKey = new RightKeyManager(this.wrapper, this.registry);
|
|
this.bottomDock = new BottomDockManager(this.wrapper, this.registry);
|
|
this.registry.bottomDock = this.bottomDock;
|
|
|
|
this.registry.engine3d = this.engine;
|
|
this.registry.dialog = this.dialog;
|
|
this.registry.rightKey = this.rightKey;
|
|
|
|
this.measureDock = new MeasureDockManager(this.registry);
|
|
this.registry.measureDock = this.measureDock;
|
|
this.measureDock.init();
|
|
|
|
this.sectionDock = new SectionDockManager(this.registry);
|
|
this.registry.sectionDock = this.sectionDock;
|
|
this.sectionDock.init();
|
|
|
|
this.walkDock = new WalkDockManager(this.registry);
|
|
this.registry.walkDock = this.walkDock;
|
|
this.walkDock.init();
|
|
|
|
this.radialToolbar = new RadialToolbarManager(this.wrapper, this.registry);
|
|
|
|
this.measure = new MeasureDialogManager(this.registry);
|
|
this.sectionPlane = new SectionPlaneDialogManager(this.registry);
|
|
this.sectionAxis = new SectionAxisDialogManager(this.registry);
|
|
this.sectionBox = new SectionBoxDialogManager(this.registry);
|
|
this.walkControl = new WalkControlManager(this.registry);
|
|
this.walkControl.init();
|
|
this.engineInfo = new EngineInfoDialogManager(this.registry);
|
|
this.engineInfo.init();
|
|
|
|
this.registry.radialToolbar = this.radialToolbar;
|
|
|
|
this.registry.measure = this.measure;
|
|
this.registry.sectionPlane = this.sectionPlane;
|
|
this.registry.sectionAxis = this.sectionAxis;
|
|
this.registry.sectionBox = this.sectionBox;
|
|
this.registry.walkControl = this.walkControl;
|
|
this.registry.engineInfo = this.engineInfo;
|
|
|
|
|
|
this.componentDetail = new ComponentDetailManager(this.registry);
|
|
this.registry.componentDetail = this.componentDetail;
|
|
this.componentDetail.init();
|
|
|
|
this.aiChat = new AiChatManager(this.registry);
|
|
this.registry.aiChat = this.aiChat;
|
|
this.aiChat.init();
|
|
|
|
this.setting = new SettingDialogManager(this.registry);
|
|
this.registry.setting = this.setting;
|
|
this.setting.init();
|
|
|
|
this.updateTheme(themeManager.getTheme());
|
|
this.unsubscribeTheme = themeManager.subscribe((theme) => {
|
|
this.updateTheme(theme);
|
|
});
|
|
}
|
|
|
|
private updateTheme(theme: ThemeConfig) {
|
|
if (this.wrapper) {
|
|
this.wrapper.style.color = theme.textPrimary;
|
|
}
|
|
}
|
|
|
|
private updateClientSizeDisplay(): void {
|
|
const width = this.container.clientWidth;
|
|
const height = this.container.clientHeight;
|
|
|
|
if (this.sizeEl) {
|
|
this.sizeEl.textContent = `${width}px x ${height}px`;
|
|
}
|
|
|
|
this.syncEngineSize(width, height);
|
|
}
|
|
|
|
private syncEngineSize(width: number, height: number): void {
|
|
if (width <= 0 || height <= 0) {
|
|
return;
|
|
}
|
|
|
|
if (width === this.lastSyncedWidth && height === this.lastSyncedHeight) {
|
|
return;
|
|
}
|
|
|
|
this.lastSyncedWidth = width;
|
|
this.lastSyncedHeight = height;
|
|
this.engine?.getEngineComponent()?.resize(width, height);
|
|
}
|
|
|
|
private bindSizeObserver(): void {
|
|
if (typeof ResizeObserver !== 'undefined') {
|
|
this.resizeObserver = new ResizeObserver(() => {
|
|
this.updateClientSizeDisplay();
|
|
});
|
|
this.resizeObserver.observe(this.container);
|
|
return;
|
|
}
|
|
|
|
window.addEventListener('resize', this.handleWindowResize);
|
|
}
|
|
|
|
private unbindSizeObserver(): void {
|
|
if (this.resizeObserver) {
|
|
this.resizeObserver.disconnect();
|
|
this.resizeObserver = null;
|
|
return;
|
|
}
|
|
|
|
window.removeEventListener('resize', this.handleWindowResize);
|
|
}
|
|
|
|
public destroy() {
|
|
this.unbindSizeObserver();
|
|
|
|
if (this.unsubscribeTheme) {
|
|
this.unsubscribeTheme();
|
|
this.unsubscribeTheme = null;
|
|
}
|
|
|
|
this.radialToolbar?.destroy();
|
|
this.measureDock?.destroy();
|
|
this.sectionDock?.destroy();
|
|
this.walkDock?.destroy();
|
|
this.bottomDock?.destroy();
|
|
this.engine?.destroy();
|
|
this.dialog?.destroy();
|
|
this.rightKey?.destroy();
|
|
|
|
this.measure?.destroy();
|
|
this.sectionPlane?.destroy();
|
|
this.sectionAxis?.destroy();
|
|
this.sectionBox?.destroy();
|
|
this.walkControl?.destroy();
|
|
this.aiChat?.destroy();
|
|
this.setting?.destroy();
|
|
|
|
this.sizeEl = null;
|
|
this.lastSyncedWidth = -1;
|
|
this.lastSyncedHeight = -1;
|
|
this.container.innerHTML = '';
|
|
this.registry.reset();
|
|
}
|
|
}
|