diff --git a/.sisyphus/drafts/TOOLBAR_API_CALLCHAIN.md b/.sisyphus/drafts/TOOLBAR_API_CALLCHAIN.md new file mode 100644 index 0000000..3bc5006 --- /dev/null +++ b/.sisyphus/drafts/TOOLBAR_API_CALLCHAIN.md @@ -0,0 +1,733 @@ +# Toolbar 按钮调用链文档 + +本文档详细记录 Toolbar 中每个按钮的完整调用链,从用户点击到底层 3D 引擎 API。 + +--- + +## 目录 + +1. [首页 (Home)](#1-首页-home) +2. [框选放大 (Zoom Box)](#2-框选放大-zoom-box) +3. [测量 (Measure)](#3-测量-measure) +4. [剖切菜单 (Section)](#4-剖切菜单-section) + - [拾取面剖切 (Section Plane)](#41-拾取面剖切-section-plane) + - [轴向剖切 (Section Axis)](#42-轴向剖切-section-axis) + - [剖切盒 (Section Box)](#43-剖切盒-section-box) +5. [漫游 (Walk)](#5-漫游-walk) +6. [地图 (Map)](#6-地图-map) +7. [构件详情 (Property)](#7-构件详情-property) +8. [设置 (Setting)](#8-设置-setting) +9. [信息 (Info)](#9-信息-info) +10. [全屏 (Fullscreen)](#10-全屏-fullscreen) + +--- + +## 1. 首页 (Home) + +**按钮 ID**: `home` +**功能**: 相机回到初始位置(主视角) +**保持激活**: 否 + +### 调用链 + +``` +用户点击 [首页] 按钮 + │ + ▼ +Button onClick (buttons/home/index.ts) + │ registry.engine3d?.CameraGoHome() + ▼ +EngineManager.CameraGoHome() (managers/engine-manager.ts) + │ this.engineInstance.CameraGoHome() + ▼ +Engine.CameraGoHome() (components/engine/index.ts) + │ this.engine.viewCube.CameraGoHome() + ▼ +底层引擎 API: engine.viewCube.CameraGoHome() +``` + +### 代码摘要 + +**按钮定义** (`buttons/home/index.ts`): +```typescript +onClick: (button) => { + const registry = ManagerRegistry.getInstance(); + registry.engine3d?.CameraGoHome(); +} +``` + +**EngineManager** (`managers/engine-manager.ts`): +```typescript +public CameraGoHome(): void { + this.engineInstance.CameraGoHome(); +} +``` + +**Engine 组件** (`components/engine/index.ts`): +```typescript +public CameraGoHome() { + this.engine.viewCube.CameraGoHome(); +} +``` + +--- + +## 2. 框选放大 (Zoom Box) + +**按钮 ID**: `zoom-box` +**功能**: 激活框选放大模式,用户可以框选区域进行放大 +**保持激活**: 否 + +### 调用链 + +``` +用户点击 [框选放大] 按钮 + │ + ▼ +Button onClick (buttons/zoom-box/index.ts) + │ registry.engine3d?.activateZoomBox() + ▼ +EngineManager.activateZoomBox() (managers/engine-manager.ts) + │ this.engineInstance.activateZoomBox() + ▼ +Engine.activateZoomBox() (components/engine/index.ts) + │ this.engine.rangeScale?.active() + ▼ +底层引擎 API: engine.rangeScale.active() +``` + +### 代码摘要 + +**按钮定义** (`buttons/zoom-box/index.ts`): +```typescript +onClick: () => { + const registry = ManagerRegistry.getInstance(); + registry.engine3d?.activateZoomBox(); +} +``` + +**EngineManager** (`managers/engine-manager.ts`): +```typescript +public activateZoomBox(): void { + this.engineInstance.activateZoomBox(); +} +``` + +**Engine 组件** (`components/engine/index.ts`): +```typescript +public activateZoomBox(): void { + this.engine.rangeScale?.active(); +} +``` + +--- + +## 3. 测量 (Measure) + +**按钮 ID**: `measure` +**功能**: 打开测量对话框,支持距离、角度、标高、体积等多种测量 +**保持激活**: 是 + +### 调用链 + +#### 3.1 打开测量对话框 + +``` +用户点击 [测量] 按钮(激活) + │ + ▼ +Button onClick (buttons/measure/index.ts) + │ registry.measure?.show() + ▼ +MeasureDialogManager.show() (继承自 BaseDialogManager) + │ createContent() → 创建 MeasurePanel + │ onDialogCreated() + ▼ +对话框显示,等待用户选择测量模式 +``` + +#### 3.2 用户选择测量模式 + +``` +用户在 Panel 中选择测量模式(如:距离测量) + │ + ▼ +MeasurePanel.onModeChange('distance') + │ + ▼ +MeasureDialogManager 回调 (managers/measure-dialog-manager.ts) + │ registry.engine3d?.activateMeasure(mode) + ▼ +EngineManager.activateMeasure('distance') (managers/engine-manager.ts) + │ this.engineInstance.activateMeasure(mode) + ▼ +Engine.activateMeasure('distance') (components/engine/index.ts) + │ switch(mode) → activateDistanceMeasure() + ▼ +Engine.activateDistanceMeasure() + │ this.engine.measure.active() // 激活主测量模块 + │ this.engine.measure.distanceMeasure.active() // 激活距离测量 + ▼ +底层引擎 API: + - engine.measure.active() + - engine.measure.distanceMeasure.active() +``` + +#### 3.3 清除所有测量 + +``` +用户点击 [删除全部] 按钮 + │ + ▼ +MeasurePanel.onClearAll() + │ + ▼ +MeasureDialogManager 回调 + │ registry.engine3d?.clearAllMeasures() + ▼ +EngineManager.clearAllMeasures() + │ this.engineInstance.clearAllMeasures() + ▼ +Engine.clearAllMeasures() + │ this.engine.measure.clearAll() + ▼ +底层引擎 API: engine.measure.clearAll() +``` + +#### 3.4 关闭测量对话框 + +``` +用户关闭对话框 / 再次点击按钮 + │ + ▼ +MeasureDialogManager.onBeforeDestroy() + │ registry.engine3d.deactivateMeasure() + ▼ +EngineManager.deactivateMeasure() + │ this.engineInstance.deactivateMeasure() + ▼ +Engine.deactivateMeasure() + │ this.engine.measure.disActive() + ▼ +底层引擎 API: engine.measure.disActive() +``` + +### 测量模式与底层 API 对照表 + +| 模式 | Engine 方法 | 底层 API | +|------|-------------|----------| +| distance (距离) | `activateDistanceMeasure()` | `engine.measure.distanceMeasure.active()` | +| minDistance (最小距离) | `activateMinDistanceMeasure()` | `engine.measure.clearDistanceMeasure.active()` | +| angle (角度) | `activateAngleMeasure()` | `engine.measure.angleMeasure.active()` | +| elevation (标高) | `activateElevationMeasure()` | `engine.measure.elevationMeasure.active()` | +| volume (体积) | `activateVolumeMeasure()` | `engine.measure.areaMeasure.active()` | +| slope (坡度) | `activateSlopeMeasure()` | `engine.measure.slopeMeasure.active()` | +| laserDistance (激光测距) | `activateLaserDistanceMeasure()` | ⚠️ 暂未实现 | +| spaceVolume (空间体积) | `activateSpaceVolumeMeasure()` | ⚠️ 暂未实现 | + +--- + +## 4. 剖切菜单 (Section) + +**按钮 ID**: `section` +**类型**: 菜单按钮,展开后显示子按钮 + +### 4.1 拾取面剖切 (Section Plane) + +**按钮 ID**: `section-plane` +**功能**: 拾取模型表面进行剖切 +**保持激活**: 是 +**互斥**: 是(同一时间只能激活一种剖切) + +#### 调用链 + +``` +用户点击 [拾取面剖切] 按钮(激活) + │ + ▼ +Button onClick (buttons/section/section-plane/index.ts) + │ registry.sectionPlane?.show() + ▼ +SectionPlaneDialogManager.show() (继承自 BaseDialogManager) + │ createContent() → 创建 SectionPlanePanel + │ onDialogCreated() + ▼ +对话框显示(拾取面剖切需要用户在3D场景中点击表面) +``` + +> ⚠️ **注意**: 拾取面剖切的底层 API 对接尚未完成,目前仅显示 UI 面板。 + +--- + +### 4.2 轴向剖切 (Section Axis) + +**按钮 ID**: `section-axis` +**功能**: 沿 X/Y/Z 轴向进行剖切 +**保持激活**: 是 +**互斥**: 是 + +#### 调用链 - 打开对话框 + +``` +用户点击 [轴向剖切] 按钮(激活) + │ + ▼ +Button onClick (buttons/section/section-axis/index.ts) + │ registry.sectionAxis?.show() + ▼ +SectionAxisDialogManager.show() (继承自 BaseDialogManager) + │ createContent() → 创建 SectionAxisPanel + │ onDialogCreated() + │ └── registry.engine3d.activateSectionAxis('x') // 默认激活 X 轴 + ▼ +EngineManager.activateSectionAxis('x') + │ this.engineInstance.activateSectionAxis(axis) + ▼ +Engine.activateSectionAxis('x') + │ this.engine.clipping.sectionPlaneX.active() + ▼ +底层引擎 API: engine.clipping.sectionPlaneX.active() +``` + +#### 调用链 - 切换轴向 + +``` +用户在 Panel 中切换轴向(如:Y 轴) + │ + ▼ +SectionAxisPanel.onAxisChange('y') + │ + ▼ +SectionAxisDialogManager 回调 + │ registry.engine3d?.activateSectionAxis('y') + ▼ +EngineManager.activateSectionAxis('y') + │ this.engineInstance.activateSectionAxis(axis) + ▼ +Engine.activateSectionAxis('y') + │ // 1. 先停用当前轴向 (X) + │ this.engine.clipping.sectionPlaneX.disActive() + │ // 2. 再激活新轴向 (Y) + │ this.engine.clipping.sectionPlaneY.active() + ▼ +底层引擎 API: + - engine.clipping.sectionPlaneX.disActive() + - engine.clipping.sectionPlaneY.active() +``` + +#### 调用链 - 关闭对话框 + +``` +用户关闭对话框 / 再次点击按钮 + │ + ▼ +SectionAxisDialogManager.onBeforeDestroy() + │ registry.engine3d?.deactivateSectionAxis() + ▼ +EngineManager.deactivateSectionAxis() + │ this.engineInstance.deactivateSectionAxis() + ▼ +Engine.deactivateSectionAxis() + │ this.engine.clipping.disActive() + ▼ +底层引擎 API: engine.clipping.disActive() +``` + +#### 轴向与底层 API 对照表 + +| 轴向 | Engine 内部调用 | 底层 API | +|------|----------------|----------| +| x | `planeMap['x']` | `engine.clipping.sectionPlaneX.active()` | +| y | `planeMap['y']` | `engine.clipping.sectionPlaneY.active()` | +| z | `planeMap['z']` | `engine.clipping.sectionPlaneZ.active()` | + +--- + +### 4.3 剖切盒 (Section Box) + +**按钮 ID**: `section-box` +**功能**: 六面体剖切盒,可调节 X/Y/Z 轴的最小/最大范围 +**保持激活**: 是 +**互斥**: 是 + +#### 调用链 - 打开对话框 + +``` +用户点击 [剖切盒] 按钮(激活) + │ + ▼ +Button onClick (buttons/section/section-box/index.ts) + │ registry.sectionBox?.show() + ▼ +SectionBoxDialogManager.show() (继承自 BaseDialogManager) + │ createContent() → 创建 SectionBoxPanel + │ onDialogCreated() + │ └── registry.engine3d?.activateSectionBox() + ▼ +EngineManager.activateSectionBox() + │ this.engineInstance.activateSectionBox() + ▼ +Engine.activateSectionBox() + │ this.engine.clipping.sectionBox.active() + ▼ +底层引擎 API: engine.clipping.sectionBox.active() +``` + +#### 调用链 - 调整范围(拖动滑块) + +``` +用户拖动 X/Y/Z 轴滑块调整范围 + │ + ▼ +SectionBoxPanel.onRangeChange(range) + │ range = { x: {min, max}, y: {min, max}, z: {min, max} } // 百分比 0-100 + ▼ +SectionBoxDialogManager 回调 + │ registry.engine3d?.setSectionBoxRange(range) + ▼ +EngineManager.setSectionBoxRange(range) + │ this.engineInstance.setSectionBoxRange(range) + ▼ +Engine.setSectionBoxRange(range) + │ this.engine.clipping.sectionBox.setboxPercent(range) // 直传百分比 + ▼ +底层引擎 API: engine.clipping.sectionBox.setboxPercent(range) + │ (底层负责百分比→坐标转换) +``` + +#### 调用链 - 适应到模型 + +``` +用户点击 [适应到模型] 按钮 + │ + ▼ +SectionBoxPanel.onFitToModel() + │ + ▼ +SectionBoxDialogManager 回调 + │ registry.engine3d?.fitSectionBoxToModel() + ▼ +EngineManager.fitSectionBoxToModel() + │ this.engineInstance.fitSectionBoxToModel() + ▼ +Engine.fitSectionBoxToModel() + │ const box = this.engine.octreeBox?.getBoundingBox() + │ this.engine.clipping.sectionBox.setBox(box) + ▼ +底层引擎 API: + - engine.octreeBox.getBoundingBox() + - engine.clipping.sectionBox.setBox(box) +``` + +#### 调用链 - 重置 + +``` +用户点击 [重置] 按钮 + │ + ▼ +SectionBoxPanel.onReset() + │ + ▼ +SectionBoxDialogManager 回调 + │ registry.engine3d?.resetSectionBox() + ▼ +EngineManager.resetSectionBox() + │ this.engineInstance.resetSectionBox() + ▼ +Engine.resetSectionBox() + │ this.fitSectionBoxToModel() // 内部调用适应到模型 + ▼ +(同上 "适应到模型" 流程) +``` + +#### 调用链 - 关闭对话框 + +``` +用户关闭对话框 / 再次点击按钮 + │ + ▼ +SectionBoxDialogManager.onBeforeDestroy() + │ registry.engine3d?.deactivateSectionBox() + ▼ +EngineManager.deactivateSectionBox() + │ this.engineInstance.deactivateSectionBox() + ▼ +Engine.deactivateSectionBox() + │ this.engine.clipping.sectionBox.disActive() + ▼ +底层引擎 API: engine.clipping.sectionBox.disActive() +``` + +#### 剖切盒 API 汇总 + +| 操作 | Engine 方法 | 底层 API | +|------|-------------|----------| +| 激活 | `activateSectionBox()` | `engine.clipping.sectionBox.active()` | +| 停用 | `deactivateSectionBox()` | `engine.clipping.sectionBox.disActive()` | +| 设置范围(百分比) | `setSectionBoxRange(range)` | `engine.clipping.sectionBox.setboxPercent(range)` | +| 设置范围(坐标) | - | `engine.clipping.sectionBox.setboxXyz(xyz)` | +| 适应到模型 | `fitSectionBoxToModel()` | `engine.clipping.sectionBox.setBox(box)` | +| 获取包围盒 | - | `engine.octreeBox.getBoundingBox()` | + +#### 数据格式说明 + +| 层级 | 数据格式 | 示例 | +|------|----------|------| +| UI / SDK | 百分比 (0-100) | `{ x: {min: 20, max: 80}, y: {...}, z: {...} }` | +| 底层引擎 | 百分比 → 内部转换为坐标 | 底层负责转换 | + +--- + +## 5. 漫游 (Walk) - 第一人称漫游 + +> 标注说明: `[SDK]` = SDK 层代码, `[底层]` = bim_engine_base 底层引擎 + +**按钮 ID**: `walk` +**功能**: 打开漫游控制面板,支持第一人称漫游模式 +**保持激活**: 否 + +### 5.1 第一人称漫游模式开关 + +``` +UI 点击漫游按钮 + → [SDK] WalkControlPanel.onWalkModeToggle(isActive) + → [SDK] WalkControlManager.onWalkModeToggle + → [SDK] EngineManager.activateFirstPersonMode() / deactivateFirstPersonMode() + → [SDK] Engine.activateFirstPersonMode() / deactivateFirstPersonMode() + → [底层] engine.controlModule.switchFirstPersonMode() / switchDefaultMode() +``` + +### 5.2 速度调节 + +``` +UI 速度按钮 (1-10) + → [SDK] WalkControlPanel.onSpeedChange(speed) + → [SDK] WalkControlManager.onSpeedChange + → [SDK] EngineManager.setWalkSpeed(speed * 0.01) // 值转换: UI 1-10 → 引擎 0.01-0.1 + → [SDK] Engine.setWalkSpeed(speed) + → [底层] engine.controlModule.firstPersonControls.moveSpeed = speed +``` + +### 5.3 重力开关 + +``` +UI 重力复选框 + → [SDK] WalkControlPanel.onGravityToggle(enabled) + → [SDK] WalkControlManager.onGravityToggle + → [SDK] EngineManager.setWalkGravity(enabled) + → [SDK] Engine.setWalkGravity(enabled) + → [底层] engine.controlModule.firstPersonControls.applyGravity = enabled +``` + +### 5.4 碰撞检测开关 + +``` +UI 碰撞复选框 + → [SDK] WalkControlPanel.onCollisionToggle(enabled) + → [SDK] WalkControlManager.onCollisionToggle + → [SDK] EngineManager.setWalkCollision(enabled) + → [SDK] Engine.setWalkCollision(enabled) + → [底层] engine.controlModule.firstPersonControls.applyCollision = enabled +``` + +--- + +## 6. 地图 (Map) + +**按钮 ID**: `map` +**功能**: 打开/关闭地图对话框 +**保持激活**: 是 + +### 调用链 + +``` +用户点击 [地图] 按钮 + │ + ▼ +Button onClick (buttons/map/index.ts) + │ if (registry.map?.isOpen()) { + │ registry.map?.hide() + │ } else { + │ registry.map?.show() + │ } + ▼ +MapDialogManager.show() / hide() + │ + ▼ +地图对话框显示/隐藏 +``` + +### 事件监听 + +```typescript +// 按钮定义中监听地图开关事件,同步按钮激活状态 +registry.on('map:opened', () => { + registry.toolbar?.setBtnActive('map', true); +}); + +registry.on('map:closed', () => { + registry.toolbar?.setBtnActive('map', false); +}); +``` + +--- + +## 7. 构件详情 (Property) + +**按钮 ID**: `property` +**功能**: 打开构件属性面板 +**保持激活**: 否 + +### 调用链 + +``` +用户点击 [构件详情] 按钮 + │ + ▼ +Button onClick (buttons/property/index.ts) + │ registry.propertyPanel?.show() + ▼ +PropertyPanelManager.show() + │ + ▼ +属性面板显示 +``` + +--- + +## 8. 设置 (Setting) + +**按钮 ID**: `setting` +**功能**: 打开设置面板 +**保持激活**: 否 + +### 调用链 + +``` +用户点击 [设置] 按钮 + │ + ▼ +Button onClick (buttons/setting/index.ts) + │ console.log('设置按钮被点击') + ▼ +⚠️ 暂未实现具体功能 +``` + +--- + +## 9. 信息 (Info) + +**按钮 ID**: `info` +**功能**: 打开信息对话框 +**保持激活**: 否 + +### 调用链 + +``` +用户点击 [信息] 按钮 + │ + ▼ +Button onClick (buttons/info/index.ts) + │ registry.emit('ui:open-dialog', { id: 'info' }) + ▼ +事件系统广播 'ui:open-dialog' 事件 + │ + ▼ +监听该事件的组件响应并打开信息对话框 +``` + +--- + +## 10. 全屏 (Fullscreen) + +**按钮 ID**: `fullscreen` +**功能**: 切换全屏模式 +**保持激活**: 否 + +### 调用链 + +``` +用户点击 [全屏] 按钮 + │ + ▼ +Button onClick (buttons/fullscreen/index.ts) + │ // 检测当前是否全屏 + │ const isFullscreen = !!document.fullscreenElement + │ + ├── 如果不是全屏: + │ targetElem.requestFullscreen({ navigationUI: 'hide' }) + │ ▼ + │ 浏览器 API: Element.requestFullscreen() + │ + └── 如果是全屏: + document.exitFullscreen() + ▼ + 浏览器 API: Document.exitFullscreen() +``` + +### 注意事项 + +- 全屏功能使用浏览器原生 API,不涉及底层 3D 引擎 +- 支持多种浏览器前缀:`webkitRequestFullscreen`、`mozRequestFullScreen`、`msRequestFullscreen` +- 在 iframe 中使用需要父级 iframe 标签添加 `allow="fullscreen"` 属性 + +--- + +## 附录:调用链层级说明 + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ L1: Toolbar Button │ +│ 文件: src/components/button-group/toolbar/buttons/*/index.ts │ +│ 职责: 定义按钮配置,处理 onClick 事件 │ +└────────────────────────────────┬────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ L2: DialogManager │ +│ 文件: src/managers/*-dialog-manager.ts │ +│ 职责: 管理对话框生命周期,绑定 Panel 回调到 EngineManager │ +└────────────────────────────────┬────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ L3: EngineManager │ +│ 文件: src/managers/engine-manager.ts │ +│ 职责: 代理 Engine 组件,提供统一 API,检查初始化状态 │ +└────────────────────────────────┬────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ L4: Engine Component │ +│ 文件: src/components/engine/index.ts │ +│ 职责: 封装底层引擎,维护状态,进行数据转换 │ +└────────────────────────────────┬────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ L5: 底层 3D 引擎 (iflow-engine-base) │ +│ 来源: 第三方 SDK │ +│ 职责: 实际 3D 渲染和功能实现 │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 附录:ManagerRegistry 访问方式 + +```typescript +// 在 Button 中获取 Registry +const registry = ManagerRegistry.getInstance(); + +// 访问各个 Manager +registry.engine3d // EngineManager - 3D 引擎 +registry.toolbar // ToolbarManager - 工具栏 +registry.dialog // DialogManager - 对话框 +registry.measure // MeasureDialogManager - 测量 +registry.sectionPlane // SectionPlaneDialogManager - 拾取面剖切 +registry.sectionAxis // SectionAxisDialogManager - 轴向剖切 +registry.sectionBox // SectionBoxDialogManager - 剖切盒 +registry.walkControl // WalkControlManager - 漫游控制 +registry.map // MapDialogManager - 地图 +registry.propertyPanel // PropertyPanelManager - 属性面板 +```