提交代码
This commit is contained in:
151
src/bim-engine-2d.ts
Normal file
151
src/bim-engine-2d.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* BimEngine2d - 独立的 2D 图纸引擎
|
||||
* 轻量级独立类,不依赖 3D 引擎的 UI 管理器(toolbar、measure 等)
|
||||
* 用法:import { BimEngine2d } from 'iflow-engine'
|
||||
*/
|
||||
import { Engine2d } from './components/engine-2d';
|
||||
import type { DrawingLoadOptions, Drawing2dLayer } from './components/engine-2d/types';
|
||||
import { ManagerRegistry } from './core/manager-registry';
|
||||
import { localeManager } from './services/locale';
|
||||
import { themeManager } from './services/theme';
|
||||
import type { LocaleType } from './locales/types';
|
||||
import type { ThemeType } from './themes/types';
|
||||
import type { EngineEvents } from './types/events';
|
||||
|
||||
/**
|
||||
* BimEngine2d 构造选项
|
||||
* 合并引擎配置与主题/语言设置
|
||||
*/
|
||||
export interface BimEngine2dOptions {
|
||||
/** 语言 */
|
||||
locale?: LocaleType;
|
||||
/** 主题 */
|
||||
theme?: ThemeType;
|
||||
/** 背景颜色(十六进制数值,如 0xffffff) */
|
||||
backgroundColor?: number;
|
||||
/** 是否启用网格 */
|
||||
gridEnabled?: boolean;
|
||||
/** 是否启用坐标轴 */
|
||||
axesEnabled?: boolean;
|
||||
/** 选中构件颜色 */
|
||||
selectionColor?: number;
|
||||
/** 高亮构件颜色 */
|
||||
highlightColor?: number;
|
||||
/** 是否启用性能监控 */
|
||||
enablePerformanceMonitoring?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 独立 2D 图纸引擎
|
||||
*
|
||||
* 内部复用 Engine2d 组件,跳过 Manager 层
|
||||
* 构造时自动初始化,可直接调用 loadDrawing() 加载图纸
|
||||
*/
|
||||
export class BimEngine2d {
|
||||
public container: HTMLElement;
|
||||
private registry: ManagerRegistry;
|
||||
private engineComponent: Engine2d | null = null;
|
||||
|
||||
constructor(container: HTMLElement | string, options?: BimEngine2dOptions) {
|
||||
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 && options.theme !== 'custom') {
|
||||
themeManager.setTheme(options.theme as 'dark' | 'light');
|
||||
}
|
||||
|
||||
// 创建并初始化 2D 引擎
|
||||
this.engineComponent = new Engine2d({
|
||||
container: this.container,
|
||||
backgroundColor: options?.backgroundColor,
|
||||
gridEnabled: options?.gridEnabled,
|
||||
axesEnabled: options?.axesEnabled,
|
||||
selectionColor: options?.selectionColor,
|
||||
highlightColor: options?.highlightColor,
|
||||
enablePerformanceMonitoring: options?.enablePerformanceMonitoring,
|
||||
}, this.registry);
|
||||
|
||||
this.engineComponent.init();
|
||||
}
|
||||
|
||||
// ─── 图纸操作 ───
|
||||
|
||||
/** 加载 2D 图纸 */
|
||||
public async loadDrawing(url: string, options?: DrawingLoadOptions): Promise<void> {
|
||||
return this.engineComponent?.loadDrawing(url, options);
|
||||
}
|
||||
|
||||
/** 获取所有图层 */
|
||||
public getLayers(): Drawing2dLayer[] {
|
||||
return this.engineComponent?.getLayers() ?? [];
|
||||
}
|
||||
|
||||
/** 设置图层可见性 */
|
||||
public setLayerVisible(name: string, visible: boolean): void {
|
||||
this.engineComponent?.setLayerVisible(name, visible);
|
||||
}
|
||||
|
||||
// ─── 视图控制 ───
|
||||
|
||||
/** 重置视图 */
|
||||
public resetView(): void {
|
||||
this.engineComponent?.resetView();
|
||||
}
|
||||
|
||||
/** 适应视图(缩放到全部内容) */
|
||||
public fitToView(): void {
|
||||
this.engineComponent?.fitToView();
|
||||
}
|
||||
|
||||
/** 设置缩放级别 */
|
||||
public setZoom(zoom: number): void {
|
||||
this.engineComponent?.setZoom(zoom);
|
||||
}
|
||||
|
||||
/** 获取当前缩放级别 */
|
||||
public getZoom(): number {
|
||||
return this.engineComponent?.getZoom() ?? 1;
|
||||
}
|
||||
|
||||
// ─── 主题 ───
|
||||
|
||||
/** 设置主题 */
|
||||
public setTheme(theme: 'dark' | 'light'): void {
|
||||
themeManager.setTheme(theme);
|
||||
}
|
||||
|
||||
// ─── 事件 ───
|
||||
|
||||
/** 订阅事件 */
|
||||
public on<K extends keyof EngineEvents>(event: K, listener: (payload: EngineEvents[K]) => void): () => void {
|
||||
return this.registry.on(event, listener);
|
||||
}
|
||||
|
||||
/** 取消订阅事件 */
|
||||
public off<K extends keyof EngineEvents>(event: K, listener: (payload: EngineEvents[K]) => void): void {
|
||||
this.registry.off(event, listener);
|
||||
}
|
||||
|
||||
/** 订阅原始 2D 引擎事件 */
|
||||
public onRawEvent(event: string, handler: (...args: any[]) => void): void {
|
||||
this.engineComponent?.onRawEvent(event, handler);
|
||||
}
|
||||
|
||||
/** 取消订阅原始 2D 引擎事件 */
|
||||
public offRawEvent(event: string, handler: (...args: any[]) => void): void {
|
||||
this.engineComponent?.offRawEvent(event, handler);
|
||||
}
|
||||
|
||||
// ─── 生命周期 ───
|
||||
|
||||
/** 销毁引擎,释放所有资源 */
|
||||
public destroy(): void {
|
||||
this.engineComponent?.destroy();
|
||||
this.engineComponent = null;
|
||||
this.registry.reset();
|
||||
}
|
||||
}
|
||||
158
src/bim-engine-720.ts
Normal file
158
src/bim-engine-720.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* BimEngine720 - 独立的 720° 全景引擎
|
||||
* 轻量级独立类,不依赖 3D 引擎的 UI 管理器(toolbar、measure 等)
|
||||
* 用法:import { BimEngine720 } from 'iflow-engine'
|
||||
*/
|
||||
import { Engine720 } from './components/engine-720';
|
||||
import type { PanoramaLoadOptions } from './components/engine-720/types';
|
||||
import { ManagerRegistry } from './core/manager-registry';
|
||||
import { localeManager } from './services/locale';
|
||||
import { themeManager } from './services/theme';
|
||||
import type { LocaleType } from './locales/types';
|
||||
import type { ThemeType } from './themes/types';
|
||||
import type { EngineEvents } from './types/events';
|
||||
|
||||
/**
|
||||
* BimEngine720 构造选项
|
||||
* 合并引擎配置与主题/语言设置
|
||||
*/
|
||||
export interface BimEngine720Options {
|
||||
/** 语言 */
|
||||
locale?: LocaleType;
|
||||
/** 主题 */
|
||||
theme?: ThemeType;
|
||||
/** 视场角(默认 75) */
|
||||
fov?: number;
|
||||
/** 是否启用缩放(默认 true) */
|
||||
enableZoom?: boolean;
|
||||
/** 是否启用旋转(默认 true) */
|
||||
enableRotate?: boolean;
|
||||
/** 球体半径(默认 500) */
|
||||
sphereRadius?: number;
|
||||
/** 旋转速度 */
|
||||
rotateSpeed?: number;
|
||||
/** 缩放速度 */
|
||||
zoomSpeed?: number;
|
||||
/** 是否启用阻尼 */
|
||||
enableDamping?: boolean;
|
||||
/** 阻尼因子 */
|
||||
dampingFactor?: number;
|
||||
/** 最小视场角 */
|
||||
minFov?: number;
|
||||
/** 最大视场角 */
|
||||
maxFov?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 独立 720° 全景引擎
|
||||
*
|
||||
* 内部复用 Engine720 组件,跳过 Manager 层
|
||||
* 构造时自动初始化,可直接调用 loadPanorama() 加载全景图
|
||||
*/
|
||||
export class BimEngine720 {
|
||||
public container: HTMLElement;
|
||||
private registry: ManagerRegistry;
|
||||
private engineComponent: Engine720 | null = null;
|
||||
|
||||
constructor(container: HTMLElement | string, options?: BimEngine720Options) {
|
||||
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 && options.theme !== 'custom') {
|
||||
themeManager.setTheme(options.theme as 'dark' | 'light');
|
||||
}
|
||||
|
||||
// 创建并初始化 720 引擎
|
||||
this.engineComponent = new Engine720({
|
||||
container: this.container,
|
||||
fov: options?.fov,
|
||||
enableZoom: options?.enableZoom,
|
||||
enableRotate: options?.enableRotate,
|
||||
sphereRadius: options?.sphereRadius,
|
||||
rotateSpeed: options?.rotateSpeed,
|
||||
zoomSpeed: options?.zoomSpeed,
|
||||
enableDamping: options?.enableDamping,
|
||||
dampingFactor: options?.dampingFactor,
|
||||
minFov: options?.minFov,
|
||||
maxFov: options?.maxFov,
|
||||
}, this.registry);
|
||||
|
||||
this.engineComponent.init();
|
||||
}
|
||||
|
||||
// ─── 全景操作 ───
|
||||
|
||||
/** 加载全景图 */
|
||||
public async loadPanorama(url: string, options?: PanoramaLoadOptions): Promise<void> {
|
||||
return this.engineComponent?.loadPanorama(url, options);
|
||||
}
|
||||
|
||||
/** 预加载多个全景图 */
|
||||
public async preloadPanoramas(urls: string[]): Promise<void> {
|
||||
return this.engineComponent?.preloadPanoramas(urls);
|
||||
}
|
||||
|
||||
// ─── 视角控制 ───
|
||||
|
||||
/** 设置视场角 */
|
||||
public setFov(fov: number): void {
|
||||
this.engineComponent?.setFov(fov);
|
||||
}
|
||||
|
||||
/** 获取当前视场角 */
|
||||
public getFov(): number {
|
||||
return this.engineComponent?.getFov() ?? 75;
|
||||
}
|
||||
|
||||
/** 设置相机朝向 */
|
||||
public lookAt(phi: number, theta: number, animated?: boolean): void {
|
||||
this.engineComponent?.lookAt(phi, theta, animated);
|
||||
}
|
||||
|
||||
/** 重置视图 */
|
||||
public resetView(): void {
|
||||
this.engineComponent?.resetView();
|
||||
}
|
||||
|
||||
// ─── 主题 ───
|
||||
|
||||
/** 设置主题 */
|
||||
public setTheme(theme: 'dark' | 'light'): void {
|
||||
themeManager.setTheme(theme);
|
||||
}
|
||||
|
||||
// ─── 事件 ───
|
||||
|
||||
/** 订阅事件 */
|
||||
public on<K extends keyof EngineEvents>(event: K, listener: (payload: EngineEvents[K]) => void): () => void {
|
||||
return this.registry.on(event, listener);
|
||||
}
|
||||
|
||||
/** 取消订阅事件 */
|
||||
public off<K extends keyof EngineEvents>(event: K, listener: (payload: EngineEvents[K]) => void): void {
|
||||
this.registry.off(event, listener);
|
||||
}
|
||||
|
||||
/** 订阅原始 720 引擎事件 */
|
||||
public onRawEvent(event: string, handler: (...args: any[]) => void): void {
|
||||
this.engineComponent?.onRawEvent(event, handler);
|
||||
}
|
||||
|
||||
/** 取消订阅原始 720 引擎事件 */
|
||||
public offRawEvent(event: string, handler: (...args: any[]) => void): void {
|
||||
this.engineComponent?.offRawEvent(event, handler);
|
||||
}
|
||||
|
||||
// ─── 生命周期 ───
|
||||
|
||||
/** 销毁引擎,释放所有资源 */
|
||||
public destroy(): void {
|
||||
this.engineComponent?.destroy();
|
||||
this.engineComponent = null;
|
||||
this.registry.reset();
|
||||
}
|
||||
}
|
||||
@@ -36,3 +36,16 @@
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.bim-engine-size {
|
||||
position: absolute;
|
||||
bottom: 6px;
|
||||
left: 56px;
|
||||
font-size: 11px;
|
||||
color: rgba(0, 0, 0, 0.35);
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
z-index: 1;
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,10 @@ export type { EngineOptions, ModelLoadOptions };
|
||||
export class BimEngine {
|
||||
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 registry: ManagerRegistry;
|
||||
|
||||
public toolbar: ToolbarManager | null = null;
|
||||
@@ -48,6 +52,10 @@ export class BimEngine {
|
||||
public aiChat: AiChatManager | null = null;
|
||||
public setting: SettingDialogManager | null = null;
|
||||
|
||||
private readonly handleWindowResize = () => {
|
||||
this.updateClientSizeDisplay();
|
||||
};
|
||||
|
||||
constructor(
|
||||
container: HTMLElement | string,
|
||||
options?: {
|
||||
@@ -108,6 +116,12 @@ export class BimEngine {
|
||||
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;
|
||||
|
||||
@@ -141,6 +155,7 @@ export class BimEngine {
|
||||
this.registry.walkControl = this.walkControl;
|
||||
this.registry.engineInfo = this.engineInfo;
|
||||
|
||||
|
||||
this.componentDetail = new ComponentDetailManager(this.registry);
|
||||
this.registry.componentDetail = this.componentDetail;
|
||||
this.componentDetail.init();
|
||||
@@ -165,7 +180,56 @@ export class BimEngine {
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
this.toolbar?.destroy();
|
||||
this.buttonGroup?.destroy();
|
||||
this.engine?.destroy();
|
||||
@@ -179,6 +243,10 @@ export class BimEngine {
|
||||
this.walkControl?.destroy();
|
||||
this.aiChat?.destroy();
|
||||
this.setting?.destroy();
|
||||
|
||||
this.sizeEl = null;
|
||||
this.lastSyncedWidth = -1;
|
||||
this.lastSyncedHeight = -1;
|
||||
this.container.innerHTML = '';
|
||||
this.registry.reset();
|
||||
}
|
||||
|
||||
379
src/components/engine-2d/index.ts
Normal file
379
src/components/engine-2d/index.ts
Normal file
@@ -0,0 +1,379 @@
|
||||
/**
|
||||
* 2D 引擎组件
|
||||
* 负责创建和管理第三方 2D 图纸引擎实例
|
||||
* 镜像 3D Engine 组件的设计模式
|
||||
*/
|
||||
import type { ThemeConfig } from '../../themes/types';
|
||||
import { IBimComponent } from '../../types/component';
|
||||
import { themeManager } from '../../services/theme';
|
||||
import type { Engine2dOptions, DrawingLoadOptions, Drawing2dLayer } from './types';
|
||||
import type { ManagerRegistry } from '../../core/manager-registry';
|
||||
// 导入第三方 SDK 的 createEngine2d 函数(从 npm 包引入)
|
||||
import { createEngine2d as createEngine2dSDK } from 'iflow-engine-base';
|
||||
import "../../../../bim_engine_base/dist/iflow-engine-base.css";
|
||||
|
||||
export type { Engine2dOptions, DrawingLoadOptions, Drawing2dLayer };
|
||||
|
||||
/**
|
||||
* 创建 Engine2d 实例的工厂函数
|
||||
* 兼容旧代码直接 import 的方式
|
||||
*/
|
||||
export const createEngine2d = (options: Engine2dOptions, registry: ManagerRegistry) => {
|
||||
return new Engine2d(options, registry);
|
||||
};
|
||||
|
||||
/**
|
||||
* 2D 图纸引擎组件
|
||||
* 负责创建和管理第三方 2D 引擎实例
|
||||
*/
|
||||
export class Engine2d implements IBimComponent {
|
||||
/** 第三方 2D 引擎实例 */
|
||||
private engine: any = null;
|
||||
/** 管理器注册表实例 */
|
||||
private registry: ManagerRegistry;
|
||||
/** 引擎挂载的容器元素 */
|
||||
private container: HTMLElement;
|
||||
/** 引擎容器 ID(用于传递给 createEngine2d) */
|
||||
private containerId: string;
|
||||
/** 引擎配置选项(不包含 container) */
|
||||
private options: Omit<Engine2dOptions, 'container'>;
|
||||
/** 是否已初始化 */
|
||||
private _isInitialized = false;
|
||||
/** 是否已销毁 */
|
||||
private _isDestroyed = false;
|
||||
/** 主题订阅取消函数 */
|
||||
private unsubscribeTheme: (() => void) | null = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param options 2D 引擎配置选项
|
||||
* @param registry 管理器注册表
|
||||
*/
|
||||
constructor(options: Engine2dOptions, registry: ManagerRegistry) {
|
||||
// 保存注册表
|
||||
this.registry = registry;
|
||||
// 解析容器元素
|
||||
this.container = options.container;
|
||||
// 如果容器没有 id,生成一个唯一的 id
|
||||
if (!this.container.id) {
|
||||
this.containerId = `engine2d-container-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
this.container.id = this.containerId;
|
||||
} else {
|
||||
this.containerId = this.container.id;
|
||||
}
|
||||
|
||||
// 保存配置选项(设置默认值)
|
||||
this.options = {
|
||||
backgroundColor: options.backgroundColor ?? 0x1a1a1a,
|
||||
gridEnabled: options.gridEnabled ?? true,
|
||||
axesEnabled: options.axesEnabled ?? true,
|
||||
selectionColor: options.selectionColor,
|
||||
highlightColor: options.highlightColor,
|
||||
enablePerformanceMonitoring: options.enablePerformanceMonitoring ?? true,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化组件 (接口实现)
|
||||
* 创建 div 容器并初始化 2D 引擎
|
||||
*/
|
||||
public init(): void {
|
||||
if (this._isInitialized) {
|
||||
console.warn('[Engine2d] Engine already initialized.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._isDestroyed) {
|
||||
console.error('[Engine2d] Cannot initialize destroyed engine.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建引擎配置对象
|
||||
const engineConfig: any = {
|
||||
containerId: this.containerId,
|
||||
backgroundColor: this.options.backgroundColor,
|
||||
gridEnabled: this.options.gridEnabled,
|
||||
axesEnabled: this.options.axesEnabled,
|
||||
};
|
||||
|
||||
// 仅在用户提供时传递可选参数
|
||||
if (this.options.selectionColor !== undefined) {
|
||||
engineConfig.selectionColor = this.options.selectionColor;
|
||||
}
|
||||
if (this.options.highlightColor !== undefined) {
|
||||
engineConfig.highlightColor = this.options.highlightColor;
|
||||
}
|
||||
if (this.options.enablePerformanceMonitoring !== undefined) {
|
||||
engineConfig.enablePerformanceMonitoring = this.options.enablePerformanceMonitoring;
|
||||
}
|
||||
|
||||
// 输出配置信息
|
||||
console.log('[Engine2d] 引擎配置信息:', engineConfig);
|
||||
|
||||
// 调用引擎创建函数创建 2D 引擎实例
|
||||
this.engine = createEngine2dSDK(engineConfig);
|
||||
|
||||
if (!this.engine) {
|
||||
throw new Error('Failed to create 2D engine instance');
|
||||
}
|
||||
|
||||
// 标记为已初始化
|
||||
this._isInitialized = true;
|
||||
|
||||
// 订阅主题变化
|
||||
this.unsubscribeTheme = themeManager.subscribe((theme) => {
|
||||
this.setTheme(theme);
|
||||
});
|
||||
|
||||
// 应用当前主题
|
||||
this.setTheme(themeManager.getTheme());
|
||||
|
||||
// 监听 2D 图纸实体点击事件
|
||||
if (this.engine.events) {
|
||||
this.engine.events.on('entity-click', (data: any) => {
|
||||
console.log('[Engine2d] 实体点击:', data);
|
||||
this.registry.emit('engine2d:entity-clicked', { data });
|
||||
});
|
||||
|
||||
this.engine.events.on('layer-visibility-changed', (data: any) => {
|
||||
console.log('[Engine2d] 图层可见性变更:', data);
|
||||
this.registry.emit('engine2d:layer-changed', { data });
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('[Engine2d] Failed to initialize 2D engine:', error);
|
||||
this._isInitialized = false;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置主题 (接口实现)
|
||||
* @param _theme 全局主题配置
|
||||
*/
|
||||
public setTheme(_theme: ThemeConfig): void {
|
||||
// 2D 引擎可根据主题切换深色/浅色模式
|
||||
// if (!this._isInitialized || !this.engine) return;
|
||||
|
||||
// try {
|
||||
// if (typeof this.engine.setTheme === 'function') {
|
||||
// 根据主题背景色判断深色/浅色
|
||||
//const isDark = _theme.bgBase ? this.isColorDark(_theme.bgBase) : false;
|
||||
//this.engine.setTheme(isDark ? 'dark' : 'light');
|
||||
// }
|
||||
// } catch (e) {
|
||||
// console.warn('[Engine2d] Failed to set theme:', e);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置语言 (接口实现)
|
||||
*/
|
||||
public setLocales(): void {
|
||||
// 2D 引擎组件暂时不需要本地化
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否已初始化
|
||||
*/
|
||||
public isInitialized(): boolean {
|
||||
return this._isInitialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载 2D 图纸
|
||||
* @param url 图纸文件 URL
|
||||
* @param options 加载选项
|
||||
*/
|
||||
public async loadDrawing(url: string, options?: DrawingLoadOptions): Promise<void> {
|
||||
if (!this._isInitialized || !this.engine) {
|
||||
console.error('[Engine2d] Engine not initialized. Please call init() first.');
|
||||
return;
|
||||
}
|
||||
if (!url) {
|
||||
console.error('[Engine2d] Drawing URL is required.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('[Engine2d] 开始加载图纸:', url);
|
||||
await this.engine.loadModel(url, options);
|
||||
console.log('[Engine2d] 图纸加载完成:', url);
|
||||
this.registry.emit('engine2d:drawing-loaded', { url });
|
||||
} catch (error) {
|
||||
console.error('[Engine2d] 图纸加载失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有图层
|
||||
* @returns 图层列表
|
||||
*/
|
||||
public getLayers(): Drawing2dLayer[] {
|
||||
if (!this._isInitialized || !this.engine) {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
return this.engine.getLayers?.() ?? [];
|
||||
} catch (e) {
|
||||
console.warn('[Engine2d] Failed to get layers:', e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置图层可见性
|
||||
* @param name 图层名称
|
||||
* @param visible 是否可见
|
||||
*/
|
||||
public setLayerVisible(name: string, visible: boolean): void {
|
||||
if (!this._isInitialized || !this.engine) {
|
||||
console.warn('[Engine2d] Engine not initialized.');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.engine.setLayerVisible?.(name, visible);
|
||||
} catch (e) {
|
||||
console.warn('[Engine2d] Failed to set layer visibility:', e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置视图(适应画布)
|
||||
*/
|
||||
public resetView(): void {
|
||||
if (!this._isInitialized || !this.engine) return;
|
||||
try {
|
||||
this.engine.resetView?.();
|
||||
} catch (e) {
|
||||
console.warn('[Engine2d] Failed to reset view:', e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 适应视图(缩放到全部内容)
|
||||
*/
|
||||
public fitToView(): void {
|
||||
if (!this._isInitialized || !this.engine) return;
|
||||
try {
|
||||
this.engine.fitToView?.();
|
||||
} catch (e) {
|
||||
console.warn('[Engine2d] Failed to fit to view:', e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置缩放级别
|
||||
* @param zoom 缩放值
|
||||
*/
|
||||
public setZoom(zoom: number): void {
|
||||
if (!this._isInitialized || !this.engine) return;
|
||||
this.engine.setZoom?.(zoom);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前缩放级别
|
||||
* @returns 当前缩放值
|
||||
*/
|
||||
public getZoom(): number {
|
||||
if (!this._isInitialized || !this.engine) return 1;
|
||||
return this.engine.getZoom?.() ?? 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置背景色
|
||||
* @param color 颜色值(十六进制数值)
|
||||
*/
|
||||
public setBackgroundColor(color: number): void {
|
||||
if (!this._isInitialized || !this.engine) return;
|
||||
this.engine.setBackgroundColor?.(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除场景
|
||||
*/
|
||||
public clearScene(): void {
|
||||
if (!this._isInitialized || !this.engine) return;
|
||||
this.engine.clearScene?.();
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整引擎尺寸
|
||||
*/
|
||||
public resize(): void {
|
||||
if (!this._isInitialized || !this.engine) return;
|
||||
this.engine.resize?.();
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅原始 2D 引擎事件
|
||||
* @param event 事件名称
|
||||
* @param handler 事件处理函数
|
||||
*/
|
||||
public onRawEvent(event: string, handler: (...args: any[]) => void): void {
|
||||
this.engine?.events?.on(event, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订阅原始 2D 引擎事件
|
||||
* @param event 事件名称
|
||||
* @param handler 事件处理函数
|
||||
*/
|
||||
public offRawEvent(event: string, handler: (...args: any[]) => void): void {
|
||||
this.engine?.events?.off(event, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁组件 (接口实现)
|
||||
* 清理资源、取消订阅、销毁引擎实例
|
||||
*/
|
||||
public destroy(): void {
|
||||
if (this._isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 取消主题订阅
|
||||
if (this.unsubscribeTheme) {
|
||||
this.unsubscribeTheme();
|
||||
this.unsubscribeTheme = null;
|
||||
}
|
||||
|
||||
// 销毁 2D 引擎,释放资源
|
||||
if (this.engine) {
|
||||
try {
|
||||
this.engine.dispose?.();
|
||||
} catch (e) {
|
||||
console.warn('[Engine2d] Error during dispose:', e);
|
||||
}
|
||||
this.engine = null;
|
||||
}
|
||||
|
||||
// 清理容器
|
||||
this.container.innerHTML = '';
|
||||
|
||||
// 更新状态
|
||||
this._isDestroyed = true;
|
||||
this._isInitialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断颜色字符串是否为深色
|
||||
* @param color CSS 颜色字符串
|
||||
* @returns 是否为深色
|
||||
*/
|
||||
// private isColorDark(color: string): boolean {
|
||||
// // 简单判断:如果包含常见深色关键字或数值较低则为深色
|
||||
// if (color.includes('#')) {
|
||||
// const hex = color.replace('#', '');
|
||||
// const r = parseInt(hex.substring(0, 2), 16) || 0;
|
||||
// const g = parseInt(hex.substring(2, 4), 16) || 0;
|
||||
// const b = parseInt(hex.substring(4, 6), 16) || 0;
|
||||
// const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
||||
// return luminance < 0.5;
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
}
|
||||
46
src/components/engine-2d/types.ts
Normal file
46
src/components/engine-2d/types.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* 2D 引擎类型定义
|
||||
* 定义 2D CAD/DWG 图纸引擎的配置选项和相关类型
|
||||
*/
|
||||
|
||||
/**
|
||||
* 2D 引擎配置选项
|
||||
*/
|
||||
export interface Engine2dOptions {
|
||||
/** 引擎挂载的容器元素 */
|
||||
container: HTMLElement;
|
||||
/** 背景颜色(十六进制数值,如 0xffffff) */
|
||||
backgroundColor?: number;
|
||||
/** 是否启用网格 */
|
||||
gridEnabled?: boolean;
|
||||
/** 是否启用坐标轴 */
|
||||
axesEnabled?: boolean;
|
||||
/** 选中构件颜色 */
|
||||
selectionColor?: number;
|
||||
/** 高亮构件颜色 */
|
||||
highlightColor?: number;
|
||||
/** 是否启用性能监控 */
|
||||
enablePerformanceMonitoring?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 2D 图纸加载选项
|
||||
*/
|
||||
export interface DrawingLoadOptions {
|
||||
/** 分块大小 */
|
||||
chunkSize?: number;
|
||||
/** 是否启用分块加载 */
|
||||
enableChunkedLoading?: boolean;
|
||||
/** 是否启用数据校验 */
|
||||
enableValidation?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 2D 图纸图层信息
|
||||
*/
|
||||
export interface Drawing2dLayer {
|
||||
/** 图层名称 */
|
||||
name: string;
|
||||
/** 图层是否可见 */
|
||||
visible: boolean;
|
||||
}
|
||||
356
src/components/engine-720/index.ts
Normal file
356
src/components/engine-720/index.ts
Normal file
@@ -0,0 +1,356 @@
|
||||
/**
|
||||
* 720 全景引擎组件
|
||||
* 负责创建和管理第三方 720° 全景引擎实例
|
||||
* 镜像 Engine2d 组件的设计模式
|
||||
*/
|
||||
import type { ThemeConfig } from '../../themes/types';
|
||||
import { IBimComponent } from '../../types/component';
|
||||
import { themeManager } from '../../services/theme';
|
||||
import type { Engine720Options, PanoramaLoadOptions, PanoramaAnnotation } from './types';
|
||||
import type { ManagerRegistry } from '../../core/manager-registry';
|
||||
// 导入第三方 SDK 的 createEngine720 函数(从 npm 包引入)
|
||||
import { createEngine720 as createEngine720SDK } from 'iflow-engine-base';
|
||||
import "../../../../bim_engine_base/dist/iflow-engine-base.css";
|
||||
|
||||
export type { Engine720Options, PanoramaLoadOptions, PanoramaAnnotation };
|
||||
|
||||
/**
|
||||
* 创建 Engine720 实例的工厂函数
|
||||
* 兼容旧代码直接 import 的方式
|
||||
*/
|
||||
export const createEngine720 = (options: Engine720Options, registry: ManagerRegistry) => {
|
||||
return new Engine720(options, registry);
|
||||
};
|
||||
|
||||
/**
|
||||
* 720° 全景引擎组件
|
||||
* 负责创建和管理第三方 720 全景引擎实例
|
||||
*/
|
||||
export class Engine720 implements IBimComponent {
|
||||
/** 第三方 720 引擎实例 */
|
||||
private engine: any = null;
|
||||
/** 管理器注册表实例 */
|
||||
private registry: ManagerRegistry;
|
||||
/** 引擎挂载的容器元素 */
|
||||
private container: HTMLElement;
|
||||
/** 引擎容器 ID(用于传递给 createEngine720) */
|
||||
private containerId: string;
|
||||
/** 引擎配置选项(不包含 container) */
|
||||
private options: Omit<Engine720Options, 'container'>;
|
||||
/** 是否已初始化 */
|
||||
private _isInitialized = false;
|
||||
/** 是否已销毁 */
|
||||
private _isDestroyed = false;
|
||||
/** 主题订阅取消函数 */
|
||||
private unsubscribeTheme: (() => void) | null = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param options 720 引擎配置选项
|
||||
* @param registry 管理器注册表
|
||||
*/
|
||||
constructor(options: Engine720Options, registry: ManagerRegistry) {
|
||||
// 保存注册表
|
||||
this.registry = registry;
|
||||
// 解析容器元素
|
||||
this.container = options.container;
|
||||
// 如果容器没有 id,生成一个唯一的 id
|
||||
if (!this.container.id) {
|
||||
this.containerId = `engine720-container-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
this.container.id = this.containerId;
|
||||
} else {
|
||||
this.containerId = this.container.id;
|
||||
}
|
||||
|
||||
// 保存配置选项(设置默认值)
|
||||
this.options = {
|
||||
fov: options.fov ?? 75,
|
||||
enableZoom: options.enableZoom ?? true,
|
||||
enableRotate: options.enableRotate ?? true,
|
||||
sphereRadius: options.sphereRadius ?? 500,
|
||||
rotateSpeed: options.rotateSpeed,
|
||||
zoomSpeed: options.zoomSpeed,
|
||||
enableDamping: options.enableDamping,
|
||||
dampingFactor: options.dampingFactor,
|
||||
minFov: options.minFov,
|
||||
maxFov: options.maxFov,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化组件 (接口实现)
|
||||
* 创建 div 容器并初始化 720 引擎
|
||||
*/
|
||||
public init(): void {
|
||||
if (this._isInitialized) {
|
||||
console.warn('[Engine720] Engine already initialized.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._isDestroyed) {
|
||||
console.error('[Engine720] Cannot initialize destroyed engine.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建引擎配置对象
|
||||
const engineConfig: any = {
|
||||
containerId: this.containerId,
|
||||
fov: this.options.fov,
|
||||
enableZoom: this.options.enableZoom,
|
||||
enableRotate: this.options.enableRotate,
|
||||
sphereRadius: this.options.sphereRadius,
|
||||
};
|
||||
|
||||
// 仅在用户提供时传递可选参数
|
||||
if (this.options.rotateSpeed !== undefined) {
|
||||
engineConfig.rotateSpeed = this.options.rotateSpeed;
|
||||
}
|
||||
if (this.options.zoomSpeed !== undefined) {
|
||||
engineConfig.zoomSpeed = this.options.zoomSpeed;
|
||||
}
|
||||
if (this.options.enableDamping !== undefined) {
|
||||
engineConfig.enableDamping = this.options.enableDamping;
|
||||
}
|
||||
if (this.options.dampingFactor !== undefined) {
|
||||
engineConfig.dampingFactor = this.options.dampingFactor;
|
||||
}
|
||||
if (this.options.minFov !== undefined) {
|
||||
engineConfig.minFov = this.options.minFov;
|
||||
}
|
||||
if (this.options.maxFov !== undefined) {
|
||||
engineConfig.maxFov = this.options.maxFov;
|
||||
}
|
||||
|
||||
// 输出配置信息
|
||||
console.log('[Engine720] 引擎配置信息:', engineConfig);
|
||||
|
||||
// 调用引擎创建函数创建 720 引擎实例
|
||||
this.engine = createEngine720SDK(engineConfig);
|
||||
|
||||
if (!this.engine) {
|
||||
throw new Error('Failed to create 720 engine instance');
|
||||
}
|
||||
|
||||
// 标记为已初始化
|
||||
this._isInitialized = true;
|
||||
|
||||
// 订阅主题变化
|
||||
this.unsubscribeTheme = themeManager.subscribe((theme) => {
|
||||
this.setTheme(theme);
|
||||
});
|
||||
|
||||
// 应用当前主题
|
||||
this.setTheme(themeManager.getTheme());
|
||||
|
||||
// 监听 720 全景事件
|
||||
if (this.engine.events) {
|
||||
this.engine.events.on('panorama-loaded', (data: any) => {
|
||||
console.log('[Engine720] 全景加载完成:', data);
|
||||
this.registry.emit('engine720:panorama-loaded', { data });
|
||||
});
|
||||
|
||||
this.engine.events.on('panorama-load-error', (data: any) => {
|
||||
console.error('[Engine720] 全景加载失败:', data);
|
||||
this.registry.emit('engine720:load-error', { data });
|
||||
});
|
||||
|
||||
this.engine.events.on('annotation-click', (data: any) => {
|
||||
console.log('[Engine720] 标注点击:', data);
|
||||
this.registry.emit('engine720:annotation-click', { data });
|
||||
});
|
||||
|
||||
this.engine.events.on('view-angle-changed', (data: any) => {
|
||||
this.registry.emit('engine720:view-changed', { data });
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('[Engine720] Failed to initialize 720 engine:', error);
|
||||
this._isInitialized = false;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置主题 (接口实现)
|
||||
* @param _theme 全局主题配置
|
||||
*/
|
||||
public setTheme(_theme: ThemeConfig): void {
|
||||
// 720 引擎暂时不需要主题切换(全景图本身覆盖整个视口)
|
||||
if (!this._isInitialized || !this.engine) return;
|
||||
// 预留:如果底层引擎支持 setTheme,可在此调用
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置语言 (接口实现)
|
||||
*/
|
||||
public setLocales(): void {
|
||||
// 720 引擎组件暂时不需要本地化
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否已初始化
|
||||
*/
|
||||
public isInitialized(): boolean {
|
||||
return this._isInitialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载全景图
|
||||
* @param url 全景图片 URL
|
||||
* @param _options 加载选项(预留)
|
||||
*/
|
||||
public async loadPanorama(url: string, _options?: PanoramaLoadOptions): Promise<void> {
|
||||
if (!this._isInitialized || !this.engine) {
|
||||
console.error('[Engine720] Engine not initialized. Please call init() first.');
|
||||
return;
|
||||
}
|
||||
if (!url) {
|
||||
console.error('[Engine720] Panorama URL is required.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('[Engine720] 开始加载全景图:', url);
|
||||
await this.engine.loadPanorama(url);
|
||||
console.log('[Engine720] 全景图加载完成:', url);
|
||||
} catch (error) {
|
||||
console.error('[Engine720] 全景图加载失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预加载多个全景图
|
||||
* @param urls 全景图 URL 列表
|
||||
*/
|
||||
public async preloadPanoramas(urls: string[]): Promise<void> {
|
||||
if (!this._isInitialized || !this.engine) return;
|
||||
try {
|
||||
await this.engine.preloadPanoramas?.(urls);
|
||||
} catch (e) {
|
||||
console.warn('[Engine720] Failed to preload panoramas:', e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置视场角
|
||||
* @param fov 视场角值
|
||||
*/
|
||||
public setFov(fov: number): void {
|
||||
if (!this._isInitialized || !this.engine) return;
|
||||
this.engine.setFov?.(fov);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前视场角
|
||||
* @returns 当前视场角值
|
||||
*/
|
||||
public getFov(): number {
|
||||
if (!this._isInitialized || !this.engine) return 75;
|
||||
return this.engine.getFov?.() ?? 75;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置相机朝向
|
||||
* @param phi 水平角度
|
||||
* @param theta 垂直角度
|
||||
* @param animated 是否动画过渡
|
||||
*/
|
||||
public lookAt(phi: number, theta: number, animated?: boolean): void {
|
||||
if (!this._isInitialized || !this.engine) return;
|
||||
this.engine.lookAt?.(phi, theta, animated);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置视图
|
||||
*/
|
||||
public resetView(): void {
|
||||
if (!this._isInitialized || !this.engine) return;
|
||||
try {
|
||||
// 重置到默认视角
|
||||
this.engine.lookAt?.(0, 0, true);
|
||||
this.engine.setFov?.(75);
|
||||
} catch (e) {
|
||||
console.warn('[Engine720] Failed to reset view:', e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停渲染
|
||||
*/
|
||||
public pauseRendering(): void {
|
||||
if (!this._isInitialized || !this.engine) return;
|
||||
this.engine.pauseRendering?.();
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复渲染
|
||||
*/
|
||||
public resumeRendering(): void {
|
||||
if (!this._isInitialized || !this.engine) return;
|
||||
this.engine.resumeRendering?.();
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整引擎尺寸
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
*/
|
||||
public resize(width?: number, height?: number): void {
|
||||
if (!this._isInitialized || !this.engine) return;
|
||||
this.engine.resize?.(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅原始 720 引擎事件
|
||||
* @param event 事件名称
|
||||
* @param handler 事件处理函数
|
||||
*/
|
||||
public onRawEvent(event: string, handler: (...args: any[]) => void): void {
|
||||
this.engine?.events?.on(event, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订阅原始 720 引擎事件
|
||||
* @param event 事件名称
|
||||
* @param handler 事件处理函数
|
||||
*/
|
||||
public offRawEvent(event: string, handler: (...args: any[]) => void): void {
|
||||
this.engine?.events?.off(event, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁组件 (接口实现)
|
||||
* 清理资源、取消订阅、销毁引擎实例
|
||||
*/
|
||||
public destroy(): void {
|
||||
if (this._isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 取消主题订阅
|
||||
if (this.unsubscribeTheme) {
|
||||
this.unsubscribeTheme();
|
||||
this.unsubscribeTheme = null;
|
||||
}
|
||||
|
||||
// 销毁 720 引擎,释放资源
|
||||
if (this.engine) {
|
||||
try {
|
||||
this.engine.dispose?.();
|
||||
} catch (e) {
|
||||
console.warn('[Engine720] Error during dispose:', e);
|
||||
}
|
||||
this.engine = null;
|
||||
}
|
||||
|
||||
// 清理容器
|
||||
this.container.innerHTML = '';
|
||||
|
||||
// 更新状态
|
||||
this._isDestroyed = true;
|
||||
this._isInitialized = false;
|
||||
}
|
||||
}
|
||||
56
src/components/engine-720/types.ts
Normal file
56
src/components/engine-720/types.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 720 全景引擎类型定义
|
||||
* 定义 720° 全景查看引擎的配置选项和相关类型
|
||||
*/
|
||||
|
||||
/**
|
||||
* 720 全景引擎配置选项
|
||||
*/
|
||||
export interface Engine720Options {
|
||||
/** 引擎挂载的容器元素 */
|
||||
container: HTMLElement;
|
||||
/** 视场角(默认 75) */
|
||||
fov?: number;
|
||||
/** 是否启用缩放(默认 true) */
|
||||
enableZoom?: boolean;
|
||||
/** 是否启用旋转(默认 true) */
|
||||
enableRotate?: boolean;
|
||||
/** 球体半径(默认 500) */
|
||||
sphereRadius?: number;
|
||||
/** 旋转速度 */
|
||||
rotateSpeed?: number;
|
||||
/** 缩放速度 */
|
||||
zoomSpeed?: number;
|
||||
/** 是否启用阻尼(惯性) */
|
||||
enableDamping?: boolean;
|
||||
/** 阻尼因子 */
|
||||
dampingFactor?: number;
|
||||
/** 最小视场角 */
|
||||
minFov?: number;
|
||||
/** 最大视场角 */
|
||||
maxFov?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 全景加载选项
|
||||
*/
|
||||
export interface PanoramaLoadOptions {
|
||||
/** 加载超时时间(毫秒) */
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 全景标注信息
|
||||
*/
|
||||
export interface PanoramaAnnotation {
|
||||
/** 标注 ID */
|
||||
id: string;
|
||||
/** 标注位置(球面坐标 phi) */
|
||||
phi?: number;
|
||||
/** 标注位置(球面坐标 theta) */
|
||||
theta?: number;
|
||||
/** 标注文本 */
|
||||
text?: string;
|
||||
/** 自定义数据 */
|
||||
data?: any;
|
||||
}
|
||||
@@ -122,6 +122,8 @@ export class Engine implements IBimComponent {
|
||||
// 标记为已初始化
|
||||
this._isInitialized = true;
|
||||
|
||||
this.resize(this.container.clientWidth, this.container.clientHeight);
|
||||
|
||||
// 订阅主题变化
|
||||
this.unsubscribeTheme = themeManager.subscribe((theme) => {
|
||||
this.setTheme(theme);
|
||||
@@ -260,11 +262,21 @@ export class Engine implements IBimComponent {
|
||||
* 调整渲染器尺寸
|
||||
* 容器大小变化时调用,自动更新渲染器、相机投影矩阵和后处理合成器
|
||||
*/
|
||||
public resize(): void {
|
||||
public resize(width?: number, height?: number): void {
|
||||
if (!this._isInitialized || !this.engine) {
|
||||
return;
|
||||
}
|
||||
// this.engine.handleWindowResize();
|
||||
|
||||
const nextWidth = typeof width === 'number' ? Math.floor(width) : this.container.clientWidth;
|
||||
const nextHeight = typeof height === 'number' ? Math.floor(height) : this.container.clientHeight;
|
||||
|
||||
if (nextWidth <= 0 || nextHeight <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof this.engine.handleWindowResize === 'function') {
|
||||
this.engine.handleWindowResize(nextWidth, nextHeight);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 测量功能方法 ====================
|
||||
|
||||
@@ -21,12 +21,10 @@ export class SectionBoxPanel implements IBimComponent {
|
||||
private range: SectionBoxRange;
|
||||
|
||||
private hideBtn!: HTMLButtonElement;
|
||||
private reverseBtn!: HTMLButtonElement;
|
||||
private fitBtn!: HTMLButtonElement;
|
||||
private resetBtn!: HTMLButtonElement;
|
||||
|
||||
private hideLabelEl!: HTMLElement;
|
||||
private reverseLabelEl!: HTMLElement;
|
||||
private fitLabelEl!: HTMLElement;
|
||||
private resetLabelEl!: HTMLElement;
|
||||
private xLabelEl!: HTMLElement;
|
||||
@@ -134,19 +132,13 @@ export class SectionBoxPanel implements IBimComponent {
|
||||
this.options.onHideToggle?.(this.isHidden);
|
||||
}, 'hide');
|
||||
|
||||
this.reverseBtn = this.createButton('reverse', t('sectionBox.actions.reverse'), () => {
|
||||
this.isReversed = !this.isReversed;
|
||||
this.updateButtonStates();
|
||||
this.options.onReverseToggle?.(this.isReversed);
|
||||
}, 'reverse');
|
||||
|
||||
this.fitBtn = this.createButton('fit', t('sectionBox.actions.fitToModel'), () => {
|
||||
this.options.onFitToModel?.();
|
||||
}, 'fit');
|
||||
|
||||
this.resetBtn = this.createButton('reset', t('sectionBox.actions.reset'), () => this.reset(), 'reset');
|
||||
|
||||
[this.hideBtn, this.reverseBtn, this.fitBtn, this.resetBtn].forEach(btn => buttonsContainer.appendChild(btn));
|
||||
[this.hideBtn, this.fitBtn, this.resetBtn].forEach(btn => buttonsContainer.appendChild(btn));
|
||||
|
||||
const slidersContainer = document.createElement('div');
|
||||
slidersContainer.className = 'section-box-sliders';
|
||||
@@ -184,7 +176,6 @@ export class SectionBoxPanel implements IBimComponent {
|
||||
labelEl.textContent = label;
|
||||
|
||||
if (ref === 'hide') this.hideLabelEl = labelEl;
|
||||
else if (ref === 'reverse') this.reverseLabelEl = labelEl;
|
||||
else if (ref === 'fit') this.fitLabelEl = labelEl;
|
||||
else if (ref === 'reset') this.resetLabelEl = labelEl;
|
||||
|
||||
@@ -325,20 +316,17 @@ export class SectionBoxPanel implements IBimComponent {
|
||||
|
||||
private updateButtonStates(): void {
|
||||
if (this.hideBtn) this.hideBtn.classList.toggle('active', this.isHidden);
|
||||
if (this.reverseBtn) this.reverseBtn.classList.toggle('active', this.isReversed);
|
||||
}
|
||||
|
||||
public setLocales(): void {
|
||||
if (!this.hideLabelEl) return;
|
||||
this.hideLabelEl.textContent = t('sectionBox.actions.hide');
|
||||
this.reverseLabelEl.textContent = t('sectionBox.actions.reverse');
|
||||
this.fitLabelEl.textContent = t('sectionBox.actions.fitToModel');
|
||||
this.resetLabelEl.textContent = t('sectionBox.actions.reset');
|
||||
this.xLabelEl.textContent = t('sectionBox.axes.x');
|
||||
this.yLabelEl.textContent = t('sectionBox.axes.y');
|
||||
this.zLabelEl.textContent = t('sectionBox.axes.z');
|
||||
this.hideBtn.title = t('sectionBox.actions.hide');
|
||||
this.reverseBtn.title = t('sectionBox.actions.reverse');
|
||||
this.fitBtn.title = t('sectionBox.actions.fitToModel');
|
||||
this.resetBtn.title = t('sectionBox.actions.reset');
|
||||
}
|
||||
@@ -365,4 +353,4 @@ export class SectionBoxPanel implements IBimComponent {
|
||||
this.element.parentElement.removeChild(this.element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Main Entry
|
||||
export * from './bim-engine';
|
||||
export * from './bim-engine-2d';
|
||||
export * from './bim-engine-720';
|
||||
|
||||
// Types - Core
|
||||
export * from './types/component';
|
||||
@@ -8,6 +10,8 @@ export type { ThemeConfig, ThemeType } from './themes/types';
|
||||
|
||||
// Types - Components
|
||||
export type { EngineOptions, ModelLoadOptions } from './components/engine/types';
|
||||
export type { Engine2dOptions, DrawingLoadOptions, Drawing2dLayer } from './components/engine-2d/types';
|
||||
export type { Engine720Options, PanoramaLoadOptions, PanoramaAnnotation } from './components/engine-720/types';
|
||||
export type { DialogOptions, DialogPosition } from './components/dialog/index.type';
|
||||
export type { ButtonConfig, ButtonGroupOptions } from './components/button-group/index.type';
|
||||
export type { TreeOptions, TreeNodeConfig, TreeNodeCheckState, NodeClickAction } from './components/tree/types';
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { BaseManager } from '../core/base-manager';
|
||||
import { ManagerRegistry } from '../core/manager-registry';
|
||||
import { BimCollapse } from '../components/collapse/index';
|
||||
import { BimTab } from '../components/tab';
|
||||
import { t } from '../services/locale';
|
||||
|
||||
export class ComponentDetailManager extends BaseManager {
|
||||
@@ -10,7 +9,6 @@ export class ComponentDetailManager extends BaseManager {
|
||||
private currentSelection: { url: string; id: string } | null = null;
|
||||
private unsubscribeSelected: (() => void) | null = null;
|
||||
private unsubscribeDeselected: (() => void) | null = null;
|
||||
private tabInstance: BimTab | null = null;
|
||||
private propertiesData: any = null;
|
||||
|
||||
constructor(registry: ManagerRegistry) {
|
||||
@@ -98,34 +96,11 @@ export class ComponentDetailManager extends BaseManager {
|
||||
private renderTabbedContent(): void {
|
||||
if (!this.dialog) return;
|
||||
|
||||
if (this.tabInstance) {
|
||||
this.tabInstance.destroy();
|
||||
this.tabInstance = null;
|
||||
}
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.style.cssText = 'height:100%;display:flex;flex-direction:column;';
|
||||
|
||||
const propertiesPanel = this.createPropertiesPanel();
|
||||
const materialsPanel = this.createMaterialsPanel();
|
||||
|
||||
this.tabInstance = new BimTab({
|
||||
container,
|
||||
activeId: 'properties',
|
||||
tabs: [
|
||||
{
|
||||
id: 'properties',
|
||||
title: 'panel.property.tab.props',
|
||||
content: propertiesPanel
|
||||
},
|
||||
{
|
||||
id: 'materials',
|
||||
title: 'panel.property.tab.material',
|
||||
content: materialsPanel
|
||||
}
|
||||
]
|
||||
});
|
||||
this.tabInstance.init();
|
||||
container.appendChild(propertiesPanel);
|
||||
|
||||
this.dialog.setContent(container);
|
||||
}
|
||||
@@ -135,13 +110,14 @@ export class ComponentDetailManager extends BaseManager {
|
||||
container.style.cssText = 'height:100%;overflow-y:auto;';
|
||||
|
||||
const properties = this.propertiesData?.properties || [];
|
||||
const reversedProperties = [...properties].reverse();
|
||||
|
||||
if (properties.length === 0) {
|
||||
if (reversedProperties.length === 0) {
|
||||
container.innerHTML = '<div style="padding:20px;text-align:center;color:var(--bim-text-secondary,#999);">无属性数据</div>';
|
||||
return container;
|
||||
}
|
||||
|
||||
const collapseItems = properties.map((category: any, index: number) => ({
|
||||
const collapseItems = reversedProperties.map((category: any, index: number) => ({
|
||||
id: `category-${index}`,
|
||||
title: category.name || `分类 ${index + 1}`,
|
||||
content: this.createCategoryContent(category.children || [])
|
||||
@@ -169,45 +145,6 @@ export class ComponentDetailManager extends BaseManager {
|
||||
return container;
|
||||
}
|
||||
|
||||
private createMaterialsPanel(): HTMLElement {
|
||||
const container = document.createElement('div');
|
||||
container.style.cssText = 'height:100%;overflow-y:auto;';
|
||||
|
||||
const materials = this.propertiesData?.materials || [];
|
||||
|
||||
if (materials.length === 0) {
|
||||
container.innerHTML = '<div style="padding:20px;text-align:center;color:var(--bim-text-secondary,#999);">无材质数据</div>';
|
||||
return container;
|
||||
}
|
||||
|
||||
const collapseItems = materials.map((material: any, index: number) => ({
|
||||
id: `material-${index}`,
|
||||
title: material.name || `材质 ${index + 1}`,
|
||||
content: this.createCategoryContent(material.children || material.properties || [])
|
||||
}));
|
||||
|
||||
new BimCollapse({
|
||||
container,
|
||||
accordion: false,
|
||||
ghost: true,
|
||||
activeIds: collapseItems.length > 0 ? [collapseItems[0].id] : [],
|
||||
items: collapseItems
|
||||
});
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
#${this.dialogId} .bim-collapse-header {
|
||||
background-color: var(--bim-component-bg-hover) !important;
|
||||
}
|
||||
#${this.dialogId} .bim-collapse-header:hover {
|
||||
background-color: var(--bim-component-bg-active) !important;
|
||||
}
|
||||
`;
|
||||
container.appendChild(style);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private createCategoryContent(items: any[]): HTMLElement {
|
||||
const container = document.createElement('div');
|
||||
|
||||
@@ -256,10 +193,6 @@ export class ComponentDetailManager extends BaseManager {
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
if (this.tabInstance) {
|
||||
this.tabInstance.destroy();
|
||||
this.tabInstance = null;
|
||||
}
|
||||
if (this.dialog) {
|
||||
this.dialog.destroy();
|
||||
this.dialog = null;
|
||||
|
||||
133
src/managers/engine-2d-manager.ts
Normal file
133
src/managers/engine-2d-manager.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* 2D 图纸引擎管理器
|
||||
* 负责管理 2D 图纸渲染引擎的初始化、图纸加载和生命周期
|
||||
*
|
||||
* 设计原则:
|
||||
* - 镜像 EngineManager 的设计模式
|
||||
* - Engine2dManager 只暴露面向外部用户的公共 API
|
||||
* - 内部管理器通过 getEngine2dComponent() 直接访问 Engine2d 组件
|
||||
*/
|
||||
import { Engine2d, type Engine2dOptions, type DrawingLoadOptions, type Drawing2dLayer } from '../components/engine-2d';
|
||||
import { BaseManager } from '../core/base-manager';
|
||||
import { ManagerRegistry } from '../core/manager-registry';
|
||||
|
||||
/**
|
||||
* 2D 图纸引擎管理器
|
||||
* 封装底层 2D 引擎,提供图纸加载、图层控制等公共 API
|
||||
*/
|
||||
export class Engine2dManager extends BaseManager {
|
||||
/** 容器元素 */
|
||||
private container: HTMLElement;
|
||||
/** 2D 引擎组件实例 */
|
||||
private engineInstance: Engine2d | null = null;
|
||||
|
||||
constructor(container: HTMLElement, registry: ManagerRegistry) {
|
||||
super(registry);
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Engine2d 组件实例
|
||||
* 内部管理器通过此方法直接访问 Engine2d 组件的全部能力
|
||||
* @returns Engine2d 组件实例,未初始化时返回 null
|
||||
*/
|
||||
public getEngine2dComponent(): Engine2d | null {
|
||||
return this.engineInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化 2D 引擎
|
||||
* @param options 引擎配置选项
|
||||
* @returns 是否初始化成功
|
||||
*/
|
||||
public initialize(options?: Omit<Engine2dOptions, 'container'>): boolean {
|
||||
if (this.engineInstance && this.engineInstance.isInitialized()) {
|
||||
console.warn('[Engine2dManager] 2D Engine already initialized. Destroying old instance...');
|
||||
this.engineInstance.destroy();
|
||||
this.engineInstance = null;
|
||||
}
|
||||
|
||||
try {
|
||||
this.engineInstance = new Engine2d({
|
||||
container: this.container,
|
||||
...options,
|
||||
}, this.registry);
|
||||
|
||||
this.engineInstance.init();
|
||||
|
||||
return this.engineInstance.isInitialized();
|
||||
} catch (error) {
|
||||
console.error('[Engine2dManager] Failed to initialize 2D engine:', error);
|
||||
this.engineInstance = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查引擎是否已初始化
|
||||
* @returns 是否已初始化
|
||||
*/
|
||||
public isInitialized(): boolean {
|
||||
return this.engineInstance !== null && this.engineInstance.isInitialized();
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载 2D 图纸
|
||||
* @param url 图纸文件 URL
|
||||
* @param options 加载选项
|
||||
*/
|
||||
public async loadDrawing(url: string, options?: DrawingLoadOptions): Promise<void> {
|
||||
if (!this.engineInstance) {
|
||||
console.warn('[Engine2dManager] 2D Engine not initialized.');
|
||||
return;
|
||||
}
|
||||
await this.engineInstance.loadDrawing(url, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有图层
|
||||
* @returns 图层列表
|
||||
*/
|
||||
public getLayers(): Drawing2dLayer[] {
|
||||
if (!this.engineInstance) {
|
||||
return [];
|
||||
}
|
||||
return this.engineInstance.getLayers();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置图层可见性
|
||||
* @param name 图层名称
|
||||
* @param visible 是否可见
|
||||
*/
|
||||
public setLayerVisible(name: string, visible: boolean): void {
|
||||
if (!this.engineInstance) {
|
||||
console.warn('[Engine2dManager] 2D Engine not initialized.');
|
||||
return;
|
||||
}
|
||||
this.engineInstance.setLayerVisible(name, visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置视图
|
||||
*/
|
||||
public resetView(): void {
|
||||
this.engineInstance?.resetView();
|
||||
}
|
||||
|
||||
/**
|
||||
* 适应视图
|
||||
*/
|
||||
public fitToView(): void {
|
||||
this.engineInstance?.fitToView();
|
||||
}
|
||||
|
||||
/** 销毁 2D 引擎管理器 */
|
||||
public destroy(): void {
|
||||
if (this.engineInstance) {
|
||||
this.engineInstance.destroy();
|
||||
this.engineInstance = null;
|
||||
}
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
130
src/managers/engine-720-manager.ts
Normal file
130
src/managers/engine-720-manager.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* 720 全景引擎管理器
|
||||
* 负责管理 720° 全景渲染引擎的初始化、全景加载和生命周期
|
||||
*
|
||||
* 设计原则:
|
||||
* - 镜像 Engine2dManager 的设计模式
|
||||
* - Engine720Manager 只暴露面向外部用户的公共 API
|
||||
* - 内部管理器通过 getEngine720Component() 直接访问 Engine720 组件
|
||||
*/
|
||||
import { Engine720, type Engine720Options, type PanoramaLoadOptions } from '../components/engine-720';
|
||||
import { BaseManager } from '../core/base-manager';
|
||||
import { ManagerRegistry } from '../core/manager-registry';
|
||||
|
||||
/**
|
||||
* 720 全景引擎管理器
|
||||
* 封装底层 720 引擎,提供全景加载、视角控制等公共 API
|
||||
*/
|
||||
export class Engine720Manager extends BaseManager {
|
||||
/** 容器元素 */
|
||||
private container: HTMLElement;
|
||||
/** 720 引擎组件实例 */
|
||||
private engineInstance: Engine720 | null = null;
|
||||
|
||||
constructor(container: HTMLElement, registry: ManagerRegistry) {
|
||||
super(registry);
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Engine720 组件实例
|
||||
* 内部管理器通过此方法直接访问 Engine720 组件的全部能力
|
||||
* @returns Engine720 组件实例,未初始化时返回 null
|
||||
*/
|
||||
public getEngine720Component(): Engine720 | null {
|
||||
return this.engineInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化 720 引擎
|
||||
* @param options 引擎配置选项
|
||||
* @returns 是否初始化成功
|
||||
*/
|
||||
public initialize(options?: Omit<Engine720Options, 'container'>): boolean {
|
||||
if (this.engineInstance && this.engineInstance.isInitialized()) {
|
||||
console.warn('[Engine720Manager] 720 Engine already initialized. Destroying old instance...');
|
||||
this.engineInstance.destroy();
|
||||
this.engineInstance = null;
|
||||
}
|
||||
|
||||
try {
|
||||
this.engineInstance = new Engine720({
|
||||
container: this.container,
|
||||
...options,
|
||||
}, this.registry);
|
||||
|
||||
this.engineInstance.init();
|
||||
|
||||
return this.engineInstance.isInitialized();
|
||||
} catch (error) {
|
||||
console.error('[Engine720Manager] Failed to initialize 720 engine:', error);
|
||||
this.engineInstance = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查引擎是否已初始化
|
||||
* @returns 是否已初始化
|
||||
*/
|
||||
public isInitialized(): boolean {
|
||||
return this.engineInstance !== null && this.engineInstance.isInitialized();
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载全景图
|
||||
* @param url 全景图片 URL
|
||||
* @param options 加载选项
|
||||
*/
|
||||
public async loadPanorama(url: string, options?: PanoramaLoadOptions): Promise<void> {
|
||||
if (!this.engineInstance) {
|
||||
console.warn('[Engine720Manager] 720 Engine not initialized.');
|
||||
return;
|
||||
}
|
||||
await this.engineInstance.loadPanorama(url, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 预加载多个全景图
|
||||
* @param urls 全景图 URL 列表
|
||||
*/
|
||||
public async preloadPanoramas(urls: string[]): Promise<void> {
|
||||
if (!this.engineInstance) {
|
||||
console.warn('[Engine720Manager] 720 Engine not initialized.');
|
||||
return;
|
||||
}
|
||||
await this.engineInstance.preloadPanoramas(urls);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置视场角
|
||||
* @param fov 视场角值
|
||||
*/
|
||||
public setFov(fov: number): void {
|
||||
this.engineInstance?.setFov(fov);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前视场角
|
||||
* @returns 当前视场角值
|
||||
*/
|
||||
public getFov(): number {
|
||||
return this.engineInstance?.getFov() ?? 75;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置视图
|
||||
*/
|
||||
public resetView(): void {
|
||||
this.engineInstance?.resetView();
|
||||
}
|
||||
|
||||
/** 销毁 720 引擎管理器 */
|
||||
public destroy(): void {
|
||||
if (this.engineInstance) {
|
||||
this.engineInstance.destroy();
|
||||
this.engineInstance = null;
|
||||
}
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
@@ -70,8 +70,7 @@ export class SectionAxisDialogManager extends BaseDialogManager {
|
||||
}
|
||||
},
|
||||
onReverse: () => {
|
||||
// 反向功能:第三方引擎无 API,仅输出日志
|
||||
console.log('[SectionAxisDialogManager] 反向剖切(暂不支持)');
|
||||
this.engineComponent?.reverseSection();
|
||||
},
|
||||
onAxisChange: (axis) => {
|
||||
console.log('[SectionAxisDialogManager] 切换轴向:', axis);
|
||||
@@ -85,13 +84,13 @@ export class SectionAxisDialogManager extends BaseDialogManager {
|
||||
/** 对话框创建后的回调,自适应高度 */
|
||||
protected onDialogCreated(): void {
|
||||
this.dialog?.fitHeight(false);
|
||||
|
||||
|
||||
// 检查 Engine 是否已初始化
|
||||
if (!this.engineComponent) {
|
||||
console.error('[SectionAxisDialogManager] Engine not initialized. Call initEngine() first.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 自动激活默认轴向剖切(X轴)
|
||||
this.engineComponent.activeSection('x');
|
||||
}
|
||||
@@ -104,7 +103,7 @@ export class SectionAxisDialogManager extends BaseDialogManager {
|
||||
/** 销毁前的清理,销毁面板实例 */
|
||||
protected onBeforeDestroy(): void {
|
||||
this.engineComponent?.deactivateSection();
|
||||
|
||||
|
||||
if (this.panel) {
|
||||
this.panel.destroy();
|
||||
this.panel = null;
|
||||
|
||||
@@ -61,11 +61,6 @@ export class SectionBoxDialogManager extends BaseDialogManager {
|
||||
this.engineComponent?.recoverSection();
|
||||
}
|
||||
},
|
||||
onReverseToggle: (isReversed) => {
|
||||
console.log('[SectionBoxDialogManager] 反向切换:', isReversed);
|
||||
// 底层 reverse() 为“切换一次”,这里不使用 isReversed 作为入参,只要用户点击就触发。
|
||||
this.engineComponent?.reverseSection();
|
||||
},
|
||||
onFitToModel: () => {
|
||||
// 对接底层 scaleBox():缩放剖切盒到场景整体包围盒
|
||||
this.engineComponent?.scaleSectionBox();
|
||||
|
||||
@@ -28,7 +28,7 @@ export class SectionPlaneDialogManager extends BaseDialogManager {
|
||||
protected get dialogHeight(): number { return 120; }
|
||||
|
||||
/** 初始化 */
|
||||
public init(): void {}
|
||||
public init(): void { }
|
||||
|
||||
/**
|
||||
* 获取对话框位置
|
||||
@@ -62,10 +62,12 @@ export class SectionPlaneDialogManager extends BaseDialogManager {
|
||||
}
|
||||
},
|
||||
onReverse: () => {
|
||||
console.log('[SectionPlaneDialogManager] 反向 (not supported in new API)');
|
||||
this.engineComponent?.reverseSection();
|
||||
},
|
||||
onReset: () => {
|
||||
console.log('[SectionPlaneDialogManager] 重置 (not supported in new API)');
|
||||
// 先关闭剖切再开启
|
||||
this.engineComponent?.deactivateSection();
|
||||
this.engineComponent?.activeSection('face');
|
||||
}
|
||||
});
|
||||
this.panel.init();
|
||||
|
||||
@@ -47,5 +47,16 @@ export interface EngineEvents {
|
||||
'aiChat:question-answered': { questionId: string; optionId: string; customAnswer?: string };
|
||||
'aiChat:new-chat': {};
|
||||
'aiChat:history-opened': {};
|
||||
|
||||
// 2D 引擎事件
|
||||
'engine2d:drawing-loaded': { url: string };
|
||||
'engine2d:entity-clicked': { data: any };
|
||||
'engine2d:layer-changed': { data: any };
|
||||
|
||||
// 720 全景引擎事件
|
||||
'engine720:panorama-loaded': { data: any };
|
||||
'engine720:load-error': { data: any };
|
||||
'engine720:annotation-click': { data: any };
|
||||
'engine720:view-changed': { data: any };
|
||||
'aiChat:settings-opened': {};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user