# 预览视图（PreviewView）

> 文档：`vjcad/doc/ui/preview-view.md`
> 示例：`vjcad/example/ui/03previewview.js` — 基础 PreviewView
## 1. 用途

`PreviewView` 是一个独立 Web Component，用来在 **不影响主视图** 的前提下显示 CAD 实体或文档。常见场景：

- 块 / 符号库小图预览
- 对话框里"预览效果"
- 缩略图 / 截图
- Hover 提示框中显示局部内容

它是只读 / 极简的：**没有命令系统，没有侧边栏**，只支持基本缩放和平移。

## 2. 模式 1：addEntity

```js
const { PreviewView, LineEnt, CircleEnt, TextEnt } = vjcad;

const preview = new PreviewView({
    backgroundColor: 0x1a242e,    // 深色背景
    interactive: true,            // 允许鼠标缩放 / 平移
    defaultColor: 0x00ff00        // 默认绘制颜色（绿色）
});

document.getElementById('preview-container').appendChild(preview);
await preview.onLoad();           // 等待初始化

const line = new LineEnt([0, 0], [100, 50]); line.setDefaults();
preview.addEntity(line);                    // 用默认颜色

const circle = new CircleEnt([50, 50], 30); circle.setDefaults();
preview.addEntity(circle, 0xff0000);        // 第二参数覆盖颜色

preview.zoomExtents();                      // 缩放到所有内容（方法名是 zoomExtents，不是 zoomToFit）
```

特点：

- 实体只是**绘制**进预览，不会真的写入文档；`addEntity` 不触发回调 / 事件
- 第二个 `color` 参数是 RGB 数值（0xRRGGBB），不是 ACI 颜色索引
- ⚠️ `addEntity` 用的是不依赖 Engine 的简化绘制器，**只画 `LINE / CIRCLE / ARC / ELLIPSE`**，`SPLINE` 画成控制点折线；分支里判断的是 `'POLYLINE'`，而 `PolylineEnt.type` 实际是 `'PLINE'`，所以**多段线也画不出**；文字、填充、块、标注等一律忽略。要预览这些用 `loadDocument`（DbDoc 数据）或 `renderEntitiesDirect(entities)`（用当前文档上下文的完整渲染器，实体须来自当前文档）

## 3. 模式 2：loadDocument（含图层 / 块 / 完整数据）

```js
import { Engine } from 'vjcad';

// loadDocument 接收 DbDoc 结构：可来自 Engine.currentDoc.toDb()，或手工拼装 DbDoc 字面量
const fullDoc = Engine.currentDoc.toDb();

// PreviewViewConfig 仅 5 键：backgroundColor/backgroundAlpha/interactive/defaultColor/resolution
// （showAxis、showUcsIcon 属于 MainViewConfig，PreviewView 不支持）
const preview = new PreviewView({
    backgroundColor: 0x1a242e,
    interactive: true
});
container.appendChild(preview);
await preview.onLoad();
await preview.loadDocument(fullDoc);          // 真正还原图层 / 颜色 / 字体
preview.zoomExtents();
```

`loadDocument` 与 `addEntity` 区别：

| 特性 | `addEntity` | `loadDocument` |
|---|---|---|
| 数据形态 | 单个实体对象（仅线 / 圆 / 弧 / 椭圆 / 样条近似） | 完整 vjcad 文档（含图层 / 字体 / 块，各类实体） |
| 颜色 | 必须自己传或用默认 | 还原原图所有属性 |
| 字体 / 中文 | 默认字体 | 文档自带字体 |
| 用途 | 临时预览几条线 | 真实图纸缩略 / 别处加载的图 |

## 4. PreviewView 选项

```ts
// 真实签名：PreviewViewConfig——只有这 5 个键
interface PreviewViewConfig {
    backgroundColor?: number;       // 背景色，默认 0x1a242e
    backgroundAlpha?: number;       // 背景透明度 0-1，默认 1（设 0 可导出透明 PNG）
    interactive?: boolean;          // 是否允许鼠标交互（缩放 / 平移），默认 true
    defaultColor?: number;          // 实体默认绘制颜色，默认白色 0xffffff
    resolution?: number;            // canvas 像素比，默认 window.devicePixelRatio（离屏导出设 1）
}
```

> ⚠️ 上游文档把 `showAxis`/`showUcsIcon`/`showFps`/`fontPath`/`fontName`/`fontKind` 列进 PreviewView 选项——**这些键在 `PreviewViewConfig` 里不存在**，传了会被忽略（`showUcsIcon` 等是 `MainViewConfig` 的键，不是 PreviewView 的）。

## 5. 常用方法

```ts
class PreviewView extends LitElement {
    onLoad(): Promise<void>;
    addEntity(ent: EntityBase, color?: number): void;
    addEntities(ents: EntityBase[], color?: number): void;
    clear(): void;                                        // 清空所有实体
    loadDocument(dbDocument: any, blockName?: string): Promise<void>;
    zoomExtents(): void;
    setView(center: Point2D, zoom: number): void;         // 定点缩放
    get zoom(): number;                                   // 当前缩放 —— getter，写 preview.zoom，不要加 ()
    get viewCenter(): Point2D;                            // getter（返回副本）
    get document(): CadDocument | null;                   // getter：loadDocument 后的预览文档
    renderEntitiesDirect(entities: EntityBase[], backgroundGray?: number): void;
    toThumbnail(width?: number, height?: number, format?: 'png' | 'jpeg'): string;  // dataURL
    getEntityBounds(): BoundingBox | null;
    forceResize(): void;
}
```

> ⚠️ `zoomToFit` / `zoomScale` / `pan` / `setBackground` / `captureScreenshot` / `destroy`
> **都不是** `PreviewView` 的方法：缩放用 `zoomExtents`/`setView`，截图用 `toThumbnail`（同步返回 dataURL），
> 销毁用 `preview.remove()`（`disconnectedCallback` 负责清理）。

## 6. 在对话框里嵌入

```js
class MySymbolPreviewDialog extends ModalDialogBase {
    static dialogTitle = '符号预览';

    async firstUpdated() {
        await super.firstUpdated();   // ★ 必须：基类在这里取 base-dialog，漏掉则 startDialog 立即返回、对话框一闪而过
        const host = this.shadowRoot.querySelector('#preview-host');
        this._preview = new PreviewView({ backgroundColor: 0x222, interactive: true });
        host.appendChild(this._preview);
        await this._preview.onLoad();
        await this._preview.loadDocument(this.symbolDoc);
        this._preview.zoomExtents();
    }

    renderContent() {
        return html`<div id="preview-host" style="width: 400px; height: 300px;"></div>`;
    }

    cancel() {
        this._preview?.remove();       // PreviewView 无 destroy()；移除即触发 disconnectedCallback 清理
        super.cancel();
    }
}
customElements.define('my-symbol-preview-dialog', MySymbolPreviewDialog);   // 不注册 new 时抛 Illegal constructor

const dlg = new MySymbolPreviewDialog();
dlg.symbolDoc = someDbDoc;
await dlg.startDialog();
```

## 7. 中文字体

```js
// PreviewViewConfig 没有字体字段——字体是全局的，经 wasm 服务加载：
const { getWebCadCoreService, Engine, PreviewView } = vjcad;
await getWebCadCoreService().loadFont('/fonts/simsun.ttf', '宋体', 'ttf');
// 或批量：loadFonts([{ path: '/fonts/simsun.ttf', name: '宋体', kind: 'ttf' }])

const preview = new PreviewView({ backgroundColor: 0x1a242e });
container.appendChild(preview);
await preview.onLoad();

// ⚠️ addEntity 不画文字（简化绘制器只认线/圆/弧/椭圆）。文字要走 loadDocument：
const doc = Engine.currentDoc.toDb();          // 或只含文字实体的 DbDoc
await preview.loadDocument(doc);
preview.zoomExtents();
// 或：已在当前文档里的 TextEnt 实例 → preview.renderEntitiesDirect([textEnt]);
```

> 不传字体时中文会显示为方框，只有 ASCII 字符正常。`loadFont` 返回 `Promise<FontInfo | null>`；`fontKind` 可选 `'ttf' | 'woff' | 'otf' | 'shx'`。

## 红旗

- **必须 `await preview.onLoad()`** 再 `addEntity` / `loadDocument`：未初始化时它们直接 `throw Error('Graphics not initialized')`（不是静默丢绘制）
- 销毁页面 / 切换路由前要把元素 `preview.remove()`（触发 `disconnectedCallback` 清理），否则有 WebGL context 泄漏
- `loadDocument` 接收 DbDoc 结构（`currentDoc.toDb()` 或手工拼装），**不能把 `Engine.currentDoc` 直接传进去（结构不同；可取 `currentDoc.toDb()` 的 DbDoc 结构）
- 一个页面里 PreviewView 不要超过 ~5 个，每个都吃一个 WebGL 上下文

## 参考（官方文档 MCP）

- 文档：`vjcad/doc/ui/preview-view.md`
- 示例：
- `vjcad/example/ui/03previewview.js` — 基础 PreviewView
