# 多 Work (沙盘) 场景 (Multi-Work / Sandbox)

- **Summary**: 在同一个 Five 实例中加载和管理多个 Work 数据（如沙盘场景），实现多楼盘/多户型的同屏展示与切换。
- **Schema**: `Work[]`, `LooseWorkWithExtrinsics`, `FiveLoadOptions`.
- **Concepts**: WorkCode, Works Collection, Coordinate Transform.
- **Configuration**: `five.load()` 参数配置。
- **Examples**: 加载多个 Work、动态添加 Work、Work 间切换。

## Schema

> **Definition**: [FiveLoadOptions](../../five/application/fiveLoadOptions.d.ts), [LooseWorkWithExtrinsics](../../five/work/looseWorkType.d.ts)

### 核心类型定义

```typescript
// 输入数据类型
type FiveLoadInput =
  | Work[]
  | LooseWorkWithExtrinsics[]
  | string[]; // JSON Strings

// 带位姿信息的 LooseWork (用于调整模型在场景中的位置)
interface LooseWorkWithExtrinsics {
  work: LooseWork;      // Work 数据内容
  transform?: number[]; // 4x4 变换矩阵 (数组格式), 用于整体偏移/旋转该 Work
  workCode?: string;    // 强制指定 WorkCode (覆盖数据中的默认值)
}

// 加载选项
interface FiveLoadOptions {
  mode?: "replace" | "add"; // 替换当前场景 vs 追加到当前场景
  // ... 其他常规选项
}
```

## Concepts

### 1. Works Collection (数据集)
当 Five 加载多个 Work 时，它们被存储在 `five.works` 集合中。
- `five.works` 是一个增强的只读数组 (`ReadonlyArray<Work>`)。
- 提供了 `getWork(workCode)` 和 `getObserver(panoId)` 等辅助方法。

### 2. WorkCode (唯一标识)
在多 Work 场景中，`workCode` 是区分不同 Work 的关键。
- **必须唯一**: 加载的每个 Work 必须有唯一的 `workCode`。如果数据中重复，建议在加载时通过 `ParseOptions` 重写。
- **状态索引**: `five.state` 和 `five.setState` 中需要通过 `workCode` 来指定目标 Work。

### 3. Coordinate Alignment (坐标对齐)
不同 Work 的原始模型可能位于其各自的坐标系原点。为了在沙盘中正确排列它们（避免重叠），需要使用 `transform` 矩阵对每个 Work 进行空间变换（平移、旋转）。

## Configuration

`five.load(input, state, options)` 是入口方法。

| 参数 | 类型 | 说明 |
| :--- | :--- | :--- |
| `input` | `FiveLoadInput` | Work 数据数组。 |
| `options.mode` | `"replace" \| "add"` | 默认为 `"replace"` (清空当前场景)。设为 `"add"` 可保留现有场景并追加新 Work。 |

## Examples

### 1. 基础加载 (Basic Loading)
一次性加载两个 Work。

```typescript
const works = [workDataA, workDataB];

five.load(works).then(() => {
  console.log("所有 Work 加载完成");
  console.log("当前 Works:", five.works);
});
```

### 2. 沙盘排布 (Sandbox Layout)
使用 `transform` 将两个户型并排摆放。

```typescript
import * as THREE from "three";

// 创建变换矩阵：向 X 轴正方向平移 20 米
const matrixB = new THREE.Matrix4().makeTranslation(20, 0, 0);

const input = [
  // Work A: 保持在原点
  { work: workDataA },
  // Work B: 偏移位置
  {
    work: workDataB,
    transform: matrixB.toArray()
  }
];

five.load(input);
```

### 3. 动态添加 (Dynamic Add)
在不销毁当前场景的情况下，追加一个新的 Work。

```typescript
// 假设当前已经加载了 Work A
// 追加 Work C
five.load(workDataC, undefined, {
  mode: "add" // 关键参数
}).then(() => {
  console.log("Work C 已加入场景");
});
```

### 4. Work 间切换 (Switching Works)
跳转到指定 Work 的特定点位。

```typescript
const targetWorkCode = "work-id-b";

// 方式一：指定点位索引
five.setState({
  workCode: targetWorkCode,
  panoIndex: 0, // 目标 Work 的第 0 个点位
  mode: "Panorama"
});

// 方式二：查找特定 Work
const targetWork = five.works.getWork(targetWorkCode);
if (targetWork) {
  // 你的业务逻辑
}
```

## Common Pitfalls

1.  **WorkCode 冲突**: 如果加载的两个 Work 拥有相同的 `workCode`，SDK 行为可能不可预期。务必确保唯一性。
    *   *解决*: 在构造 `LooseWorkWithExtrinsics` 时传入 `workCode` 字段强制覆盖。
2.  **资源隔离**: 每个 Work 的纹理/模型请求是独立的。如果 Work A 和 Work B 引用了相同的 URL 资源，浏览器缓存会生效，但 SDK 内部逻辑仍视为独立资源。
3.  **内存压力**: 同时加载多个大型模型会显著增加显存占用。建议根据设备性能限制同时加载的 Work 数量。
4.  **坐标系混淆**: `transform` 仅影响 3D 模型的渲染位置。Work 内部的点位坐标 (`observer.position`) 也会自动应用此变换，但开发者在进行手动射线检测或计算时，需注意区分**原始坐标**和**世界坐标**。

## Related

*   [Five](./five.md): Five 核心类说明。
*   [State](./state.md): 状态管理说明。
*   [Work](./work.md): Work 数据结构详解。

---
```yaml
tags: [多场景, 沙盘, 多户型, 拼接, 追加, 切换场景, 多楼盘, multi-work, sandbox, works, load, transform, workCode]
```
