npm package: @njboea/js-3d-model
Description: Cornerstone3D DICOM viewer library - Simplified API for medical image viewing
Author: njboea
Latest version: 0.3.0

# @njboea/js-3d-model

> Cornerstone3D DICOM 查看器库 - 简化的医学影像查看 API

[![Version](https://img.shields.io/badge/version-0.2.4-blue.svg)](https://github.com/njboea/js-3d-model)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)

## 🌟 特性

- 🚀 **简化的 API**：封装 Cornerstone3D 复杂性，提供简洁易用的接口
- 📦 **开箱即用**：自动初始化，无需复杂配置
- 🎯 **多种加载方式**：支持本地文件、WADO-RS、ZIP 文件
- 🛠️ **丰富的工具**：内置窗宽窗位、测量、标注等工具
- 💾 **标注管理**：完整的标注增删改查和导出功能
- 🎨 **框架无关**：可在 Vue、React、Angular 或原生 JS 中使用

## 📦 安装

```bash
npm install @njboea/js-3d-model
# 或
yarn add @njboea/js-3d-model
```

### Peer Dependencies

需要同时安装以下依赖：

```bash
npm install @cornerstonejs/core @cornerstonejs/tools @cornerstonejs/dicom-image-loader jszip
```

## 🚀 快速开始

### 1. 基础使用

```javascript
import { Js3DModel } from '@njboea/js-3d-model';

// 创建实例
const js3d = new Js3DModel({
  rendering: {
    useCPURendering: false  // 使用 GPU 渲染
  },
  imageLoader: {
    maxWebWorkers: 2,
    maxCacheSize: 512 * 1024 * 1024  // 512MB
  }
});

// 初始化
await js3d.initialize();

// 创建视口
const viewportId = js3d.createViewport({
  viewportId: 'main',
  type: 'STACK',
  element: document.getElementById('viewport')
});

// 加载 DICOM 文件
await js3d.loadLocalFiles(viewportId, files);

// 激活工具
js3d.activateTool(viewportId, 'Length');

// 获取标注
const annotations = js3d.getAllAnnotations();

// 清理资源
await js3d.destroy();
```

### 2. 在 Vue3 中使用

```vue
<template>
  <div>
    <div v-if="isInitialized">
      <!-- 工具栏 -->
      <div class="toolbar">
        <button @click="activateTool('WindowLevel')">窗宽窗位</button>
        <button @click="activateTool('Pan')">平移</button>
        <button @click="activateTool('Zoom')">缩放</button>
        <button @click="activateTool('Length')">长度测量</button>
      </div>
      
      <!-- 视口 -->
      <div 
        ref="viewportElement" 
        style="width: 512px; height: 512px; background: black;"
      ></div>
      
      <!-- 文件上传 -->
      <input type="file" multiple @change="handleFiles" accept=".dcm" />
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, watch } from 'vue';
import { Js3DModel } from '@njboea/js-3d-model';

const viewportElement = ref(null);
const isInitialized = ref(false);
const js3d = ref(null);
let viewportId = '';

onMounted(async () => {
  // 创建并初始化
  js3d.value = new Js3DModel({
    imageLoader: { maxWebWorkers: 2, maxCacheSize: 512 * 1024 * 1024 }
  });
  
  await js3d.value.initialize();
  isInitialized.value = true;
  
  // 等待 DOM 元素挂载后创建视口
  watch(viewportElement, (el) => {
    if (el) {
      viewportId = js3d.value.createViewport({
        viewportId: 'main',
        type: 'STACK',
        element: el
      });
    }
  }, { immediate: true });
});

onUnmounted(async () => {
  if (js3d.value) {
    await js3d.value.destroy();
  }
});

async function handleFiles(event) {
  const files = Array.from(event.target.files);
  await js3d.value.loadLocalFiles(viewportId, files);
}

function activateTool(toolName) {
  js3d.value.activateTool(viewportId, toolName);
}
</script>
```

## 📖 API 文档

### 构造函数

```javascript
const js3d = new Js3DModel(config);
```

**配置选项：**

```javascript
{
  rendering: {
    useCPURendering: false,        // 是否使用 CPU 渲染
    renderingEngineMode: 'contextPool',
    webGlContextCount: 7
  },
  imageLoader: {
    maxWebWorkers: 2,              // Web Worker 数量
    maxCacheSize: 512 * 1024 * 1024,  // 缓存大小（字节）
    decodeConfig: {
      use16BitDataType: true
    }
  }
}
```

### 核心方法

#### `initialize()`

初始化 Cornerstone3D。

```javascript
await js3d.initialize();
```

#### `createViewport(config)`

创建视口。

```javascript
const viewportId = js3d.createViewport({
  viewportId: 'main',
  type: 'STACK',  // 'STACK' | 'ORTHOGRAPHIC' | 'VOLUME_3D'
  element: document.getElementById('viewport'),
  defaultOptions: {
    background: [0, 0, 0]
  }
});
```

#### `loadLocalFiles(viewportId, files, options)`

加载本地 DICOM 文件。

```javascript
await js3d.loadLocalFiles(viewportId, files, {
  autoSort: true,
  onProgress: (progress) => {
    console.log(progress.message, progress.percent);
  }
});
```

#### `loadWadoRs(viewportId, studyUID, seriesUID, config, onProgress)`

加载 WADO-RS 系列。

```javascript
await js3d.loadWadoRs(
  viewportId,
  'studyUID',
  'seriesUID',
  {
    wadoRsRoot: 'http://server/dicomweb',
    headers: { 'Authorization': 'Bearer token' }
  },
  (progress) => console.log(progress.message)
);
```

#### `loadZipFile(viewportId, zipFile, onProgress)`

加载 ZIP 文件。

```javascript
await js3d.loadZipFile(viewportId, zipFile, (progress) => {
  console.log(progress.stage, progress.percent);
});
```

### 工具方法

#### `activateTool(viewportId, toolName)`

激活工具。

```javascript
js3d.activateTool(viewportId, 'Length');
```

**可用工具：**

- `WindowLevel` - 窗宽窗位
- `Pan` - 平移
- `Zoom` - 缩放
- `StackScroll` - 翻页（滚轮，默认激活）
- `Crosshairs` - 十字线（⚠️ 需要多视口才能使用）
- `Length` - 长度测量
- `RectangleROI` - 矩形 ROI
- `Angle` - 角度测量
- `EllipticalROI` - 椭圆 ROI
- `Probe` - 探针

**注意：**
- `Crosshairs`（十字线）工具需要至少两个视口才能正常工作，适用于 MPR 多视图场景
- 在单视口场景下请不要使用十字线工具，否则会导致错误

#### `getAvailableTools()`

获取可用工具列表。

```javascript
const tools = js3d.getAvailableTools();
```

### 标注方法

#### `getAllAnnotations(toolName)`

获取所有标注。

```javascript
const annotations = js3d.getAllAnnotations();
// 或获取特定工具的标注
const lengthAnnotations = js3d.getAllAnnotations('Length');
```

#### `removeAnnotation(annotationUID)`

删除标注。

```javascript
js3d.removeAnnotation(annotationUID);
```

#### `clearAllAnnotations()`

清除所有标注。

```javascript
js3d.clearAllAnnotations();
```

#### `exportAnnotations(toolName)`

导出标注为 JSON。

```javascript
const json = js3d.exportAnnotations();
```

#### `getAnnotationStats()`

获取标注统计。

```javascript
const stats = js3d.getAnnotationStats();
// { total: 5, byTool: { Length: 3, Angle: 2 }, byViewport: { ... } }
```

### 其他方法

#### `getViewport(viewportId)`

获取视口对象。

```javascript
const viewport = js3d.getViewport(viewportId);
```

#### `resetCamera(viewportId)`

重置相机。

```javascript
js3d.resetCamera(viewportId);
```

#### `getCacheInfo()`

获取缓存信息。

```javascript
const info = js3d.getCacheInfo();
// { size: 12345678, maxSize: 536870912, sizeFormatted: '11.77 MB', ... }
```

#### `clearCache()`

清理缓存。

```javascript
js3d.clearCache();
```

#### `destroy()`

销毁所有资源。

```javascript
await js3d.destroy();
```

## 🔧 高级用法

### 单独使用管理器

```javascript
import { 
  CornerstoneFacade,
  ViewportManager,
  ToolManager,
  AnnotationManager
} from '@njboea/js-3d-model';

// 自定义初始化
const facade = new CornerstoneFacade(config);
await facade.initialize();

// 使用各个管理器
const viewportManager = new ViewportManager();
const toolManager = new ToolManager();
```

### 使用工具函数

```javascript
import { sortImageIdsByPosition, isDicomFile } from '@njboea/js-3d-model';

// 排序图像
const sortedIds = await sortImageIdsByPosition(imageIds);

// 检查文件
if (isDicomFile(file)) {
  // 处理 DICOM 文件
}
```

## 📝 示例

查看 `examples/` 目录获取更多示例：

- [基础使用](examples/basic.html)
- [Vue3 集成](examples/vue3-example.vue)
- [React 集成](examples/react-example.jsx)
- [多视口](examples/multiple-viewports.html)

## 🤝 贡献

欢迎贡献代码！请阅读 [CONTRIBUTING.md](CONTRIBUTING.md) 了解详情。

## 📄 许可

MIT License - 详见 [LICENSE](LICENSE)

## 🔗 相关链接

- [Cornerstone3D 文档](https://www.cornerstonejs.org/)
- [DICOM 标准](https://www.dicomstandard.org/)
- [问题反馈](https://github.com/njboea/js-3d-model/issues)

## 🙏 致谢

本项目基于 [Cornerstone3D](https://github.com/cornerstonejs/cornerstone3D) 构建。

---

**版本**: v0.2.0  
**更新日期**: 2025-11-16