refactor: slim down EngineManager from 861 to 290 lines by removing passthrough proxy pattern
- EngineManager now only exposes public SDK API (initialize, loadModel, pause/resumeRendering, getEngineComponent, destroy) - Internal managers access Engine component directly via this.engineComponent getter on BaseManager - Non-manager components use registry.engine3d.getEngineComponent() for direct Engine access - Replaced getEngine() with onRawEvent()/offRawEvent() for raw engine event access - Migrated 62 call sites across 13 files (9 managers, 1 panel, 3 toolbar buttons) - Updated all architecture docs, API docs, and README to reflect new patterns
This commit is contained in:
1260
docs/API调用链.md
1260
docs/API调用链.md
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,21 @@
|
||||
|
||||
本文档为 BIM Engine SDK 的完整 API 参考,适合大模型阅读和理解引擎的功能结构。
|
||||
|
||||
> 2026-03 更新说明(SDK 接入层)
|
||||
>
|
||||
> 本文档主体描述的是底层 3D 内核与模块能力。SDK 接入层在本次重构后有以下关键变化:
|
||||
>
|
||||
> 1. `EngineManager` 已收敛为少量公共 API,不再作为全量透传层。
|
||||
> 2. SDK 内部 Manager 统一通过 `BaseManager.engineComponent` 直接访问 `Engine` 组件能力。
|
||||
> 3. 非 Manager 组件通过 `registry.engine3d?.getEngineComponent()?.xxx()` 访问 `Engine` 组件。
|
||||
> 4. `Engine.getEngine()` 已移除,原始事件访问改为 `onRawEvent()/offRawEvent()`。
|
||||
>
|
||||
> 若你在接入层实现新功能,请优先参考:
|
||||
>
|
||||
> - `docs/架构设计.md`
|
||||
> - `docs/API调用链.md`
|
||||
> - `docs/引擎API对接.md`
|
||||
|
||||
## 目录
|
||||
|
||||
1. [核心引擎类](#1-核心引擎类)
|
||||
|
||||
@@ -2,39 +2,45 @@
|
||||
|
||||
## 模块概述
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **模块名** | managers |
|
||||
| **职责** | 处理业务逻辑,协调组件交互,管理组件生命周期 |
|
||||
| **公开 API** | 15 个 Manager 类 |
|
||||
| **状态** | ✅ 稳定 |
|
||||
`src/managers/` 负责业务编排与生命周期管理,不直接承载底层 3D 细节。
|
||||
|
||||
2026-03 更新后,管理器层的核心变化:
|
||||
|
||||
- `EngineManager` 从“全量透传”改为“对外公共 API + 初始化编排”
|
||||
- `BaseManager` 新增 `engineComponent` getter,内部管理器直接调用 `Engine` 组件
|
||||
- Manager 层统一收敛为“编排者”,而不是“透传中间层”
|
||||
|
||||
---
|
||||
|
||||
## 代码地图
|
||||
|
||||
```
|
||||
```text
|
||||
src/managers/
|
||||
├── engine-manager.ts # 3D 引擎管理器
|
||||
├── toolbar-manager.ts # 工具栏管理器
|
||||
├── dialog-manager.ts # 对话框管理器
|
||||
├── button-group-manager.ts # 按钮组管理器
|
||||
├── right-key-manager.ts # 右键菜单管理器
|
||||
├── component-detail-manager.ts # 构件详情弹窗管理器
|
||||
├── construct-tree-manager-btn.ts # 构件树管理器
|
||||
├── walk-control-manager.ts # 漫游控制管理器
|
||||
├── measure-dialog-manager.ts # 测量对话框管理器
|
||||
├── section-plane-dialog-manager.ts # 平面剖切对话框管理器
|
||||
├── section-box-dialog-manager.ts # 剖切盒对话框管理器
|
||||
├── section-axis-dialog-manager.ts # 轴向剖切对话框管理器
|
||||
├── map-dialog-manager.ts # 地图对话框管理器
|
||||
├── walk-path-dialog-manager.ts # 漫游路径对话框管理器
|
||||
└── walk-plan-view-dialog-manager.ts # 漫游平面图对话框管理器
|
||||
├── engine-manager.ts
|
||||
├── toolbar-manager.ts
|
||||
├── dialog-manager.ts
|
||||
├── button-group-manager.ts
|
||||
├── right-key-manager.ts
|
||||
├── component-detail-manager.ts
|
||||
├── construct-tree-manager-btn.ts
|
||||
├── walk-control-manager.ts
|
||||
├── measure-dialog-manager.ts
|
||||
├── section-plane-dialog-manager.ts
|
||||
├── section-axis-dialog-manager.ts
|
||||
├── section-box-dialog-manager.ts
|
||||
├── map-dialog-manager.ts
|
||||
├── walk-path-dialog-manager.ts
|
||||
├── walk-plan-view-dialog-manager.ts
|
||||
├── setting-dialog-manager.ts
|
||||
├── engine-info-dialog-manager.ts
|
||||
└── ai-chat-manager.ts
|
||||
```
|
||||
|
||||
## 管理器分类
|
||||
---
|
||||
|
||||
### 按继承关系
|
||||
## 继承关系
|
||||
|
||||
```
|
||||
```text
|
||||
BaseManager
|
||||
├── EngineManager
|
||||
├── ToolbarManager
|
||||
@@ -43,9 +49,12 @@ BaseManager
|
||||
├── RightKeyManager
|
||||
├── ComponentDetailManager
|
||||
├── ConstructTreeManagerBtn
|
||||
└── WalkControlManager
|
||||
├── WalkControlManager
|
||||
├── SettingDialogManager
|
||||
├── EngineInfoDialogManager
|
||||
└── AiChatManager
|
||||
|
||||
BaseDialogManager (继承自 BaseManager)
|
||||
BaseDialogManager (extends BaseManager)
|
||||
├── MeasureDialogManager
|
||||
├── SectionPlaneDialogManager
|
||||
├── SectionAxisDialogManager
|
||||
@@ -55,435 +64,100 @@ BaseDialogManager (继承自 BaseManager)
|
||||
└── WalkPlanViewDialogManager
|
||||
```
|
||||
|
||||
### 按功能分类
|
||||
---
|
||||
|
||||
| 类别 | 管理器 | 职责 |
|
||||
|------|--------|------|
|
||||
| **核心** | EngineManager | 3D 引擎管理 |
|
||||
| **UI 容器** | ToolbarManager, DialogManager, ButtonGroupManager | 容器组件管理 |
|
||||
| **交互** | RightKeyManager, ComponentDetailManager, ConstructTreeManagerBtn | 用户交互 |
|
||||
| **3D 工具** | MeasureDialogManager, SectionPlaneDialogManager, SectionAxisDialogManager, SectionBoxDialogManager | 3D 操作工具 |
|
||||
| **漫游** | WalkControlManager, WalkPathDialogManager, WalkPlanViewDialogManager, MapDialogManager | 漫游功能 |
|
||||
## BaseManager 统一能力
|
||||
|
||||
文件:`src/core/base-manager.ts`
|
||||
|
||||
```ts
|
||||
protected get engineComponent(): Engine | null;
|
||||
```
|
||||
|
||||
用途:
|
||||
|
||||
- 内部 manager 直接调用 `Engine` 组件能力
|
||||
- 替代历史写法 `this.registry.engine3d?.xxx()`
|
||||
|
||||
示例:
|
||||
|
||||
```ts
|
||||
this.engineComponent?.activeSection('box');
|
||||
this.engineComponent?.setWalkSpeed(speed);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## EngineManager
|
||||
## EngineManager(最新职责)
|
||||
|
||||
### 概述
|
||||
文件:`src/managers/engine-manager.ts`
|
||||
|
||||
管理 3D 引擎的初始化、模型加载和测量功能。
|
||||
### 职责
|
||||
|
||||
### API
|
||||
1. 创建/销毁 `Engine` 组件
|
||||
2. 创建/销毁 `RightKeyManager`
|
||||
3. 组装右键菜单逻辑
|
||||
4. 暴露外部公共 API
|
||||
|
||||
```typescript
|
||||
### 公共 API
|
||||
|
||||
```ts
|
||||
class EngineManager extends BaseManager {
|
||||
initialize(options?: EngineOptions): void;
|
||||
rightKey: RightKeyManager | null;
|
||||
|
||||
getEngineComponent(): Engine | null;
|
||||
initialize(options?: Omit<EngineOptions, 'container'>): boolean;
|
||||
isInitialized(): boolean;
|
||||
loadModel(url: string, options?: ModelLoadOptions): void;
|
||||
getEngine(): any;
|
||||
CameraGoHome(): void;
|
||||
activateMeasure(mode: MeasureMode): void;
|
||||
deactivateMeasure(): void;
|
||||
getCurrentMeasureType(): MeasureMode | null;
|
||||
loadModel(urls: string[], options?: ModelLoadOptions): void;
|
||||
pauseRendering(): void;
|
||||
resumeRendering(): void;
|
||||
destroy(): void;
|
||||
}
|
||||
```
|
||||
|
||||
### 使用示例
|
||||
说明:
|
||||
|
||||
```typescript
|
||||
const engine = new EngineManager(container);
|
||||
engine.initialize();
|
||||
engine.loadModel('model.gltf', { autoFit: true });
|
||||
engine.activateMeasure('distance');
|
||||
- 不再提供 `activateMeasure()/activeSection()/setWalkSpeed()` 等透传 API
|
||||
- 这些能力由内部 manager 直接调用 `engineComponent`
|
||||
|
||||
---
|
||||
|
||||
## 各管理器职责分层
|
||||
|
||||
| 类别 | 代表 Manager | 主要职责 |
|
||||
|---|---|---|
|
||||
| 核心入口 | `EngineManager` | 引擎生命周期与外部 API |
|
||||
| UI 容器 | `ToolbarManager`, `DialogManager`, `ButtonGroupManager` | UI 容器与通用交互 |
|
||||
| 业务编排 | `MeasureDialogManager`, `Section*DialogManager`, `WalkControlManager`, `SettingDialogManager` | 对话框/面板回调与引擎能力编排 |
|
||||
| 数据/交互 | `ConstructTreeManagerBtn`, `ComponentDetailManager`, `RightKeyManager` | 构件树、属性、右键菜单 |
|
||||
|
||||
---
|
||||
|
||||
## 推荐调用方式
|
||||
|
||||
### Manager 内部
|
||||
|
||||
```ts
|
||||
// 推荐
|
||||
this.engineComponent?.setSectionBoxRange(range);
|
||||
|
||||
// 不推荐
|
||||
this.registry.engine3d?.setSectionBoxRange(range);
|
||||
```
|
||||
|
||||
### 非 Manager 组件
|
||||
|
||||
```ts
|
||||
registry.engine3d?.getEngineComponent()?.toggleMiniMap();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ToolbarManager
|
||||
## 迁移约束(团队规范)
|
||||
|
||||
### 概述
|
||||
新增功能时遵循:
|
||||
|
||||
管理底部工具栏的按钮配置、显示控制、主题管理。
|
||||
|
||||
### API
|
||||
|
||||
```typescript
|
||||
class ToolbarManager extends BaseManager {
|
||||
addGroup(groupId: string, beforeGroupId?: string): void;
|
||||
addButton(config: ButtonConfig): void;
|
||||
setBtnActive(id: string, active?: boolean): void;
|
||||
setButtonVisibility(id: string, visible: boolean): void;
|
||||
setShowLabel(show: boolean): void;
|
||||
updateTheme(theme: ThemeConfig): void;
|
||||
setBackgroundColor(color: string): void;
|
||||
setColors(colors: ButtonGroupColors): void;
|
||||
show(): void;
|
||||
hide(): void;
|
||||
setVisible(visible: boolean): void;
|
||||
refresh(): void;
|
||||
destroy(): void;
|
||||
}
|
||||
```
|
||||
|
||||
### 使用示例
|
||||
|
||||
```typescript
|
||||
const toolbar = new ToolbarManager(container);
|
||||
|
||||
toolbar.addGroup('custom-group');
|
||||
toolbar.addButton({
|
||||
id: 'custom-btn',
|
||||
groupId: 'custom-group',
|
||||
type: 'button',
|
||||
label: 'Custom',
|
||||
icon: '<svg>...</svg>',
|
||||
onClick: () => console.log('Clicked')
|
||||
});
|
||||
|
||||
toolbar.setBtnActive('custom-btn', true);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DialogManager
|
||||
|
||||
### 概述
|
||||
|
||||
创建和管理通用对话框实例。
|
||||
|
||||
### API
|
||||
|
||||
```typescript
|
||||
class DialogManager extends BaseManager {
|
||||
create(options: DialogOptions): BimDialog;
|
||||
showInfoDialog(): void;
|
||||
updateTheme(theme: ThemeConfig): void;
|
||||
destroy(): void;
|
||||
}
|
||||
```
|
||||
|
||||
### 使用示例
|
||||
|
||||
```typescript
|
||||
const dialogManager = new DialogManager(container);
|
||||
|
||||
const dialog = dialogManager.create({
|
||||
title: 'My Dialog',
|
||||
content: element,
|
||||
width: 400,
|
||||
onClose: () => console.log('Closed')
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## MeasureDialogManager
|
||||
|
||||
### 概述
|
||||
|
||||
管理测量工具的对话框和测量面板。
|
||||
|
||||
### API
|
||||
|
||||
```typescript
|
||||
class MeasureDialogManager extends BaseDialogManager {
|
||||
getActiveMode(): MeasureMode;
|
||||
switchMode(mode: MeasureMode): void;
|
||||
setMeasureResult(result: MeasureResult | null): void;
|
||||
getConfig(): MeasureConfig;
|
||||
setConfig(partial: Partial<MeasureConfig>, persist?: boolean): void;
|
||||
clearAll(): void;
|
||||
openSettings(): void;
|
||||
show(): void;
|
||||
hide(): void;
|
||||
toggle(): void;
|
||||
}
|
||||
```
|
||||
|
||||
### 测量模式
|
||||
|
||||
| 模式 | 说明 |
|
||||
|------|------|
|
||||
| `distance` | 距离测量 |
|
||||
| `minDistance` | 最小距离 |
|
||||
| `angle` | 角度测量 |
|
||||
| `elevation` | 标高测量 |
|
||||
| `volume` | 体积测量 |
|
||||
| `laserDistance` | 激光测距 |
|
||||
| `slope` | 坡度测量 |
|
||||
| `spaceVolume` | 空间体积 |
|
||||
|
||||
### 使用示例
|
||||
|
||||
```typescript
|
||||
const measure = new MeasureDialogManager();
|
||||
measure.show();
|
||||
measure.switchMode('distance');
|
||||
|
||||
const config = measure.getConfig();
|
||||
measure.setConfig({ unit: 'cm', precision: 1 });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SectionPlaneDialogManager
|
||||
|
||||
### 概述
|
||||
|
||||
管理平面剖切工具对话框。
|
||||
|
||||
### API
|
||||
|
||||
```typescript
|
||||
class SectionPlaneDialogManager extends BaseDialogManager {
|
||||
show(): void;
|
||||
hide(): void;
|
||||
toggle(): void;
|
||||
isOpen(): boolean;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SectionAxisDialogManager
|
||||
|
||||
### 概述
|
||||
|
||||
管理轴向剖切工具对话框。
|
||||
|
||||
### API
|
||||
|
||||
```typescript
|
||||
class SectionAxisDialogManager extends BaseDialogManager {
|
||||
getHiddenState(): boolean;
|
||||
setHiddenState(isHidden: boolean): void;
|
||||
getActiveAxis(): 'x' | 'y' | 'z';
|
||||
setActiveAxis(axis: 'x' | 'y' | 'z'): void;
|
||||
show(): void;
|
||||
hide(): void;
|
||||
toggle(): void;
|
||||
}
|
||||
```
|
||||
|
||||
### 使用示例
|
||||
|
||||
```typescript
|
||||
const sectionAxis = new SectionAxisDialogManager();
|
||||
sectionAxis.show();
|
||||
sectionAxis.setActiveAxis('y');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SectionBoxDialogManager
|
||||
|
||||
### 概述
|
||||
|
||||
管理剖切盒工具对话框。
|
||||
|
||||
### API
|
||||
|
||||
```typescript
|
||||
class SectionBoxDialogManager extends BaseDialogManager {
|
||||
getHiddenState(): boolean;
|
||||
setHiddenState(isHidden: boolean): void;
|
||||
getReversedState(): boolean;
|
||||
setReversedState(isReversed: boolean): void;
|
||||
getRange(): SectionBoxRange;
|
||||
setRange(range: Partial<SectionBoxRange>): void;
|
||||
show(): void;
|
||||
hide(): void;
|
||||
toggle(): void;
|
||||
}
|
||||
|
||||
interface SectionBoxRange {
|
||||
minX: number;
|
||||
maxX: number;
|
||||
minY: number;
|
||||
maxY: number;
|
||||
minZ: number;
|
||||
maxZ: number;
|
||||
}
|
||||
```
|
||||
|
||||
### 使用示例
|
||||
|
||||
```typescript
|
||||
const sectionBox = new SectionBoxDialogManager();
|
||||
sectionBox.show();
|
||||
|
||||
const range = sectionBox.getRange();
|
||||
sectionBox.setRange({ minX: 0, maxX: 100 });
|
||||
sectionBox.setReversedState(true);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## WalkControlManager
|
||||
|
||||
### 概述
|
||||
|
||||
管理漫游控制面板和相关交互。
|
||||
|
||||
### API
|
||||
|
||||
```typescript
|
||||
class WalkControlManager extends BaseManager {
|
||||
init(): void;
|
||||
show(): void;
|
||||
hide(): void;
|
||||
destroy(): void;
|
||||
}
|
||||
```
|
||||
|
||||
### 事件
|
||||
|
||||
| 事件 | 说明 |
|
||||
|------|------|
|
||||
| `walk:plan-view-toggle` | 平面图切换 |
|
||||
| `walk:path-mode-toggle` | 路径模式切换 |
|
||||
| `walk:walk-mode-toggle` | 漫游模式切换 |
|
||||
| `walk:speed-change` | 速度变化 |
|
||||
| `walk:gravity-toggle` | 重力开关 |
|
||||
| `walk:collision-toggle` | 碰撞开关 |
|
||||
|
||||
---
|
||||
|
||||
## ComponentDetailManager
|
||||
|
||||
### 概述
|
||||
|
||||
管理构件详情弹窗(属性展示)的显示。
|
||||
|
||||
### API
|
||||
|
||||
```typescript
|
||||
class ComponentDetailManager extends BaseManager {
|
||||
init(): void;
|
||||
show(): void;
|
||||
isOpen(): boolean;
|
||||
hide(): void;
|
||||
destroy(): void;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ConstructTreeManagerBtn
|
||||
|
||||
### 概述
|
||||
|
||||
管理构件树按钮和对话框。
|
||||
|
||||
### API
|
||||
|
||||
```typescript
|
||||
class ConstructTreeManagerBtn extends BaseManager {
|
||||
openConstructTreeDialog(): void;
|
||||
addGroup(groupId: string, beforeGroupId?: string): void;
|
||||
addButton(config: ButtonConfig): void;
|
||||
setButtonVisibility(id: string, visible: boolean): void;
|
||||
setShowLabel(show: boolean): void;
|
||||
setVisible(visible: boolean): void;
|
||||
setBackgroundColor(color: string): void;
|
||||
setColors(colors: ButtonGroupColors): void;
|
||||
refresh(): void;
|
||||
destroy(): void;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## RightKeyManager
|
||||
|
||||
### 概述
|
||||
|
||||
管理右键上下文菜单。
|
||||
|
||||
### API
|
||||
|
||||
```typescript
|
||||
class RightKeyManager extends BaseManager {
|
||||
registerHandler(handler: ContextMenuHandler): void;
|
||||
showMenu(x: number, y: number, items: MenuItemConfig[], groupOrder?: string[]): void;
|
||||
hide(): void;
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
type ContextMenuHandler = (e: MouseEvent) => MenuItemConfig[];
|
||||
```
|
||||
|
||||
### 使用示例
|
||||
|
||||
```typescript
|
||||
const rightKey = new RightKeyManager(container);
|
||||
|
||||
rightKey.registerHandler((e) => [
|
||||
{ id: 'view', label: 'View', onClick: () => {} },
|
||||
{ id: 'delete', label: 'Delete', onClick: () => {} }
|
||||
]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ButtonGroupManager
|
||||
|
||||
### 概述
|
||||
|
||||
创建和管理多个按钮组实例。
|
||||
|
||||
### API
|
||||
|
||||
```typescript
|
||||
class ButtonGroupManager extends BaseManager {
|
||||
create(id: string, options: ButtonGroupOptions): BimButtonGroup;
|
||||
get(id: string): BimButtonGroup | undefined;
|
||||
updateTheme(theme: ThemeConfig): void;
|
||||
destroy(): void;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 交互流程
|
||||
|
||||
### 工具栏按钮触发测量
|
||||
|
||||
```
|
||||
ToolbarManager (按钮点击)
|
||||
↓
|
||||
MeasureDialogManager.show()
|
||||
↓
|
||||
onModeChange 事件
|
||||
↓
|
||||
EngineManager.activateMeasure(mode)
|
||||
↓
|
||||
3D 引擎进入测量模式
|
||||
```
|
||||
|
||||
### 漫游模式激活
|
||||
|
||||
```
|
||||
ToolbarManager (walk 按钮)
|
||||
↓
|
||||
WalkControlManager.show()
|
||||
↓
|
||||
隐藏底部工具栏
|
||||
↓
|
||||
显示漫游控制面板
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 依赖关系
|
||||
|
||||
所有 Manager 都依赖:
|
||||
- `BaseManager` 或 `BaseDialogManager`
|
||||
- `ManagerRegistry`
|
||||
|
||||
各 Manager 之间通过事件系统通信,避免直接依赖。
|
||||
|
||||
---
|
||||
|
||||
**文档生成时间**: 2026-01-23
|
||||
1. 先在 `Engine` 组件实现能力
|
||||
2. 内部 manager 通过 `engineComponent` 访问
|
||||
3. 仅当确有外部 SDK 需求时,才新增 `EngineManager` 公共方法
|
||||
4. 不新增大规模透传方法
|
||||
|
||||
716
docs/引擎API对接.md
716
docs/引擎API对接.md
@@ -1,679 +1,173 @@
|
||||
# Engine API 接口对接指南
|
||||
# Engine API 对接指南(2026-03)
|
||||
|
||||
本文档详细说明 iflow-engine SDK 中 Toolbar 按钮如何调用底层 3D 引擎 API 的完整调用链。
|
||||
本文档说明 SDK 层与底层 `iflow-engine-base` 的最新对接方式。
|
||||
|
||||
## 核心原则
|
||||
|
||||
1. `Engine` 组件是底层能力封装层(状态管理 + 参数转换 + 底层调用)
|
||||
2. `EngineManager` 只保留少量对外公共 API,不再做全量透传
|
||||
3. 内部 Manager 直接通过 `this.engineComponent` 调 `Engine`
|
||||
4. 非 Manager 组件通过 `registry.engine3d?.getEngineComponent()` 调 `Engine`
|
||||
5. 不暴露 raw engine 实例,事件订阅走 `onRawEvent/offRawEvent`
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
## 对接层级
|
||||
|
||||
1. [架构概览](#架构概览)
|
||||
2. [调用链层级](#调用链层级)
|
||||
3. [完整调用链示例:剖切盒](#完整调用链示例剖切盒)
|
||||
4. [各层职责说明](#各层职责说明)
|
||||
5. [新增功能对接步骤](#新增功能对接步骤)
|
||||
6. [数据流向](#数据流向)
|
||||
|
||||
---
|
||||
|
||||
## 架构概览
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 用户交互层 │
|
||||
│ Toolbar Button (src/components/button-group/toolbar/buttons/*) │
|
||||
└────────────────────────────────┬────────────────────────────────────┘
|
||||
│ onClick → registry.xxxManager.show()
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 对话框管理层 (DialogManager) │
|
||||
│ src/managers/*-dialog-manager.ts │
|
||||
│ 继承自 BaseDialogManager,管理 UI 面板生命周期 │
|
||||
└────────────────────────────────┬────────────────────────────────────┘
|
||||
│ Panel 回调 → registry.engine3d.xxx()
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 引擎管理层 (EngineManager) │
|
||||
│ src/managers/engine-manager.ts │
|
||||
│ 封装 Engine 组件,提供统一 API │
|
||||
└────────────────────────────────┬────────────────────────────────────┘
|
||||
│ 调用 this.engineInstance.xxx()
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 引擎组件层 (Engine Component) │
|
||||
│ src/components/engine/index.ts │
|
||||
│ 封装底层 3D 引擎,处理状态管理和数据转换 │
|
||||
└────────────────────────────────┬────────────────────────────────────┘
|
||||
│ 调用 this.engine.xxx.xxx()
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 底层 3D 引擎 (iflow-engine-base) │
|
||||
│ 第三方 SDK,通过 createEngine() 创建实例 │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```text
|
||||
L1 UI 层(Button/Panel/Dialog)
|
||||
-> L2 Manager 层(业务编排)
|
||||
-> L3 Engine 组件(统一封装)
|
||||
-> L4 iflow-engine-base(底层能力)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 调用链层级
|
||||
## A. 对外 API(EngineManager)
|
||||
|
||||
| 层级 | 文件位置 | 职责 |
|
||||
|------|----------|------|
|
||||
| **L1: Toolbar Button** | `src/components/button-group/toolbar/buttons/` | 定义按钮配置,处理点击事件 |
|
||||
| **L2: DialogManager** | `src/managers/*-dialog-manager.ts` | 管理对话框/面板 UI,绑定回调 |
|
||||
| **L3: EngineManager** | `src/managers/engine-manager.ts` | 封装引擎组件,提供公共 API |
|
||||
| **L4: Engine Component** | `src/components/engine/index.ts` | 封装底层引擎,处理状态和转换 |
|
||||
| **L5: 底层引擎** | `iflow-engine-base` (npm 包) | 实际 3D 渲染和功能实现 |
|
||||
对外稳定入口:`bimEngine.engine`
|
||||
|
||||
---
|
||||
|
||||
## 完整调用链示例:剖切盒
|
||||
|
||||
以剖切盒(Section Box)功能为例,展示完整的调用链:
|
||||
|
||||
### 1. Toolbar Button 定义
|
||||
|
||||
**文件**: `src/components/button-group/toolbar/buttons/section/section-box/index.ts`
|
||||
|
||||
```typescript
|
||||
export const createSectionBoxButton = (): ButtonConfig => {
|
||||
return {
|
||||
id: 'section-box',
|
||||
groupId: 'group-1',
|
||||
parentId: 'section', // 父菜单 ID
|
||||
type: 'button',
|
||||
keepActive: true, // 保持激活状态
|
||||
exclusive: true, // 互斥(同组只能激活一个)
|
||||
label: 'toolbar.sectionBox', // 国际化 key
|
||||
icon: getIcon('剖切盒'),
|
||||
onClick: (button) => {
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
if (button.isActive) {
|
||||
registry.sectionBox?.show(); // ← 调用 DialogManager
|
||||
} else {
|
||||
registry.sectionBox?.hide();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
**要点**:
|
||||
- 通过 `ManagerRegistry.getInstance()` 获取全局管理器实例
|
||||
- `button.isActive` 表示当前激活状态
|
||||
- 调用 `registry.sectionBox.show()` 打开对话框
|
||||
|
||||
### 2. DialogManager 处理
|
||||
|
||||
**文件**: `src/managers/section-box-dialog-manager.ts`
|
||||
|
||||
```typescript
|
||||
export class SectionBoxDialogManager extends BaseDialogManager {
|
||||
private panel: SectionBoxPanel | null = null;
|
||||
|
||||
// 创建对话框内容,绑定 Panel 回调
|
||||
protected createContent(): HTMLElement {
|
||||
this.panel = new SectionBoxPanel({
|
||||
onFitToModel: () => {
|
||||
console.log('[SectionBoxDialogManager] Fit to model not supported');
|
||||
},
|
||||
onReset: () => {
|
||||
console.log('[SectionBoxDialogManager] Reset not supported');
|
||||
},
|
||||
onRangeChange: (range) => {
|
||||
this.registry.engine3d?.setSectionBoxRange(range);
|
||||
}
|
||||
});
|
||||
this.panel.init();
|
||||
return this.panel.element;
|
||||
}
|
||||
|
||||
// 对话框创建后,激活剖切盒
|
||||
protected onDialogCreated(): void {
|
||||
this.registry.engine3d?.activeSection('box'); // ← 调用 EngineManager 统一 API
|
||||
this.dialog?.fitHeight(false);
|
||||
}
|
||||
|
||||
// 对话框销毁前,停用剖切
|
||||
protected onBeforeDestroy(): void {
|
||||
this.registry.engine3d?.deactivateSection(); // ← 统一停用方法
|
||||
// ... 清理
|
||||
}
|
||||
|
||||
// 对话框关闭时,取消工具栏按钮激活
|
||||
protected onDialogClose(): void {
|
||||
this.registry.toolbar?.setBtnActive('section-box', false);
|
||||
}
|
||||
```ts
|
||||
interface EngineManagerPublicApi {
|
||||
initialize(options?: Omit<EngineOptions, 'container'>): boolean;
|
||||
isInitialized(): boolean;
|
||||
loadModel(urls: string[], options?: ModelLoadOptions): void;
|
||||
pauseRendering(): void;
|
||||
resumeRendering(): void;
|
||||
getEngineComponent(): Engine | null;
|
||||
destroy(): void;
|
||||
}
|
||||
```
|
||||
|
||||
**要点**:
|
||||
- 继承 `BaseDialogManager`,自动获得 `show()/hide()/toggle()` 方法
|
||||
- 通过 `this.registry.engine3d` 访问 EngineManager
|
||||
- Panel 的回调函数中调用 EngineManager 方法
|
||||
- 生命周期钩子:`onDialogCreated` / `onBeforeDestroy` / `onDialogClose`
|
||||
`EngineManager` 内部仍负责:
|
||||
|
||||
### 3. EngineManager 封装
|
||||
- 创建 `Engine` 实例
|
||||
- 创建并持有 `RightKeyManager`
|
||||
- 组装右键菜单逻辑
|
||||
|
||||
**文件**: `src/managers/engine-manager.ts`
|
||||
---
|
||||
|
||||
```typescript
|
||||
export class EngineManager extends BaseManager {
|
||||
private engineInstance: Engine | null = null;
|
||||
## B. 内部 Manager 对接(推荐)
|
||||
|
||||
// 激活剖切(统一入口)
|
||||
public activeSection(mode: 'x' | 'y' | 'z' | 'box' | 'face'): void {
|
||||
if (!this.engineInstance) {
|
||||
console.warn('[EngineManager] 3D Engine not initialized.');
|
||||
return;
|
||||
}
|
||||
this.engineInstance.activeSection(mode); // ← 调用 Engine 组件
|
||||
}
|
||||
`BaseManager` 已提供:
|
||||
|
||||
// 获取当前剖切模式
|
||||
public getCurrentSectionMode(): 'x' | 'y' | 'z' | 'box' | 'face' | null {
|
||||
if (!this.engineInstance) {
|
||||
return null;
|
||||
}
|
||||
return this.engineInstance.getCurrentSectionMode();
|
||||
}
|
||||
|
||||
// 停用剖切(统一出口)
|
||||
public deactivateSection(): void {
|
||||
if (!this.engineInstance) {
|
||||
return;
|
||||
}
|
||||
this.engineInstance.deactivateSection();
|
||||
}
|
||||
|
||||
// 设置剖切盒范围
|
||||
public setSectionBoxRange(range: SectionBoxRange): void {
|
||||
if (!this.engineInstance) {
|
||||
console.warn('[EngineManager] 3D Engine not initialized.');
|
||||
return;
|
||||
}
|
||||
this.engineInstance.setSectionBoxRange(range);
|
||||
}
|
||||
|
||||
// ... 其他方法
|
||||
}
|
||||
```ts
|
||||
protected get engineComponent(): Engine | null;
|
||||
```
|
||||
|
||||
**要点**:
|
||||
- 每个方法都要检查 `engineInstance` 是否存在
|
||||
- 方法签名与 Engine 组件一致,起到代理作用
|
||||
- 负责错误处理和日志输出
|
||||
### 示例:剖切盒
|
||||
|
||||
### 4. Engine 组件实现
|
||||
|
||||
**文件**: `src/components/engine/index.ts`
|
||||
|
||||
```typescript
|
||||
export class Engine implements IBimComponent {
|
||||
private engine: any = null; // 底层引擎实例
|
||||
private currentSectionMode: 'x' | 'y' | 'z' | 'box' | 'face' | null = null; // 当前剖切模式
|
||||
|
||||
// 激活剖切(统一入口)
|
||||
public activeSection(mode: 'x' | 'y' | 'z' | 'box' | 'face'): void {
|
||||
if (!this._isInitialized || !this.engine) {
|
||||
console.error('[Engine] Cannot activate section box: engine not initialized.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.engine.clipping?.sectionBox) {
|
||||
console.error('[Engine] Section box module not available.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isSectionBoxActive) {
|
||||
return; // 幂等操作
|
||||
}
|
||||
|
||||
// 保存模型包围盒(用于百分比计算)
|
||||
this.sectionBoxFullBounds = this.engine.octreeBox?.getBoundingBox()?.clone();
|
||||
|
||||
// 调用底层 API
|
||||
this.engine.clipping.sectionBox.active(); // ← 底层引擎 API
|
||||
this.isSectionBoxActive = true;
|
||||
}
|
||||
|
||||
// 设置剖切盒范围(百分比 → 坐标转换)
|
||||
public setSectionBoxRange(range: SectionBoxRange): void {
|
||||
if (!this.sectionBoxFullBounds) {
|
||||
console.error('[Engine] Cannot set section box range: full bounds not available.');
|
||||
return;
|
||||
}
|
||||
|
||||
const full = this.sectionBoxFullBounds;
|
||||
|
||||
// 百分比转实际坐标
|
||||
const toCoord = (percent: number, min: number, max: number): number => {
|
||||
return min + (max - min) * (percent / 100);
|
||||
};
|
||||
|
||||
const xyz = {
|
||||
minX: toCoord(range.x.min, full.min.x, full.max.x),
|
||||
maxX: toCoord(range.x.max, full.min.x, full.max.x),
|
||||
minY: toCoord(range.y.min, full.min.y, full.max.y),
|
||||
maxY: toCoord(range.y.max, full.min.y, full.max.y),
|
||||
minZ: toCoord(range.z.min, full.min.z, full.max.z),
|
||||
maxZ: toCoord(range.z.max, full.min.z, full.max.z),
|
||||
};
|
||||
|
||||
// 调用底层 API
|
||||
this.engine.clipping.sectionBox.setboxXyz(xyz); // ← 底层引擎 API
|
||||
}
|
||||
}
|
||||
```ts
|
||||
this.engineComponent?.activeSection('box');
|
||||
this.engineComponent?.setSectionBoxRange(range);
|
||||
this.engineComponent?.deactivateSection();
|
||||
```
|
||||
|
||||
**要点**:
|
||||
- 维护功能状态(`isSectionBoxActive`)
|
||||
- 缓存必要数据(`sectionBoxFullBounds`)
|
||||
- 进行数据转换(百分比 → 坐标)
|
||||
- 调用底层引擎的实际 API
|
||||
### 示例:漫游
|
||||
|
||||
### 5. 底层引擎 API
|
||||
|
||||
**来源**: `iflow-engine-base` (第三方 SDK)
|
||||
|
||||
```typescript
|
||||
// 剖切盒相关 API
|
||||
engine.clipping.sectionBox.active() // 激活剖切盒
|
||||
engine.clipping.sectionBox.disActive() // 停用剖切盒
|
||||
engine.clipping.sectionBox.getboxXyz() // 获取范围 { minX, maxX, minY, maxY, minZ, maxZ }
|
||||
engine.clipping.sectionBox.setboxXyz(xyz) // 设置范围
|
||||
engine.clipping.sectionBox.setBox(box) // 设置 THREE.Box3 格式范围
|
||||
engine.clipping.sectionBox.reverseBox() // 反向剖切(当前为空实现)
|
||||
|
||||
// 获取模型包围盒
|
||||
engine.octreeBox.getBoundingBox() // 返回 THREE.Box3
|
||||
```ts
|
||||
this.engineComponent?.activateFirstPersonMode();
|
||||
this.engineComponent?.setWalkSpeed(speed);
|
||||
this.engineComponent?.deactivateFirstPersonMode();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 各层职责说明
|
||||
## C. 非 Manager 组件对接
|
||||
|
||||
### L1: Toolbar Button
|
||||
适用于 toolbar 按钮、独立组件(如 `WalkPathPanel`):
|
||||
|
||||
| 职责 | 说明 |
|
||||
|------|------|
|
||||
| 定义按钮配置 | `id`, `label`, `icon`, `groupId`, `parentId` 等 |
|
||||
| 处理点击事件 | 在 `onClick` 中调用 DialogManager 的 `show()`/`hide()` |
|
||||
| 控制激活状态 | `keepActive`, `exclusive` 等属性控制按钮行为 |
|
||||
|
||||
### L2: DialogManager
|
||||
|
||||
| 职责 | 说明 |
|
||||
|------|------|
|
||||
| 管理对话框生命周期 | `show()` / `hide()` / `toggle()` |
|
||||
| 创建 UI 面板 | 在 `createContent()` 中实例化 Panel 组件 |
|
||||
| 绑定回调函数 | 将 Panel 的事件回调连接到 EngineManager |
|
||||
| 处理生命周期钩子 | `onDialogCreated` / `onBeforeDestroy` / `onDialogClose` |
|
||||
| 同步工具栏状态 | 关闭时调用 `toolbar.setBtnActive(id, false)` |
|
||||
|
||||
### L3: EngineManager
|
||||
|
||||
| 职责 | 说明 |
|
||||
|------|------|
|
||||
| 代理 Engine 组件 | 提供统一的公共 API |
|
||||
| 检查初始化状态 | 每个方法都检查 `engineInstance` 是否存在 |
|
||||
| 错误处理 | 输出警告日志 |
|
||||
| 暴露给 Registry | 通过 `registry.engine3d` 访问 |
|
||||
|
||||
### L4: Engine Component
|
||||
|
||||
| 职责 | 说明 |
|
||||
|------|------|
|
||||
| 封装底层引擎 | 隔离第三方 SDK 的具体实现 |
|
||||
| 状态管理 | 维护 `isSectionBoxActive` 等状态标记 |
|
||||
| 数据转换 | UI 层数据格式 ↔ 底层引擎数据格式 |
|
||||
| 缓存数据 | 保存 `sectionBoxFullBounds` 等中间数据 |
|
||||
| 幂等操作 | 防止重复激活/停用 |
|
||||
|
||||
### L5: 底层引擎
|
||||
|
||||
| 职责 | 说明 |
|
||||
|------|------|
|
||||
| 3D 渲染 | WebGL/Three.js 渲染 |
|
||||
| 功能实现 | 测量、剖切、漫游等实际功能 |
|
||||
| 场景管理 | 模型加载、相机控制等 |
|
||||
```ts
|
||||
registry.engine3d?.getEngineComponent()?.CameraGoHome();
|
||||
registry.engine3d?.getEngineComponent()?.activateZoomBox();
|
||||
registry.engine3d?.getEngineComponent()?.toggleMiniMap();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 新增功能对接步骤
|
||||
## D. 原始事件对接(替代 getEngine)
|
||||
|
||||
以新增一个 "XX 功能" 为例:
|
||||
`Engine.getEngine()` 已删除,改为事件桥接:
|
||||
|
||||
### Step 1: 创建 Toolbar Button
|
||||
|
||||
**文件**: `src/components/button-group/toolbar/buttons/xx/index.ts`
|
||||
|
||||
```typescript
|
||||
import type { ButtonConfig } from '../../../index.type';
|
||||
import { getIcon } from '../../../../../utils/icon-manager';
|
||||
import { ManagerRegistry } from '../../../../../core/manager-registry';
|
||||
|
||||
export const createXxButton = (): ButtonConfig => {
|
||||
return {
|
||||
id: 'xx-feature',
|
||||
groupId: 'group-1',
|
||||
type: 'button',
|
||||
keepActive: true,
|
||||
label: 'toolbar.xxFeature',
|
||||
icon: getIcon('XX图标'),
|
||||
onClick: (button) => {
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
if (button.isActive) {
|
||||
registry.xxFeature?.show();
|
||||
} else {
|
||||
registry.xxFeature?.hide();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
```ts
|
||||
engineComponent?.onRawEvent('measure-changed', handler);
|
||||
engineComponent?.offRawEvent('measure-changed', handler);
|
||||
```
|
||||
|
||||
### Step 2: 注册按钮到 Toolbar
|
||||
适用场景:
|
||||
|
||||
**文件**: `src/components/button-group/toolbar/index.ts`
|
||||
- 测量回调(`measure-changed` / `measure-click`)
|
||||
- 剖切盒拖动回调(`section-move`)
|
||||
|
||||
```typescript
|
||||
// 添加导入
|
||||
const { createXxButton } = await import('./buttons/xx');
|
||||
---
|
||||
|
||||
// 添加按钮
|
||||
this.addButton(createXxButton());
|
||||
```
|
||||
## E. 新功能对接步骤(最新模板)
|
||||
|
||||
### Step 3: 创建 DialogManager
|
||||
### Step 1: 在 Engine 组件实现能力
|
||||
|
||||
**文件**: `src/managers/xx-dialog-manager.ts`
|
||||
文件:`src/components/engine/index.ts`
|
||||
|
||||
```typescript
|
||||
import { BaseDialogManager } from '../core/base-dialog-manager';
|
||||
import { XxPanel } from '../components/xx-panel';
|
||||
|
||||
export class XxDialogManager extends BaseDialogManager {
|
||||
private panel: XxPanel | null = null;
|
||||
|
||||
protected get dialogId(): string {
|
||||
return 'xx-dialog';
|
||||
}
|
||||
|
||||
protected get dialogTitle(): string {
|
||||
return 'xxFeature.dialogTitle';
|
||||
}
|
||||
|
||||
protected createContent(): HTMLElement {
|
||||
this.panel = new XxPanel({
|
||||
onSomeAction: () => {
|
||||
this.registry.engine3d?.doSomething();
|
||||
}
|
||||
});
|
||||
this.panel.init();
|
||||
return this.panel.element;
|
||||
}
|
||||
|
||||
protected onDialogCreated(): void {
|
||||
this.registry.engine3d?.activateXx();
|
||||
}
|
||||
|
||||
protected onBeforeDestroy(): void {
|
||||
this.registry.engine3d?.deactivateXx();
|
||||
this.panel?.destroy();
|
||||
this.panel = null;
|
||||
}
|
||||
|
||||
protected onDialogClose(): void {
|
||||
this.registry.toolbar?.setBtnActive('xx-feature', false);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: 注册到 ManagerRegistry
|
||||
|
||||
**文件**: `src/core/manager-registry.ts`
|
||||
|
||||
```typescript
|
||||
// 添加类型导入
|
||||
import type { XxDialogManager } from '../managers/xx-dialog-manager';
|
||||
|
||||
// 添加属性
|
||||
public xxFeature: XxDialogManager | null = null;
|
||||
```
|
||||
|
||||
### Step 5: 在 BimEngine 中初始化
|
||||
|
||||
**文件**: `src/bim-engine.ts`
|
||||
|
||||
```typescript
|
||||
// 添加导入
|
||||
import { XxDialogManager } from './managers/xx-dialog-manager';
|
||||
|
||||
// 添加属性
|
||||
public xxFeature: XxDialogManager | null = null;
|
||||
|
||||
// 在 init() 中初始化
|
||||
this.xxFeature = new XxDialogManager();
|
||||
this.registry.xxFeature = this.xxFeature;
|
||||
|
||||
// 在 destroy() 中销毁
|
||||
this.xxFeature?.destroy();
|
||||
```
|
||||
|
||||
### Step 6: 在 EngineManager 中添加方法
|
||||
|
||||
**文件**: `src/managers/engine-manager.ts`
|
||||
|
||||
```typescript
|
||||
```ts
|
||||
public activateXx(): void {
|
||||
if (!this.engineInstance) {
|
||||
console.warn('[EngineManager] 3D Engine not initialized.');
|
||||
return;
|
||||
}
|
||||
this.engineInstance.activateXx();
|
||||
if (!this._isInitialized || !this.engine?.xxModule) return;
|
||||
this.engine.xxModule.active();
|
||||
}
|
||||
|
||||
public deactivateXx(): void {
|
||||
if (!this.engineInstance) {
|
||||
return;
|
||||
}
|
||||
this.engineInstance.deactivateXx();
|
||||
}
|
||||
|
||||
public doSomething(): void {
|
||||
if (!this.engineInstance) {
|
||||
console.warn('[EngineManager] 3D Engine not initialized.');
|
||||
return;
|
||||
}
|
||||
this.engineInstance.doSomething();
|
||||
if (!this._isInitialized || !this.engine?.xxModule) return;
|
||||
this.engine.xxModule.disActive();
|
||||
}
|
||||
```
|
||||
|
||||
### Step 7: 在 Engine 组件中实现
|
||||
### Step 2: 在 Manager 中调用(不要新增 EngineManager 透传)
|
||||
|
||||
**文件**: `src/components/engine/index.ts`
|
||||
文件:`src/managers/xx-dialog-manager.ts`
|
||||
|
||||
```typescript
|
||||
private isXxActive: boolean = false;
|
||||
|
||||
public activateXx(): void {
|
||||
if (!this._isInitialized || !this.engine) {
|
||||
console.error('[Engine] Cannot activate XX: engine not initialized.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isXxActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用底层 API
|
||||
this.engine.xxModule.active();
|
||||
this.isXxActive = true;
|
||||
```ts
|
||||
protected onDialogCreated(): void {
|
||||
this.engineComponent?.activateXx();
|
||||
}
|
||||
|
||||
public deactivateXx(): void {
|
||||
if (!this._isInitialized || !this.engine?.xxModule) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isXxActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.engine.xxModule.disActive();
|
||||
this.isXxActive = false;
|
||||
}
|
||||
|
||||
public doSomething(): void {
|
||||
if (!this._isInitialized || !this.engine?.xxModule) {
|
||||
return;
|
||||
}
|
||||
this.engine.xxModule.doSomething();
|
||||
protected onBeforeDestroy(): void {
|
||||
this.engineComponent?.deactivateXx();
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: 若是非 Manager 组件,走 getEngineComponent
|
||||
|
||||
```ts
|
||||
registry.engine3d?.getEngineComponent()?.activateXx();
|
||||
```
|
||||
|
||||
### Step 4: 仅当需要“对外 SDK 公共 API”时,再考虑给 EngineManager 增加方法
|
||||
|
||||
判定标准:
|
||||
|
||||
- 该能力是否需要 `bimEngine.engine?.xxx()` 供外部用户直接调用
|
||||
- 若仅为内部链路使用,不应在 `EngineManager` 再包一层
|
||||
|
||||
---
|
||||
|
||||
## 数据流向
|
||||
## F. 常见反模式
|
||||
|
||||
### 用户操作 → 底层引擎
|
||||
- ❌ 在 `EngineManager` 里新增大批 `this.engineInstance?.xxx()` 透传
|
||||
- ❌ 内部 Manager 继续写 `this.registry.engine3d?.xxx()`
|
||||
- ❌ 通过 `Engine.getEngine()` 暴露 raw engine
|
||||
|
||||
```
|
||||
用户点击按钮
|
||||
↓
|
||||
Button.onClick(button)
|
||||
↓
|
||||
registry.xxxManager.show()
|
||||
↓
|
||||
BaseDialogManager.show()
|
||||
↓
|
||||
createContent() → 创建 Panel
|
||||
↓
|
||||
onDialogCreated() → registry.engine3d.activateXxx()
|
||||
↓
|
||||
EngineManager.activateXxx()
|
||||
↓
|
||||
Engine.activateXxx()
|
||||
↓
|
||||
this.engine.xxxModule.active() ← 底层引擎 API
|
||||
```
|
||||
对应正确方式:
|
||||
|
||||
### UI 面板操作 → 底层引擎
|
||||
|
||||
```
|
||||
用户操作 Panel(拖动滑块、点击按钮等)
|
||||
↓
|
||||
Panel 回调触发 onXxxChange(data)
|
||||
↓
|
||||
DialogManager 中的回调:registry.engine3d.setXxx(data)
|
||||
↓
|
||||
EngineManager.setXxx(data)
|
||||
↓
|
||||
Engine.setXxx(data)
|
||||
↓
|
||||
数据转换(如百分比 → 坐标)
|
||||
↓
|
||||
this.engine.xxxModule.setXxx(convertedData) ← 底层引擎 API
|
||||
```
|
||||
|
||||
### 关闭对话框
|
||||
|
||||
```
|
||||
用户点击关闭按钮 / 再次点击工具栏按钮
|
||||
↓
|
||||
BimDialog.onClose() / Button.onClick()
|
||||
↓
|
||||
BaseDialogManager.hide() / registry.xxxManager.hide()
|
||||
↓
|
||||
destroyDialog()
|
||||
↓
|
||||
onBeforeDestroy() → registry.engine3d.deactivateXxx()
|
||||
↓
|
||||
EngineManager.deactivateXxx()
|
||||
↓
|
||||
Engine.deactivateXxx()
|
||||
↓
|
||||
this.engine.xxxModule.disActive() ← 底层引擎 API
|
||||
↓
|
||||
onDialogClose() → registry.toolbar.setBtnActive('xxx', false)
|
||||
```
|
||||
- ✅ 内部 Manager 使用 `this.engineComponent?.xxx()`
|
||||
- ✅ 非 Manager 使用 `registry.engine3d?.getEngineComponent()?.xxx()`
|
||||
- ✅ 原始事件使用 `onRawEvent/offRawEvent`
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
## G. 验收清单
|
||||
|
||||
### Q1: 如何确保激活/停用的幂等性?
|
||||
重构或新增功能后,至少检查:
|
||||
|
||||
在 Engine 组件中维护状态标记,在方法开头检查:
|
||||
|
||||
```typescript
|
||||
public activateSectionBox(): void {
|
||||
// ...
|
||||
if (this.isSectionBoxActive) {
|
||||
console.log('[Engine] Section box already active, skipping.');
|
||||
return; // 幂等:已激活则跳过
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Q2: 如何处理底层 API 不支持的功能?
|
||||
|
||||
在 DialogManager 的回调中输出日志说明:
|
||||
|
||||
```typescript
|
||||
onReverseToggle: (isReversed) => {
|
||||
// 底层暂不支持反向功能
|
||||
console.log('[SectionBoxDialogManager] 反向切换(底层暂不支持):', isReversed);
|
||||
}
|
||||
```
|
||||
|
||||
### Q3: 如何进行数据格式转换?
|
||||
|
||||
在 Engine 组件中进行,不要在 DialogManager 或 EngineManager 中:
|
||||
|
||||
```typescript
|
||||
// Engine 组件中
|
||||
public setSectionBoxRange(range: SectionBoxRange): void {
|
||||
// UI 层:百分比 (0-100)
|
||||
// 底层:实际坐标
|
||||
|
||||
const toCoord = (percent: number, min: number, max: number): number => {
|
||||
return min + (max - min) * (percent / 100);
|
||||
};
|
||||
|
||||
const xyz = {
|
||||
minX: toCoord(range.x.min, full.min.x, full.max.x),
|
||||
// ...
|
||||
};
|
||||
|
||||
this.engine.clipping.sectionBox.setboxXyz(xyz);
|
||||
}
|
||||
```
|
||||
|
||||
### Q4: 如何访问其他 Manager?
|
||||
|
||||
通过 `ManagerRegistry` 单例访问:
|
||||
|
||||
```typescript
|
||||
// 在 Button 中
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
registry.sectionBox?.show();
|
||||
|
||||
// 在 DialogManager 中(继承自 BaseManager,自动获得 this.registry)
|
||||
this.registry.engine3d?.activateSectionBox();
|
||||
this.registry.toolbar?.setBtnActive('section-box', false);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
| 层级 | 关注点 |
|
||||
|------|--------|
|
||||
| **Button** | 按钮配置、点击事件、调用 DialogManager |
|
||||
| **DialogManager** | UI 生命周期、Panel 回调绑定、调用 EngineManager |
|
||||
| **EngineManager** | API 代理、初始化检查、错误处理 |
|
||||
| **Engine** | 状态管理、数据转换、调用底层 API |
|
||||
| **底层引擎** | 实际功能实现 |
|
||||
|
||||
遵循这个分层架构,可以保持代码的清晰性和可维护性。
|
||||
1. `EngineManager` 是否只保留必要公共 API
|
||||
2. Manager 层是否统一走 `engineComponent`
|
||||
3. 是否无 `getEngine()` 调用
|
||||
4. 事件订阅是否成对 `onRawEvent/offRawEvent`
|
||||
5. `npm run build` 是否通过
|
||||
|
||||
639
docs/架构设计.md
639
docs/架构设计.md
@@ -1,502 +1,153 @@
|
||||
# iflow-engine SDK 架构文档
|
||||
# iflow-engine SDK 架构设计
|
||||
|
||||
## 概述
|
||||
|
||||
iflow-engine 是一个面向开发者的 BIM 3D 引擎 SDK,采用分层架构设计,提供模型查看、测量、剖切、漫游等功能。
|
||||
iflow-engine 采用分层架构:`BimEngine` 作为入口,`ManagerRegistry` 作为实例级注册表,Manager 层负责编排,Component 层负责 UI/引擎封装,Services/Core 层提供基础能力。
|
||||
|
||||
## 架构图
|
||||
2026-03 的核心调整:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ BimEngine │
|
||||
│ (主入口 / 门面模式) │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ ManagerRegistry │ │
|
||||
│ │ (单例 / 全局注册表) │ │
|
||||
│ │ ┌─────────────────────────────────────────────────┐ │ │
|
||||
│ │ │ EventEmitter (事件总线) │ │ │
|
||||
│ │ └─────────────────────────────────────────────────┘ │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ Managers Layer │ │
|
||||
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
|
||||
│ │ │EngineManager │ │ToolbarManager│ │ DialogManager│ │ │
|
||||
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
|
||||
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
|
||||
│ │ │MeasureDialog │ │SectionDialog │ │WalkControl │ │ │
|
||||
│ │ │Manager │ │Manager │ │Manager │ │ │
|
||||
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
|
||||
│ │ ... (15 个管理器) │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ Components Layer │ │
|
||||
│ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ │
|
||||
│ │ │ Engine │ │ Dialog │ │ Tree │ │ Menu │ │ButtonGr│ │ │
|
||||
│ │ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘ │ │
|
||||
│ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ │
|
||||
│ │ │Collapse│ │ Tab │ │Descript│ │Measure │ │Section │ │ │
|
||||
│ │ │ │ │ │ │ │ │Panel │ │Panel │ │ │
|
||||
│ │ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘ │ │
|
||||
│ │ ... (20+ 个组件) │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ Services Layer │ │
|
||||
│ │ ┌──────────────────┐ ┌──────────────────┐ │ │
|
||||
│ │ │ LocaleManager │ │ ThemeManager │ │ │
|
||||
│ │ │ (国际化服务) │ │ (主题服务) │ │ │
|
||||
│ │ └──────────────────┘ └──────────────────┘ │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ Core Layer │ │
|
||||
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
|
||||
│ │ │ EventEmitter │ │ BaseManager │ │BaseDialog │ │ │
|
||||
│ │ │ │ │ │ │Manager │ │ │
|
||||
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ External Dependencies │
|
||||
│ ┌──────────────────────┐ ┌──────────────────────┐ │
|
||||
│ │ iflow-engine-base │ │ Three.js │ │
|
||||
│ │ (第三方 3D 引擎核心) │ │ (3D 渲染库) │ │
|
||||
│ └──────────────────────┘ └──────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 分层说明
|
||||
|
||||
### 1. BimEngine (入口层)
|
||||
|
||||
**职责**: 作为 SDK 的唯一入口,整合所有功能,提供简洁的公开 API。
|
||||
|
||||
**设计模式**: 门面模式 (Facade Pattern)
|
||||
|
||||
**核心 API**:
|
||||
- 初始化引擎和容器
|
||||
- 设置主题和语言
|
||||
- 访问各个管理器
|
||||
|
||||
```typescript
|
||||
const engine = new BimEngine('container', {
|
||||
locale: 'zh-CN',
|
||||
theme: 'dark'
|
||||
});
|
||||
|
||||
// 访问管理器
|
||||
engine.engine.loadModel('model.gltf');
|
||||
engine.toolbar.show();
|
||||
engine.measure.show();
|
||||
```
|
||||
|
||||
### 2. ManagerRegistry (注册表层)
|
||||
|
||||
**职责**: 全局单例,集中管理所有 Manager 实例,提供跨 Manager 通信。
|
||||
|
||||
**设计模式**: 单例模式 (Singleton) + 服务定位器 (Service Locator)
|
||||
|
||||
**特点**:
|
||||
- 避免 Manager 之间的循环依赖
|
||||
- 提供类型安全的事件系统
|
||||
- 支持动态访问任意 Manager
|
||||
|
||||
```typescript
|
||||
const registry = ManagerRegistry.getInstance();
|
||||
registry.toolbar?.show();
|
||||
registry.emit('engine:model-loaded', { url: 'model.gltf' });
|
||||
```
|
||||
|
||||
### 3. Managers Layer (管理器层)
|
||||
|
||||
**职责**: 处理业务逻辑,协调组件交互,管理组件生命周期。
|
||||
|
||||
**设计模式**: 模板方法模式 (Template Method)
|
||||
|
||||
**管理器分类**:
|
||||
|
||||
| 类别 | 管理器 | 职责 |
|
||||
|------|--------|------|
|
||||
| 核心 | EngineManager | 3D 引擎管理 |
|
||||
| UI 容器 | ToolbarManager, DialogManager, ButtonGroupManager | 容器组件管理 |
|
||||
| 交互 | RightKeyManager, ComponentDetailManager, ConstructTreeManagerBtn | 用户交互 |
|
||||
| 工具 | MeasureDialogManager, SectionPlaneDialogManager, SectionAxisDialogManager, SectionBoxDialogManager | 3D 操作工具 |
|
||||
| 漫游 | WalkControlManager, WalkPathDialogManager, WalkPlanViewDialogManager, MapDialogManager | 漫游功能 |
|
||||
|
||||
### 4. Components Layer (组件层)
|
||||
|
||||
**职责**: 纯 UI 渲染,不包含业务逻辑。
|
||||
|
||||
**设计模式**: 组合模式 (Composite Pattern)
|
||||
|
||||
**组件接口**:
|
||||
```typescript
|
||||
interface IBimComponent {
|
||||
element: HTMLElement;
|
||||
init(): void;
|
||||
setTheme(theme: ThemeConfig): void;
|
||||
setLocales?(): void;
|
||||
destroy(): void;
|
||||
}
|
||||
```
|
||||
|
||||
**组件分类**:
|
||||
|
||||
| 类别 | 组件 | 说明 |
|
||||
|------|------|------|
|
||||
| 核心 | Engine | 3D 视口容器 |
|
||||
| 容器 | Dialog, Tree, Menu, ButtonGroup | 承载内容的容器组件 |
|
||||
| 面板 | MeasurePanel, SectionPlanePanel, WalkControlPanel | 功能面板 |
|
||||
| 展示 | Collapse, Tab, Description | 数据展示组件 |
|
||||
|
||||
### 5. Services Layer (服务层)
|
||||
|
||||
**职责**: 提供横切关注点的全局服务。
|
||||
|
||||
**设计模式**: 单例模式 + 观察者模式
|
||||
|
||||
| 服务 | 职责 |
|
||||
|------|------|
|
||||
| LocaleManager | 国际化,多语言支持 |
|
||||
| ThemeManager | 主题管理,明暗切换 |
|
||||
|
||||
### 6. Core Layer (核心层)
|
||||
|
||||
**职责**: 提供基础设施和抽象。
|
||||
|
||||
| 类 | 职责 |
|
||||
|-----|------|
|
||||
| EventEmitter | 事件发布/订阅 |
|
||||
| BaseManager | Manager 基类,提供注册表访问和事件管理 |
|
||||
| BaseDialogManager | 对话框 Manager 基类,封装对话框生命周期 |
|
||||
|
||||
## 设计模式详解
|
||||
|
||||
### 1. 单例模式 (Singleton)
|
||||
|
||||
**应用**: ManagerRegistry, LocaleManager, ThemeManager
|
||||
|
||||
**目的**: 确保全局唯一实例,提供全局访问点。
|
||||
|
||||
```typescript
|
||||
export class ManagerRegistry {
|
||||
private static instance: ManagerRegistry | null = null;
|
||||
|
||||
public static getInstance(): ManagerRegistry {
|
||||
if (!ManagerRegistry.instance) {
|
||||
ManagerRegistry.instance = new ManagerRegistry();
|
||||
}
|
||||
return ManagerRegistry.instance;
|
||||
}
|
||||
|
||||
public static reset(): void {
|
||||
ManagerRegistry.instance = null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 观察者模式 (Observer)
|
||||
|
||||
**应用**: EventEmitter, 服务订阅
|
||||
|
||||
**目的**: 解耦事件发送者和接收者,支持多对多通信。
|
||||
|
||||
```typescript
|
||||
// 订阅
|
||||
const unsubscribe = registry.on('engine:model-loaded', (payload) => {
|
||||
console.log('Model loaded:', payload.url);
|
||||
});
|
||||
|
||||
// 发布
|
||||
registry.emit('engine:model-loaded', { url: 'model.gltf' });
|
||||
|
||||
// 取消订阅
|
||||
unsubscribe();
|
||||
```
|
||||
|
||||
### 3. 模板方法模式 (Template Method)
|
||||
|
||||
**应用**: BaseManager, BaseDialogManager
|
||||
|
||||
**目的**: 定义算法骨架,允许子类重写特定步骤。
|
||||
|
||||
```typescript
|
||||
export abstract class BaseDialogManager extends BaseManager {
|
||||
// 模板方法
|
||||
public show(): void {
|
||||
this.destroyDialog();
|
||||
const content = this.createContent(); // 抽象方法
|
||||
this.createDialog(content);
|
||||
this.onDialogCreated(); // 钩子方法
|
||||
}
|
||||
|
||||
// 抽象方法 - 子类必须实现
|
||||
protected abstract createContent(): HTMLElement;
|
||||
|
||||
// 钩子方法 - 子类可选重写
|
||||
protected onDialogCreated(): void {}
|
||||
protected onDialogClose(): void {}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 工厂模式 (Factory)
|
||||
|
||||
**应用**: BimEngine 初始化
|
||||
|
||||
**目的**: 封装对象创建逻辑,统一创建流程。
|
||||
|
||||
```typescript
|
||||
private init() {
|
||||
this.engine = new EngineManager(this.wrapper);
|
||||
this.dialog = new DialogManager(this.wrapper);
|
||||
this.toolbar = new ToolbarManager(this.wrapper);
|
||||
// ... 创建其他管理器
|
||||
}
|
||||
```
|
||||
|
||||
## 数据流
|
||||
|
||||
### 1. 用户交互流
|
||||
|
||||
```
|
||||
用户操作 (点击按钮)
|
||||
↓
|
||||
Toolbar 组件捕获事件
|
||||
↓
|
||||
ToolbarManager 处理
|
||||
↓
|
||||
发送事件到 Registry
|
||||
↓
|
||||
目标 Manager 响应
|
||||
↓
|
||||
更新相关组件 UI
|
||||
```
|
||||
|
||||
### 2. 主题变更流
|
||||
|
||||
```
|
||||
用户调用 setTheme('dark')
|
||||
↓
|
||||
ThemeManager.setTheme()
|
||||
↓
|
||||
更新内部状态
|
||||
↓
|
||||
通知所有订阅者
|
||||
↓
|
||||
各组件 setTheme() 被调用
|
||||
↓
|
||||
更新 CSS 变量和样式
|
||||
```
|
||||
|
||||
### 3. 国际化变更流
|
||||
|
||||
```
|
||||
用户调用 setLocale('en-US')
|
||||
↓
|
||||
LocaleManager.setLocale()
|
||||
↓
|
||||
更新内部状态
|
||||
↓
|
||||
通知所有订阅者
|
||||
↓
|
||||
各组件 setLocales() 被调用
|
||||
↓
|
||||
重新渲染文本内容
|
||||
```
|
||||
|
||||
## 模块依赖关系
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
BimEngine --> ManagerRegistry
|
||||
BimEngine --> LocaleManager
|
||||
BimEngine --> ThemeManager
|
||||
|
||||
ManagerRegistry --> EventEmitter
|
||||
|
||||
EngineManager --> BaseManager
|
||||
ToolbarManager --> BaseManager
|
||||
DialogManager --> BaseManager
|
||||
|
||||
MeasureDialogManager --> BaseDialogManager
|
||||
SectionPlaneDialogManager --> BaseDialogManager
|
||||
WalkControlManager --> BaseManager
|
||||
|
||||
BaseDialogManager --> BaseManager
|
||||
BaseManager --> ManagerRegistry
|
||||
|
||||
Engine --> ThemeManager
|
||||
Dialog --> ThemeManager
|
||||
Tree --> ThemeManager
|
||||
Tree --> LocaleManager
|
||||
Menu --> ThemeManager
|
||||
Menu --> LocaleManager
|
||||
|
||||
ThemeManager --> darkTheme
|
||||
ThemeManager --> lightTheme
|
||||
LocaleManager --> zhCN
|
||||
LocaleManager --> enUS
|
||||
```
|
||||
|
||||
## 扩展点
|
||||
|
||||
### 1. 添加新 Manager
|
||||
|
||||
```typescript
|
||||
import { BaseManager } from '../core/base-manager';
|
||||
|
||||
export class CustomManager extends BaseManager {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
public initialize(): void {
|
||||
this.subscribe('engine:model-loaded', this.onModelLoaded.bind(this));
|
||||
}
|
||||
|
||||
private onModelLoaded(payload: any): void {
|
||||
// 处理逻辑
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 添加新对话框 Manager
|
||||
|
||||
```typescript
|
||||
import { BaseDialogManager } from '../core/base-dialog-manager';
|
||||
|
||||
export class CustomDialogManager extends BaseDialogManager {
|
||||
protected get dialogId(): string {
|
||||
return 'custom-dialog';
|
||||
}
|
||||
|
||||
protected get dialogTitle(): string {
|
||||
return 'custom.dialogTitle';
|
||||
}
|
||||
|
||||
protected createContent(): HTMLElement {
|
||||
const div = document.createElement('div');
|
||||
// 创建内容
|
||||
return div;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 添加新组件
|
||||
|
||||
```typescript
|
||||
import type { ThemeConfig } from '../themes/types';
|
||||
|
||||
export class CustomComponent {
|
||||
public element: HTMLElement;
|
||||
|
||||
constructor(private options: CustomOptions) {
|
||||
this.element = document.createElement('div');
|
||||
}
|
||||
|
||||
public init(): void {
|
||||
// 初始化
|
||||
}
|
||||
|
||||
public setTheme(theme: ThemeConfig): void {
|
||||
// 应用主题
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
this.element.remove();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 添加新语言
|
||||
|
||||
1. 创建语言文件 `src/locales/ja-JP.ts`
|
||||
2. 更新类型 `src/locales/types.ts`
|
||||
3. 注册到 LocaleManager
|
||||
|
||||
### 5. 添加新主题
|
||||
|
||||
```typescript
|
||||
import { createThemeFromPartial, lightTheme } from 'iflow-engine';
|
||||
|
||||
const customTheme = createThemeFromPartial(lightTheme, {
|
||||
name: 'custom',
|
||||
primary: '#ff6b6b',
|
||||
// 其他自定义属性
|
||||
});
|
||||
|
||||
themeManager.setCustomTheme(customTheme);
|
||||
```
|
||||
|
||||
## 事件系统
|
||||
|
||||
### 事件类型
|
||||
|
||||
| 类别 | 事件 | 数据 |
|
||||
|------|------|------|
|
||||
| UI | `ui:open-dialog` | `{ id: string; data?: any }` |
|
||||
| UI | `ui:close-dialog` | `{ id: string }` |
|
||||
| 引擎 | `engine:model-loaded` | `{ url: string }` |
|
||||
| 引擎 | `engine:object-clicked` | `{ objectId: string; position: Point3D }` |
|
||||
| 树 | `ui:tree-node-check` | `{ id: string; checked: boolean; node: any }` |
|
||||
| 树 | `ui:tree-node-select` | `{ id: string; selected: boolean; node: any }` |
|
||||
| 系统 | `sys:theme-changed` | `{ theme: string }` |
|
||||
| 系统 | `sys:locale-changed` | `{ locale: string }` |
|
||||
| 漫游 | `walk:speed-change` | `{ speed: number }` |
|
||||
| 漫游 | `walk:path-mode-toggle` | `{ isActive: boolean }` |
|
||||
| 地图 | `map:opened` | `{}` |
|
||||
| 地图 | `map:closed` | `{}` |
|
||||
|
||||
### 事件使用
|
||||
|
||||
```typescript
|
||||
// 发送事件
|
||||
registry.emit('engine:model-loaded', { url: 'model.gltf' });
|
||||
|
||||
// 订阅事件
|
||||
const unsubscribe = registry.on('engine:model-loaded', (payload) => {
|
||||
console.log('Model loaded:', payload.url);
|
||||
});
|
||||
|
||||
// 在 Manager 中使用
|
||||
this.subscribe('engine:model-loaded', this.handleModelLoaded.bind(this));
|
||||
this.emit('custom:action', { data: 'value' });
|
||||
```
|
||||
|
||||
## 性能考虑
|
||||
|
||||
### 1. 懒加载
|
||||
|
||||
- 组件按需创建,不预先实例化
|
||||
- 对话框在首次 show() 时才创建 DOM
|
||||
|
||||
### 2. 事件清理
|
||||
|
||||
- BaseManager 自动追踪订阅,destroy 时统一清理
|
||||
- 避免内存泄漏
|
||||
|
||||
### 3. DOM 操作优化
|
||||
|
||||
- 使用 requestAnimationFrame 节流拖拽/缩放
|
||||
- 批量 DOM 更新
|
||||
|
||||
### 4. 主题切换
|
||||
|
||||
- 使用 CSS 变量,避免重绘整个组件树
|
||||
- 订阅机制确保只更新必要的组件
|
||||
- `EngineManager` 从“大量透传”收敛为“少量公共 API + 生命周期编排”
|
||||
- 内部 Manager 不再通过 `registry.engine3d?.xxx()` 间接透传,而是通过 `BaseManager.engineComponent` 直接访问 `Engine` 组件
|
||||
- 非 Manager 组件(如 toolbar 按钮、walk-path-panel)通过 `registry.engine3d?.getEngineComponent()?.xxx()` 访问 `Engine`
|
||||
- `Engine.getEngine()` 已移除,改为 `onRawEvent()/offRawEvent()` 订阅底层事件
|
||||
|
||||
---
|
||||
|
||||
**文档生成时间**: 2026-01-23
|
||||
**文档版本**: 1.0.0
|
||||
## 分层结构
|
||||
|
||||
```text
|
||||
BimEngine (入口 / Facade)
|
||||
└─ ManagerRegistry (实例级,不是全局单例)
|
||||
├─ Managers (业务编排)
|
||||
│ ├─ EngineManager (公共 API + 初始化 + 右键菜单组装)
|
||||
│ ├─ *DialogManager / *ControlManager
|
||||
│ └─ ...
|
||||
├─ Components (UI + Engine 封装)
|
||||
│ ├─ Engine (封装 iflow-engine-base)
|
||||
│ ├─ Dialog/Toolbar/Panel...
|
||||
│ └─ ...
|
||||
├─ Services (Theme / Locale)
|
||||
└─ Core (EventEmitter / BaseManager / BaseDialogManager)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ManagerRegistry 设计
|
||||
|
||||
`ManagerRegistry` 是 **实例模式**:每个 `BimEngine` 都创建独立 registry,支持多实例隔离。
|
||||
|
||||
- ✅ 无全局共享状态污染
|
||||
- ✅ 多画布/多引擎实例可并行
|
||||
- ✅ 事件总线隔离
|
||||
|
||||
关键文件:`src/core/manager-registry.ts`
|
||||
|
||||
---
|
||||
|
||||
## Engine 访问策略(最新)
|
||||
|
||||
### 1) 外部 SDK 用户
|
||||
|
||||
通过 `bimEngine.engine`(`EngineManager`)调用稳定公共 API:
|
||||
|
||||
- `initialize(options?)`
|
||||
- `isInitialized()`
|
||||
- `loadModel(urls, options?)`
|
||||
- `pauseRendering()`
|
||||
- `resumeRendering()`
|
||||
- `destroy()`
|
||||
|
||||
### 2) 内部 Manager(BaseManager 子类)
|
||||
|
||||
通过 `BaseManager` 新增 getter:
|
||||
|
||||
```ts
|
||||
protected get engineComponent(): Engine | null
|
||||
```
|
||||
|
||||
直接调用 `Engine` 组件方法,例如:
|
||||
|
||||
```ts
|
||||
this.engineComponent?.activeSection('box');
|
||||
this.engineComponent?.setWalkSpeed(speed);
|
||||
```
|
||||
|
||||
### 3) 非 Manager 组件
|
||||
|
||||
通过 registry 获取组件实例:
|
||||
|
||||
```ts
|
||||
registry.engine3d?.getEngineComponent()?.toggleMiniMap();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## EngineManager 职责边界
|
||||
|
||||
`EngineManager` 现在聚焦在“入口能力 + 组装能力”:
|
||||
|
||||
1. 创建并持有 `Engine` 组件实例
|
||||
2. 初始化并持有 `RightKeyManager`
|
||||
3. 注册右键菜单项并编排右键行为
|
||||
4. 对外暴露少量稳定 API
|
||||
|
||||
不再承担:
|
||||
|
||||
- 对 `Engine` 全量方法的 1:1 透传
|
||||
|
||||
关键文件:`src/managers/engine-manager.ts`
|
||||
|
||||
---
|
||||
|
||||
## 原始引擎事件订阅策略
|
||||
|
||||
为避免暴露底层 raw engine 实例,`Engine` 组件提供:
|
||||
|
||||
- `onRawEvent(event, handler)`
|
||||
- `offRawEvent(event, handler)`
|
||||
|
||||
例如测量与剖切盒事件:
|
||||
|
||||
```ts
|
||||
this.engineComponent?.onRawEvent('measure-changed', handler);
|
||||
this.engineComponent?.offRawEvent('measure-changed', handler);
|
||||
```
|
||||
|
||||
关键文件:`src/components/engine/index.ts`
|
||||
|
||||
---
|
||||
|
||||
## 典型调用链
|
||||
|
||||
### Toolbar Home
|
||||
|
||||
```text
|
||||
Button onClick
|
||||
-> registry.engine3d?.getEngineComponent()?.CameraGoHome()
|
||||
-> Engine.CameraGoHome()
|
||||
-> rawEngine.viewCube.CameraGoHome()
|
||||
```
|
||||
|
||||
### Section Box
|
||||
|
||||
```text
|
||||
SectionBoxDialogManager (BaseDialogManager)
|
||||
-> this.engineComponent?.activeSection('box')
|
||||
-> Engine.activeSection('box')
|
||||
-> rawEngine.clipping.active('box')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 设计收益
|
||||
|
||||
- 降低中间层重复代码:`EngineManager` 大幅瘦身
|
||||
- 减少三层转发链:Manager 可直接访问 `Engine` 组件
|
||||
- 对外 API 稳定:仍保留 `bimEngine.engine` 作为入口
|
||||
- 保持封装边界:不暴露 raw engine,仅开放受控事件桥接
|
||||
|
||||
---
|
||||
|
||||
## 后续建议
|
||||
|
||||
当前已完成“Manager 瘦身”阶段。若后续继续演进,可考虑将 `Engine` 组件按领域继续拆分(measure/section/walk/model/scene),进一步降低单类复杂度。
|
||||
|
||||
Reference in New Issue
Block a user