Files
bim_engine/src/components/engine/index.ts
yuding 19f7e3ffbc feat(theme): 重构主题系统,新增 glass-pill 按钮样式
- ThemeConfig 接口扩展至 60+ 语义化属性
- 新增深浅主题预设 (glassPill overrides)
- button-group 支持 glass-pill 样式变体
- 默认主题改为浅色
- 移除 toolbar 容器硬编码定位
- 统一组件 CSS 变量命名规范
- 暂时隐藏下拉箭头
2026-01-21 15:50:07 +08:00

387 lines
12 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 type { ThemeConfig } from '../../themes/types';
import { IBimComponent } from '../../types/component';
import { themeManager } from '../../services/theme';
import type { EngineOptions, ModelLoadOptions } from './types';
import type { MeasureMode } from '../../types/measure';
// 导入第三方 SDK 的 createEngine 函数
import { createEngine as createEngineSDK } from '../../engine_base/bim-engine-sdk.es.js';
// 重新导出类型,方便外部引用
export type { EngineOptions, ModelLoadOptions };
/**
* 创建 Engine 实例的工厂函数
* 兼容旧代码直接 import { createEngine } 的方式
*/
export const createEngine = (options: EngineOptions) => {
return new Engine(options);
};
/**
* 3D 引擎组件
* 负责创建和管理第三方 3D 引擎实例
*/
export class Engine implements IBimComponent {
/** 第三方 3D 引擎实例 */
private engine: any = null;
/** 引擎挂载的容器元素 */
private container: HTMLElement;
/** 引擎容器 ID用于传递给 createEngine */
private containerId: string;
/** 引擎配置选项(不包含 container */
private options: Omit<EngineOptions, 'container'>;
/** 是否已初始化 */
private _isInitialized = false;
/** 是否已销毁 */
private _isDestroyed = false;
/** 主题订阅取消函数 */
private unsubscribeTheme: (() => void) | null = null;
/** 当前激活的测量类型 */
private currentMeasureType: MeasureMode | null = null;
/** 主测量功能是否已激活 */
private isMeasureActive: boolean = false;
/**
* 构造函数
* @param options 3D 引擎配置选项
*/
constructor(options: EngineOptions) {
// 解析容器元素
this.container = options.container;
// 如果容器没有 id生成一个唯一的 id
if (!this.container.id) {
this.containerId = `engine-container-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
this.container.id = this.containerId;
} else {
this.containerId = this.container.id;
}
// 保存配置选项(设置默认值)
this.options = {
backgroundColor: 'linear-gradient(to bottom, rgb(214, 224, 235), rgb(246, 250, 255))', // 固定背景渐变色
version: options.version ?? 'v1', // 默认使用 v1 版本
showStats: options.showStats ?? false, // 默认不显示统计
showViewCube: options.showViewCube ?? true, // 默认显示视图立方体
};
}
/**
* 初始化组件 (接口实现)
* 创建 div 容器并初始化引擎
*/
public init(): void {
if (this._isInitialized) {
console.warn('[Engine] Engine already initialized.');
return;
}
if (this._isDestroyed) {
console.error('[Engine] Cannot initialize destroyed engine.');
return;
}
try {
// 应用背景色到容器
if (typeof this.options.backgroundColor === 'string') {
this.container.style.background = this.options.backgroundColor;
}
// 创建引擎配置对象
const engineConfig = {
containerId: this.containerId,
backgroundColor: this.options.backgroundColor,
version: this.options.version,
showStats: this.options.showStats,
showViewCube: this.options.showViewCube,
};
// 输出配置信息
console.log('引擎配置信息:', engineConfig);
// 调用引擎创建函数创建引擎实例
// 将 options 中的配置复制给 createEngine
this.engine = createEngineSDK(engineConfig);
if (!this.engine) {
throw new Error('Failed to create engine instance');
}
// 标记为已初始化
this._isInitialized = true;
// 订阅主题变化
this.unsubscribeTheme = themeManager.subscribe((theme) => {
this.setTheme(theme);
});
// 应用当前主题
this.setTheme(themeManager.getTheme());
} catch (error) {
console.error('[Engine] Failed to initialize engine:', error);
this._isInitialized = false;
throw error;
}
}
/**
* 设置主题 (接口实现)
* 根据主题调整 3D 引擎的视觉效果(如背景色)
* @param theme 全局主题配置
*/
public setTheme(_theme: ThemeConfig): void {
}
/**
* 设置语言 (接口实现)
*/
public setLocales(): void {
// 3D 引擎组件暂时不需要本地化
}
/**
* 检查是否已初始化
*/
public isInitialized(): boolean {
return this._isInitialized;
}
/**
* 加载 3D 模型
* @param url 模型文件 URL
* @param options 加载选项(位置、旋转、缩放)
*/
public loadModel(url: string, options?: ModelLoadOptions): void {
if (!this._isInitialized || !this.engine) {
console.error('[Engine] Engine not initialized. Please call init() first.');
return;
}
if (!url) {
console.error('[Engine] Model URL is required.');
return;
}
this.engine.loaderModule.loadModels([url], options);
}
/**
* 回到主视角
* @returns
*/
public CameraGoHome() {
this.engine.viewCube.CameraGoHome();
}
/**
* 获取原始 3D 引擎实例
*/
public getEngine(): any {
return this.engine;
}
// ==================== 测量功能方法 ====================
/**
* 激活具体测量类型的统一入口(私有方法)
* @param type 测量类型
* @param activateFunc 第三方引擎的激活函数
*/
private activateMeasureType(type: MeasureMode, activateFunc: () => void): void {
// 1. 检查引擎是否初始化
if (!this._isInitialized || !this.engine) {
console.error('Cannot activate measure: engine not initialized.');
return;
}
// 2. 检查 measure 模块是否存在
if (!this.engine.measure) {
console.error('Measure module not available.');
return;
}
// 3. 如果主功能未激活,先激活主功能
if (!this.isMeasureActive) {
console.log(`激活测测量功能`);
this.engine.measure.active();
this.isMeasureActive = true;
}
// 4. 激活具体的测量类型(直接切换,不需要停用前一个)
activateFunc();
this.currentMeasureType = type;
}
/**
* 激活距离测量
*/
public activateDistanceMeasure(): void {
this.activateMeasureType('distance', () => {
console.log(`激活距离测量`);
this.engine.measure.distanceMeasure.active();
});
}
/**
* 激活最小距离测量
*/
public activateMinDistanceMeasure(): void {
this.activateMeasureType('minDistance', () => {
console.log(`激活最小距离测量`);
this.engine.measure.minDistanceMeasure.active();
});
}
/**
* 激活角度测量
*/
public activateAngleMeasure(): void {
this.activateMeasureType('angle', () => {
console.log(`激活角度测量`);
this.engine.measure.angleMeasure.active();
console.log('[Engine] Angle measure activated (placeholder)');
});
}
/**
* 激活标高测量
*/
public activateElevationMeasure(): void {
this.activateMeasureType('elevation', () => {
console.log(`激活标高测量`);
this.engine.measure.elevationMeasure.active();
});
}
/**
* 激活体积测量
*/
public activateVolumeMeasure(): void {
this.activateMeasureType('volume', () => {
console.log(`激活体积测量`);
this.engine.measure.volumeMeasure.active();
});
}
/**
* 激活激光测距
*/
public activateLaserDistanceMeasure(): void {
this.activateMeasureType('laserDistance', () => {
// TODO: 调用第三方引擎方法(当前先空着)
// this.engine.measure.laserDistanceMeasure.active();
console.log('[Engine] Laser distance measure activated (placeholder)');
});
}
/**
* 激活坡度测量
*/
public activateSlopeMeasure(): void {
this.activateMeasureType('slope', () => {
console.log(`激活坡度测量`);
this.engine.measure.slopeMeasure.active();
});
}
/**
* 激活空间体积测量
*/
public activateSpaceVolumeMeasure(): void {
this.activateMeasureType('spaceVolume', () => {
// TODO: 调用第三方引擎方法(当前先空着)
// this.engine.measure.spaceVolumeMeasure.active();
console.log('[Engine] Space volume measure activated (placeholder)');
});
}
/**
* 激活测量功能(根据类型统一入口)
* @param mode 测量类型
*/
public activateMeasure(mode: MeasureMode): void {
switch (mode) {
case 'distance':
this.activateDistanceMeasure();
break;
case 'minDistance':
this.activateMinDistanceMeasure();
break;
case 'angle':
this.activateAngleMeasure();
break;
case 'elevation':
this.activateElevationMeasure();
break;
case 'volume':
this.activateVolumeMeasure();
break;
case 'laserDistance':
this.activateLaserDistanceMeasure();
break;
case 'slope':
this.activateSlopeMeasure();
break;
case 'spaceVolume':
this.activateSpaceVolumeMeasure();
break;
}
}
/**
* 停用测量功能(关闭测量时调用)
*/
public deactivateMeasure(): void {
if (!this._isInitialized || !this.engine?.measure) {
return;
}
if (!this.isMeasureActive) {
return; // 已经是停用状态
}
console.log('停用测量功能');
this.engine.measure.disActive();
this.isMeasureActive = false;
this.currentMeasureType = null;
}
/**
* 获取当前激活的测量类型
* @returns 当前测量类型,如果未激活则返回 null
*/
public getCurrentMeasureType(): MeasureMode | null {
return this.currentMeasureType;
}
// ==================== 结束:测量功能方法 ====================
/**
* 销毁组件 (接口实现)
* 清理资源、取消订阅、销毁引擎实例
*/
public destroy(): void {
if (this._isDestroyed) {
return;
}
// 停用测量功能
this.deactivateMeasure();
// 取消主题订阅
if (this.unsubscribeTheme) {
this.unsubscribeTheme();
this.unsubscribeTheme = null;
}
// 清理容器(可选,根据需求决定是否清空容器)
this.container.innerHTML = '';
// 更新状态
this.currentMeasureType = null;
this.isMeasureActive = false;
this._isDestroyed = true;
this._isInitialized = false;
}
}