# topology-drawer

在 3D 场景中交互式绘制拓扑路径图。点击场景中的任意对象表面放置节点，自动连接成路径；点击空白处则落回不可见的地面平面。事件检测通过 `viewer.interactionManager` 实现（`scene.addEventListener` 冒泡）。

```typescript
import { TopologyDrawer } from 'u-space/plugins/topology-drawer';

const drawer = new TopologyDrawer(viewer, {
  groundY: 0,       // 空白区域兜底地面的 Y 坐标，默认 0
  snapRadius: 1.0,  // 自动吸附已有节点的世界单位半径，默认 1.0
  nodeRadius: 0.3,
  nodeColor: 0x0088ff,
  edgeRadius: 0.1,
  edgeColor: 0x00ddff,
  previewColor: 0xffff00, // 预览线颜色
});

drawer.enable();
```

**操作说明：**

| 操作 | 效果 |
| :--- | :--- |
| 左键点击空白 / 模型表面 | 放置新节点，与上一节点自动连边 |
| 左键点击已有节点（`snapRadius` 范围内） | 吸附并连接到该节点，不新增节点 |
| 左键点击已有边 | 切断该边，在点击处插入新节点并重连两端 |
| 右键 / `Escape` | 结束当前路径链，下次点击开始新路径 |
| `Ctrl / Cmd + Z` | 撤销最后一步（节点、边或结束路径） |

**方法：**

| 方法 | 说明 |
| :--- | :--- |
| `enable()` | 进入绘制模式，将拓扑与地面平面加入场景。同时自动将 `viewer.interactionManager.pointerMoveEventsEnabled` 置为 `true`（用于预览线跟随光标），并在 `disable()` 时恢复为原值。 |
| `disable()` | 退出绘制模式，已绘内容仍保留在场景中。恢复 `pointerMoveEventsEnabled` 为调用 `enable()` 前的值。 |
| `finishPath()` | 结束当前路径链（等同于右键 / Escape）。 |
| `undo()` | 撤销最后一步操作。 |
| `clear()` | 清空所有已绘节点、边和路径。 |
| `exportData()` | 以 JSON 可序列化格式导出当前拓扑数据。 |
| `importData(data)` | 加载 `exportData()` 返回的数据，替换当前内容。 |
| `dispose()` | 完全释放所有资源并从场景移除拓扑。 |

**属性：**

| 属性 | 类型 | 说明 |
| :--- | :--- | :--- |
| `topology` | `Topology` | 底层 `Topology` 对象，可进一步访问节点、边和路径方法。 |

**导出 / 导入：**

```typescript
// 导出
const data = drawer.exportData();
// data: { nodes: Record<string, {x,y,z}>, edges: Array<{from,to,weight}> }

// 导入
drawer.importData(data);
```

**结合 `SceneLoader` 在模型表面绘制：**

```typescript
import { SceneLoader } from 'u-space/plugins/u-manager';
import { TopologyDrawer } from 'u-space/plugins/topology-drawer';

const sceneLoader = new SceneLoader(viewer);
sceneLoader.setPath('./scenes/my-scene');
const group = await sceneLoader.loadAsync();
viewer.scene.add(group);

const drawer = new TopologyDrawer(viewer, { snapRadius: 1.0 });
drawer.enable(); // 可直接在加载的模型表面点击绘制
```
