提交代码

This commit is contained in:
yuding
2026-03-16 16:13:36 +08:00
parent 507112fcf9
commit dd4600bb5b
35 changed files with 31811 additions and 9696 deletions

151
src/bim-engine-2d.ts Normal file
View 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
View 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();
}
}

View File

@@ -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;
}

View File

@@ -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();
}

View 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;
// }
}

View 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;
}

View 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;
}
}

View 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;
}

View File

@@ -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);
}
}
// ==================== 测量功能方法 ====================

View File

@@ -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);
}
}
}
}

View File

@@ -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';

View File

@@ -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;

View 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();
}
}

View 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();
}
}

View File

@@ -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;

View File

@@ -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();

View File

@@ -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();

View File

@@ -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': {};
}