Files
bim_engine/src/managers/engine-manager.ts
yuding fdc6f884aa refactor: change loadModel parameter from string to string array
- Update loadModel signature: url: string -> urls: string[]
- Update validation logic for array parameter
- Update all demo files to pass array instead of single string
2026-02-26 16:03:38 +08:00

701 lines
22 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.

/**
* 3D 引擎管理器
* 负责管理 3D 渲染引擎的初始化、模型加载和测量功能
*/
import { Engine, type EngineOptions, type ModelLoadOptions, type EngineInfo } from '../components/engine';
import { BaseManager } from '../core/base-manager';
import { RightKeyManager } from './right-key-manager';
import type { MeasureMode } from '../types/measure';
import type { MeasureUnit, MeasurePrecision } from '../components/measure-panel/types';
import type { SectionBoxRange } from '../components/section-box-panel/types';
import type { MenuItemConfig } from '../components/menu/item';
import { ManagerRegistry } from '../core/manager-registry';
/**
* 3D 引擎管理器
* 封装底层 3D 引擎,提供模型加载、相机控制、测量等功能
*/
export class EngineManager extends BaseManager {
/** 容器元素 */
private container: HTMLElement;
/** 引擎实例 */
private engineInstance: Engine | null = null;
/** 右键菜单管理器 */
public rightKey: RightKeyManager | null = null;
constructor(container: HTMLElement) {
super();
this.container = container;
}
/**
* 初始化 3D 引擎
* @param options 引擎配置选项
* @returns 是否初始化成功
*/
public initialize(options?: Omit<EngineOptions, 'container'>): boolean {
if (this.engineInstance && this.engineInstance.isInitialized()) {
console.warn('[EngineManager] 3D Engine already initialized. Destroying old instance...');
this.engineInstance.destroy();
this.engineInstance = null;
}
try {
this.engineInstance = new Engine({
container: this.container,
...options,
});
this.engineInstance.init();
this.rightKey = new RightKeyManager(this.container);
this.rightKey.registerHandler((_e) => {
const selected = this.getSelectedComponent();
const items: MenuItemConfig[] = [];
if (selected) {
// 1. 构件详情
items.push({
id: 'componentDetail',
label: 'menu.componentDetail',
group: 'component',
order: 1,
divider: true,
onClick: () => {
const registry = ManagerRegistry.getInstance();
registry.componentDetail?.show();
this.rightKey?.hide();
}
});
// 2. 隐藏选中构件
items.push({
id: 'hideSelected',
label: 'menu.hideSelected',
group: 'component',
order: 2,
onClick: () => {
const models = this.getHighlightModels();
if (models) {
this.hideModels(models);
}
this.rightKey?.hide();
}
});
// 3. 半透明选中构件
items.push({
id: 'transparentSelected',
label: 'menu.transparentSelected',
group: 'component',
order: 3,
onClick: () => {
const models = this.getHighlightModels();
if (models) {
this.translucentModels(models);
}
this.rightKey?.hide();
}
});
// 4. 取消半透明
items.push({
id: 'cancelTranslucent',
label: 'menu.cancelTranslucent',
group: 'component',
order: 4,
onClick: () => {
this.unTranslucentModel();
this.rightKey?.hide();
}
});
// 5. 隔离选中构件(带子菜单)
items.push({
id: 'isolateSelected',
label: 'menu.isolateSelected',
group: 'component',
order: 5,
divider: true,
children: [
{
id: 'hideOthers',
label: 'menu.hideOthers',
onClick: () => {
const models = this.getHighlightModels();
if (models) {
this.isolateModels(models);
}
this.rightKey?.hide();
}
},
{
id: 'transparentOthers',
label: 'menu.transparentOthers',
onClick: () => {
const models = this.getHighlightModels();
if (models) {
this.translucentOtherModels(models);
}
this.rightKey?.hide();
}
}
]
});
// 6. 快速选择(带子菜单)
items.push({
id: 'quickSelect',
label: 'menu.quickSelect',
group: 'component',
order: 6,
children: [
{
id: 'selectSameType',
label: 'menu.selectSameType',
onClick: () => {
const models = this.getHighlightModels();
if (models) {
this.batchSelectSameTypeModel(models);
}
this.rightKey?.hide();
}
},
{
id: 'selectSameLevel',
label: 'menu.selectSameLevel',
onClick: () => {
const models = this.getHighlightModels();
if (models) {
this.batchSelectSameLevelModel(models);
}
this.rightKey?.hide();
}
},
{
id: 'selectSameLevelType',
label: 'menu.selectSameLevelType',
onClick: () => {
const models = this.getHighlightModels();
if (models) {
this.batchSelectSameLevelTypeModel(models);
}
this.rightKey?.hide();
}
}
]
});
// 7. 剖切盒适应
items.push({
id: 'fitSectionBox',
label: 'menu.fitSectionBox',
group: 'component',
order: 7,
onClick: () => {
this.fitSectionBoxToModel();
this.rightKey?.hide();
}
});
}
// 8. 显示全部(始终显示)
items.push({
id: 'showAll',
label: 'menu.showAll',
group: 'component',
order: 8,
onClick: () => {
this.showAllModels();
this.emit('menu:show-all', {});
this.rightKey?.hide();
}
});
return items;
});
return this.engineInstance.isInitialized();
} catch (error) {
console.error('[EngineManager] Failed to initialize 3D engine:', error);
this.engineInstance = null;
return false;
}
}
/**
* 检查引擎是否已初始化
* @returns 是否已初始化
*/
public isInitialized(): boolean {
return this.engineInstance !== null && this.engineInstance.isInitialized();
}
/**
* 加载模型
* @param urls 模型 URL 数组
* @param options 加载选项
*/
public loadModel(urls: string[], options?: ModelLoadOptions): void {
if (!this.engineInstance) {
console.warn('[EngineManager] 3D Engine not initialized.');
return;
}
this.engineInstance.loadModel(urls, options);
}
/**
* 获取底层引擎实例
* @returns 引擎实例
*/
public getEngine(): any {
if (!this.engineInstance) {
console.warn('[EngineManager] 3D Engine not initialized.');
return null;
}
return this.engineInstance.getEngine();
}
/** 相机回到初始位置 */
public CameraGoHome(): void {
if (!this.engineInstance) {
console.warn('[EngineManager] 3D Engine not initialized.');
return;
}
this.engineInstance.CameraGoHome();
}
/** 暂停渲染 */
public pauseRendering(): void {
if (!this.engineInstance) {
console.warn('[EngineManager] 3D Engine not initialized.');
return;
}
this.engineInstance.pauseRendering();
}
/** 恢复渲染 */
public resumeRendering(): void {
if (!this.engineInstance) {
console.warn('[EngineManager] 3D Engine not initialized.');
return;
}
this.engineInstance.resumeRendering();
}
/**
* 激活测量模式
* @param mode 测量模式
*/
public activateMeasure(mode: MeasureMode): void {
if (!this.engineInstance) {
console.warn('[EngineManager] 3D Engine not initialized.');
return;
}
this.engineInstance.activateMeasure(mode);
}
/** 停用测量模式 */
public deactivateMeasure(): void {
if (!this.engineInstance) {
return;
}
this.engineInstance.deactivateMeasure();
}
/**
* 获取当前测量类型
* @returns 当前测量模式
*/
public getCurrentMeasureType(): MeasureMode | null {
if (!this.engineInstance) {
return null;
}
return this.engineInstance.getCurrentMeasureType();
}
public clearAllMeasures(): void {
if (!this.engineInstance) {
return;
}
this.engineInstance.clearAllMeasures();
}
public saveMeasureSetting(setting: { unit: MeasureUnit; precision: MeasurePrecision }): void {
if (!this.engineInstance) {
return;
}
this.engineInstance.saveMeasureSetting(setting);
}
public activeSection(mode: 'x' | 'y' | 'z' | 'box' | 'face'): void {
if (!this.engineInstance) {
console.warn('[EngineManager] 3D Engine not initialized.');
return;
}
this.engineInstance.activeSection(mode);
}
/**
* 获取当前激活的剖切模式
* @returns 当前剖切模式,未激活时返回 null
*/
public getCurrentSectionMode(): 'x' | 'y' | 'z' | 'box' | 'face' | null {
if (!this.engineInstance) {
return null;
}
return this.engineInstance.getCurrentSectionMode();
}
/**
* 停用所有剖切功能
* @remarks 关闭剖切对话框时调用
*/
public deactivateSection(): void {
if (!this.engineInstance) {
return;
}
this.engineInstance.deactivateSection();
}
/**
* 设置剖切盒范围
* @param range 各轴的剖切范围 (百分比 0-100)
* @remarks 仅在 'box' 模式下有效
*/
public setSectionBoxRange(range: SectionBoxRange): void {
if (!this.engineInstance) {
console.warn('[EngineManager] 3D Engine not initialized.');
return;
}
this.engineInstance.setSectionBoxRange(range);
}
/**
* 隐藏剖切面(临时隐藏,可恢复)
* @remarks 配合 recoverSection() 使用
*/
public hideSection(): void {
if (!this.engineInstance) {
return;
}
this.engineInstance.hideSection();
}
/**
* 恢复剖切面(从隐藏状态恢复)
* @remarks 恢复被 hideSection() 隐藏的剖切面
*/
public recoverSection(): void {
if (!this.engineInstance) {
return;
}
this.engineInstance.recoverSection();
}
public fitSectionBoxToModel(): void {
if (!this.engineInstance) {
return;
}
this.engineInstance.fitSectionBoxToModel();
}
/**
* 剖切盒适应(缩放到场景整体包围盒)
* @remarks 对接底层 clipping.scaleBox()
*/
public scaleSectionBox(): void {
if (!this.engineInstance) {
return;
}
this.engineInstance.scaleSectionBox();
}
/**
* 反向剖切
* @remarks 对接底层 clipping.reverse()
*/
public reverseSection(): void {
if (!this.engineInstance) {
return;
}
this.engineInstance.reverseSection();
}
/** 激活框选放大功能 */
public activateZoomBox(): void {
if (!this.engineInstance) {
console.warn('[EngineManager] 3D Engine not initialized.');
return;
}
this.engineInstance.activateZoomBox();
}
// ==================== 漫游功能 ====================
/** 激活第一人称漫游模式 */
public activateFirstPersonMode(): void {
if (!this.engineInstance) {
console.warn('[EngineManager] 3D Engine not initialized.');
return;
}
this.engineInstance.activateFirstPersonMode();
}
/** 停用第一人称漫游模式 */
public deactivateFirstPersonMode(): void {
if (!this.engineInstance) {
return;
}
this.engineInstance.deactivateFirstPersonMode();
}
public setWalkSpeed(speed: number): void {
this.engineInstance?.setWalkSpeed(speed);
}
public setWalkGravity(enabled: boolean): void {
this.engineInstance?.setWalkGravity(enabled);
}
public setWalkCollision(enabled: boolean): void {
this.engineInstance?.setWalkCollision(enabled);
}
public toggleMiniMap(): void {
this.engineInstance?.toggleMiniMap();
}
public isFirstPersonModeActive(): boolean {
return this.engineInstance?.isFirstPersonModeActive() ?? false;
}
// ==================== 结束:漫游功能 ====================
// ==================== 构件选中 ====================
/**
* 获取当前选中的构件
* @returns 选中构件的 URL 和 ID未选中时返回 null
*/
public getSelectedComponent(): { url: string; id: string } | null {
return this.engineInstance?.getSelectedComponent() ?? null;
}
/**
* 获取构件属性
* @param url 模型 URL
* @param id 构件 ID
* @param callback 回调函数,接收属性数据
*/
public getComponentProperties(
url: string,
id: string,
callback: (data: any) => void
): void {
this.engineInstance?.getComponentProperties(url, id, callback);
}
// ==================== 结束:构件选中 ====================
// ==================== 模型树 ====================
public getTypeTreeData(): any[] {
return this.engineInstance?.getTypeTreeData() ?? [];
}
public getLevelTreeData(): any[] {
return this.engineInstance?.getLevelTreeData() ?? [];
}
public getMajorTreeData(): any[] {
return this.engineInstance?.getMajorTreeData() ?? [];
}
public getEngineInfo(): EngineInfo | null {
return this.engineInstance?.getEngineInfo() ?? null;
}
// ==================== 结束:模型树 ====================
// ==================== 路径漫游 ====================
/**
* 添加漫游点
* 将当前相机位置添加为一个漫游点
*/
public pathRoamingAddPoint(): void {
this.engineInstance?.pathRoamingAddPoint();
}
/**
* 删除指定索引的漫游点
* @param index 要删除的漫游点索引
*/
public pathRoamingRemovePoint(index: number): void {
this.engineInstance?.pathRoamingRemovePoint(index);
}
/**
* 清除所有漫游点
*/
public pathRoamingClearPoints(): void {
this.engineInstance?.pathRoamingClearPoints();
}
/**
* 获取所有漫游点
* @returns 漫游点数组
*/
public pathRoamingGetPoints(): any[] {
return this.engineInstance?.pathRoamingGetPoints() ?? [];
}
/**
* 跳转到指定漫游点
* @param index 目标漫游点索引
*/
public pathRoamingJumpToPoint(index: number): void {
this.engineInstance?.pathRoamingJumpToPoint(index);
}
/**
* 播放漫游
* @param options 播放选项,包含时长、循环、回调等配置
*/
public pathRoamingPlay(options?: {
duration?: number;
loop?: boolean;
onComplete?: () => void;
onPointComplete?: (pointIndex: number) => void;
}): void {
this.engineInstance?.pathRoamingPlay(options);
}
/**
* 停止漫游
*/
public pathRoamingStop(): void {
this.engineInstance?.pathRoamingStop();
}
// ==================== 结束:路径漫游 ====================
// ==================== 构件操作 ====================
/**
* 获取当前高亮(选中)的模型
* @returns 高亮模型对象,未选中时返回 null
*/
public getHighlightModels(): any {
return this.engineInstance?.getHighlightModels() ?? null;
}
/**
* 高亮指定模型构件
*
* @param models - 要高亮的模型数组,格式: [{ url: string, ids: string[] }]
*
* @example
* manager.highlightModel([
* { url: 'https://xxx/models/xxx/', ids: [350518, 350520] }
* ]);
*/
public highlightModel(models: { url: string; ids: number[] }[]): void {
this.engineInstance?.highlightModel(models);
}
public unhighlightAllModels(): void {
this.engineInstance?.unhighlightAllModels();
}
public viewScaleToModel(models: { url: string; ids: number[] }[]): void {
this.engineInstance?.viewScaleToModel(models);
}
public hideModels(models: { url: string; ids: number[] }[]): void {
this.engineInstance?.hideModels(models);
}
public showModel(models: { url: string; ids: number[] }[]): void {
this.engineInstance?.showModel(models);
}
/**
* 半透明指定模型
* @param models 要半透明的模型对象
*/
public translucentModels(models: any): void {
this.engineInstance?.translucentModels(models);
}
/**
* 取消半透明
*/
public unTranslucentModel(): void {
this.engineInstance?.unTranslucentModel();
}
/**
* 隔离指定模型(隐藏其他)
* @param models 要隔离的模型对象
*/
public isolateModels(models: any): void {
this.engineInstance?.isolateModels(models);
}
/**
* 半透明其他构件
* @param models 基准模型对象
*/
public translucentOtherModels(models: any): void {
this.engineInstance?.translucentOtherModels(models);
}
/**
* 显示所有模型
*/
public showAllModels(): void {
this.engineInstance?.showAllModels();
}
/**
* 批量选择同类模型
* @param models 基准模型对象
*/
public batchSelectSameTypeModel(models: any): void {
this.engineInstance?.batchSelectSameTypeModel(models);
}
/**
* 批量选择同层模型
* @param models 基准模型对象
*/
public batchSelectSameLevelModel(models: any): void {
this.engineInstance?.batchSelectSameLevelModel(models);
}
/**
* 批量选择同层同类模型
* @param models 基准模型对象
*/
public batchSelectSameLevelTypeModel(models: any): void {
this.engineInstance?.batchSelectSameLevelTypeModel(models);
}
// ==================== 结束:构件操作 ====================
/** 销毁引擎管理器 */
public destroy(): void {
if (this.engineInstance) {
this.engineInstance.destroy();
this.engineInstance = null;
}
if (this.rightKey) {
this.rightKey.destroy();
this.rightKey = null;
}
super.destroy();
}
}