- Update method references: activateSectionAxis → activeSection - Update call chains for all clipping modes (x/y/z/box/face) - Document new unified API: engine.clipping.active(mode) - Mark deprecated methods: fitSectionBoxToModel, resetSectionBox - Update underlying API: updateClippingValue instead of setboxPercent
680 lines
21 KiB
Markdown
680 lines
21 KiB
Markdown
# Engine API 接口对接指南
|
||
|
||
本文档详细说明 iflow-engine SDK 中 Toolbar 按钮如何调用底层 3D 引擎 API 的完整调用链。
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
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() 创建实例 │
|
||
└─────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 调用链层级
|
||
|
||
| 层级 | 文件位置 | 职责 |
|
||
|------|----------|------|
|
||
| **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 渲染和功能实现 |
|
||
|
||
---
|
||
|
||
## 完整调用链示例:剖切盒
|
||
|
||
以剖切盒(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);
|
||
}
|
||
}
|
||
```
|
||
|
||
**要点**:
|
||
- 继承 `BaseDialogManager`,自动获得 `show()/hide()/toggle()` 方法
|
||
- 通过 `this.registry.engine3d` 访问 EngineManager
|
||
- Panel 的回调函数中调用 EngineManager 方法
|
||
- 生命周期钩子:`onDialogCreated` / `onBeforeDestroy` / `onDialogClose`
|
||
|
||
### 3. EngineManager 封装
|
||
|
||
**文件**: `src/managers/engine-manager.ts`
|
||
|
||
```typescript
|
||
export class EngineManager extends BaseManager {
|
||
private engineInstance: Engine | null = null;
|
||
|
||
// 激活剖切(统一入口)
|
||
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 组件
|
||
}
|
||
|
||
// 获取当前剖切模式
|
||
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);
|
||
}
|
||
|
||
// ... 其他方法
|
||
}
|
||
```
|
||
|
||
**要点**:
|
||
- 每个方法都要检查 `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
|
||
}
|
||
}
|
||
```
|
||
|
||
**要点**:
|
||
- 维护功能状态(`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
|
||
```
|
||
|
||
---
|
||
|
||
## 各层职责说明
|
||
|
||
### L1: Toolbar Button
|
||
|
||
| 职责 | 说明 |
|
||
|------|------|
|
||
| 定义按钮配置 | `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 渲染 |
|
||
| 功能实现 | 测量、剖切、漫游等实际功能 |
|
||
| 场景管理 | 模型加载、相机控制等 |
|
||
|
||
---
|
||
|
||
## 新增功能对接步骤
|
||
|
||
以新增一个 "XX 功能" 为例:
|
||
|
||
### 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();
|
||
}
|
||
}
|
||
};
|
||
};
|
||
```
|
||
|
||
### Step 2: 注册按钮到 Toolbar
|
||
|
||
**文件**: `src/components/button-group/toolbar/index.ts`
|
||
|
||
```typescript
|
||
// 添加导入
|
||
const { createXxButton } = await import('./buttons/xx');
|
||
|
||
// 添加按钮
|
||
this.addButton(createXxButton());
|
||
```
|
||
|
||
### Step 3: 创建 DialogManager
|
||
|
||
**文件**: `src/managers/xx-dialog-manager.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
|
||
public activateXx(): void {
|
||
if (!this.engineInstance) {
|
||
console.warn('[EngineManager] 3D Engine not initialized.');
|
||
return;
|
||
}
|
||
this.engineInstance.activateXx();
|
||
}
|
||
|
||
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();
|
||
}
|
||
```
|
||
|
||
### Step 7: 在 Engine 组件中实现
|
||
|
||
**文件**: `src/components/engine/index.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;
|
||
}
|
||
|
||
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();
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 数据流向
|
||
|
||
### 用户操作 → 底层引擎
|
||
|
||
```
|
||
用户点击按钮
|
||
↓
|
||
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)
|
||
```
|
||
|
||
---
|
||
|
||
## 常见问题
|
||
|
||
### 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 |
|
||
| **底层引擎** | 实际功能实现 |
|
||
|
||
遵循这个分层架构,可以保持代码的清晰性和可维护性。
|