med-viewer-sdk 0.1.14 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/package.json +2 -3
  2. package/src/adapters/vue/MedViewer.ts +0 -38
  3. package/src/adapters/vue/index.ts +0 -4
  4. package/src/assets/icons/button_grouphover.png +0 -0
  5. package/src/assets/icons/button_hover.png +0 -0
  6. package/src/assets/icons/button_pressed.png +0 -0
  7. package/src/assets/icons/button_rest.png +0 -0
  8. package/src/assets/icons/flip_grouphover.png +0 -0
  9. package/src/assets/icons/flip_hover.png +0 -0
  10. package/src/assets/icons/flip_pressed.png +0 -0
  11. package/src/assets/icons/flip_rest.png +0 -0
  12. package/src/assets/icons/fullpage_grouphover.png +0 -0
  13. package/src/assets/icons/fullpage_hover.png +0 -0
  14. package/src/assets/icons/fullpage_pressed.png +0 -0
  15. package/src/assets/icons/fullpage_rest.png +0 -0
  16. package/src/assets/icons/home_grouphover.png +0 -0
  17. package/src/assets/icons/home_hover.png +0 -0
  18. package/src/assets/icons/home_pressed.png +0 -0
  19. package/src/assets/icons/home_rest.png +0 -0
  20. package/src/assets/icons/next_grouphover.png +0 -0
  21. package/src/assets/icons/next_hover.png +0 -0
  22. package/src/assets/icons/next_pressed.png +0 -0
  23. package/src/assets/icons/next_rest.png +0 -0
  24. package/src/assets/icons/previous_grouphover.png +0 -0
  25. package/src/assets/icons/previous_hover.png +0 -0
  26. package/src/assets/icons/previous_pressed.png +0 -0
  27. package/src/assets/icons/previous_rest.png +0 -0
  28. package/src/assets/icons/rotateleft_grouphover.png +0 -0
  29. package/src/assets/icons/rotateleft_hover.png +0 -0
  30. package/src/assets/icons/rotateleft_pressed.png +0 -0
  31. package/src/assets/icons/rotateleft_rest.png +0 -0
  32. package/src/assets/icons/rotateright_grouphover.png +0 -0
  33. package/src/assets/icons/rotateright_hover.png +0 -0
  34. package/src/assets/icons/rotateright_pressed.png +0 -0
  35. package/src/assets/icons/rotateright_rest.png +0 -0
  36. package/src/assets/icons/selection_cancel_grouphover.png +0 -0
  37. package/src/assets/icons/selection_cancel_hover.png +0 -0
  38. package/src/assets/icons/selection_cancel_pressed.png +0 -0
  39. package/src/assets/icons/selection_cancel_rest.png +0 -0
  40. package/src/assets/icons/selection_confirm_grouphover.png +0 -0
  41. package/src/assets/icons/selection_confirm_hover.png +0 -0
  42. package/src/assets/icons/selection_confirm_pressed.png +0 -0
  43. package/src/assets/icons/selection_confirm_rest.png +0 -0
  44. package/src/assets/icons/selection_grouphover.png +0 -0
  45. package/src/assets/icons/selection_hover.png +0 -0
  46. package/src/assets/icons/selection_pressed.png +0 -0
  47. package/src/assets/icons/selection_rest.png +0 -0
  48. package/src/assets/icons/tool_anno.png +0 -0
  49. package/src/assets/icons/tool_color.png +0 -0
  50. package/src/assets/icons/tool_reset.png +0 -0
  51. package/src/assets/icons/tool_selection.png +0 -0
  52. package/src/assets/icons/zoomin_grouphover.png +0 -0
  53. package/src/assets/icons/zoomin_hover.png +0 -0
  54. package/src/assets/icons/zoomin_pressed.png +0 -0
  55. package/src/assets/icons/zoomin_rest.png +0 -0
  56. package/src/assets/icons/zoomout_grouphover.png +0 -0
  57. package/src/assets/icons/zoomout_hover.png +0 -0
  58. package/src/assets/icons/zoomout_pressed.png +0 -0
  59. package/src/assets/icons/zoomout_rest.png +0 -0
  60. package/src/core/AnnoAnnotator.ts +0 -94
  61. package/src/core/BaseAnnotator.ts +0 -43
  62. package/src/core/ColorAdjustPlugin.ts +0 -183
  63. package/src/core/Coords.ts +0 -9
  64. package/src/core/Engine.ts +0 -401
  65. package/src/core/Magnification.ts +0 -301
  66. package/src/core/Scalebar.ts +0 -88
  67. package/src/core/SelectionPlugin.ts +0 -251
  68. package/src/core/Toolbar.ts +0 -854
  69. package/src/i18n/i18n.ts +0 -104
  70. package/src/index.ts +0 -45
  71. package/src/plugins/ShapeLabelsFormatter.js +0 -512
  72. package/src/plugins/openseadragon-filtering.js +0 -211
  73. package/src/plugins/openseadragon-scalebar.js +0 -592
  74. package/src/plugins/openseadragon-selection.js +0 -657
  75. package/src/types/ShapeLabelsFormatter.d.ts +0 -12
  76. package/src/types/type.d.ts +0 -7
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "med-viewer-sdk",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "main": "dist/med-viewer-sdk.umd.js",
5
5
  "module": "dist/med-viewer-sdk.mjs",
6
6
  "types": "dist/med-viewer-sdk.d.ts",
7
7
  "files": [
8
8
  "dist",
9
- "README.md",
10
- "src"
9
+ "README.md"
11
10
  ],
12
11
  "scripts": {
13
12
  "build": "npx vite build && tsc -p tsconfig.dts.json && powershell -Command \"if (Test-Path -Path 'dist\\med-viewer-sdk.d.ts') { Remove-Item -Path 'dist\\med-viewer-sdk.d.ts' } ; Move-Item -Path 'dist\\index.d.ts' -Destination 'dist\\med-viewer-sdk.d.ts'\"",
@@ -1,38 +0,0 @@
1
- // 渲染函数写的通用组件
2
- import { defineComponent, h, onBeforeUnmount, onMounted, shallowRef } from 'vue-demi';
3
- import type { MedEngineOptions } from '../../core/Engine';
4
- import { MedViewerEngine } from '../../core/Engine';
5
-
6
- export default defineComponent({
7
- name: 'MedViewer',
8
- props: {
9
- options: {
10
- type: Object as () => Omit<MedEngineOptions, 'element'>,
11
- required: true
12
- }
13
- },
14
- emits: ['ready'],
15
- setup(props, { slots, expose, emit }) {
16
- const containerRef = shallowRef<HTMLElement | null>(null);
17
- const engineRef = shallowRef<MedViewerEngine | null>(null);
18
-
19
- onMounted(() => {
20
- if (!containerRef.value) return;
21
- engineRef.value = new MedViewerEngine({
22
- osdOptions: props.options.osdOptions,
23
- locale: props.options.locale,
24
- plugins: props.options.plugins,
25
- });
26
- emit('ready', engineRef.value);
27
- });
28
-
29
- onBeforeUnmount(() => {
30
- engineRef.value?.destroy();
31
- engineRef.value = null;
32
- });
33
-
34
- expose({ engine: engineRef });
35
-
36
- return () => h('div', { class: 'med-viewer', ref: containerRef }, slots.default?.());
37
- }
38
- });
@@ -1,4 +0,0 @@
1
- // 插件入口
2
- import MedViewer from './MedViewer';
3
-
4
- export default MedViewer;
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,94 +0,0 @@
1
- // 使用本地下载的 Annotorious v2.7.17 文件
2
- import Annotorious from "annotorious-openseadragon-ld";
3
- import "annotorious-openseadragon-ld/dist/annotorious.min.css";
4
- import { MedViewerEngine } from "./Engine";
5
- import { BaseAnnotator } from "./BaseAnnotator";
6
-
7
-
8
- // // 全局类型声明
9
- // declare global {
10
- // interface Window {
11
- // Annotorious: any;
12
- // }
13
- // }
14
-
15
- export class AnnoAnnotator extends BaseAnnotator {
16
- public anno: any;
17
-
18
- constructor(engine: MedViewerEngine, config: any = {}) {
19
- super(engine);
20
-
21
- // 使用 v2.7.17 的 Annotorious (全局变量)
22
- this.anno = Annotorious(this.engine.viewer, {
23
- ...config,
24
- // 可以在此配置样式、偏好等
25
- });
26
- this.injectStyles();
27
- this.initEvents();
28
- }
29
-
30
- public setEnabled(enabled: boolean): void {
31
- this.anno.setDrawingEnabled(enabled);
32
- }
33
-
34
- public setTool(
35
- tool:
36
- | "rect"
37
- | "polygon"
38
- | "line"
39
- | "point"
40
- | "circle"
41
- | "ellipse"
42
- | "freehand"
43
- | null,
44
- color?: string,
45
- ): void {
46
- if (!tool) {
47
- this.setEnabled(false);
48
- } else {
49
- this.anno.setDrawingTool(tool, color);
50
- this.setEnabled(true);
51
- }
52
- }
53
-
54
- public getAnnotations(): any[] {
55
- // 获取当前所有标注
56
- return this.anno.getAnnotations();
57
- }
58
-
59
- public setAnnotations(data: any[]): void {
60
- this.anno.setAnnotations(data);
61
- }
62
-
63
- public clear(): void {
64
- this.anno.clearAnnotations();
65
- }
66
-
67
- public destroy(): void {
68
- this.anno.destroy();
69
- }
70
-
71
- private initEvents() {
72
- this.anno.on("createAnnotation", (anno: any) => {
73
- console.log("新标注已创建:", anno);
74
- });
75
- }
76
-
77
- private injectStyles() {
78
- // 适配医学影像的样式(v2.7.17 默认样式有时在深色背景下不明显)
79
- const styleId = "med-anno-v2-7-17-overrides";
80
- if (document.getElementById(styleId)) return;
81
- const style = document.createElement("style");
82
- style.id = styleId;
83
- style.innerHTML = `
84
- .a9s-handle .a9s-handle-inner {
85
- stroke: #FFEB3B;
86
- fill: #FF9800;
87
- }
88
- .a9s-handle .a9s-handle-outer {
89
- stroke: #000;
90
- fill: #fff;
91
- }`;
92
- document.head.appendChild(style);
93
- }
94
- }
@@ -1,43 +0,0 @@
1
- import { MedViewerEngine } from './Engine';
2
-
3
- /**
4
- * 标注插件的基类
5
- */
6
- export abstract class BaseAnnotator {
7
- protected engine: MedViewerEngine;
8
- protected isEnabled: boolean = false;
9
-
10
- constructor(engine: MedViewerEngine) {
11
- this.engine = engine;
12
- }
13
-
14
- /**
15
- * 启用/禁用标注功能
16
- */
17
- public abstract setEnabled(enabled: boolean): void;
18
-
19
- /**
20
- * 设置当前的工具模式 (例如:矩形、箭头、多边形)
21
- */
22
- public abstract setTool(tool: string | null): void;
23
-
24
- /**
25
- * 获取所有标注数据 (建议在此处进行格式标准化)
26
- */
27
- public abstract getAnnotations(): any[];
28
-
29
- /**
30
- * 加载标注数据
31
- */
32
- public abstract setAnnotations(data: any[]): void;
33
-
34
- /**
35
- * 清除所有标注
36
- */
37
- public abstract clear(): void;
38
-
39
- /**
40
- * 销毁插件,移除事件监听
41
- */
42
- public abstract destroy(): void;
43
- }
@@ -1,183 +0,0 @@
1
- import "openseadragon-filtering";
2
-
3
- export interface ColorAdjustments {
4
- brightness?: number;
5
- contrast?: number;
6
- saturation?: number;
7
- gamma?: number;
8
- hue?: number;
9
- invert?: boolean;
10
- sepia?: boolean;
11
- greyscale?: boolean;
12
- }
13
-
14
- export interface ColorAdjustOptions {
15
- adjustments?: ColorAdjustments;
16
- debounceMs?: number; // 建议 60ms 左右,平衡实时性与性能
17
- loadMode?: "async" | "sync";
18
- }
19
-
20
- export class ColorAdjustPlugin {
21
- private viewer: any;
22
- private _adjustments: ColorAdjustments;
23
- private options: Required<ColorAdjustOptions>;
24
- private lut = new Uint8ClampedArray(256);
25
- private needUpdate = true;
26
- private debounceTimer: ReturnType<typeof setTimeout> | null = null;
27
-
28
- constructor(viewer: any, options?: ColorAdjustOptions) {
29
- this.viewer = viewer.viewer || viewer;
30
-
31
- // 默认配置初始化
32
- this.options = {
33
- adjustments: {
34
- brightness: 1.0, contrast: 1.0, saturation: 1.0,
35
- gamma: 1.0, hue: 0, invert: false, sepia: false, greyscale: false,
36
- },
37
- debounceMs: 60,
38
- loadMode: "async",
39
- ...options
40
- };
41
-
42
- this._adjustments = { ...this.options.adjustments };
43
-
44
- this.viewer.addHandler("open", () => this.apply());
45
- if (this.viewer.isOpen()) this.apply();
46
- }
47
-
48
- /**
49
- * 更新查找表 (LUT)
50
- * 将亮度、对比度、反色、伽马合并为一个 256 长度的数组,大幅减少循环内计算
51
- */
52
- private updateLut() {
53
- const adj = this._adjustments;
54
- const invGamma = 1.0 / (adj.gamma || 1.0);
55
- const { brightness: br = 1, contrast: co = 1, invert, gamma } = adj;
56
-
57
- for (let i = 0; i < 256; i++) {
58
- let v = i;
59
- if (invert) v = 255 - v;
60
- if (br !== 1.0) v *= br;
61
- if (co !== 1.0) v = (v - 128) * co + 128;
62
- if (gamma !== 1.0) v = 255 * Math.pow(v / 255, invGamma);
63
- this.lut[i] = v; // Uint8ClampedArray 会自动处理 round 和 0-255 截断
64
- }
65
- this.needUpdate = false;
66
- }
67
-
68
- /**
69
- * 核心处理器:利用异步调度减少主线程占用
70
- */
71
- private combinedProcessor = (context: CanvasRenderingContext2D, callback: () => void) => {
72
- // 1. 将重计算任务推入宏任务队列,让出主线程给 UI 交互(如缩放、拖拽)
73
- setTimeout(() => {
74
- const { width, height } = context.canvas;
75
- if (width <= 0 || height <= 0) {
76
- callback();
77
- return;
78
- }
79
-
80
- if (this.needUpdate) this.updateLut();
81
-
82
- const imageData = context.getImageData(0, 0, width, height);
83
- const data = imageData.data;
84
- const len = data.length;
85
- const lut = this.lut;
86
- const { hue = 0, saturation = 1, greyscale, sepia } = this._adjustments;
87
-
88
- // 2. 性能优化:根据滤镜配置选择执行路径,避免循环内部做 if 判断
89
- if (greyscale) {
90
- for (let i = 0; i < len; i += 4) {
91
- const avg = 0.2126 * lut[data[i]] + 0.7152 * lut[data[i + 1]] + 0.0722 * lut[data[i + 2]];
92
- data[i] = data[i + 1] = data[i + 2] = avg;
93
- }
94
- } else if (sepia) {
95
- for (let i = 0; i < len; i += 4) {
96
- const r = lut[data[i]], g = lut[data[i + 1]], b = lut[data[i + 2]];
97
- data[i] = 0.393 * r + 0.769 * g + 0.189 * b;
98
- data[i + 1] = 0.349 * r + 0.686 * g + 0.168 * b;
99
- data[i + 2] = 0.272 * r + 0.534 * g + 0.131 * b;
100
- }
101
- } else {
102
- // 处理 Hue 和 Saturation
103
- const hasHue = hue !== 0;
104
- const hasSat = saturation !== 1.0;
105
- const angle = hue * (Math.PI / 180);
106
- const cosA = Math.cos(angle), sinA = Math.sin(angle);
107
- const r1 = cosA + (1-cosA)/3, g1 = 1/3*(1-cosA)-Math.sqrt(1/3)*sinA, b1 = 1/3*(1-cosA)+Math.sqrt(1/3)*sinA;
108
- const r2 = 1/3*(1-cosA)+Math.sqrt(1/3)*sinA, g2 = cosA+(1-cosA)/3, b2 = 1/3*(1-cosA)-Math.sqrt(1/3)*sinA;
109
- const r3 = 1/3*(1-cosA)-Math.sqrt(1/3)*sinA, g3 = 1/3*(1-cosA)+Math.sqrt(1/3)*sinA, b3 = cosA+(1-cosA)/3;
110
-
111
- for (let i = 0; i < len; i += 4) {
112
- let r = lut[data[i]], g = lut[data[i + 1]], b = lut[data[i + 2]];
113
- if (hasHue) {
114
- const tr = r * r1 + g * r2 + b * r3;
115
- const tg = r * g1 + g * g2 + b * g3;
116
- const tb = r * b1 + g * b2 + b * b3;
117
- r = tr; g = tg; b = tb;
118
- }
119
- if (hasSat) {
120
- const gray = 0.2989 * r + 0.587 * g + 0.114 * b;
121
- r = gray + (r - gray) * saturation;
122
- g = gray + (g - gray) * saturation;
123
- b = gray + (b - gray) * saturation;
124
- }
125
- data[i] = r; data[i + 1] = g; data[i + 2] = b;
126
- }
127
- }
128
-
129
- context.putImageData(imageData, 0, 0);
130
-
131
- // 3. 必须调用 callback,告知 OSD 瓦片处理完毕
132
- callback();
133
- }, 0);
134
- };
135
-
136
- /**
137
- * 应用滤镜配置到 Viewer
138
- */
139
- public apply() {
140
- if (!this.viewer.setFilterOptions) return;
141
- this.needUpdate = true;
142
-
143
- this.viewer.setFilterOptions({
144
- filters: { processors: this.combinedProcessor },
145
- loadMode: this.options.loadMode,
146
- });
147
-
148
- // 强制触发可见重绘
149
- this.viewer.world.draw();
150
- }
151
-
152
- /**
153
- * 业务层调用的 API,内置防抖
154
- */
155
- public setAdjustments(adj: ColorAdjustments) {
156
- this._adjustments = { ...this._adjustments, ...adj };
157
-
158
- // 性能优化:防抖处理。避免滑动滑块时瞬间产生数百个绘制请求
159
- if (this.debounceTimer) clearTimeout(this.debounceTimer);
160
-
161
- this.debounceTimer = setTimeout(() => {
162
- this.apply();
163
- this.debounceTimer = null;
164
- }, this.options.debounceMs);
165
- }
166
-
167
- public get adjustments(): ColorAdjustments {
168
- return this._adjustments;
169
- }
170
-
171
- public reset() {
172
- this._adjustments = { ...this.options.adjustments };
173
- this.apply();
174
- }
175
-
176
- public destroy() {
177
- if (this.debounceTimer) clearTimeout(this.debounceTimer);
178
- if (this.viewer?.setFilterOptions) {
179
- this.viewer.setFilterOptions({ filters: { processors: [] } });
180
- }
181
- this.viewer = null;
182
- }
183
- }
@@ -1,9 +0,0 @@
1
- // 坐标算法 (Image to Screen)
2
- export class Coords {
3
- static imageToScreen(imageX: number, imageY: number, scale: number): { x: number; y: number } {
4
- return {
5
- x: imageX * scale,
6
- y: imageY * scale
7
- };
8
- }
9
- }