image-annotation-drawer 1.0.2 → 1.1.0
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.
- package/CHANGELOG.md +34 -0
- package/dist/index.d.ts +82 -2
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.1.0] - 2026-08-11
|
|
11
|
+
|
|
12
|
+
### Added - 标注标题功能
|
|
13
|
+
|
|
14
|
+
- **标题能力**:
|
|
15
|
+
- 矩形/多边形标注支持设置标题(画布上渲染文字 + 背景框)
|
|
16
|
+
- 双击标注快速编辑标题,Enter 完成 / ESC 取消
|
|
17
|
+
- `enableTitle` 选项显式开启(默认 false)
|
|
18
|
+
- **标题样式与位置**:
|
|
19
|
+
- 字体、文字色、背景色、内边距、圆角均可配置
|
|
20
|
+
- 位置支持上方 / 下方 / 内部上方,水平左中右对齐 + 像素偏移
|
|
21
|
+
- 支持 rgba/hsla/8 位 hex 等任意 CSS 颜色(含透明度)
|
|
22
|
+
- **标题输入框 placeholder 自定义**:
|
|
23
|
+
- 通过 `TitleStyle.placeholder` 配置,默认"输入标题..."
|
|
24
|
+
- 修改全局 placeholder 只影响之后设置标题的标注(快照隔离)
|
|
25
|
+
- 支持单个标注独立覆盖 placeholder
|
|
26
|
+
- **样式快照隔离**:设置标题时将当前全局样式快照存入标注,后续全局修改不影响已有标题
|
|
27
|
+
- **新增 API**:
|
|
28
|
+
- `setAnnotationTitle(index, title)` / `getAnnotationTitle(index)` - 设置/获取标注标题
|
|
29
|
+
- `setTitleStyle(style)` / `getTitleStyle()` - 全局标题样式
|
|
30
|
+
- `setTitlePosition(position)` / `getTitlePosition()` - 全局标题位置
|
|
31
|
+
- `setAnnotationTitleStyle(index, style)` - 单独设置某个标注的标题样式
|
|
32
|
+
- `setAnnotationTitlePosition(index, position)` - 单独设置某个标注的标题位置
|
|
33
|
+
|
|
10
34
|
### Added - 标注选中与移动功能
|
|
11
35
|
|
|
12
36
|
- **选中功能**:
|
|
@@ -44,6 +68,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
44
68
|
- **智能删除**:清空文本后确认,自动删除标注
|
|
45
69
|
- **自适应输入框**:输入时自动调整宽度适应内容
|
|
46
70
|
- **自动选中文本**:进入编辑模式时自动选中文本,方便直接替换
|
|
71
|
+
- **已有文本标注样式可更新**:新增 `updateTextAnnotationStyle(index, style)`,可将当前全局样式(如加粗)应用到指定文本标注,不影响全局默认和其他标注
|
|
72
|
+
|
|
73
|
+
### Fixed
|
|
74
|
+
|
|
75
|
+
- **键盘事件越界**:全局 `keydown` 处理器不再响应来自页面输入框(INPUT/TEXTAREA/SELECT/contentEditable)的按键,修复在输入框中按 Delete/Backspace 误删选中标注、按 Ctrl+Z 误撤销标注的问题
|
|
76
|
+
|
|
77
|
+
### Examples
|
|
78
|
+
|
|
79
|
+
- 示例页新增标题"占位提示"配置项(`examples/basic.html` / `basic-en.html`)
|
|
80
|
+
- 示例页调整文本/标题样式时,同步应用到当前选中的标注
|
|
47
81
|
|
|
48
82
|
## [1.0.0] - 2024-03-05
|
|
49
83
|
|
package/dist/index.d.ts
CHANGED
|
@@ -10,14 +10,14 @@
|
|
|
10
10
|
* - 图片导出:支持导出标注后的图片
|
|
11
11
|
* - TypeScript 支持:完整的类型定义
|
|
12
12
|
*/
|
|
13
|
-
export type { Shape, Point, Rect, Polygon, TextAnnotation, Operate, DrawerOptions, DrawType, SelectedAnnotation, ActiveHandle, TextStyle, TextInputStyle, SelectionStyle, AnnotationStyle, LineStyle, VertexStyle, ColorConfig, ViewportSize, ViewportOffset, DrawerEventHandlers, } from './modules/types';
|
|
13
|
+
export type { Shape, Point, Rect, Polygon, TextAnnotation, Operate, DrawerOptions, DrawType, SelectedAnnotation, ActiveHandle, TextStyle, TextInputStyle, SelectionStyle, AnnotationStyle, LineStyle, VertexStyle, ColorConfig, ViewportSize, ViewportOffset, DrawerEventHandlers, TitleStyle, TitlePosition, } from './modules/types';
|
|
14
14
|
export { isPointInRect, isPointInPolygon, getImageTypeFromUrl, base64ToFile, base64ToBlob, getZoomDelta, } from './modules/utils';
|
|
15
15
|
export { ViewportManager } from './modules/viewport';
|
|
16
16
|
export { AnnotationManager } from './modules/annotations';
|
|
17
17
|
export { TextAnnotationManager } from './modules/text-annotation';
|
|
18
18
|
export { Renderer } from './modules/renderer';
|
|
19
19
|
export { EventHandler } from './modules/events';
|
|
20
|
-
import type { Rect, Polygon, TextAnnotation, Operate, DrawerOptions, DrawType, ColorConfig, LineStyle, VertexStyle, TextStyle, TextInputStyle } from './modules/types';
|
|
20
|
+
import type { Rect, Polygon, TextAnnotation, Operate, DrawerOptions, DrawType, ColorConfig, LineStyle, VertexStyle, TextStyle, TextInputStyle, TitleStyle, TitlePosition } from './modules/types';
|
|
21
21
|
/**
|
|
22
22
|
* Drawer 类 - 图片标注库的主入口
|
|
23
23
|
*
|
|
@@ -99,6 +99,75 @@ export declare class Drawer {
|
|
|
99
99
|
* 获取当前所有标注数据
|
|
100
100
|
*/
|
|
101
101
|
getAnnotations(): Operate<Rect | Polygon | TextAnnotation>[];
|
|
102
|
+
/**
|
|
103
|
+
* 设置标注标题
|
|
104
|
+
* @param index - 标注在 recordList 中的索引
|
|
105
|
+
* @param title - 标题文本
|
|
106
|
+
* @returns 是否设置成功
|
|
107
|
+
* @example
|
|
108
|
+
* // 设置第一个标注的标题
|
|
109
|
+
* drawer.setAnnotationTitle(0, '这是一个人脸')
|
|
110
|
+
*/
|
|
111
|
+
setAnnotationTitle(index: number, title: string): boolean;
|
|
112
|
+
/**
|
|
113
|
+
* 获取标注标题
|
|
114
|
+
* @param index - 标注在 recordList 中的索引
|
|
115
|
+
* @returns 标题文本,如果没有则返回 undefined
|
|
116
|
+
*/
|
|
117
|
+
getAnnotationTitle(index: number): string | undefined;
|
|
118
|
+
/**
|
|
119
|
+
* 设置标题样式(全局默认)
|
|
120
|
+
* @param style - 标题样式配置
|
|
121
|
+
* @example
|
|
122
|
+
* drawer.setTitleStyle({
|
|
123
|
+
* font: 'bold 14px Microsoft YaHei',
|
|
124
|
+
* color: '#FFD700',
|
|
125
|
+
* backgroundColor: 'rgba(0,0,0,0.8)',
|
|
126
|
+
* paddingX: 8,
|
|
127
|
+
* paddingY: 4,
|
|
128
|
+
* borderRadius: 6,
|
|
129
|
+
* placeholder: '请输入标题' // 标题输入框占位提示,修改后只影响之后设置标题的标注
|
|
130
|
+
* })
|
|
131
|
+
*/
|
|
132
|
+
setTitleStyle(style: Partial<TitleStyle>): void;
|
|
133
|
+
/**
|
|
134
|
+
* 获取当前标题样式
|
|
135
|
+
*/
|
|
136
|
+
getTitleStyle(): TitleStyle;
|
|
137
|
+
/**
|
|
138
|
+
* 设置标题位置(全局默认)
|
|
139
|
+
* @param position - 标题位置配置
|
|
140
|
+
* @example
|
|
141
|
+
* drawer.setTitlePosition({
|
|
142
|
+
* vertical: 'top', // 'top' | 'bottom' | 'inside-top'
|
|
143
|
+
* align: 'left', // 'left' | 'center' | 'right'
|
|
144
|
+
* offsetX: 0,
|
|
145
|
+
* offsetY: 0
|
|
146
|
+
* })
|
|
147
|
+
*/
|
|
148
|
+
setTitlePosition(position: Partial<TitlePosition>): void;
|
|
149
|
+
/**
|
|
150
|
+
* 获取当前标题位置
|
|
151
|
+
*/
|
|
152
|
+
getTitlePosition(): TitlePosition;
|
|
153
|
+
/**
|
|
154
|
+
* 单独设置某个标注的标题样式(不影响全局默认,也不影响其他标注)
|
|
155
|
+
* @param index - 标注索引
|
|
156
|
+
* @param style - 标题样式(部分覆盖)
|
|
157
|
+
* @example
|
|
158
|
+
* drawer.setAnnotationTitleStyle(0, { color: '#FF0000', font: 'bold 16px Arial' })
|
|
159
|
+
* // 单独自定义某个标注的标题输入框占位提示
|
|
160
|
+
* drawer.setAnnotationTitleStyle(0, { placeholder: '请输入人脸名称' })
|
|
161
|
+
*/
|
|
162
|
+
setAnnotationTitleStyle(index: number, style: Partial<TitleStyle>): boolean;
|
|
163
|
+
/**
|
|
164
|
+
* 单独设置某个标注的标题位置(不影响全局默认,也不影响其他标注)
|
|
165
|
+
* @param index - 标注索引
|
|
166
|
+
* @param position - 标题位置(部分覆盖)
|
|
167
|
+
* @example
|
|
168
|
+
* drawer.setAnnotationTitlePosition(0, { vertical: 'bottom', align: 'left' })
|
|
169
|
+
*/
|
|
170
|
+
setAnnotationTitlePosition(index: number, position: Partial<TitlePosition>): boolean;
|
|
102
171
|
/**
|
|
103
172
|
* 获取所有文本标注
|
|
104
173
|
*/
|
|
@@ -238,6 +307,17 @@ export declare class Drawer {
|
|
|
238
307
|
* })
|
|
239
308
|
*/
|
|
240
309
|
setTextStyle(style: Partial<TextStyle>): void;
|
|
310
|
+
/**
|
|
311
|
+
* 更新指定文本标注的样式(只影响该标注,不影响全局默认和其他标注)
|
|
312
|
+
* 可用于把当前全局样式(如加粗)应用到已存在的文本标注上
|
|
313
|
+
* @param index - 文本标注索引
|
|
314
|
+
* @param style - 样式(部分覆盖)
|
|
315
|
+
* @returns 是否更新成功
|
|
316
|
+
* @example
|
|
317
|
+
* // 让第一个文本标注变为粗体
|
|
318
|
+
* drawer.updateTextAnnotationStyle(0, { font: 'bold 16px Arial' })
|
|
319
|
+
*/
|
|
320
|
+
updateTextAnnotationStyle(index: number, style: Partial<Pick<TextStyle, 'font' | 'color' | 'backgroundColor'>>): boolean;
|
|
241
321
|
/**
|
|
242
322
|
* 设置文本输入框样式
|
|
243
323
|
* @param style - 输入框样式配置
|
package/dist/index.esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function t(t,e){const n=e.width>=0?e.start.x:e.start.x+e.width,i=e.height>=0?e.start.y:e.start.y+e.height,s=Math.abs(e.width),a=Math.abs(e.height);return t.x>=n&&t.x<=n+s&&t.y>=i&&t.y<=i+a}function e(t,e){if(e.length<3)return!1;let n=!1;for(let i=0,s=e.length-1;i<e.length;s=i++){const a=e[i].point.x,o=e[i].point.y,r=e[s].point.x,h=e[s].point.y;o>t.y!=h>t.y&&t.x<(r-a)*(t.y-o)/(h-o)+a&&(n=!n)}return n}function n(t){try{const e=new URL(t).pathname.toLowerCase();return e.endsWith(".png")?"png":e.endsWith(".jpg")?"jpg":e.endsWith(".jpeg")?"jpeg":e.endsWith(".webp")?"webp":e.endsWith(".gif")?"gif":e.endsWith(".bmp")?"bmp":e.endsWith(".svg")?"svg":"jpeg"}catch(t){return"jpeg"}}function i(t,e){const n=t.split(";base64,"),i=n[0].split(":")[1],s=window.atob(n[1]),a=s.length,o=new Uint8Array(a);for(let t=0;t<a;++t)o[t]=s.charCodeAt(t);const r=new Blob([o],{type:i});return new File([r],e,{type:i})}function s(t){return new Promise((e,n)=>{const i=t.split(";base64,");if(i.length<2)return void n(new Error("Invalid base64 string"));const s=i[0].split(":")[1],a=atob(i[1]),o=new ArrayBuffer(a.length),r=new Uint8Array(o);for(let t=0;t<a.length;t++)r[t]=a.charCodeAt(t);e(new Blob([o],{type:s}))})}function a(t,e){return t<2?e?.1:-.1:t<5?e?.05:-.05:e?.02:-.02}class o{constructor(){this.width=0,this.height=0,this.originalWidth=0,this.originalHeight=0,this.scale=1,this.minScale=1,this.maxScale=10,this.offset={x:0,y:0},this.initialScale=1,this.initialOffset={x:0,y:0},this.isInitialScale=!0,this.minOffsetX=0,this.minOffsetY=0,this.maxOffsetX=0,this.maxOffsetY=0}setSize(t,e){this.width=t,this.height=e}setOriginalSize(t,e){this.originalWidth=t,this.originalHeight=e}calculateInitialView(){if(0===this.originalWidth||0===this.originalHeight)return;const t=this.width/this.originalWidth,e=this.height/this.originalHeight;this.scale=Math.min(t,e),this.initialScale=this.scale,this.minScale=this.initialScale,this.offset={x:(this.width-this.originalWidth*this.scale)/2,y:(this.height-this.originalHeight*this.scale)/2},this.initialOffset=Object.assign({},this.offset),this.calculateBoundaries(),this.isInitialScale=!0}calculateBoundaries(){const t=this.originalWidth*this.scale,e=this.originalHeight*this.scale;this.maxOffsetX=0,this.maxOffsetY=0,this.minOffsetX=this.width-t,this.minOffsetY=this.height-e,this.minOffsetX>this.maxOffsetX&&([this.minOffsetX,this.maxOffsetX]=[this.maxOffsetX,this.minOffsetX]),this.minOffsetY>this.maxOffsetY&&([this.minOffsetY,this.maxOffsetY]=[this.maxOffsetY,this.minOffsetY])}constrainViewport(){this.offset.x>this.maxOffsetX?this.offset.x=this.maxOffsetX:this.offset.x<this.minOffsetX&&(this.offset.x=this.minOffsetX),this.offset.y>this.maxOffsetY?this.offset.y=this.maxOffsetY:this.offset.y<this.minOffsetY&&(this.offset.y=this.minOffsetY)}toImageCoordinates(t,e){return{x:(t-this.offset.x)/this.scale,y:(e-this.offset.y)/this.scale}}toCanvasCoordinates(t,e){return{x:this.offset.x+t*this.scale,y:this.offset.y+e*this.scale}}updateScale(t,e,n){const i=this.scale,s=(e-this.offset.x)/i,a=(n-this.offset.y)/i;this.scale=Math.max(this.minScale,Math.min(t,this.maxScale));const o=s*this.scale+this.offset.x,r=a*this.scale+this.offset.y;this.offset.x+=e-o,this.offset.y+=n-r,this.calculateBoundaries(),this.constrainViewport(),this.isInitialScale=this.scale===this.initialScale}resetToInitial(){this.scale=this.initialScale,this.offset=Object.assign({},this.initialOffset),this.calculateBoundaries(),this.isInitialScale=!0}updateOffset(t,e){this.offset.x+=t,this.offset.y+=e,this.constrainViewport()}reset(){this.scale=1,this.minScale=1,this.offset={x:0,y:0},this.initialScale=1,this.initialOffset={x:0,y:0},this.isInitialScale=!0,this.minOffsetX=0,this.minOffsetY=0,this.maxOffsetX=0,this.maxOffsetY=0}}class r{constructor(t){this.viewport=t,this.recordList=[],this.operate={type:"rect",data:[],status:"pending"},this.isDrawing=!1,this.drawStartPoint={x:0,y:0},this.tempPolygonPoint=null,this.selectedAnnotation=null,this.isMovingAnnotation=!1,this.isResizing=!1,this.activeHandle=null,this.annotationMoveStart={x:0,y:0},this.originalRect=null,this.originalPolygon=null,this.deleteHistory=[],this.strokeStyle="red",this.lineWidth=5,this.lineStyle="solid",this.vertexStyle={size:8,fillColor:"red",strokeColor:"white",strokeWidth:2,shape:"circle"},this.selectionStyle={strokeColor:"#00D9FF",fillColor:"rgba(0,217,255,0.15)",handleSize:12,handleColor:"#00D9FF"},this.colorConfig={rect:"red",polygon:"red",default:"red"},this.applyColorConfig()}setColorConfig(t){this.colorConfig="string"==typeof t?{rect:t,polygon:t,default:t}:Object.assign(Object.assign({},this.colorConfig),t),this.applyColorConfig()}getColorConfig(){return Object.assign({},this.colorConfig)}applyColorConfig(){this.strokeStyle=this.colorConfig.default||"red"}getColorForType(t){return this.colorConfig[t]||this.colorConfig.default||"red"}getCurrentStyle(){return{strokeColor:this.strokeStyle,lineWidth:this.lineWidth,lineStyle:this.lineStyle,vertexStyle:Object.assign({},this.vertexStyle)}}setLineStyle(t){this.lineStyle=t}setVertexStyle(t){this.vertexStyle=Object.assign(Object.assign({},this.vertexStyle),t),t.fillColor&&(this.strokeStyle=t.fillColor)}getLineStyle(){return this.lineStyle}getVertexStyle(){return Object.assign({},this.vertexStyle)}getAnnotationStyle(t){return t.style||this.getCurrentStyle()}startRectDrawing(t){this.isDrawing=!0,this.drawStartPoint=t,this.operate={type:"rect",data:[{start:Object.assign({},t),width:0,height:0}],status:"pending"}}updateRectDrawing(t){if(!this.isDrawing||"rect"!==this.operate.type)return;const e=this.operate.data[0];e.width=t.x-this.drawStartPoint.x,e.height=t.y-this.drawStartPoint.y}finishRectDrawing(){if(!this.isDrawing||"rect"!==this.operate.type)return!1;const t=this.operate.data[0];if(Math.abs(t.width)>5&&Math.abs(t.height)>5){const t=Object.assign(Object.assign({},this.operate),{style:this.getCurrentStyle()});this.recordList.push(t),this.deleteHistory=[]}return this.operate.data=[],this.isDrawing=!1,!0}startPolygonDrawing(t){this.isDrawing=!0,this.operate={type:"polygon",data:[{point:t}],status:"pending"},this.tempPolygonPoint=t}addPolygonPoint(t){if(!this.isDrawing||"polygon"!==this.operate.type)return!1;const e=this.operate.data.length>0?this.operate.data[this.operate.data.length-1]:null;return!(e&&Math.abs(e.point.x-t.x)<=5&&Math.abs(e.point.y-t.y)<=5)&&(this.operate.data.push({point:t}),!0)}updatePolygonTempPoint(t){this.tempPolygonPoint=t}finishPolygonDrawing(){if(!this.isDrawing||"polygon"!==this.operate.type)return!1;if(this.operate.data.length>=3){this.operate.status="fullfilled";const t=Object.assign(Object.assign({},this.operate),{style:this.getCurrentStyle()});this.recordList.push(t),this.deleteHistory=[]}return this.operate={type:"polygon",data:[],status:"pending"},this.isDrawing=!1,this.tempPolygonPoint=null,!0}cancelDrawing(){if(this.isDrawing&&"polygon"===this.operate.type&&this.operate.data.length>=2){this.operate.status="fullfilled";const t=Object.assign(Object.assign({},this.operate),{style:this.getCurrentStyle()});this.recordList.push(t)}this.operate.data=[],this.isDrawing=!1,this.tempPolygonPoint=null}withdraw(){if(this.operate.data.length>0)return"polygon"===this.operate.type?(this.operate.data.pop(),0===this.operate.data.length&&(this.isDrawing=!1)):(this.operate.data=[],this.isDrawing=!1),!0;if(this.deleteHistory.length>0){const{annotation:t,index:e}=this.deleteHistory.pop();return this.recordList.splice(e,0,t),!0}return this.recordList.length>0&&(this.recordList.pop(),!0)}selectAnnotation(t){if(t<0||t>=this.recordList.length)return this.deselectAnnotation(),!1;const e=this.recordList[t];return this.selectedAnnotation={index:t,type:e.type},!0}deselectAnnotation(){this.selectedAnnotation=null,this.isMovingAnnotation=!1,this.isResizing=!1,this.activeHandle=null,this.originalRect=null,this.originalPolygon=null}deleteSelectedAnnotation(){if(!this.selectedAnnotation)return!1;const t=this.selectedAnnotation.index,e=this.recordList[t];return this.deleteHistory.push({annotation:Object.assign({},e),index:t}),this.recordList.splice(t,1),this.deselectAnnotation(),!0}startMovingAnnotation(t){return!!this.selectedAnnotation&&(this.isMovingAnnotation=!0,this.annotationMoveStart={x:t.clientX,y:t.clientY},!0)}moveSelectedAnnotation(t,e){if(!this.selectedAnnotation)return!1;const n=this.recordList[this.selectedAnnotation.index];if(!n)return!1;if("rect"===n.type){const i=n.data[0];i.start.x+=t,i.start.y+=e}else if("polygon"===n.type){n.data.forEach(n=>{n.point.x+=t,n.point.y+=e})}return!0}finishMovingAnnotation(){this.isMovingAnnotation=!1,this.isResizing=!1,this.activeHandle=null,this.originalRect=null,this.originalPolygon=null}startResizing(t,e){if(!this.selectedAnnotation)return!1;this.activeHandle=t,this.isResizing=!0;const n=this.recordList[this.selectedAnnotation.index];return"rect"===n.type?this.originalRect=Object.assign({},n.data[0]):"polygon"===n.type&&(this.originalPolygon=n.data.map(t=>({point:Object.assign({},t.point)}))),!0}resizeRect(t){if(!this.selectedAnnotation||"rect"!==this.selectedAnnotation.type||!this.originalRect||!this.activeHandle)return!1;const e=this.recordList[this.selectedAnnotation.index].data[0],n=this.originalRect,i=n.start.x,s=n.start.x+n.width,a=n.start.y,o=n.start.y+n.height;let r,h;switch(this.activeHandle.index){case 0:r=s,h=o;break;case 1:r=i,h=o;break;case 2:r=s,h=a;break;case 3:r=i,h=a;break;default:return!1}const l=Math.min(t.x,r),c=Math.max(t.x,r),d=Math.min(t.y,h),g=Math.max(t.y,h);e.start.x=l,e.start.y=d,e.width=c-l,e.height=g-d;const x=t.x<r,f=t.y<h;return 0===this.activeHandle.index?x||f?!x&&f?this.activeHandle.index=1:x&&!f&&(this.activeHandle.index=2):this.activeHandle.index=3:1===this.activeHandle.index?x&&!f?this.activeHandle.index=2:x&&f?this.activeHandle.index=0:x||f||(this.activeHandle.index=3):2===this.activeHandle.index?!x&&f?this.activeHandle.index=1:x&&f?this.activeHandle.index=0:x||f||(this.activeHandle.index=3):3===this.activeHandle.index&&(x&&f?this.activeHandle.index=0:!x&&f?this.activeHandle.index=1:x&&!f&&(this.activeHandle.index=2)),this.originalRect=Object.assign({},e),!0}resizePolygon(t){if(!this.selectedAnnotation||"polygon"!==this.selectedAnnotation.type||!this.activeHandle)return!1;const e=this.recordList[this.selectedAnnotation.index].data,n=this.activeHandle.index;return n>=0&&n<e.length&&(e[n].point.x=t.x,e[n].point.y=t.y,!0)}getAnnotationAtPoint(n){for(let i=this.recordList.length-1;i>=0;i--){const s=this.recordList[i];if("rect"===s.type){if(t(n,s.data[0]))return{index:i,type:"rect"}}else if("polygon"===s.type){if(e(n,s.data))return{index:i,type:"polygon"}}}return null}getHandleAtPoint(t,e){if(!this.selectedAnnotation)return null;const n=this.recordList[this.selectedAnnotation.index],i=this.selectionStyle.handleSize/2;if("rect"===n.type){const s=n.data[0],a=this.viewport.offset.x+s.start.x*this.viewport.scale,o=this.viewport.offset.y+s.start.y*this.viewport.scale,r=s.width*this.viewport.scale,h=s.height*this.viewport.scale,l=[{x:a,y:o,index:0},{x:a+r,y:o,index:1},{x:a,y:o+h,index:2},{x:a+r,y:o+h,index:3}];for(const n of l)if(t>=n.x-i&&t<=n.x+i&&e>=n.y-i&&e<=n.y+i)return{type:"rect-corner",index:n.index}}else if("polygon"===n.type){const s=n.data;for(let n=0;n<s.length;n++){const a=s[n],o=this.viewport.offset.x+a.point.x*this.viewport.scale,r=this.viewport.offset.y+a.point.y*this.viewport.scale;if(t>=o-i&&t<=o+i&&e>=r-i&&e<=r+i)return{type:"polygon-vertex",index:n}}}return null}clear(){this.recordList=[],this.operate={type:"rect",data:[],status:"pending"},this.isDrawing=!1,this.tempPolygonPoint=null,this.deselectAnnotation(),this.deleteHistory=[]}getAnnotations(){return[...this.recordList]}getSelectedAnnotation(){if(!this.selectedAnnotation)return null;const t=this.recordList[this.selectedAnnotation.index];return t?{index:this.selectedAnnotation.index,type:this.selectedAnnotation.type,data:t}:null}}class h{constructor(t,e,n,i){this.viewport=t,this.container=e,this.ctx=n,this.renderCallback=i,this.textAnnotations=[],this.editingTextIndex=null,this.currentEditingIndex=null,this.isTextMoving=!1,this.textMoveStart={x:0,y:0},this.textBeforeEditing="",this.selectedTextIndex=null,this.textInput=null,this.deleteHistory=[],this.defaultInputStyle={border:"2px solid #00D9FF",borderRadius:"6px",padding:"6px 10px",fontSize:"16px",fontFamily:"Arial, sans-serif",backgroundColor:"#ffffff",color:"#333",boxShadow:"0 4px 12px rgba(0,0,0,0.3)",minWidth:"60px",maxWidth:"200px"},this.textStyle={font:"16px Arial",color:"#FFD700",padding:6,backgroundColor:"rgba(0,0,0,0.6)",borderRadius:4,selectedBorderColor:"#00D9FF",selectedBackgroundColor:"rgba(0,217,255,0.15)",inputStyle:Object.assign({},this.defaultInputStyle)},this.createTextInput()}createTextInput(){this.textInput=document.createElement("input"),this.textInput.type="text",this.textInput.style.position="absolute",this.textInput.style.zIndex="99999",this.textInput.style.display="none",this.textInput.style.outline="none",this.textInput.style.width="fit-content",this.textInput.placeholder="输入文字...",this.applyInputStyle(),this.textInput.addEventListener("blur",()=>{this.finishEditing(),this.renderCallback&&this.renderCallback()}),this.textInput.addEventListener("keydown",t=>{t.stopPropagation(),"Enter"===t.key?(this.finishEditing(),this.renderCallback&&this.renderCallback()):"Escape"===t.key?(t.preventDefault(),this.cancelEditing()):"Delete"===t.key&&""===this.textInput.value&&(t.preventDefault(),this.deleteEditingAnnotation())}),this.textInput.addEventListener("input",()=>{this.adjustInputWidth()}),this.container.appendChild(this.textInput)}applyInputStyle(){if(!this.textInput)return;const t=Object.assign(Object.assign({},this.defaultInputStyle),this.textStyle.inputStyle);this.textInput.style.border=t.border,this.textInput.style.borderRadius=t.borderRadius,this.textInput.style.padding=t.padding,this.textInput.style.fontSize=t.fontSize,this.textInput.style.fontFamily=t.fontFamily,this.textInput.style.backgroundColor=t.backgroundColor,this.textInput.style.color=t.color,this.textInput.style.boxShadow=t.boxShadow,this.textInput.style.minWidth=t.minWidth,this.textInput.style.maxWidth=t.maxWidth}setTextStyle(t){this.textStyle=Object.assign(Object.assign({},this.textStyle),t),this.applyInputStyle()}setInputStyle(t){this.textStyle.inputStyle=Object.assign(Object.assign({},this.textStyle.inputStyle),t),this.applyInputStyle()}setSelectionStyle(t){void 0!==t.selectedBorderColor&&(this.textStyle.selectedBorderColor=t.selectedBorderColor),void 0!==t.selectedBackgroundColor&&(this.textStyle.selectedBackgroundColor=t.selectedBackgroundColor)}getCurrentStyle(){return{font:this.textStyle.font,color:this.textStyle.color,backgroundColor:this.textStyle.backgroundColor}}addTextAnnotation(t,e,n=""){var i;this.ctx.font=this.textStyle.font;const s=n||"输入文字...",a=this.ctx.measureText(s);let o;if(a.actualBoundingBoxAscent&&a.actualBoundingBoxDescent)o=a.actualBoundingBoxAscent+a.actualBoundingBoxDescent;else{o=1.2*parseInt((null===(i=this.textStyle.font.match(/\d+/))||void 0===i?void 0:i[0])||"16")}const r={position:{x:t,y:e},text:n,style:this.getCurrentStyle(),width:Math.max(a.width,60),height:o};this.textAnnotations.push(r);const h=this.textAnnotations.length-1;return this.deleteHistory=[],this.startEditing(h),h}updateTextAnnotation(t,e){if(t<0||t>=this.textAnnotations.length)return!1;const n=this.textAnnotations[t];n.text=e,this.ctx.font=this.textStyle.font;const i=this.ctx.measureText(e);return n.width=i.width,n.height=1.2*parseInt(this.textStyle.font),!0}moveTextAnnotation(t,e,n){return!(t<0||t>=this.textAnnotations.length)&&(this.textAnnotations[t].position={x:e,y:n},!0)}removeTextAnnotation(t){if(t<0||t>=this.textAnnotations.length)return!1;const e=this.textAnnotations[t];return this.deleteHistory.push({annotation:Object.assign({},e),index:t}),this.textAnnotations.splice(t,1),this.editingTextIndex===t?this.cancelEditing():null!==this.editingTextIndex&&this.editingTextIndex>t&&this.editingTextIndex--,!0}clearTextAnnotations(){this.textAnnotations=[],this.selectedTextIndex=null,this.cancelEditing(),this.deleteHistory=[]}startEditing(t){if(!this.textInput||t<0||t>=this.textAnnotations.length)return!1;const e=this.textAnnotations[t];this.editingTextIndex=t,this.currentEditingIndex=t,this.textBeforeEditing=e.text;const n=this.viewport.offset.x+e.position.x*this.viewport.scale,i=this.viewport.offset.y+e.position.y*this.viewport.scale;return this.textInput.value=e.text,this.textInput.style.left=n-this.textStyle.padding+"px",this.textInput.style.top=i-this.textStyle.padding+"px",this.textInput.style.display="block",this.adjustInputWidth(),setTimeout(()=>{this.textInput&&(this.textInput.focus(),this.textInput.select())},10),!0}finishEditing(){if(!this.textInput)return!1;const t=this.currentEditingIndex,e=this.textInput.value.trim();if(null===t||t<0||t>=this.textAnnotations.length)return this.resetEditingState(),!1;if(""===e)this.textAnnotations.splice(t,1);else{const n=this.textAnnotations[t];n.text=e,this.ctx.font=this.textStyle.font;const i=this.ctx.measureText(n.text);n.width=i.width,n.height=1.2*parseInt(this.textStyle.font)}return this.resetEditingState(),!0}cancelEditing(){if(!this.textInput||null===this.editingTextIndex)return!1;this.textInput.value=this.textBeforeEditing;const t=this.editingTextIndex;if(t>=0&&t<this.textAnnotations.length){""===this.textAnnotations[t].text&&""===this.textBeforeEditing&&this.textAnnotations.splice(t,1)}return this.resetEditingState(),!0}deleteEditingAnnotation(){if(null===this.editingTextIndex)return!1;const t=this.editingTextIndex;return this.textInput&&(this.textInput.style.display="none"),t>=0&&t<this.textAnnotations.length&&this.textAnnotations.splice(t,1),this.resetEditingState(),!0}resetEditingState(){this.textInput&&(this.textInput.style.display="none"),this.editingTextIndex=null,this.currentEditingIndex=null,this.textBeforeEditing=""}adjustInputWidth(){if(!this.textInput||null===this.editingTextIndex)return;this.ctx.font=this.textStyle.font;const t=this.textInput.value||this.textInput.placeholder||"",e=this.ctx.measureText(t).width,n=Math.min(Math.max(e+24,60),200);this.textInput.style.width=`${n}px`}startMoving(t,e){return!(e<0||e>=this.textAnnotations.length)&&(this.editingTextIndex=e,this.isTextMoving=!0,this.textMoveStart={x:t.clientX,y:t.clientY},!0)}moveAnnotation(t){if(!this.isTextMoving||null===this.editingTextIndex)return!1;const e=t.clientX-this.textMoveStart.x,n=t.clientY-this.textMoveStart.y,i=this.textAnnotations[this.editingTextIndex];return!!i&&(i.position.x+=e/this.viewport.scale,i.position.y+=n/this.viewport.scale,this.textMoveStart={x:t.clientX,y:t.clientY},!0)}finishMoving(){this.isTextMoving=!1}checkTextClick(t,e){for(let n=this.textAnnotations.length-1;n>=0;n--){const i=this.textAnnotations[n],s=this.viewport.offset.x+i.position.x*this.viewport.scale,a=this.viewport.offset.y+i.position.y*this.viewport.scale;if(t>=s-this.textStyle.padding&&t<=s+i.width+this.textStyle.padding&&e>=a-this.textStyle.padding&&e<=a+i.height+this.textStyle.padding)return n}return null}checkTextClickForMove(t){const e=this.checkTextClick(t.offsetX,t.offsetY);return null!==e?(this.startMoving(t,e),{index:e,handled:!0}):{index:-1,handled:!1}}getTextAnnotations(){return[...this.textAnnotations]}withdraw(){if(this.deleteHistory.length>0){const{annotation:t,index:e}=this.deleteHistory.pop();return this.textAnnotations.splice(e,0,t),!0}return this.textAnnotations.length>0&&(this.textAnnotations.pop(),!0)}selectTextAnnotation(t){return t<0||t>=this.textAnnotations.length?(this.deselectTextAnnotation(),!1):(this.selectedTextIndex=t,null!==this.editingTextIndex&&this.finishEditing(),!0)}deselectTextAnnotation(){this.selectedTextIndex=null}deleteSelectedTextAnnotation(){if(null===this.selectedTextIndex)return!1;const t=this.selectedTextIndex,e=this.textAnnotations[t];return this.deleteHistory.push({annotation:Object.assign({},e),index:t}),this.textAnnotations.splice(t,1),this.selectedTextIndex=null,null!==this.editingTextIndex&&this.editingTextIndex>t&&this.editingTextIndex--,!0}getSelectedTextAnnotation(){if(null===this.selectedTextIndex)return null;const t=this.textAnnotations[this.selectedTextIndex];return t?{index:this.selectedTextIndex,data:t}:null}destroy(){this.textInput&&this.textInput.parentNode&&this.textInput.parentNode.removeChild(this.textInput)}}class l{constructor(t,e,n,i,s){this.ctx=t,this.viewport=e,this.annotationManager=n,this.textManager=i,this.canvas=s}render(t){this.ctx.clearRect(0,0,this.viewport.width,this.viewport.height),t&&this.drawBackgroundImage(t),this.drawAnnotations(),this.drawTextAnnotations()}drawBackgroundImage(t){this.ctx.drawImage(t,this.viewport.offset.x,this.viewport.offset.y,this.viewport.originalWidth*this.viewport.scale,this.viewport.originalHeight*this.viewport.scale)}drawAnnotations(){this.annotationManager.recordList.forEach((t,e)=>{var n;const i=this.annotationManager.getAnnotationStyle(t);this.ctx.strokeStyle=i.strokeColor,this.ctx.lineWidth=i.lineWidth,this.setLineDash(i.lineStyle||"solid"),this.ctx.fillStyle=this.ctx.strokeStyle;const s=(null===(n=this.annotationManager.selectedAnnotation)||void 0===n?void 0:n.index)===e;if("rect"===t.type)this.drawRect(t.data[0],s);else if("polygon"===t.type){const e=i.vertexStyle||this.annotationManager.getVertexStyle();this.drawPolygon(t.data,"fullfilled"===t.status,e)}}),this.annotationManager.operate.data.length>0&&(this.ctx.strokeStyle=this.annotationManager.strokeStyle,this.ctx.lineWidth=this.annotationManager.lineWidth,this.ctx.fillStyle=this.ctx.strokeStyle,this.setLineDash(this.annotationManager.lineStyle),"rect"===this.annotationManager.operate.type?this.drawRect(this.annotationManager.operate.data[0],!1):"polygon"===this.annotationManager.operate.type&&this.drawCurrentPolygon()),this.ctx.setLineDash([]),this.drawSelectionHighlight()}setLineDash(t){switch(t){case"dashed":this.ctx.setLineDash([10,5]);break;case"dotted":this.ctx.setLineDash([2,4]);break;default:this.ctx.setLineDash([])}}drawRect(t,e=!1){this.ctx.strokeRect(this.viewport.offset.x+t.start.x*this.viewport.scale,this.viewport.offset.y+t.start.y*this.viewport.scale,t.width*this.viewport.scale,t.height*this.viewport.scale)}drawPolygon(t,e,n){if(0!==t.length){if(this.ctx.beginPath(),t.forEach((t,e)=>{const n=Math.round(this.viewport.offset.x+t.point.x*this.viewport.scale),i=Math.round(this.viewport.offset.y+t.point.y*this.viewport.scale);0===e?this.ctx.moveTo(n,i):this.ctx.lineTo(n,i)}),e&&t.length>1){const e=t[0];this.ctx.lineTo(this.viewport.offset.x+e.point.x*this.viewport.scale,this.viewport.offset.y+e.point.y*this.viewport.scale)}this.ctx.stroke(),this.drawPolygonVertices(t,n)}}drawPolygonVertices(t,e){const n=e||this.annotationManager.getVertexStyle(),i=n.size/2;t.forEach(t=>{const e=Math.round(this.viewport.offset.x+t.point.x*this.viewport.scale),s=Math.round(this.viewport.offset.y+t.point.y*this.viewport.scale);switch(this.ctx.fillStyle=n.fillColor,this.ctx.strokeStyle=n.strokeColor,this.ctx.lineWidth=n.strokeWidth,this.ctx.beginPath(),n.shape){case"square":this.ctx.rect(e-i,s-i,2*i,2*i);break;case"diamond":this.ctx.moveTo(e,s-i),this.ctx.lineTo(e+i,s),this.ctx.lineTo(e,s+i),this.ctx.lineTo(e-i,s),this.ctx.closePath();break;default:this.ctx.arc(e,s,i,0,2*Math.PI)}this.ctx.fill(),n.strokeWidth>0&&this.ctx.stroke()})}drawCurrentPolygon(){const t=this.annotationManager.operate.data,e=this.annotationManager.tempPolygonPoint;if(this.ctx.beginPath(),t.forEach((t,e)=>{const n=Math.round(this.viewport.offset.x+t.point.x*this.viewport.scale),i=Math.round(this.viewport.offset.y+t.point.y*this.viewport.scale);0===e?this.ctx.moveTo(n,i):this.ctx.lineTo(n,i)}),e){const t=Math.round(this.viewport.offset.x+e.x*this.viewport.scale),n=Math.round(this.viewport.offset.y+e.y*this.viewport.scale);this.ctx.lineTo(t,n)}if(this.ctx.stroke(),this.drawPolygonVertices(t,this.annotationManager.getVertexStyle()),e){const t=this.annotationManager.getVertexStyle(),n=Math.round(this.viewport.offset.x+e.x*this.viewport.scale),i=Math.round(this.viewport.offset.y+e.y*this.viewport.scale),s=t.size/2;switch(this.ctx.fillStyle=t.fillColor,this.ctx.strokeStyle=t.strokeColor,this.ctx.lineWidth=t.strokeWidth,this.ctx.beginPath(),t.shape){case"square":this.ctx.rect(n-s,i-s,2*s,2*s);break;case"diamond":this.ctx.moveTo(n,i-s),this.ctx.lineTo(n+s,i),this.ctx.lineTo(n,i+s),this.ctx.lineTo(n-s,i),this.ctx.closePath();break;default:this.ctx.arc(n,i,s,0,2*Math.PI)}this.ctx.fill(),t.strokeWidth>0&&this.ctx.stroke()}}drawSelectionHighlight(){const t=this.annotationManager.selectedAnnotation;if(!t)return;const e=this.annotationManager.recordList[t.index];if(!e)return;const n=this.annotationManager.selectionStyle;this.ctx.save(),"rect"===e.type?this.drawRectSelectionHighlight(e.data[0],n):"polygon"===e.type&&this.drawPolygonSelectionHighlight(e.data,n),this.ctx.restore()}drawRectSelectionHighlight(t,e){const n=this.viewport.offset.x+t.start.x*this.viewport.scale,i=this.viewport.offset.y+t.start.y*this.viewport.scale,s=t.width*this.viewport.scale,a=t.height*this.viewport.scale;this.ctx.fillStyle=e.fillColor,this.ctx.fillRect(n,i,s,a),this.ctx.strokeStyle=e.strokeColor,this.ctx.lineWidth=2,this.ctx.setLineDash([5,5]),this.ctx.strokeRect(n,i,s,a);const o=e.handleSize;this.ctx.fillStyle=e.handleColor,this.ctx.setLineDash([]),this.ctx.fillRect(n-o/2,i-o/2,o,o),this.ctx.fillRect(n+s-o/2,i-o/2,o,o),this.ctx.fillRect(n-o/2,i+a-o/2,o,o),this.ctx.fillRect(n+s-o/2,i+a-o/2,o,o)}drawPolygonSelectionHighlight(t,e){this.ctx.beginPath(),t.forEach((t,e)=>{const n=this.viewport.offset.x+t.point.x*this.viewport.scale,i=this.viewport.offset.y+t.point.y*this.viewport.scale;0===e?this.ctx.moveTo(n,i):this.ctx.lineTo(n,i)}),this.ctx.closePath(),this.ctx.fillStyle=e.fillColor,this.ctx.fill(),this.ctx.strokeStyle=e.strokeColor,this.ctx.lineWidth=2,this.ctx.setLineDash([5,5]),this.ctx.stroke();const n=e.handleSize;this.ctx.fillStyle=e.handleColor,this.ctx.setLineDash([]),t.forEach(t=>{const e=Math.round(this.viewport.offset.x+t.point.x*this.viewport.scale),i=Math.round(this.viewport.offset.y+t.point.y*this.viewport.scale);this.ctx.fillRect(e-n/2,i-n/2,n,n)})}drawTextAnnotations(){const t=this.textManager.textStyle;this.textManager.textAnnotations.forEach((e,n)=>{if(this.textManager.editingTextIndex===n&&this.textManager.textInput&&"none"!==this.textManager.textInput.style.display)return;if(!e.text)return;const i=e.style||{font:t.font,color:t.color,backgroundColor:t.backgroundColor},s=this.viewport.offset.x+e.position.x*this.viewport.scale,a=this.viewport.offset.y+e.position.y*this.viewport.scale,o=this.textManager.selectedTextIndex===n;this.ctx.font=i.font;const r=this.ctx.measureText(e.text),h=r.actualBoundingBoxAscent||.8*e.height,l=h+(r.actualBoundingBoxDescent||.2*e.height),c=t.borderRadius,d=s-t.padding,g=e.width+2*t.padding,x=l+2*t.padding,f=a-h-t.padding;this.ctx.fillStyle=i.backgroundColor,this.ctx.beginPath(),this.ctx.roundRect(d,f,g,x,c),this.ctx.fill(),o&&(this.ctx.strokeStyle=t.selectedBorderColor,this.ctx.lineWidth=2,this.ctx.setLineDash([5,5]),this.ctx.beginPath(),this.ctx.roundRect(d,f,g,x,c),this.ctx.stroke(),this.ctx.setLineDash([])),this.ctx.fillStyle=i.color,this.ctx.fillText(e.text,s,a)})}drawForExport(t,e,n,i){t.drawImage(e,0,0,n,i),this.annotationManager.recordList.forEach(e=>{const n=this.annotationManager.getAnnotationStyle(e);if(t.strokeStyle=n.strokeColor,t.lineWidth=n.lineWidth,"rect"===e.type){const n=e.data[0];t.strokeRect(n.start.x,n.start.y,n.width,n.height)}else if("polygon"===e.type&&"fullfilled"===e.status){t.beginPath(),e.data.forEach((e,n)=>{0===n?t.moveTo(e.point.x,e.point.y):t.lineTo(e.point.x,e.point.y)});const i=e.data[0];t.lineTo(i.point.x,i.point.y),t.stroke(),e.data.forEach(e=>{t.beginPath(),t.arc(e.point.x,e.point.y,4,0,2*Math.PI),t.fillStyle=n.strokeColor,t.fill()})}}),this.textManager.textAnnotations.forEach(e=>{const n=this.textManager.textStyle,i=e.style||{font:n.font,color:n.color,backgroundColor:n.backgroundColor};t.font=i.font;const s=t.measureText(e.text),a=s.actualBoundingBoxAscent||.8*e.height,o=a+(s.actualBoundingBoxDescent||.2*e.height),r=e.position.x-n.padding,h=e.width+2*n.padding,l=o+2*n.padding,c=e.position.y-a-n.padding,d=n.borderRadius;t.fillStyle=i.backgroundColor,t.roundRect?(t.roundRect(r,c,h,l,d),t.fill()):t.fillRect(r,c,h,l),t.fillStyle=i.color,t.fillText(e.text,e.position.x,e.position.y)})}}class c{constructor(t,e,n,i,s,a,o,r,h){this.canvas=t,this.viewport=e,this.annotationManager=n,this.textManager=i,this.renderer=s,this.getDrawType=a,this.getBgImage=o,this.renderCallback=r,this.setDrawTypeCallback=h,this.isDragging=!1,this.dragStart={x:0,y:0},this.justFinishedMove=!1,this.justDeselected=!1}handleWheel(t){if(t.preventDefault(),!this.getBgImage())return;const e=t.deltaY>0?-.02:.02,n=this.viewport.scale,i=Math.max(this.viewport.minScale,Math.min(n+e,this.viewport.maxScale)),s=this.canvas.getBoundingClientRect(),a=t.clientX-s.left,o=t.clientY-s.top,r=(a-this.viewport.offset.x)/n,h=(o-this.viewport.offset.y)/n;this.viewport.scale=i;const l=r*i+this.viewport.offset.x,c=h*i+this.viewport.offset.y;this.viewport.offset.x+=a-l,this.viewport.offset.y+=o-c,this.viewport.calculateBoundaries(),this.viewport.constrainViewport(),this.viewport.isInitialScale=this.viewport.scale===this.viewport.initialScale,this.renderCallback()}handleMouseDown(t){var e,n;if(0!==t.button)return;const i=this.getDrawType(),s=this.getBgImage();if(this.annotationManager.selectedAnnotation&&s){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY),n=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(n)return this.annotationManager.startResizing(n,e),void this.renderCallback()}if("text"===i){const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return this.textManager.selectedTextIndex===e?this.textManager.startMoving(t,e):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(e)),void this.renderCallback();if(s){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY),n=this.annotationManager.getAnnotationAtPoint(e);if("rect"===(null==n?void 0:n.type))return this.annotationManager.selectAnnotation(n.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();const i=this.annotationManager.getAnnotationAtPoint(e);if("polygon"===(null==i?void 0:i.type))return this.annotationManager.selectAnnotation(i.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();if(this.annotationManager.selectedAnnotation)return this.annotationManager.deselectAnnotation(),void this.renderCallback()}return null!==this.textManager.selectedTextIndex?(this.textManager.deselectTextAnnotation(),void this.renderCallback()):void this.handleTextModeClick(t)}if(!s)return;const a=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);if("rect"===i){if("rect"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type)){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return this.annotationManager.startResizing(e,a),void this.renderCallback();const n=this.annotationManager.getAnnotationAtPoint(a);return n&&"rect"===n.type?(this.annotationManager.selectAnnotation(n.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback()):(this.annotationManager.deselectAnnotation(),this.justDeselected=!0,void this.renderCallback())}const n=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==n)return this.textManager.selectedTextIndex===n?this.textManager.startMoving(t,n):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(n)),void this.renderCallback();const i=this.annotationManager.getAnnotationAtPoint(a);if("polygon"===(null==i?void 0:i.type))return this.annotationManager.selectAnnotation(i.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();const s=this.annotationManager.getAnnotationAtPoint(a);if("rect"===(null==s?void 0:s.type))return this.annotationManager.selectAnnotation(s.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();this.annotationManager.startRectDrawing(a)}else if("polygon"===i){if("polygon"===(null===(n=this.annotationManager.selectedAnnotation)||void 0===n?void 0:n.type)){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e&&"polygon-vertex"===e.type)return this.annotationManager.startResizing(e,a),void this.renderCallback();const n=this.annotationManager.getAnnotationAtPoint(a);return n&&"polygon"===n.type?(this.annotationManager.selectAnnotation(n.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback()):(this.annotationManager.deselectAnnotation(),this.justDeselected=!0,void this.renderCallback())}const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return this.textManager.selectedTextIndex===e?this.textManager.startMoving(t,e):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(e)),void this.renderCallback();const i=this.annotationManager.getAnnotationAtPoint(a);if("rect"===(null==i?void 0:i.type))return this.annotationManager.selectAnnotation(i.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();const s=this.annotationManager.getAnnotationAtPoint(a);if("polygon"===(null==s?void 0:s.type))return this.annotationManager.selectAnnotation(s.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();this.annotationManager.isDrawing?this.checkPolygonPointValid(a)&&this.annotationManager.addPolygonPoint(a):this.annotationManager.startPolygonDrawing(a),this.annotationManager.updatePolygonTempPoint(a),this.renderCallback()}else if("drag"===i){if(this.annotationManager.selectedAnnotation){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return this.annotationManager.startResizing(e,a),void this.renderCallback()}const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return this.textManager.selectedTextIndex===e?this.textManager.startMoving(t,e):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(e)),void this.renderCallback();if(this.handleRectModeClick(t,a))return;if(this.handlePolygonModeClick(t,a))return;this.isDragging=!0,this.dragStart={x:t.clientX,y:t.clientY},this.canvas.style.cursor="grabbing"}else""===i&&this.handleNoModeClick(t,a)}checkTextClickInDragMode(t){return!!this.textManager.checkTextClickForMove(t).handled&&(this.canvas.style.cursor="grabbing",!0)}handleTextModeClick(t){const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return this.textManager.startMoving(t,e),this.canvas.style.cursor="grabbing",this.renderCallback(),!0;const n=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);return this.textManager.addTextAnnotation(n.x,n.y),!0}handleRectModeClick(t,e){var n,i;if("rect"===(null===(n=this.annotationManager.selectedAnnotation)||void 0===n?void 0:n.type)){const n=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(n)return this.annotationManager.startResizing(n,e),this.renderCallback(),!0}const s=this.annotationManager.getAnnotationAtPoint(e);return s&&"rect"===s.type?(this.annotationManager.selectAnnotation(s.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback(),!0):"rect"===(null===(i=this.annotationManager.selectedAnnotation)||void 0===i?void 0:i.type)&&(this.annotationManager.deselectAnnotation(),this.renderCallback(),!0)}handlePolygonModeClick(t,e){var n,i;if("polygon"===(null===(n=this.annotationManager.selectedAnnotation)||void 0===n?void 0:n.type)){const n=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(n&&"polygon-vertex"===n.type)return this.annotationManager.startResizing(n,e),this.renderCallback(),!0}const s=this.annotationManager.getAnnotationAtPoint(e);return s&&"polygon"===s.type?(this.annotationManager.selectAnnotation(s.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback(),!0):"polygon"===(null===(i=this.annotationManager.selectedAnnotation)||void 0===i?void 0:i.type)&&(this.annotationManager.deselectAnnotation(),this.renderCallback(),!0)}handleRectModeClickForMove(t,e){const n=this.annotationManager.getAnnotationAtPoint(e);return!(!n||"rect"!==n.type)&&(this.annotationManager.selectAnnotation(n.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback(),!0)}handlePolygonModeClickForMove(t,e){const n=this.annotationManager.getAnnotationAtPoint(e);return!(!n||"polygon"!==n.type)&&(this.annotationManager.selectAnnotation(n.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback(),!0)}handleNoModeClick(t,e){const n=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==n)return this.textManager.selectedTextIndex===n?this.textManager.startMoving(t,n):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(n)),void this.renderCallback();this.textManager.deselectTextAnnotation();const i=this.annotationManager.getAnnotationAtPoint(e);i?(this.annotationManager.selectAnnotation(i.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback()):this.annotationManager.deselectAnnotation()}checkPolygonPointValid(t){const e=this.annotationManager.operate.data.length>0?this.annotationManager.operate.data[this.annotationManager.operate.data.length-1]:null;return!e||Math.abs(e.point.x-t.x)>5||Math.abs(e.point.y-t.y)>5}handleMouseMove(t){if(!this.getBgImage())return;const e=this.getDrawType();if(this.isDragging&&"drag"===e){const e=t.clientX-this.dragStart.x,n=t.clientY-this.dragStart.y;return this.viewport.updateOffset(e,n),this.dragStart={x:t.clientX,y:t.clientY},void this.renderCallback()}if(this.textManager.isTextMoving&&null!==this.textManager.editingTextIndex)this.textManager.moveAnnotation(t)&&this.renderCallback();else if((this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing)&&this.annotationManager.selectedAnnotation)this.handleAnnotationMove(t);else{if(this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing||this.textManager.isTextMoving||this.updateCursor(t),this.annotationManager.isDrawing&&"rect"===this.annotationManager.operate.type){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.annotationManager.updateRectDrawing(e),this.renderCallback()}if("polygon"===e&&this.annotationManager.isDrawing){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.annotationManager.updatePolygonTempPoint(e),this.renderCallback()}}}handleAnnotationMove(t){var e,n;if(this.annotationManager.isResizing&&this.annotationManager.activeHandle){const i=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);return"rect"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type)?this.annotationManager.resizeRect(i):"polygon"===(null===(n=this.annotationManager.selectedAnnotation)||void 0===n?void 0:n.type)&&this.annotationManager.resizePolygon(i),void this.renderCallback()}if(!this.annotationManager.isMovingAnnotation||!this.annotationManager.selectedAnnotation)return;const i=(t.clientX-this.annotationManager.annotationMoveStart.x)/this.viewport.scale,s=(t.clientY-this.annotationManager.annotationMoveStart.y)/this.viewport.scale;this.annotationManager.moveSelectedAnnotation(i,s),this.annotationManager.annotationMoveStart={x:t.clientX,y:t.clientY},this.renderCallback()}updateCursor(t){const e=this.getDrawType();if("text"===e){if(this.getBgImage()){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);if(this.annotationManager.selectedAnnotation){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return void("rect-corner"===e.type?this.canvas.style.cursor=0===e.index||3===e.index?"nwse-resize":"nesw-resize":this.canvas.style.cursor="crosshair")}if(this.annotationManager.getAnnotationAtPoint(e))return void(this.canvas.style.cursor="move")}const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);return void(this.canvas.style.cursor=null!==e?"move":"crosshair")}"rect"!==e?"polygon"!==e?"drag"!==e?""!==e?this.canvas.style.cursor="default":this.updateNoModeCursor(t):this.updateDragModeCursor(t):this.updatePolygonModeCursor(t):this.updateRectModeCursor(t)}updateRectModeCursor(t){var e;if("rect"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type)){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return void(this.canvas.style.cursor=0===e.index||3===e.index?"nwse-resize":"nesw-resize")}const n=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.annotationManager.getAnnotationAtPoint(n)?this.canvas.style.cursor="move":null===this.textManager.checkTextClick(t.offsetX,t.offsetY)?this.canvas.style.cursor="crosshair":this.canvas.style.cursor="move"}updatePolygonModeCursor(t){var e;if("polygon"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type)){if(this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY))return void(this.canvas.style.cursor="crosshair")}const n=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.annotationManager.getAnnotationAtPoint(n)?this.canvas.style.cursor="move":null===this.textManager.checkTextClick(t.offsetX,t.offsetY)?this.canvas.style.cursor="crosshair":this.canvas.style.cursor="move"}updateDragModeCursor(t){if(this.annotationManager.selectedAnnotation){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return void("rect-corner"===e.type?this.canvas.style.cursor=0===e.index||3===e.index?"nwse-resize":"nesw-resize":this.canvas.style.cursor="crosshair")}if(null!==this.textManager.checkTextClick(t.offsetX,t.offsetY))return void(this.canvas.style.cursor="move");const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY),n=this.annotationManager.getAnnotationAtPoint(e);this.canvas.style.cursor=n?"move":"grab"}updateNoModeCursor(t){if(this.annotationManager.selectedAnnotation){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return void("rect-corner"===e.type?this.canvas.style.cursor=0===e.index||3===e.index?"nwse-resize":"nesw-resize":this.canvas.style.cursor="crosshair")}if(null!==this.textManager.checkTextClick(t.offsetX,t.offsetY))return void(this.canvas.style.cursor="move");const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY),n=this.annotationManager.getAnnotationAtPoint(e);this.canvas.style.cursor=n?"move":"default"}handleMouseUp(t){if(this.isDragging){this.isDragging=!1;const t=this.getDrawType();return void(this.canvas.style.cursor="drag"===t?"grab":"default")}if(this.textManager.isTextMoving){this.justFinishedMove=!0,this.textManager.finishMoving();const t=this.getDrawType();"drag"===t?this.canvas.style.cursor="grab":""===t?this.canvas.style.cursor="move":(this.textManager.editingTextIndex=null,this.canvas.style.cursor="default")}(this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing)&&(this.justFinishedMove=!0,this.annotationManager.finishMovingAnnotation(),this.canvas.style.cursor=this.annotationManager.selectedAnnotation?"move":"default"),this.annotationManager.isDrawing&&"rect"===this.annotationManager.operate.type&&(this.annotationManager.finishRectDrawing(),this.renderCallback())}handleMouseLeave(t){(this.isDragging||this.textManager.isTextMoving||this.annotationManager.isResizing||this.annotationManager.isMovingAnnotation)&&this.handleMouseUp(t)}handleDoubleClick(t){if(!this.getBgImage())return;this.justFinishedMove=!1;const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return void this.textManager.startEditing(e);if("polygon"===this.getDrawType()){if(this.annotationManager.isDrawing&&this.annotationManager.operate.data.length<=2)return;this.annotationManager.finishPolygonDrawing(),this.renderCallback()}}handleClick(t){var e;const n=this.getDrawType();if(!this.getBgImage()||"polygon"!==n||this.isDragging||this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing||this.textManager.isTextMoving)return;if(this.justDeselected)return void(this.justDeselected=!1);if("polygon"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type))return;const i=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.checkPolygonPointValid(i)&&(this.annotationManager.isDrawing?this.annotationManager.addPolygonPoint(i):this.annotationManager.startPolygonDrawing(i)),this.annotationManager.updatePolygonTempPoint(i),this.renderCallback()}handleKeyDown(t){if("z"===t.key&&(t.ctrlKey||t.metaKey))return this.annotationManager.withdraw()||this.textManager.withdraw(),void this.renderCallback();if(" "===t.key)return this.viewport.resetToInitial(),void this.renderCallback();if("Escape"!==t.key){if("Delete"===t.key||"Backspace"===t.key){if(null!==this.textManager.editingTextIndex)return this.textManager.removeTextAnnotation(this.textManager.editingTextIndex),void this.renderCallback();if(null!==this.textManager.selectedTextIndex)return this.textManager.deleteSelectedTextAnnotation(),void this.renderCallback();this.annotationManager.selectedAnnotation&&(this.annotationManager.deleteSelectedAnnotation(),this.renderCallback())}}else this.annotationManager.isDrawing?(this.annotationManager.cancelDrawing(),this.renderCallback()):this.annotationManager.selectedAnnotation&&(this.annotationManager.deselectAnnotation(),this.renderCallback())}}class d{constructor(t){this.canvas=document.createElement("canvas"),this.bgImage=null,this.bgImageSrc="",this.bgImageExt="jpeg",this.drawType="";const{drawType:e,useEvents:n=!0,id:i="container",annotationColor:s,lineStyle:a,vertexStyle:d,textStyle:g}=t,x=document.getElementById(i);if(!x)throw new Error(`Container with id "${i}" not found`);this.container=x,"absolute"!==x.style.position&&"fixed"!==x.style.position&&"relative"!==x.style.position&&(x.style.position="relative"),this.ctx=this.canvas.getContext("2d"),this.viewport=new o,this.viewport.setSize(x.clientWidth,x.clientHeight),this.canvas.width=this.viewport.width,this.canvas.height=this.viewport.height,this.canvas.style.width=`${this.viewport.width}px`,this.canvas.style.height=`${this.viewport.height}px`,this.canvas.style.cursor="default",this.canvas.style.display="block",x.appendChild(this.canvas),this.annotationManager=new r(this.viewport),s&&this.annotationManager.setColorConfig(s),a&&this.annotationManager.setLineStyle(a),d&&this.annotationManager.setVertexStyle(d),this.textManager=new h(this.viewport,this.container,this.ctx,()=>this.render()),g&&this.textManager.setTextStyle(g),this.renderer=new l(this.ctx,this.viewport,this.annotationManager,this.textManager,this.canvas),this.eventHandler=new c(this.canvas,this.viewport,this.annotationManager,this.textManager,this.renderer,()=>this.drawType,()=>this.bgImage,()=>this.render(),t=>this.setDrawType(t)),this.events={wheel:t=>this.eventHandler.handleWheel(t),mousedown:t=>this.eventHandler.handleMouseDown(t),mousemove:t=>this.eventHandler.handleMouseMove(t),mouseup:t=>this.eventHandler.handleMouseUp(t),mouseleave:t=>this.eventHandler.handleMouseLeave(t),dblclick:t=>this.eventHandler.handleDoubleClick(t),click:t=>this.eventHandler.handleClick(t),keydown:t=>this.eventHandler.handleKeyDown(t)},this.setDrawType(e||""),n&&this.addEventListeners()}setDrawType(t){"polygon"===this.drawType&&this.annotationManager.isDrawing&&this.annotationManager.operate.data.length>0?console.warn("Cannot switch mode while polygon is being drawn. Please complete or cancel the polygon first."):this.drawType!==t&&(this.annotationManager.operate.data=[],this.annotationManager.isDrawing=!1,this.annotationManager.tempPolygonPoint=null,""!==t&&this.deselectAnnotation(),this.annotationManager.operate.type=t,this.drawType=t,this.canvas.style.cursor="drag"===t?"grab":"text"===t?"text":"default")}drawImage(t){this.bgImageSrc=t,this.bgImageExt=n(t);const e=new Image;e.crossOrigin="anonymous",e.src=t,e.onload=()=>{this.bgImage=e,this.viewport.setOriginalSize(e.width,e.height),this.viewport.calculateInitialView(),this.render()}}render(){this.renderer.render(this.bgImage)}clear(t=""){this.annotationManager.clear(),this.textManager.clearTextAnnotations(),this.setDrawType(t),this.render()}withdraw(){const t=this.annotationManager.withdraw(),e=this.textManager.withdraw();(t||e)&&this.render()}resize(){const t=this.canvas.parentElement;t&&(this.viewport.setSize(t.clientWidth,t.clientHeight),this.canvas.width=this.viewport.width,this.canvas.height=this.viewport.height,this.canvas.style.width=`${this.viewport.width}px`,this.canvas.style.height=`${this.viewport.height}px`,this.render())}addEventListeners(){this.canvas.addEventListener("mousedown",this.events.mousedown),this.canvas.addEventListener("mousemove",this.events.mousemove),this.canvas.addEventListener("mouseup",this.events.mouseup),this.canvas.addEventListener("mouseleave",this.events.mouseleave),this.canvas.addEventListener("wheel",this.events.wheel),this.canvas.addEventListener("contextmenu",t=>t.preventDefault()),document.addEventListener("keydown",this.events.keydown),this.handleClickEventListener()}handleClickEventListener(){let t=null,e=0;this.canvas.addEventListener("click",n=>{if(this.eventHandler.isDragging||this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing||this.textManager.isTextMoving)return;const i=this.eventHandler.justFinishedMove;i&&(this.eventHandler.justFinishedMove=!1),e++,1===e?t=window.setTimeout(()=>{1!==e||i||this.events.click(n),e=0,t=null},300):2===e&&(this.eventHandler.justFinishedMove=!1,this.events.dblclick(n),e=0,null!==t&&(clearTimeout(t),t=null))})}changeScale(t){if(!this.bgImage)return;const e=a(this.viewport.scale,t),n=this.viewport.width/2,i=this.viewport.height/2,s=this.viewport.scale+e;this.viewport.updateScale(s,n,i),this.render()}clearCanvas(t=!1,e=!0){this.annotationManager.clear(),this.textManager.clearTextAnnotations(),this.textManager.deselectTextAnnotation(),!t&&e&&(this.bgImage=null,this.bgImageSrc="",this.viewport.setOriginalSize(0,0),this.viewport.reset()),this.ctx.clearRect(0,0,this.viewport.width,this.viewport.height),t&&this.bgImage&&this.render(),"drag"===this.drawType&&(this.canvas.style.cursor="default")}clearAnnotations(){this.clearCanvas(!0,!1)}getAnnotations(){return this.annotationManager.getAnnotations()}getTextAnnotations(){return this.textManager.getTextAnnotations()}exportAnnotationImage(){return new Promise((t,e)=>{if(!this.bgImage)return void e(new Error("No background image loaded"));const n=document.createElement("canvas");n.width=this.viewport.originalWidth,n.height=this.viewport.originalHeight;const i=n.getContext("2d");if(!i)return void e(new Error("Could not create offscreen canvas context"));const s=new Image;s.src=this.bgImageSrc,s.crossOrigin="anonymous",s.onload=()=>{try{this.renderer.drawForExport(i,s,this.viewport.originalWidth,this.viewport.originalHeight);const e=n.toDataURL(`image/${this.bgImageExt}`,.7);t(e)}catch(n){this.exportCurrentViewImage().then(t).catch(t=>e(new Error(`Both methods failed: ${t.message}`)))}},s.onerror=()=>{e(new Error("Failed to load image for export"))}})}exportCurrentViewImage(){return new Promise((t,e)=>{try{t(this.canvas.toDataURL(`image/${this.bgImageExt}`))}catch(t){e(new Error(`Failed to export current view: ${t.message}`))}})}base64ToFile(t,e){return i(t,e)}static base64ToBlob(t){return s(t)}addTextAnnotation(t,e,n=""){this.textManager.addTextAnnotation(t,e,n),this.render()}updateTextAnnotation(t,e){this.textManager.updateTextAnnotation(t,e)&&this.render()}moveTextAnnotation(t,e,n){this.textManager.moveTextAnnotation(t,e,n)&&this.render()}removeTextAnnotation(t){this.textManager.removeTextAnnotation(t)&&this.render()}clearTextAnnotations(){this.textManager.clearTextAnnotations(),this.render()}selectAnnotation(t){this.annotationManager.selectAnnotation(t)&&this.render()}deselectAnnotation(){this.annotationManager.deselectAnnotation(),this.textManager.deselectTextAnnotation(),this.textManager.editingTextIndex=null,this.render()}getSelectedAnnotation(){const t=this.annotationManager.getSelectedAnnotation();if(t)return t;const e=this.textManager.getSelectedTextAnnotation();return e?{index:e.index,type:"text",data:e.data}:null}deleteSelectedAnnotation(){if(null!==this.textManager.selectedTextIndex)return this.textManager.deleteSelectedTextAnnotation(),void this.render();this.annotationManager.deleteSelectedAnnotation()&&this.render()}moveSelectedAnnotation(t,e){this.annotationManager.moveSelectedAnnotation(t,e)&&this.render()}setAnnotationColor(t){this.annotationManager.setColorConfig(t),this.render()}getAnnotationColor(){return this.annotationManager.getColorConfig()}updateSelectedAnnotationStyle(){const t=this.annotationManager.selectedAnnotation;if(!t)return!1;const e=this.annotationManager.recordList[t.index];return!!e&&(e.style=this.annotationManager.getCurrentStyle(),this.render(),!0)}setLineStyle(t){this.annotationManager.setLineStyle(t),this.render()}getLineStyle(){return this.annotationManager.getLineStyle()}setVertexStyle(t){this.annotationManager.setVertexStyle(t),this.render()}getVertexStyle(){return this.annotationManager.getVertexStyle()}setTextStyle(t){this.textManager.setTextStyle(t),this.render()}setTextInputStyle(t){this.textManager.setInputStyle(t)}setTextSelectionStyle(t){this.textManager.setSelectionStyle(t),this.render()}destroy(){this.textManager.destroy(),this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas)}}export{r as AnnotationManager,d as Drawer,c as EventHandler,l as Renderer,h as TextAnnotationManager,o as ViewportManager,s as base64ToBlob,i as base64ToFile,d as default,n as getImageTypeFromUrl,a as getZoomDelta,e as isPointInPolygon,t as isPointInRect};
|
|
1
|
+
function t(t,e){const i=e.width>=0?e.start.x:e.start.x+e.width,n=e.height>=0?e.start.y:e.start.y+e.height,s=Math.abs(e.width),a=Math.abs(e.height);return t.x>=i&&t.x<=i+s&&t.y>=n&&t.y<=n+a}function e(t,e){if(e.length<3)return!1;let i=!1;for(let n=0,s=e.length-1;n<e.length;s=n++){const a=e[n].point.x,o=e[n].point.y,r=e[s].point.x,h=e[s].point.y;o>t.y!=h>t.y&&t.x<(r-a)*(t.y-o)/(h-o)+a&&(i=!i)}return i}function i(t){try{const e=new URL(t).pathname.toLowerCase();return e.endsWith(".png")?"png":e.endsWith(".jpg")?"jpg":e.endsWith(".jpeg")?"jpeg":e.endsWith(".webp")?"webp":e.endsWith(".gif")?"gif":e.endsWith(".bmp")?"bmp":e.endsWith(".svg")?"svg":"jpeg"}catch(t){return"jpeg"}}function n(t,e){const i=t.split(";base64,"),n=i[0].split(":")[1],s=window.atob(i[1]),a=s.length,o=new Uint8Array(a);for(let t=0;t<a;++t)o[t]=s.charCodeAt(t);const r=new Blob([o],{type:n});return new File([r],e,{type:n})}function s(t){return new Promise((e,i)=>{const n=t.split(";base64,");if(n.length<2)return void i(new Error("Invalid base64 string"));const s=n[0].split(":")[1],a=atob(n[1]),o=new ArrayBuffer(a.length),r=new Uint8Array(o);for(let t=0;t<a.length;t++)r[t]=a.charCodeAt(t);e(new Blob([o],{type:s}))})}function a(t,e){return t<2?e?.1:-.1:t<5?e?.05:-.05:e?.02:-.02}class o{constructor(){this.width=0,this.height=0,this.originalWidth=0,this.originalHeight=0,this.scale=1,this.minScale=1,this.maxScale=10,this.offset={x:0,y:0},this.initialScale=1,this.initialOffset={x:0,y:0},this.isInitialScale=!0,this.minOffsetX=0,this.minOffsetY=0,this.maxOffsetX=0,this.maxOffsetY=0}setSize(t,e){this.width=t,this.height=e}setOriginalSize(t,e){this.originalWidth=t,this.originalHeight=e}calculateInitialView(){if(0===this.originalWidth||0===this.originalHeight)return;const t=this.width/this.originalWidth,e=this.height/this.originalHeight;this.scale=Math.min(t,e),this.initialScale=this.scale,this.minScale=this.initialScale,this.offset={x:(this.width-this.originalWidth*this.scale)/2,y:(this.height-this.originalHeight*this.scale)/2},this.initialOffset=Object.assign({},this.offset),this.calculateBoundaries(),this.isInitialScale=!0}calculateBoundaries(){const t=this.originalWidth*this.scale,e=this.originalHeight*this.scale;this.maxOffsetX=0,this.maxOffsetY=0,this.minOffsetX=this.width-t,this.minOffsetY=this.height-e,this.minOffsetX>this.maxOffsetX&&([this.minOffsetX,this.maxOffsetX]=[this.maxOffsetX,this.minOffsetX]),this.minOffsetY>this.maxOffsetY&&([this.minOffsetY,this.maxOffsetY]=[this.maxOffsetY,this.minOffsetY])}constrainViewport(){this.offset.x>this.maxOffsetX?this.offset.x=this.maxOffsetX:this.offset.x<this.minOffsetX&&(this.offset.x=this.minOffsetX),this.offset.y>this.maxOffsetY?this.offset.y=this.maxOffsetY:this.offset.y<this.minOffsetY&&(this.offset.y=this.minOffsetY)}toImageCoordinates(t,e){return{x:(t-this.offset.x)/this.scale,y:(e-this.offset.y)/this.scale}}toCanvasCoordinates(t,e){return{x:this.offset.x+t*this.scale,y:this.offset.y+e*this.scale}}updateScale(t,e,i){const n=this.scale,s=(e-this.offset.x)/n,a=(i-this.offset.y)/n;this.scale=Math.max(this.minScale,Math.min(t,this.maxScale));const o=s*this.scale+this.offset.x,r=a*this.scale+this.offset.y;this.offset.x+=e-o,this.offset.y+=i-r,this.calculateBoundaries(),this.constrainViewport(),this.isInitialScale=this.scale===this.initialScale}resetToInitial(){this.scale=this.initialScale,this.offset=Object.assign({},this.initialOffset),this.calculateBoundaries(),this.isInitialScale=!0}updateOffset(t,e){this.offset.x+=t,this.offset.y+=e,this.constrainViewport()}reset(){this.scale=1,this.minScale=1,this.offset={x:0,y:0},this.initialScale=1,this.initialOffset={x:0,y:0},this.isInitialScale=!0,this.minOffsetX=0,this.minOffsetY=0,this.maxOffsetX=0,this.maxOffsetY=0}}class r{constructor(t,e,i){this.viewport=t,this.container=e,this.renderCallback=i,this.recordList=[],this.operate={type:"rect",data:[],status:"pending"},this.isDrawing=!1,this.drawStartPoint={x:0,y:0},this.tempPolygonPoint=null,this.selectedAnnotation=null,this.isMovingAnnotation=!1,this.isResizing=!1,this.activeHandle=null,this.annotationMoveStart={x:0,y:0},this.originalRect=null,this.originalPolygon=null,this.deleteHistory=[],this.editingTitleIndex=null,this.titleInput=null,this.strokeStyle="red",this.lineWidth=5,this.lineStyle="solid",this.vertexStyle={size:8,fillColor:"red",strokeColor:"white",strokeWidth:2,shape:"circle"},this.selectionStyle={strokeColor:"#00D9FF",fillColor:"rgba(0,217,255,0.15)",handleSize:12,handleColor:"#00D9FF"},this.colorConfig={rect:"red",polygon:"red",default:"red"},this.titleStyle={font:"12px Arial",color:"#FFFFFF",backgroundColor:"rgba(0, 0, 0, 0.7)",paddingX:6,paddingY:3,borderRadius:4,placeholder:"输入标题..."},this.titlePosition={vertical:"top",align:"center",offsetX:0,offsetY:0},this.enableTitle=!1,this.applyColorConfig()}initTitleSupport(){this.container&&this.enableTitle&&this.createTitleInput()}setColorConfig(t){this.colorConfig="string"==typeof t?{rect:t,polygon:t,default:t}:Object.assign(Object.assign({},this.colorConfig),t),this.applyColorConfig()}getColorConfig(){return Object.assign({},this.colorConfig)}applyColorConfig(){this.strokeStyle=this.colorConfig.default||"red"}getColorForType(t){return this.colorConfig[t]||this.colorConfig.default||"red"}getCurrentStyle(){return{strokeColor:this.strokeStyle,lineWidth:this.lineWidth,lineStyle:this.lineStyle,vertexStyle:Object.assign({},this.vertexStyle)}}setLineStyle(t){this.lineStyle=t}setVertexStyle(t){this.vertexStyle=Object.assign(Object.assign({},this.vertexStyle),t),t.fillColor&&(this.strokeStyle=t.fillColor)}getLineStyle(){return this.lineStyle}getVertexStyle(){return Object.assign({},this.vertexStyle)}getAnnotationStyle(t){return t.style||this.getCurrentStyle()}startRectDrawing(t){this.isDrawing=!0,this.drawStartPoint=t,this.operate={type:"rect",data:[{start:Object.assign({},t),width:0,height:0}],status:"pending"}}updateRectDrawing(t){if(!this.isDrawing||"rect"!==this.operate.type)return;const e=this.operate.data[0];e.width=t.x-this.drawStartPoint.x,e.height=t.y-this.drawStartPoint.y}finishRectDrawing(){if(!this.isDrawing||"rect"!==this.operate.type)return!1;const t=this.operate.data[0];if(Math.abs(t.width)>5&&Math.abs(t.height)>5){const t=Object.assign(Object.assign({},this.operate),{style:this.getCurrentStyle()});this.recordList.push(t),this.deleteHistory=[]}return this.operate.data=[],this.isDrawing=!1,!0}startPolygonDrawing(t){this.isDrawing=!0,this.operate={type:"polygon",data:[{point:t}],status:"pending"},this.tempPolygonPoint=t}addPolygonPoint(t){if(!this.isDrawing||"polygon"!==this.operate.type)return!1;const e=this.operate.data.length>0?this.operate.data[this.operate.data.length-1]:null;return!(e&&Math.abs(e.point.x-t.x)<=5&&Math.abs(e.point.y-t.y)<=5)&&(this.operate.data.push({point:t}),!0)}updatePolygonTempPoint(t){this.tempPolygonPoint=t}finishPolygonDrawing(){if(!this.isDrawing||"polygon"!==this.operate.type)return!1;if(this.operate.data.length>=3){this.operate.status="fullfilled";const t=Object.assign(Object.assign({},this.operate),{style:this.getCurrentStyle()});this.recordList.push(t),this.deleteHistory=[]}return this.operate={type:"polygon",data:[],status:"pending"},this.isDrawing=!1,this.tempPolygonPoint=null,!0}cancelDrawing(){if(this.isDrawing&&"polygon"===this.operate.type&&this.operate.data.length>=2){this.operate.status="fullfilled";const t=Object.assign(Object.assign({},this.operate),{style:this.getCurrentStyle()});this.recordList.push(t)}this.operate.data=[],this.isDrawing=!1,this.tempPolygonPoint=null}withdraw(){if(this.operate.data.length>0)return"polygon"===this.operate.type?(this.operate.data.pop(),0===this.operate.data.length&&(this.isDrawing=!1)):(this.operate.data=[],this.isDrawing=!1),!0;if(this.deleteHistory.length>0){const{annotation:t,index:e}=this.deleteHistory.pop();return this.recordList.splice(e,0,t),!0}return this.recordList.length>0&&(this.recordList.pop(),!0)}selectAnnotation(t){if(t<0||t>=this.recordList.length)return this.deselectAnnotation(),!1;const e=this.recordList[t];return this.selectedAnnotation={index:t,type:e.type},!0}deselectAnnotation(){this.selectedAnnotation=null,this.isMovingAnnotation=!1,this.isResizing=!1,this.activeHandle=null,this.originalRect=null,this.originalPolygon=null}deleteSelectedAnnotation(){if(!this.selectedAnnotation)return!1;const t=this.selectedAnnotation.index,e=this.recordList[t];return this.deleteHistory.push({annotation:Object.assign({},e),index:t}),this.recordList.splice(t,1),this.deselectAnnotation(),!0}startMovingAnnotation(t){return!!this.selectedAnnotation&&(this.isMovingAnnotation=!0,this.annotationMoveStart={x:t.clientX,y:t.clientY},!0)}moveSelectedAnnotation(t,e){if(!this.selectedAnnotation)return!1;const i=this.recordList[this.selectedAnnotation.index];if(!i)return!1;if("rect"===i.type){const n=i.data[0];n.start.x+=t,n.start.y+=e}else if("polygon"===i.type){i.data.forEach(i=>{i.point.x+=t,i.point.y+=e})}return!0}finishMovingAnnotation(){this.isMovingAnnotation=!1,this.isResizing=!1,this.activeHandle=null,this.originalRect=null,this.originalPolygon=null}startResizing(t,e){if(!this.selectedAnnotation)return!1;this.activeHandle=t,this.isResizing=!0;const i=this.recordList[this.selectedAnnotation.index];return"rect"===i.type?this.originalRect=Object.assign({},i.data[0]):"polygon"===i.type&&(this.originalPolygon=i.data.map(t=>({point:Object.assign({},t.point)}))),!0}resizeRect(t){if(!this.selectedAnnotation||"rect"!==this.selectedAnnotation.type||!this.originalRect||!this.activeHandle)return!1;const e=this.recordList[this.selectedAnnotation.index].data[0],i=this.originalRect,n=i.start.x,s=i.start.x+i.width,a=i.start.y,o=i.start.y+i.height;let r,h;switch(this.activeHandle.index){case 0:r=s,h=o;break;case 1:r=n,h=o;break;case 2:r=s,h=a;break;case 3:r=n,h=a;break;default:return!1}const l=Math.min(t.x,r),c=Math.max(t.x,r),d=Math.min(t.y,h),g=Math.max(t.y,h);e.start.x=l,e.start.y=d,e.width=c-l,e.height=g-d;const x=t.x<r,p=t.y<h;return 0===this.activeHandle.index?x||p?!x&&p?this.activeHandle.index=1:x&&!p&&(this.activeHandle.index=2):this.activeHandle.index=3:1===this.activeHandle.index?x&&!p?this.activeHandle.index=2:x&&p?this.activeHandle.index=0:x||p||(this.activeHandle.index=3):2===this.activeHandle.index?!x&&p?this.activeHandle.index=1:x&&p?this.activeHandle.index=0:x||p||(this.activeHandle.index=3):3===this.activeHandle.index&&(x&&p?this.activeHandle.index=0:!x&&p?this.activeHandle.index=1:x&&!p&&(this.activeHandle.index=2)),this.originalRect=Object.assign({},e),!0}resizePolygon(t){if(!this.selectedAnnotation||"polygon"!==this.selectedAnnotation.type||!this.activeHandle)return!1;const e=this.recordList[this.selectedAnnotation.index].data,i=this.activeHandle.index;return i>=0&&i<e.length&&(e[i].point.x=t.x,e[i].point.y=t.y,!0)}getAnnotationAtPoint(i){for(let n=this.recordList.length-1;n>=0;n--){const s=this.recordList[n];if("rect"===s.type){if(t(i,s.data[0]))return{index:n,type:"rect"}}else if("polygon"===s.type){if(e(i,s.data))return{index:n,type:"polygon"}}}return null}getHandleAtPoint(t,e){if(!this.selectedAnnotation)return null;const i=this.recordList[this.selectedAnnotation.index],n=this.selectionStyle.handleSize/2;if("rect"===i.type){const s=i.data[0],a=this.viewport.offset.x+s.start.x*this.viewport.scale,o=this.viewport.offset.y+s.start.y*this.viewport.scale,r=s.width*this.viewport.scale,h=s.height*this.viewport.scale,l=[{x:a,y:o,index:0},{x:a+r,y:o,index:1},{x:a,y:o+h,index:2},{x:a+r,y:o+h,index:3}];for(const i of l)if(t>=i.x-n&&t<=i.x+n&&e>=i.y-n&&e<=i.y+n)return{type:"rect-corner",index:i.index}}else if("polygon"===i.type){const s=i.data;for(let i=0;i<s.length;i++){const a=s[i],o=this.viewport.offset.x+a.point.x*this.viewport.scale,r=this.viewport.offset.y+a.point.y*this.viewport.scale;if(t>=o-n&&t<=o+n&&e>=r-n&&e<=r+n)return{type:"polygon-vertex",index:i}}}return null}clear(){this.recordList=[],this.operate={type:"rect",data:[],status:"pending"},this.isDrawing=!1,this.tempPolygonPoint=null,this.deselectAnnotation(),this.deleteHistory=[]}getAnnotations(){return[...this.recordList]}getSelectedAnnotation(){if(!this.selectedAnnotation)return null;const t=this.recordList[this.selectedAnnotation.index];return t?{index:this.selectedAnnotation.index,type:this.selectedAnnotation.type,data:t}:null}setTitleStyle(t){this.titleStyle=Object.assign(Object.assign({},this.titleStyle),t)}getTitleStyle(){return Object.assign({},this.titleStyle)}setTitlePosition(t){this.titlePosition=Object.assign(Object.assign({},this.titlePosition),t)}getTitlePosition(){return Object.assign({},this.titlePosition)}getEffectiveTitleStyle(t){const e=this.getAnnotationStyle(t);return Object.assign(Object.assign({},this.titleStyle),e.titleStyle||{})}getEffectiveTitlePosition(t){const e=this.getAnnotationStyle(t);return Object.assign(Object.assign({},this.titlePosition),e.titlePosition||{})}captureTitleStyleSnapshot(t){t.style||(t.style=this.getCurrentStyle()),t.style.titleStyle=Object.assign({},this.titleStyle),t.style.titlePosition=Object.assign({},this.titlePosition)}setAnnotationTitleStyle(t,e){if(t<0||t>=this.recordList.length)return!1;const i=this.recordList[t];return i.style||(i.style=this.getCurrentStyle()),i.style.titleStyle=Object.assign(Object.assign({},i.style.titleStyle||this.titleStyle),e),!0}setAnnotationTitlePosition(t,e){if(t<0||t>=this.recordList.length)return!1;const i=this.recordList[t];return i.style||(i.style=this.getCurrentStyle()),i.style.titlePosition=Object.assign(Object.assign({},i.style.titlePosition||this.titlePosition),e),!0}createTitleInput(){this.container&&(this.titleInput=document.createElement("input"),this.titleInput.type="text",this.titleInput.style.position="absolute",this.titleInput.style.zIndex="99998",this.titleInput.style.display="none",this.titleInput.style.outline="none",this.titleInput.style.border="1px solid #00D9FF",this.titleInput.style.borderRadius="4px",this.titleInput.style.padding="4px 8px",this.titleInput.style.fontSize="13px",this.titleInput.style.fontFamily="Arial, sans-serif",this.titleInput.style.backgroundColor="#ffffff",this.titleInput.style.color="#333",this.titleInput.style.boxShadow="0 2px 8px rgba(0,0,0,0.3)",this.titleInput.style.minWidth="80px",this.titleInput.style.maxWidth="200px",this.titleInput.placeholder=this.titleStyle.placeholder||"输入标题...",this.titleInput.addEventListener("blur",()=>{this.finishTitleEditing()}),this.titleInput.addEventListener("keydown",t=>{t.stopPropagation(),"Enter"===t.key?this.finishTitleEditing():"Escape"===t.key&&(t.preventDefault(),this.cancelTitleEditing())}),this.container.appendChild(this.titleInput))}setTitle(t,e){if(t<0||t>=this.recordList.length)return!1;const i=this.recordList[t];return i.title=e||void 0,e&&this.captureTitleStyleSnapshot(i),!0}getTitle(t){if(!(t<0||t>=this.recordList.length))return this.recordList[t].title}startTitleEditing(t){if(!this.enableTitle||!this.titleInput||t<0||t>=this.recordList.length)return!1;const e=this.recordList[t];let i,n;if(this.editingTitleIndex=t,this.deselectAnnotation(),"rect"===e.type){const t=e.data[0];i=this.viewport.offset.x+t.start.x*this.viewport.scale,n=this.viewport.offset.y+t.start.y*this.viewport.scale}else{const t=e.data[0];i=this.viewport.offset.x+t.point.x*this.viewport.scale,n=this.viewport.offset.y+t.point.y*this.viewport.scale}return this.titleInput.value=e.title||"",this.titleInput.placeholder=this.getEffectiveTitleStyle(e).placeholder||"输入标题...",this.titleInput.style.left=`${i}px`,this.titleInput.style.top=n-32+"px",this.titleInput.style.display="block",setTimeout(()=>{this.titleInput&&(this.titleInput.focus(),this.titleInput.select())},10),!0}finishTitleEditing(){if(!this.titleInput||null===this.editingTitleIndex)return!1;const t=this.editingTitleIndex,e=this.titleInput.value.trim();if(t>=0&&t<this.recordList.length){const i=this.recordList[t];i.title=e||void 0,e&&this.captureTitleStyleSnapshot(i),this.selectAnnotation(t)}return this.resetTitleEditingState(),this.renderCallback&&this.renderCallback(),!0}cancelTitleEditing(){if(!this.titleInput||null===this.editingTitleIndex)return!1;const t=this.editingTitleIndex;return t>=0&&t<this.recordList.length&&this.selectAnnotation(t),this.resetTitleEditingState(),this.renderCallback&&this.renderCallback(),!0}resetTitleEditingState(){this.titleInput&&(this.titleInput.style.display="none"),this.editingTitleIndex=null}destroyTitleInput(){this.titleInput&&this.titleInput.parentNode&&(this.titleInput.parentNode.removeChild(this.titleInput),this.titleInput=null)}}class h{constructor(t,e,i,n){this.viewport=t,this.container=e,this.ctx=i,this.renderCallback=n,this.textAnnotations=[],this.editingTextIndex=null,this.currentEditingIndex=null,this.isTextMoving=!1,this.textMoveStart={x:0,y:0},this.textBeforeEditing="",this.selectedTextIndex=null,this.textInput=null,this.deleteHistory=[],this.defaultInputStyle={border:"2px solid #00D9FF",borderRadius:"6px",padding:"6px 10px",fontSize:"16px",fontFamily:"Arial, sans-serif",backgroundColor:"#ffffff",color:"#333",boxShadow:"0 4px 12px rgba(0,0,0,0.3)",minWidth:"60px",maxWidth:"200px"},this.textStyle={font:"16px Arial",color:"#FFD700",padding:6,backgroundColor:"rgba(0,0,0,0.6)",borderRadius:4,selectedBorderColor:"#00D9FF",selectedBackgroundColor:"rgba(0,217,255,0.15)",inputStyle:Object.assign({},this.defaultInputStyle)},this.createTextInput()}createTextInput(){this.textInput=document.createElement("input"),this.textInput.type="text",this.textInput.style.position="absolute",this.textInput.style.zIndex="99999",this.textInput.style.display="none",this.textInput.style.outline="none",this.textInput.style.width="fit-content",this.textInput.placeholder="输入文字...",this.applyInputStyle(),this.textInput.addEventListener("blur",()=>{this.finishEditing(),this.renderCallback&&this.renderCallback()}),this.textInput.addEventListener("keydown",t=>{t.stopPropagation(),"Enter"===t.key?(this.finishEditing(),this.renderCallback&&this.renderCallback()):"Escape"===t.key?(t.preventDefault(),this.cancelEditing()):"Delete"===t.key&&""===this.textInput.value&&(t.preventDefault(),this.deleteEditingAnnotation())}),this.textInput.addEventListener("input",()=>{this.adjustInputWidth()}),this.container.appendChild(this.textInput)}applyInputStyle(){if(!this.textInput)return;const t=Object.assign(Object.assign({},this.defaultInputStyle),this.textStyle.inputStyle);this.textInput.style.border=t.border,this.textInput.style.borderRadius=t.borderRadius,this.textInput.style.padding=t.padding,this.textInput.style.fontSize=t.fontSize,this.textInput.style.fontFamily=t.fontFamily,this.textInput.style.backgroundColor=t.backgroundColor,this.textInput.style.color=t.color,this.textInput.style.boxShadow=t.boxShadow,this.textInput.style.minWidth=t.minWidth,this.textInput.style.maxWidth=t.maxWidth}setTextStyle(t){this.textStyle=Object.assign(Object.assign({},this.textStyle),t),this.applyInputStyle()}setInputStyle(t){this.textStyle.inputStyle=Object.assign(Object.assign({},this.textStyle.inputStyle),t),this.applyInputStyle()}setSelectionStyle(t){void 0!==t.selectedBorderColor&&(this.textStyle.selectedBorderColor=t.selectedBorderColor),void 0!==t.selectedBackgroundColor&&(this.textStyle.selectedBackgroundColor=t.selectedBackgroundColor)}getCurrentStyle(){return{font:this.textStyle.font,color:this.textStyle.color,backgroundColor:this.textStyle.backgroundColor}}updateTextAnnotationStyle(t,e){if(t<0||t>=this.textAnnotations.length)return!1;const i=this.textAnnotations[t];return i.style=Object.assign(Object.assign({},i.style||this.getCurrentStyle()),e),!0}addTextAnnotation(t,e,i=""){var n;this.ctx.font=this.textStyle.font;const s=i||"输入文字...",a=this.ctx.measureText(s);let o;if(a.actualBoundingBoxAscent&&a.actualBoundingBoxDescent)o=a.actualBoundingBoxAscent+a.actualBoundingBoxDescent;else{o=1.2*parseInt((null===(n=this.textStyle.font.match(/\d+/))||void 0===n?void 0:n[0])||"16")}const r={position:{x:t,y:e},text:i,style:this.getCurrentStyle(),width:Math.max(a.width,60),height:o};this.textAnnotations.push(r);const h=this.textAnnotations.length-1;return this.deleteHistory=[],this.startEditing(h),h}updateTextAnnotation(t,e){if(t<0||t>=this.textAnnotations.length)return!1;const i=this.textAnnotations[t];i.text=e,this.ctx.font=this.textStyle.font;const n=this.ctx.measureText(e);return i.width=n.width,i.height=1.2*parseInt(this.textStyle.font),!0}moveTextAnnotation(t,e,i){return!(t<0||t>=this.textAnnotations.length)&&(this.textAnnotations[t].position={x:e,y:i},!0)}removeTextAnnotation(t){if(t<0||t>=this.textAnnotations.length)return!1;const e=this.textAnnotations[t];return this.deleteHistory.push({annotation:Object.assign({},e),index:t}),this.textAnnotations.splice(t,1),this.editingTextIndex===t?this.cancelEditing():null!==this.editingTextIndex&&this.editingTextIndex>t&&this.editingTextIndex--,!0}clearTextAnnotations(){this.textAnnotations=[],this.selectedTextIndex=null,this.cancelEditing(),this.deleteHistory=[]}startEditing(t){if(!this.textInput||t<0||t>=this.textAnnotations.length)return!1;const e=this.textAnnotations[t];this.editingTextIndex=t,this.currentEditingIndex=t,this.textBeforeEditing=e.text;const i=this.viewport.offset.x+e.position.x*this.viewport.scale,n=this.viewport.offset.y+e.position.y*this.viewport.scale;return this.textInput.value=e.text,this.textInput.style.left=`${i}px`,this.textInput.style.top=`${n}px`,this.textInput.style.display="block",this.adjustInputWidth(),setTimeout(()=>{this.textInput&&(this.textInput.focus(),this.textInput.select())},10),!0}finishEditing(){if(!this.textInput)return!1;const t=this.currentEditingIndex,e=this.textInput.value.trim();if(null===t||t<0||t>=this.textAnnotations.length)return this.resetEditingState(),!1;if(""===e)this.textAnnotations.splice(t,1);else{const i=this.textAnnotations[t];i.text=e,this.ctx.font=this.textStyle.font;const n=this.ctx.measureText(i.text);i.width=n.width,i.height=1.2*parseInt(this.textStyle.font)}return this.resetEditingState(),!0}cancelEditing(){if(!this.textInput||null===this.editingTextIndex)return!1;this.textInput.value=this.textBeforeEditing;const t=this.editingTextIndex;if(t>=0&&t<this.textAnnotations.length){""===this.textAnnotations[t].text&&""===this.textBeforeEditing&&this.textAnnotations.splice(t,1)}return this.resetEditingState(),!0}deleteEditingAnnotation(){if(null===this.editingTextIndex)return!1;const t=this.editingTextIndex;return this.textInput&&(this.textInput.style.display="none"),t>=0&&t<this.textAnnotations.length&&this.textAnnotations.splice(t,1),this.resetEditingState(),!0}resetEditingState(){this.textInput&&(this.textInput.style.display="none"),this.editingTextIndex=null,this.currentEditingIndex=null,this.textBeforeEditing=""}adjustInputWidth(){if(!this.textInput||null===this.editingTextIndex)return;this.ctx.font=this.textStyle.font;const t=this.textInput.value||this.textInput.placeholder||"",e=this.ctx.measureText(t).width,i=Math.min(Math.max(e+24,60),200);this.textInput.style.width=`${i}px`}startMoving(t,e){return!(e<0||e>=this.textAnnotations.length)&&(this.editingTextIndex=e,this.isTextMoving=!0,this.textMoveStart={x:t.clientX,y:t.clientY},!0)}moveAnnotation(t){if(!this.isTextMoving||null===this.editingTextIndex)return!1;const e=t.clientX-this.textMoveStart.x,i=t.clientY-this.textMoveStart.y,n=this.textAnnotations[this.editingTextIndex];return!!n&&(n.position.x+=e/this.viewport.scale,n.position.y+=i/this.viewport.scale,this.textMoveStart={x:t.clientX,y:t.clientY},!0)}finishMoving(){this.isTextMoving=!1}checkTextClick(t,e){for(let i=this.textAnnotations.length-1;i>=0;i--){const n=this.textAnnotations[i],s=this.viewport.offset.x+n.position.x*this.viewport.scale,a=this.viewport.offset.y+n.position.y*this.viewport.scale,o=Math.max(n.width,60)+2*this.textStyle.padding,r=n.height+2*this.textStyle.padding;if(t>=s&&t<=s+o&&e>=a&&e<=a+r)return i}return null}checkTextClickForMove(t){const e=this.checkTextClick(t.offsetX,t.offsetY);return null!==e?(this.startMoving(t,e),{index:e,handled:!0}):{index:-1,handled:!1}}getTextAnnotations(){return[...this.textAnnotations]}withdraw(){if(this.deleteHistory.length>0){const{annotation:t,index:e}=this.deleteHistory.pop();return this.textAnnotations.splice(e,0,t),!0}return this.textAnnotations.length>0&&(this.textAnnotations.pop(),!0)}selectTextAnnotation(t){return t<0||t>=this.textAnnotations.length?(this.deselectTextAnnotation(),!1):(this.selectedTextIndex=t,null!==this.editingTextIndex&&this.finishEditing(),!0)}deselectTextAnnotation(){this.selectedTextIndex=null}deleteSelectedTextAnnotation(){if(null===this.selectedTextIndex)return!1;const t=this.selectedTextIndex,e=this.textAnnotations[t];return this.deleteHistory.push({annotation:Object.assign({},e),index:t}),this.textAnnotations.splice(t,1),this.selectedTextIndex=null,null!==this.editingTextIndex&&this.editingTextIndex>t&&this.editingTextIndex--,!0}getSelectedTextAnnotation(){if(null===this.selectedTextIndex)return null;const t=this.textAnnotations[this.selectedTextIndex];return t?{index:this.selectedTextIndex,data:t}:null}destroy(){this.textInput&&this.textInput.parentNode&&this.textInput.parentNode.removeChild(this.textInput)}}class l{constructor(t,e,i,n,s){this.ctx=t,this.viewport=e,this.annotationManager=i,this.textManager=n,this.canvas=s}render(t){this.ctx.clearRect(0,0,this.viewport.width,this.viewport.height),t&&this.drawBackgroundImage(t),this.drawAnnotations(),this.drawTextAnnotations()}drawBackgroundImage(t){this.ctx.drawImage(t,this.viewport.offset.x,this.viewport.offset.y,this.viewport.originalWidth*this.viewport.scale,this.viewport.originalHeight*this.viewport.scale)}drawAnnotations(){this.annotationManager.recordList.forEach((t,e)=>{var i;const n=this.annotationManager.getAnnotationStyle(t);this.ctx.strokeStyle=n.strokeColor,this.ctx.lineWidth=n.lineWidth,this.setLineDash(n.lineStyle||"solid"),this.ctx.fillStyle=this.ctx.strokeStyle;const s=(null===(i=this.annotationManager.selectedAnnotation)||void 0===i?void 0:i.index)===e;if("rect"===t.type)this.drawRect(t.data[0],s);else if("polygon"===t.type){const e=n.vertexStyle||this.annotationManager.getVertexStyle();this.drawPolygon(t.data,"fullfilled"===t.status,e)}this.annotationManager.enableTitle&&t.title&&this.drawAnnotationTitle(t)}),this.annotationManager.operate.data.length>0&&(this.ctx.strokeStyle=this.annotationManager.strokeStyle,this.ctx.lineWidth=this.annotationManager.lineWidth,this.ctx.fillStyle=this.ctx.strokeStyle,this.setLineDash(this.annotationManager.lineStyle),"rect"===this.annotationManager.operate.type?this.drawRect(this.annotationManager.operate.data[0],!1):"polygon"===this.annotationManager.operate.type&&this.drawCurrentPolygon()),this.ctx.setLineDash([]),this.drawSelectionHighlight()}setLineDash(t){switch(t){case"dashed":this.ctx.setLineDash([10,5]);break;case"dotted":this.ctx.setLineDash([2,4]);break;default:this.ctx.setLineDash([])}}drawRect(t,e=!1){this.ctx.strokeRect(this.viewport.offset.x+t.start.x*this.viewport.scale,this.viewport.offset.y+t.start.y*this.viewport.scale,t.width*this.viewport.scale,t.height*this.viewport.scale)}drawPolygon(t,e,i){if(0!==t.length){if(this.ctx.beginPath(),t.forEach((t,e)=>{const i=Math.round(this.viewport.offset.x+t.point.x*this.viewport.scale),n=Math.round(this.viewport.offset.y+t.point.y*this.viewport.scale);0===e?this.ctx.moveTo(i,n):this.ctx.lineTo(i,n)}),e&&t.length>1){const e=t[0];this.ctx.lineTo(this.viewport.offset.x+e.point.x*this.viewport.scale,this.viewport.offset.y+e.point.y*this.viewport.scale)}this.ctx.stroke(),this.drawPolygonVertices(t,i)}}drawPolygonVertices(t,e){const i=e||this.annotationManager.getVertexStyle(),n=i.size/2;t.forEach(t=>{const e=Math.round(this.viewport.offset.x+t.point.x*this.viewport.scale),s=Math.round(this.viewport.offset.y+t.point.y*this.viewport.scale);switch(this.ctx.fillStyle=i.fillColor,this.ctx.strokeStyle=i.strokeColor,this.ctx.lineWidth=i.strokeWidth,this.ctx.beginPath(),i.shape){case"square":this.ctx.rect(e-n,s-n,2*n,2*n);break;case"diamond":this.ctx.moveTo(e,s-n),this.ctx.lineTo(e+n,s),this.ctx.lineTo(e,s+n),this.ctx.lineTo(e-n,s),this.ctx.closePath();break;default:this.ctx.arc(e,s,n,0,2*Math.PI)}this.ctx.fill(),i.strokeWidth>0&&this.ctx.stroke()})}drawCurrentPolygon(){const t=this.annotationManager.operate.data,e=this.annotationManager.tempPolygonPoint;if(this.ctx.beginPath(),t.forEach((t,e)=>{const i=Math.round(this.viewport.offset.x+t.point.x*this.viewport.scale),n=Math.round(this.viewport.offset.y+t.point.y*this.viewport.scale);0===e?this.ctx.moveTo(i,n):this.ctx.lineTo(i,n)}),e){const t=Math.round(this.viewport.offset.x+e.x*this.viewport.scale),i=Math.round(this.viewport.offset.y+e.y*this.viewport.scale);this.ctx.lineTo(t,i)}if(this.ctx.stroke(),this.drawPolygonVertices(t,this.annotationManager.getVertexStyle()),e){const t=this.annotationManager.getVertexStyle(),i=Math.round(this.viewport.offset.x+e.x*this.viewport.scale),n=Math.round(this.viewport.offset.y+e.y*this.viewport.scale),s=t.size/2;switch(this.ctx.fillStyle=t.fillColor,this.ctx.strokeStyle=t.strokeColor,this.ctx.lineWidth=t.strokeWidth,this.ctx.beginPath(),t.shape){case"square":this.ctx.rect(i-s,n-s,2*s,2*s);break;case"diamond":this.ctx.moveTo(i,n-s),this.ctx.lineTo(i+s,n),this.ctx.lineTo(i,n+s),this.ctx.lineTo(i-s,n),this.ctx.closePath();break;default:this.ctx.arc(i,n,s,0,2*Math.PI)}this.ctx.fill(),t.strokeWidth>0&&this.ctx.stroke()}}drawSelectionHighlight(){const t=this.annotationManager.selectedAnnotation;if(!t)return;const e=this.annotationManager.recordList[t.index];if(!e)return;const i=this.annotationManager.selectionStyle;this.ctx.save(),"rect"===e.type?this.drawRectSelectionHighlight(e.data[0],i):"polygon"===e.type&&this.drawPolygonSelectionHighlight(e.data,i),this.ctx.restore()}drawRectSelectionHighlight(t,e){const i=this.viewport.offset.x+t.start.x*this.viewport.scale,n=this.viewport.offset.y+t.start.y*this.viewport.scale,s=t.width*this.viewport.scale,a=t.height*this.viewport.scale;this.ctx.fillStyle=e.fillColor,this.ctx.fillRect(i,n,s,a),this.ctx.strokeStyle=e.strokeColor,this.ctx.lineWidth=2,this.ctx.setLineDash([5,5]),this.ctx.strokeRect(i,n,s,a);const o=e.handleSize;this.ctx.fillStyle=e.handleColor,this.ctx.setLineDash([]),this.ctx.fillRect(i-o/2,n-o/2,o,o),this.ctx.fillRect(i+s-o/2,n-o/2,o,o),this.ctx.fillRect(i-o/2,n+a-o/2,o,o),this.ctx.fillRect(i+s-o/2,n+a-o/2,o,o)}drawPolygonSelectionHighlight(t,e){this.ctx.beginPath(),t.forEach((t,e)=>{const i=this.viewport.offset.x+t.point.x*this.viewport.scale,n=this.viewport.offset.y+t.point.y*this.viewport.scale;0===e?this.ctx.moveTo(i,n):this.ctx.lineTo(i,n)}),this.ctx.closePath(),this.ctx.fillStyle=e.fillColor,this.ctx.fill(),this.ctx.strokeStyle=e.strokeColor,this.ctx.lineWidth=2,this.ctx.setLineDash([5,5]),this.ctx.stroke();const i=e.handleSize;this.ctx.fillStyle=e.handleColor,this.ctx.setLineDash([]),t.forEach(t=>{const e=Math.round(this.viewport.offset.x+t.point.x*this.viewport.scale),n=Math.round(this.viewport.offset.y+t.point.y*this.viewport.scale);this.ctx.fillRect(e-i/2,n-i/2,i,i)})}drawAnnotationTitle(t){var e,i,n;if(!t.title)return;const s=this.annotationManager.getEffectiveTitleStyle(t),a=this.annotationManager.getEffectiveTitlePosition(t),o=s.font||"12px Arial",r=s.color||"#FFFFFF",h=s.backgroundColor||"rgba(0, 0, 0, 0.7)",l=null!==(e=s.paddingX)&&void 0!==e?e:6,c=null!==(i=s.paddingY)&&void 0!==i?i:3,d=null!==(n=s.borderRadius)&&void 0!==n?n:4;this.ctx.save(),this.ctx.font=o;const g=this.ctx.measureText(t.title).width,x=o.match(/(\d+)px/),p=x?parseInt(x[1]):12;let f,u,y,v;if("rect"===t.type){const e=t.data[0];f=this.viewport.offset.x+e.start.x*this.viewport.scale,u=this.viewport.offset.y+e.start.y*this.viewport.scale,y=e.width*this.viewport.scale,v=e.height*this.viewport.scale}else{const e=t.data;if(0===e.length)return void this.ctx.restore();let i=1/0,n=1/0,s=-1/0,a=-1/0;e.forEach(t=>{const e=this.viewport.offset.x+t.point.x*this.viewport.scale,o=this.viewport.offset.y+t.point.y*this.viewport.scale;e<i&&(i=e),o<n&&(n=o),e>s&&(s=e),o>a&&(a=o)}),f=i,u=n,y=s-i,v=a-n}const M=g+2*l,w=p+2*c,A=a.vertical||"top",S=a.offsetX||0,T=a.offsetY||0;let b;switch(A){case"bottom":b=u+v+4+T;break;case"inside-top":b=u+4+T;break;default:b=u-w-4+T}let I;switch(a.align||"center"){case"left":I=f+S;break;case"right":I=f+y-M+S;break;default:I=f+y/2-M/2+S}this.ctx.fillStyle=h,this.ctx.setLineDash([]),this.ctx.beginPath(),this.ctx.roundRect(I,b,M,w,d),this.ctx.fill(),this.ctx.fillStyle=r,this.ctx.textAlign="left",this.ctx.textBaseline="top",this.ctx.fillText(t.title,I+l,b+c),this.ctx.restore()}drawTextAnnotations(){const t=this.textManager.textStyle;this.textManager.textAnnotations.forEach((e,i)=>{if(this.textManager.editingTextIndex===i&&this.textManager.textInput&&"none"!==this.textManager.textInput.style.display)return;if(!e.text)return;const n=e.style||{font:t.font,color:t.color,backgroundColor:t.backgroundColor},s=this.viewport.offset.x+e.position.x*this.viewport.scale,a=this.viewport.offset.y+e.position.y*this.viewport.scale,o=this.textManager.selectedTextIndex===i;this.ctx.font=n.font;const r=this.ctx.measureText(e.text),h=r.actualBoundingBoxAscent||.8*e.height,l=h+(r.actualBoundingBoxDescent||.2*e.height),c=t.borderRadius,d=s,g=a,x=e.width+2*t.padding,p=l+2*t.padding;this.ctx.fillStyle=n.backgroundColor,this.ctx.beginPath(),this.ctx.roundRect(d,g,x,p,c),this.ctx.fill(),o&&(this.ctx.strokeStyle=t.selectedBorderColor,this.ctx.lineWidth=2,this.ctx.setLineDash([5,5]),this.ctx.beginPath(),this.ctx.roundRect(d,g,x,p,c),this.ctx.stroke(),this.ctx.setLineDash([]));const f=s+t.padding,u=a+t.padding+h;this.ctx.fillStyle=n.color,this.ctx.fillText(e.text,f,u)})}drawForExport(t,e,i,n){t.drawImage(e,0,0,i,n),this.annotationManager.recordList.forEach(e=>{const i=this.annotationManager.getAnnotationStyle(e);if(t.strokeStyle=i.strokeColor,t.lineWidth=i.lineWidth,"rect"===e.type){const i=e.data[0];t.strokeRect(i.start.x,i.start.y,i.width,i.height)}else if("polygon"===e.type&&"fullfilled"===e.status){t.beginPath(),e.data.forEach((e,i)=>{0===i?t.moveTo(e.point.x,e.point.y):t.lineTo(e.point.x,e.point.y)});const n=e.data[0];t.lineTo(n.point.x,n.point.y),t.stroke(),e.data.forEach(e=>{t.beginPath(),t.arc(e.point.x,e.point.y,4,0,2*Math.PI),t.fillStyle=i.strokeColor,t.fill()})}}),this.textManager.textAnnotations.forEach(e=>{const i=this.textManager.textStyle,n=e.style||{font:i.font,color:i.color,backgroundColor:i.backgroundColor};t.font=n.font;const s=t.measureText(e.text),a=s.actualBoundingBoxAscent||.8*e.height,o=a+(s.actualBoundingBoxDescent||.2*e.height),r=e.position.x,h=e.position.y,l=e.width+2*i.padding,c=o+2*i.padding,d=i.borderRadius;t.fillStyle=n.backgroundColor,t.roundRect?(t.roundRect(r,h,l,c,d),t.fill()):t.fillRect(r,h,l,c),t.fillStyle=n.color,t.fillText(e.text,e.position.x+i.padding,e.position.y+i.padding+a)}),this.annotationManager.enableTitle&&this.annotationManager.recordList.forEach(e=>{var i,n,s;if(!e.title)return;const a=this.annotationManager.getEffectiveTitleStyle(e),o=this.annotationManager.getEffectiveTitlePosition(e),r=a.font||"12px Arial",h=a.color||"#FFFFFF",l=a.backgroundColor||"rgba(0, 0, 0, 0.7)",c=null!==(i=a.paddingX)&&void 0!==i?i:6,d=null!==(n=a.paddingY)&&void 0!==n?n:3,g=null!==(s=a.borderRadius)&&void 0!==s?s:4;t.font=r;const x=t.measureText(e.title).width,p=r.match(/(\d+)px/),f=p?parseInt(p[1]):12;let u,y,v,M;if("rect"===e.type){const t=e.data[0];u=t.start.x,y=t.start.y,v=t.width,M=t.height}else{const t=e.data;if(0===t.length)return;let i=1/0,n=1/0,s=-1/0,a=-1/0;t.forEach(t=>{t.point.x<i&&(i=t.point.x),t.point.y<n&&(n=t.point.y),t.point.x>s&&(s=t.point.x),t.point.y>a&&(a=t.point.y)}),u=i,y=n,v=s-i,M=a-n}const w=x+2*c,A=f+2*d,S=o.vertical||"top",T=o.offsetX||0,b=o.offsetY||0;let I;switch(S){case"bottom":I=y+M+4+b;break;case"inside-top":I=y+4+b;break;default:I=y-A-4+b}let m;switch(o.align||"center"){case"left":m=u+T;break;case"right":m=u+v-w+T;break;default:m=u+v/2-w/2+T}t.fillStyle=l,t.roundRect?(t.roundRect(m,I,w,A,g),t.fill()):t.fillRect(m,I,w,A),t.fillStyle=h,t.textAlign="left",t.textBaseline="top",t.fillText(e.title,m+c,I+d)})}}class c{constructor(t,e,i,n,s,a,o,r,h){this.canvas=t,this.viewport=e,this.annotationManager=i,this.textManager=n,this.renderer=s,this.getDrawType=a,this.getBgImage=o,this.renderCallback=r,this.setDrawTypeCallback=h,this.isDragging=!1,this.dragStart={x:0,y:0},this.justFinishedMove=!1,this.justDeselected=!1}handleWheel(t){if(t.preventDefault(),!this.getBgImage())return;const e=t.deltaY>0?-.02:.02,i=this.viewport.scale,n=Math.max(this.viewport.minScale,Math.min(i+e,this.viewport.maxScale)),s=this.canvas.getBoundingClientRect(),a=t.clientX-s.left,o=t.clientY-s.top,r=(a-this.viewport.offset.x)/i,h=(o-this.viewport.offset.y)/i;this.viewport.scale=n;const l=r*n+this.viewport.offset.x,c=h*n+this.viewport.offset.y;this.viewport.offset.x+=a-l,this.viewport.offset.y+=o-c,this.viewport.calculateBoundaries(),this.viewport.constrainViewport(),this.viewport.isInitialScale=this.viewport.scale===this.viewport.initialScale,this.renderCallback()}handleMouseDown(t){var e,i;if(0!==t.button)return;const n=this.getDrawType(),s=this.getBgImage();if(this.annotationManager.selectedAnnotation&&s){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY),i=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(i)return this.annotationManager.startResizing(i,e),void this.renderCallback()}if("text"===n){const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return this.textManager.selectedTextIndex===e?this.textManager.startMoving(t,e):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(e)),void this.renderCallback();if(s){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY),i=this.annotationManager.getAnnotationAtPoint(e);if("rect"===(null==i?void 0:i.type))return this.annotationManager.selectAnnotation(i.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();const n=this.annotationManager.getAnnotationAtPoint(e);if("polygon"===(null==n?void 0:n.type))return this.annotationManager.selectAnnotation(n.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();if(this.annotationManager.selectedAnnotation)return this.annotationManager.deselectAnnotation(),void this.renderCallback()}return null!==this.textManager.selectedTextIndex?(this.textManager.deselectTextAnnotation(),void this.renderCallback()):void this.handleTextModeClick(t)}if(!s)return;const a=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);if("rect"===n){if("rect"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type)){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return this.annotationManager.startResizing(e,a),void this.renderCallback();const i=this.annotationManager.getAnnotationAtPoint(a);return i&&"rect"===i.type?(this.annotationManager.selectAnnotation(i.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback()):(this.annotationManager.deselectAnnotation(),this.justDeselected=!0,void this.renderCallback())}const i=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==i)return this.textManager.selectedTextIndex===i?this.textManager.startMoving(t,i):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(i)),void this.renderCallback();const n=this.annotationManager.getAnnotationAtPoint(a);if("polygon"===(null==n?void 0:n.type))return this.annotationManager.selectAnnotation(n.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();const s=this.annotationManager.getAnnotationAtPoint(a);if("rect"===(null==s?void 0:s.type))return this.annotationManager.selectAnnotation(s.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();this.annotationManager.startRectDrawing(a)}else if("polygon"===n){if("polygon"===(null===(i=this.annotationManager.selectedAnnotation)||void 0===i?void 0:i.type)){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e&&"polygon-vertex"===e.type)return this.annotationManager.startResizing(e,a),void this.renderCallback();const i=this.annotationManager.getAnnotationAtPoint(a);return i&&"polygon"===i.type?(this.annotationManager.selectAnnotation(i.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback()):(this.annotationManager.deselectAnnotation(),this.justDeselected=!0,void this.renderCallback())}const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return this.textManager.selectedTextIndex===e?this.textManager.startMoving(t,e):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(e)),void this.renderCallback();const n=this.annotationManager.getAnnotationAtPoint(a);if("rect"===(null==n?void 0:n.type))return this.annotationManager.selectAnnotation(n.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();const s=this.annotationManager.getAnnotationAtPoint(a);if("polygon"===(null==s?void 0:s.type))return this.annotationManager.selectAnnotation(s.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();this.annotationManager.isDrawing?this.checkPolygonPointValid(a)&&this.annotationManager.addPolygonPoint(a):this.annotationManager.startPolygonDrawing(a),this.annotationManager.updatePolygonTempPoint(a),this.renderCallback()}else if("drag"===n){if(this.annotationManager.selectedAnnotation){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return this.annotationManager.startResizing(e,a),void this.renderCallback()}const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return this.textManager.selectedTextIndex===e?this.textManager.startMoving(t,e):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(e)),void this.renderCallback();if(this.handleRectModeClick(t,a))return;if(this.handlePolygonModeClick(t,a))return;this.isDragging=!0,this.dragStart={x:t.clientX,y:t.clientY},this.canvas.style.cursor="grabbing"}else""===n&&this.handleNoModeClick(t,a)}checkTextClickInDragMode(t){return!!this.textManager.checkTextClickForMove(t).handled&&(this.canvas.style.cursor="grabbing",!0)}handleTextModeClick(t){const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return this.textManager.startMoving(t,e),this.canvas.style.cursor="grabbing",this.renderCallback(),!0;const i=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);return this.textManager.addTextAnnotation(i.x,i.y),!0}handleRectModeClick(t,e){var i,n;if("rect"===(null===(i=this.annotationManager.selectedAnnotation)||void 0===i?void 0:i.type)){const i=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(i)return this.annotationManager.startResizing(i,e),this.renderCallback(),!0}const s=this.annotationManager.getAnnotationAtPoint(e);return s&&"rect"===s.type?(this.annotationManager.selectAnnotation(s.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback(),!0):"rect"===(null===(n=this.annotationManager.selectedAnnotation)||void 0===n?void 0:n.type)&&(this.annotationManager.deselectAnnotation(),this.renderCallback(),!0)}handlePolygonModeClick(t,e){var i,n;if("polygon"===(null===(i=this.annotationManager.selectedAnnotation)||void 0===i?void 0:i.type)){const i=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(i&&"polygon-vertex"===i.type)return this.annotationManager.startResizing(i,e),this.renderCallback(),!0}const s=this.annotationManager.getAnnotationAtPoint(e);return s&&"polygon"===s.type?(this.annotationManager.selectAnnotation(s.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback(),!0):"polygon"===(null===(n=this.annotationManager.selectedAnnotation)||void 0===n?void 0:n.type)&&(this.annotationManager.deselectAnnotation(),this.renderCallback(),!0)}handleRectModeClickForMove(t,e){const i=this.annotationManager.getAnnotationAtPoint(e);return!(!i||"rect"!==i.type)&&(this.annotationManager.selectAnnotation(i.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback(),!0)}handlePolygonModeClickForMove(t,e){const i=this.annotationManager.getAnnotationAtPoint(e);return!(!i||"polygon"!==i.type)&&(this.annotationManager.selectAnnotation(i.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback(),!0)}handleNoModeClick(t,e){const i=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==i)return this.textManager.selectedTextIndex===i?this.textManager.startMoving(t,i):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(i)),void this.renderCallback();this.textManager.deselectTextAnnotation();const n=this.annotationManager.getAnnotationAtPoint(e);n?(this.annotationManager.selectAnnotation(n.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback()):this.annotationManager.deselectAnnotation()}checkPolygonPointValid(t){const e=this.annotationManager.operate.data.length>0?this.annotationManager.operate.data[this.annotationManager.operate.data.length-1]:null;return!e||Math.abs(e.point.x-t.x)>5||Math.abs(e.point.y-t.y)>5}handleMouseMove(t){if(!this.getBgImage())return;const e=this.getDrawType();if(this.isDragging&&"drag"===e){const e=t.clientX-this.dragStart.x,i=t.clientY-this.dragStart.y;return this.viewport.updateOffset(e,i),this.dragStart={x:t.clientX,y:t.clientY},void this.renderCallback()}if(this.textManager.isTextMoving&&null!==this.textManager.editingTextIndex)this.textManager.moveAnnotation(t)&&this.renderCallback();else if((this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing)&&this.annotationManager.selectedAnnotation)this.handleAnnotationMove(t);else{if(this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing||this.textManager.isTextMoving||this.updateCursor(t),this.annotationManager.isDrawing&&"rect"===this.annotationManager.operate.type){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.annotationManager.updateRectDrawing(e),this.renderCallback()}if("polygon"===e&&this.annotationManager.isDrawing){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.annotationManager.updatePolygonTempPoint(e),this.renderCallback()}}}handleAnnotationMove(t){var e,i;if(this.annotationManager.isResizing&&this.annotationManager.activeHandle){const n=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);return"rect"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type)?this.annotationManager.resizeRect(n):"polygon"===(null===(i=this.annotationManager.selectedAnnotation)||void 0===i?void 0:i.type)&&this.annotationManager.resizePolygon(n),void this.renderCallback()}if(!this.annotationManager.isMovingAnnotation||!this.annotationManager.selectedAnnotation)return;const n=(t.clientX-this.annotationManager.annotationMoveStart.x)/this.viewport.scale,s=(t.clientY-this.annotationManager.annotationMoveStart.y)/this.viewport.scale;this.annotationManager.moveSelectedAnnotation(n,s),this.annotationManager.annotationMoveStart={x:t.clientX,y:t.clientY},this.renderCallback()}updateCursor(t){const e=this.getDrawType();if("text"===e){if(this.getBgImage()){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);if(this.annotationManager.selectedAnnotation){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return void("rect-corner"===e.type?this.canvas.style.cursor=0===e.index||3===e.index?"nwse-resize":"nesw-resize":this.canvas.style.cursor="crosshair")}if(this.annotationManager.getAnnotationAtPoint(e))return void(this.canvas.style.cursor="move")}const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);return void(this.canvas.style.cursor=null!==e?"move":"crosshair")}"rect"!==e?"polygon"!==e?"drag"!==e?""!==e?this.canvas.style.cursor="default":this.updateNoModeCursor(t):this.updateDragModeCursor(t):this.updatePolygonModeCursor(t):this.updateRectModeCursor(t)}updateRectModeCursor(t){var e;if("rect"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type)){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return void(this.canvas.style.cursor=0===e.index||3===e.index?"nwse-resize":"nesw-resize")}const i=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.annotationManager.getAnnotationAtPoint(i)?this.canvas.style.cursor="move":null===this.textManager.checkTextClick(t.offsetX,t.offsetY)?this.canvas.style.cursor="crosshair":this.canvas.style.cursor="move"}updatePolygonModeCursor(t){var e;if("polygon"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type)){if(this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY))return void(this.canvas.style.cursor="crosshair")}const i=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.annotationManager.getAnnotationAtPoint(i)?this.canvas.style.cursor="move":null===this.textManager.checkTextClick(t.offsetX,t.offsetY)?this.canvas.style.cursor="crosshair":this.canvas.style.cursor="move"}updateDragModeCursor(t){if(this.annotationManager.selectedAnnotation){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return void("rect-corner"===e.type?this.canvas.style.cursor=0===e.index||3===e.index?"nwse-resize":"nesw-resize":this.canvas.style.cursor="crosshair")}if(null!==this.textManager.checkTextClick(t.offsetX,t.offsetY))return void(this.canvas.style.cursor="move");const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY),i=this.annotationManager.getAnnotationAtPoint(e);this.canvas.style.cursor=i?"move":"grab"}updateNoModeCursor(t){if(this.annotationManager.selectedAnnotation){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return void("rect-corner"===e.type?this.canvas.style.cursor=0===e.index||3===e.index?"nwse-resize":"nesw-resize":this.canvas.style.cursor="crosshair")}if(null!==this.textManager.checkTextClick(t.offsetX,t.offsetY))return void(this.canvas.style.cursor="move");const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY),i=this.annotationManager.getAnnotationAtPoint(e);this.canvas.style.cursor=i?"move":"default"}handleMouseUp(t){if(this.isDragging){this.isDragging=!1;const t=this.getDrawType();return void(this.canvas.style.cursor="drag"===t?"grab":"default")}if(this.textManager.isTextMoving){this.justFinishedMove=!0,this.textManager.finishMoving();const t=this.getDrawType();"drag"===t?this.canvas.style.cursor="grab":""===t?this.canvas.style.cursor="move":(this.textManager.editingTextIndex=null,this.canvas.style.cursor="default")}(this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing)&&(this.justFinishedMove=!0,this.annotationManager.finishMovingAnnotation(),this.canvas.style.cursor=this.annotationManager.selectedAnnotation?"move":"default"),this.annotationManager.isDrawing&&"rect"===this.annotationManager.operate.type&&(this.annotationManager.finishRectDrawing(),this.renderCallback())}handleMouseLeave(t){(this.isDragging||this.textManager.isTextMoving||this.annotationManager.isResizing||this.annotationManager.isMovingAnnotation)&&this.handleMouseUp(t)}handleDoubleClick(t){if(!this.getBgImage())return;this.justFinishedMove=!1;const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return void this.textManager.startEditing(e);if(this.annotationManager.selectedAnnotation){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY),i=this.annotationManager.getAnnotationAtPoint(e);if(i&&i.index===this.annotationManager.selectedAnnotation.index)return void this.annotationManager.startTitleEditing(i.index)}if("polygon"===this.getDrawType()){if(this.annotationManager.isDrawing&&this.annotationManager.operate.data.length<=2)return;this.annotationManager.finishPolygonDrawing(),this.renderCallback()}}handleClick(t){var e;const i=this.getDrawType();if(!this.getBgImage()||"polygon"!==i||this.isDragging||this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing||this.textManager.isTextMoving)return;if(this.justDeselected)return void(this.justDeselected=!1);if("polygon"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type))return;const n=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.checkPolygonPointValid(n)&&(this.annotationManager.isDrawing?this.annotationManager.addPolygonPoint(n):this.annotationManager.startPolygonDrawing(n)),this.annotationManager.updatePolygonTempPoint(n),this.renderCallback()}handleKeyDown(t){const e=t.target;if(!e||"INPUT"!==e.tagName&&"TEXTAREA"!==e.tagName&&"SELECT"!==e.tagName&&!e.isContentEditable){if("z"===t.key&&(t.ctrlKey||t.metaKey))return this.annotationManager.withdraw()||this.textManager.withdraw(),void this.renderCallback();if(" "===t.key)return this.viewport.resetToInitial(),void this.renderCallback();if("Escape"!==t.key){if("Delete"===t.key||"Backspace"===t.key){if(null!==this.textManager.editingTextIndex)return this.textManager.removeTextAnnotation(this.textManager.editingTextIndex),void this.renderCallback();if(null!==this.textManager.selectedTextIndex)return this.textManager.deleteSelectedTextAnnotation(),void this.renderCallback();this.annotationManager.selectedAnnotation&&(this.annotationManager.deleteSelectedAnnotation(),this.renderCallback())}}else this.annotationManager.isDrawing?(this.annotationManager.cancelDrawing(),this.renderCallback()):this.annotationManager.selectedAnnotation&&(this.annotationManager.deselectAnnotation(),this.renderCallback())}}}class d{constructor(t){this.canvas=document.createElement("canvas"),this.bgImage=null,this.bgImageSrc="",this.bgImageExt="jpeg",this.drawType="";const{drawType:e,useEvents:i=!0,id:n="container",annotationColor:s,lineStyle:a,vertexStyle:d,textStyle:g,enableTitle:x=!1}=t,p=document.getElementById(n);if(!p)throw new Error(`Container with id "${n}" not found`);this.container=p,"absolute"!==p.style.position&&"fixed"!==p.style.position&&"relative"!==p.style.position&&(p.style.position="relative"),this.ctx=this.canvas.getContext("2d"),this.viewport=new o,this.viewport.setSize(p.clientWidth,p.clientHeight),this.canvas.width=this.viewport.width,this.canvas.height=this.viewport.height,this.canvas.style.width=`${this.viewport.width}px`,this.canvas.style.height=`${this.viewport.height}px`,this.canvas.style.cursor="default",this.canvas.style.display="block",p.appendChild(this.canvas),this.annotationManager=new r(this.viewport,this.container,()=>this.render()),s&&this.annotationManager.setColorConfig(s),a&&this.annotationManager.setLineStyle(a),d&&this.annotationManager.setVertexStyle(d),this.annotationManager.enableTitle=x,x&&this.annotationManager.initTitleSupport(),this.textManager=new h(this.viewport,this.container,this.ctx,()=>this.render()),g&&this.textManager.setTextStyle(g),this.renderer=new l(this.ctx,this.viewport,this.annotationManager,this.textManager,this.canvas),this.eventHandler=new c(this.canvas,this.viewport,this.annotationManager,this.textManager,this.renderer,()=>this.drawType,()=>this.bgImage,()=>this.render(),t=>this.setDrawType(t)),this.events={wheel:t=>this.eventHandler.handleWheel(t),mousedown:t=>this.eventHandler.handleMouseDown(t),mousemove:t=>this.eventHandler.handleMouseMove(t),mouseup:t=>this.eventHandler.handleMouseUp(t),mouseleave:t=>this.eventHandler.handleMouseLeave(t),dblclick:t=>this.eventHandler.handleDoubleClick(t),click:t=>this.eventHandler.handleClick(t),keydown:t=>this.eventHandler.handleKeyDown(t)},this.setDrawType(e||""),i&&this.addEventListeners()}setDrawType(t){"polygon"===this.drawType&&this.annotationManager.isDrawing&&this.annotationManager.operate.data.length>0?console.warn("Cannot switch mode while polygon is being drawn. Please complete or cancel the polygon first."):this.drawType!==t&&(this.annotationManager.operate.data=[],this.annotationManager.isDrawing=!1,this.annotationManager.tempPolygonPoint=null,""!==t&&this.deselectAnnotation(),this.annotationManager.operate.type=t,this.drawType=t,this.canvas.style.cursor="drag"===t?"grab":"text"===t?"text":"default")}drawImage(t){this.bgImageSrc=t,this.bgImageExt=i(t);const e=new Image;e.crossOrigin="anonymous",e.src=t,e.onload=()=>{this.bgImage=e,this.viewport.setOriginalSize(e.width,e.height),this.viewport.calculateInitialView(),this.render()}}render(){this.renderer.render(this.bgImage)}clear(t=""){this.annotationManager.clear(),this.textManager.clearTextAnnotations(),this.setDrawType(t),this.render()}withdraw(){const t=this.annotationManager.withdraw(),e=this.textManager.withdraw();(t||e)&&this.render()}resize(){const t=this.canvas.parentElement;t&&(this.viewport.setSize(t.clientWidth,t.clientHeight),this.canvas.width=this.viewport.width,this.canvas.height=this.viewport.height,this.canvas.style.width=`${this.viewport.width}px`,this.canvas.style.height=`${this.viewport.height}px`,this.render())}addEventListeners(){this.canvas.addEventListener("mousedown",this.events.mousedown),this.canvas.addEventListener("mousemove",this.events.mousemove),this.canvas.addEventListener("mouseup",this.events.mouseup),this.canvas.addEventListener("mouseleave",this.events.mouseleave),this.canvas.addEventListener("wheel",this.events.wheel),this.canvas.addEventListener("contextmenu",t=>t.preventDefault()),document.addEventListener("keydown",this.events.keydown),this.handleClickEventListener()}handleClickEventListener(){let t=null,e=0;this.canvas.addEventListener("click",i=>{if(this.eventHandler.isDragging||this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing||this.textManager.isTextMoving)return;const n=this.eventHandler.justFinishedMove;n&&(this.eventHandler.justFinishedMove=!1),e++,1===e?t=window.setTimeout(()=>{1!==e||n||this.events.click(i),e=0,t=null},300):2===e&&(this.eventHandler.justFinishedMove=!1,this.events.dblclick(i),e=0,null!==t&&(clearTimeout(t),t=null))})}changeScale(t){if(!this.bgImage)return;const e=a(this.viewport.scale,t),i=this.viewport.width/2,n=this.viewport.height/2,s=this.viewport.scale+e;this.viewport.updateScale(s,i,n),this.render()}clearCanvas(t=!1,e=!0){this.annotationManager.clear(),this.textManager.clearTextAnnotations(),this.textManager.deselectTextAnnotation(),!t&&e&&(this.bgImage=null,this.bgImageSrc="",this.viewport.setOriginalSize(0,0),this.viewport.reset()),this.ctx.clearRect(0,0,this.viewport.width,this.viewport.height),t&&this.bgImage&&this.render(),"drag"===this.drawType&&(this.canvas.style.cursor="default")}clearAnnotations(){this.clearCanvas(!0,!1)}getAnnotations(){return this.annotationManager.getAnnotations()}setAnnotationTitle(t,e){const i=this.annotationManager.setTitle(t,e);return i&&this.render(),i}getAnnotationTitle(t){return this.annotationManager.getTitle(t)}setTitleStyle(t){this.annotationManager.setTitleStyle(t),this.render()}getTitleStyle(){return this.annotationManager.getTitleStyle()}setTitlePosition(t){this.annotationManager.setTitlePosition(t),this.render()}getTitlePosition(){return this.annotationManager.getTitlePosition()}setAnnotationTitleStyle(t,e){const i=this.annotationManager.setAnnotationTitleStyle(t,e);return i&&this.render(),i}setAnnotationTitlePosition(t,e){const i=this.annotationManager.setAnnotationTitlePosition(t,e);return i&&this.render(),i}getTextAnnotations(){return this.textManager.getTextAnnotations()}exportAnnotationImage(){return new Promise((t,e)=>{if(!this.bgImage)return void e(new Error("No background image loaded"));const i=document.createElement("canvas");i.width=this.viewport.originalWidth,i.height=this.viewport.originalHeight;const n=i.getContext("2d");if(!n)return void e(new Error("Could not create offscreen canvas context"));const s=new Image;s.src=this.bgImageSrc,s.crossOrigin="anonymous",s.onload=()=>{try{this.renderer.drawForExport(n,s,this.viewport.originalWidth,this.viewport.originalHeight);const e=i.toDataURL(`image/${this.bgImageExt}`,.7);t(e)}catch(i){this.exportCurrentViewImage().then(t).catch(t=>e(new Error(`Both methods failed: ${t.message}`)))}},s.onerror=()=>{e(new Error("Failed to load image for export"))}})}exportCurrentViewImage(){return new Promise((t,e)=>{try{t(this.canvas.toDataURL(`image/${this.bgImageExt}`))}catch(t){e(new Error(`Failed to export current view: ${t.message}`))}})}base64ToFile(t,e){return n(t,e)}static base64ToBlob(t){return s(t)}addTextAnnotation(t,e,i=""){this.textManager.addTextAnnotation(t,e,i),this.render()}updateTextAnnotation(t,e){this.textManager.updateTextAnnotation(t,e)&&this.render()}moveTextAnnotation(t,e,i){this.textManager.moveTextAnnotation(t,e,i)&&this.render()}removeTextAnnotation(t){this.textManager.removeTextAnnotation(t)&&this.render()}clearTextAnnotations(){this.textManager.clearTextAnnotations(),this.render()}selectAnnotation(t){this.annotationManager.selectAnnotation(t)&&this.render()}deselectAnnotation(){this.annotationManager.deselectAnnotation(),this.textManager.deselectTextAnnotation(),this.textManager.editingTextIndex=null,this.render()}getSelectedAnnotation(){const t=this.annotationManager.getSelectedAnnotation();if(t)return t;const e=this.textManager.getSelectedTextAnnotation();return e?{index:e.index,type:"text",data:e.data}:null}deleteSelectedAnnotation(){if(null!==this.textManager.selectedTextIndex)return this.textManager.deleteSelectedTextAnnotation(),void this.render();this.annotationManager.deleteSelectedAnnotation()&&this.render()}moveSelectedAnnotation(t,e){this.annotationManager.moveSelectedAnnotation(t,e)&&this.render()}setAnnotationColor(t){this.annotationManager.setColorConfig(t),this.render()}getAnnotationColor(){return this.annotationManager.getColorConfig()}updateSelectedAnnotationStyle(){const t=this.annotationManager.selectedAnnotation;if(!t)return!1;const e=this.annotationManager.recordList[t.index];return!!e&&(e.style=this.annotationManager.getCurrentStyle(),this.render(),!0)}setLineStyle(t){this.annotationManager.setLineStyle(t),this.render()}getLineStyle(){return this.annotationManager.getLineStyle()}setVertexStyle(t){this.annotationManager.setVertexStyle(t),this.render()}getVertexStyle(){return this.annotationManager.getVertexStyle()}setTextStyle(t){this.textManager.setTextStyle(t),this.render()}updateTextAnnotationStyle(t,e){const i=this.textManager.updateTextAnnotationStyle(t,e);return i&&this.render(),i}setTextInputStyle(t){this.textManager.setInputStyle(t)}setTextSelectionStyle(t){this.textManager.setSelectionStyle(t),this.render()}destroy(){this.textManager.destroy(),this.annotationManager.destroyTitleInput(),this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas)}}export{r as AnnotationManager,d as Drawer,c as EventHandler,l as Renderer,h as TextAnnotationManager,o as ViewportManager,s as base64ToBlob,n as base64ToFile,d as default,i as getImageTypeFromUrl,a as getZoomDelta,e as isPointInPolygon,t as isPointInRect};
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).ImageAnnotationDrawer=t.ImageAnnotationDrawer||{})}(this,function(t){"use strict";function e(t,e){const n=e.width>=0?e.start.x:e.start.x+e.width,i=e.height>=0?e.start.y:e.start.y+e.height,s=Math.abs(e.width),a=Math.abs(e.height);return t.x>=n&&t.x<=n+s&&t.y>=i&&t.y<=i+a}function n(t,e){if(e.length<3)return!1;let n=!1;for(let i=0,s=e.length-1;i<e.length;s=i++){const a=e[i].point.x,o=e[i].point.y,r=e[s].point.x,h=e[s].point.y;o>t.y!=h>t.y&&t.x<(r-a)*(t.y-o)/(h-o)+a&&(n=!n)}return n}function i(t){try{const e=new URL(t).pathname.toLowerCase();return e.endsWith(".png")?"png":e.endsWith(".jpg")?"jpg":e.endsWith(".jpeg")?"jpeg":e.endsWith(".webp")?"webp":e.endsWith(".gif")?"gif":e.endsWith(".bmp")?"bmp":e.endsWith(".svg")?"svg":"jpeg"}catch(t){return"jpeg"}}function s(t,e){const n=t.split(";base64,"),i=n[0].split(":")[1],s=window.atob(n[1]),a=s.length,o=new Uint8Array(a);for(let t=0;t<a;++t)o[t]=s.charCodeAt(t);const r=new Blob([o],{type:i});return new File([r],e,{type:i})}function a(t){return new Promise((e,n)=>{const i=t.split(";base64,");if(i.length<2)return void n(new Error("Invalid base64 string"));const s=i[0].split(":")[1],a=atob(i[1]),o=new ArrayBuffer(a.length),r=new Uint8Array(o);for(let t=0;t<a.length;t++)r[t]=a.charCodeAt(t);e(new Blob([o],{type:s}))})}function o(t,e){return t<2?e?.1:-.1:t<5?e?.05:-.05:e?.02:-.02}class r{constructor(){this.width=0,this.height=0,this.originalWidth=0,this.originalHeight=0,this.scale=1,this.minScale=1,this.maxScale=10,this.offset={x:0,y:0},this.initialScale=1,this.initialOffset={x:0,y:0},this.isInitialScale=!0,this.minOffsetX=0,this.minOffsetY=0,this.maxOffsetX=0,this.maxOffsetY=0}setSize(t,e){this.width=t,this.height=e}setOriginalSize(t,e){this.originalWidth=t,this.originalHeight=e}calculateInitialView(){if(0===this.originalWidth||0===this.originalHeight)return;const t=this.width/this.originalWidth,e=this.height/this.originalHeight;this.scale=Math.min(t,e),this.initialScale=this.scale,this.minScale=this.initialScale,this.offset={x:(this.width-this.originalWidth*this.scale)/2,y:(this.height-this.originalHeight*this.scale)/2},this.initialOffset=Object.assign({},this.offset),this.calculateBoundaries(),this.isInitialScale=!0}calculateBoundaries(){const t=this.originalWidth*this.scale,e=this.originalHeight*this.scale;this.maxOffsetX=0,this.maxOffsetY=0,this.minOffsetX=this.width-t,this.minOffsetY=this.height-e,this.minOffsetX>this.maxOffsetX&&([this.minOffsetX,this.maxOffsetX]=[this.maxOffsetX,this.minOffsetX]),this.minOffsetY>this.maxOffsetY&&([this.minOffsetY,this.maxOffsetY]=[this.maxOffsetY,this.minOffsetY])}constrainViewport(){this.offset.x>this.maxOffsetX?this.offset.x=this.maxOffsetX:this.offset.x<this.minOffsetX&&(this.offset.x=this.minOffsetX),this.offset.y>this.maxOffsetY?this.offset.y=this.maxOffsetY:this.offset.y<this.minOffsetY&&(this.offset.y=this.minOffsetY)}toImageCoordinates(t,e){return{x:(t-this.offset.x)/this.scale,y:(e-this.offset.y)/this.scale}}toCanvasCoordinates(t,e){return{x:this.offset.x+t*this.scale,y:this.offset.y+e*this.scale}}updateScale(t,e,n){const i=this.scale,s=(e-this.offset.x)/i,a=(n-this.offset.y)/i;this.scale=Math.max(this.minScale,Math.min(t,this.maxScale));const o=s*this.scale+this.offset.x,r=a*this.scale+this.offset.y;this.offset.x+=e-o,this.offset.y+=n-r,this.calculateBoundaries(),this.constrainViewport(),this.isInitialScale=this.scale===this.initialScale}resetToInitial(){this.scale=this.initialScale,this.offset=Object.assign({},this.initialOffset),this.calculateBoundaries(),this.isInitialScale=!0}updateOffset(t,e){this.offset.x+=t,this.offset.y+=e,this.constrainViewport()}reset(){this.scale=1,this.minScale=1,this.offset={x:0,y:0},this.initialScale=1,this.initialOffset={x:0,y:0},this.isInitialScale=!0,this.minOffsetX=0,this.minOffsetY=0,this.maxOffsetX=0,this.maxOffsetY=0}}class h{constructor(t){this.viewport=t,this.recordList=[],this.operate={type:"rect",data:[],status:"pending"},this.isDrawing=!1,this.drawStartPoint={x:0,y:0},this.tempPolygonPoint=null,this.selectedAnnotation=null,this.isMovingAnnotation=!1,this.isResizing=!1,this.activeHandle=null,this.annotationMoveStart={x:0,y:0},this.originalRect=null,this.originalPolygon=null,this.deleteHistory=[],this.strokeStyle="red",this.lineWidth=5,this.lineStyle="solid",this.vertexStyle={size:8,fillColor:"red",strokeColor:"white",strokeWidth:2,shape:"circle"},this.selectionStyle={strokeColor:"#00D9FF",fillColor:"rgba(0,217,255,0.15)",handleSize:12,handleColor:"#00D9FF"},this.colorConfig={rect:"red",polygon:"red",default:"red"},this.applyColorConfig()}setColorConfig(t){this.colorConfig="string"==typeof t?{rect:t,polygon:t,default:t}:Object.assign(Object.assign({},this.colorConfig),t),this.applyColorConfig()}getColorConfig(){return Object.assign({},this.colorConfig)}applyColorConfig(){this.strokeStyle=this.colorConfig.default||"red"}getColorForType(t){return this.colorConfig[t]||this.colorConfig.default||"red"}getCurrentStyle(){return{strokeColor:this.strokeStyle,lineWidth:this.lineWidth,lineStyle:this.lineStyle,vertexStyle:Object.assign({},this.vertexStyle)}}setLineStyle(t){this.lineStyle=t}setVertexStyle(t){this.vertexStyle=Object.assign(Object.assign({},this.vertexStyle),t),t.fillColor&&(this.strokeStyle=t.fillColor)}getLineStyle(){return this.lineStyle}getVertexStyle(){return Object.assign({},this.vertexStyle)}getAnnotationStyle(t){return t.style||this.getCurrentStyle()}startRectDrawing(t){this.isDrawing=!0,this.drawStartPoint=t,this.operate={type:"rect",data:[{start:Object.assign({},t),width:0,height:0}],status:"pending"}}updateRectDrawing(t){if(!this.isDrawing||"rect"!==this.operate.type)return;const e=this.operate.data[0];e.width=t.x-this.drawStartPoint.x,e.height=t.y-this.drawStartPoint.y}finishRectDrawing(){if(!this.isDrawing||"rect"!==this.operate.type)return!1;const t=this.operate.data[0];if(Math.abs(t.width)>5&&Math.abs(t.height)>5){const t=Object.assign(Object.assign({},this.operate),{style:this.getCurrentStyle()});this.recordList.push(t),this.deleteHistory=[]}return this.operate.data=[],this.isDrawing=!1,!0}startPolygonDrawing(t){this.isDrawing=!0,this.operate={type:"polygon",data:[{point:t}],status:"pending"},this.tempPolygonPoint=t}addPolygonPoint(t){if(!this.isDrawing||"polygon"!==this.operate.type)return!1;const e=this.operate.data.length>0?this.operate.data[this.operate.data.length-1]:null;return!(e&&Math.abs(e.point.x-t.x)<=5&&Math.abs(e.point.y-t.y)<=5)&&(this.operate.data.push({point:t}),!0)}updatePolygonTempPoint(t){this.tempPolygonPoint=t}finishPolygonDrawing(){if(!this.isDrawing||"polygon"!==this.operate.type)return!1;if(this.operate.data.length>=3){this.operate.status="fullfilled";const t=Object.assign(Object.assign({},this.operate),{style:this.getCurrentStyle()});this.recordList.push(t),this.deleteHistory=[]}return this.operate={type:"polygon",data:[],status:"pending"},this.isDrawing=!1,this.tempPolygonPoint=null,!0}cancelDrawing(){if(this.isDrawing&&"polygon"===this.operate.type&&this.operate.data.length>=2){this.operate.status="fullfilled";const t=Object.assign(Object.assign({},this.operate),{style:this.getCurrentStyle()});this.recordList.push(t)}this.operate.data=[],this.isDrawing=!1,this.tempPolygonPoint=null}withdraw(){if(this.operate.data.length>0)return"polygon"===this.operate.type?(this.operate.data.pop(),0===this.operate.data.length&&(this.isDrawing=!1)):(this.operate.data=[],this.isDrawing=!1),!0;if(this.deleteHistory.length>0){const{annotation:t,index:e}=this.deleteHistory.pop();return this.recordList.splice(e,0,t),!0}return this.recordList.length>0&&(this.recordList.pop(),!0)}selectAnnotation(t){if(t<0||t>=this.recordList.length)return this.deselectAnnotation(),!1;const e=this.recordList[t];return this.selectedAnnotation={index:t,type:e.type},!0}deselectAnnotation(){this.selectedAnnotation=null,this.isMovingAnnotation=!1,this.isResizing=!1,this.activeHandle=null,this.originalRect=null,this.originalPolygon=null}deleteSelectedAnnotation(){if(!this.selectedAnnotation)return!1;const t=this.selectedAnnotation.index,e=this.recordList[t];return this.deleteHistory.push({annotation:Object.assign({},e),index:t}),this.recordList.splice(t,1),this.deselectAnnotation(),!0}startMovingAnnotation(t){return!!this.selectedAnnotation&&(this.isMovingAnnotation=!0,this.annotationMoveStart={x:t.clientX,y:t.clientY},!0)}moveSelectedAnnotation(t,e){if(!this.selectedAnnotation)return!1;const n=this.recordList[this.selectedAnnotation.index];if(!n)return!1;if("rect"===n.type){const i=n.data[0];i.start.x+=t,i.start.y+=e}else if("polygon"===n.type){n.data.forEach(n=>{n.point.x+=t,n.point.y+=e})}return!0}finishMovingAnnotation(){this.isMovingAnnotation=!1,this.isResizing=!1,this.activeHandle=null,this.originalRect=null,this.originalPolygon=null}startResizing(t,e){if(!this.selectedAnnotation)return!1;this.activeHandle=t,this.isResizing=!0;const n=this.recordList[this.selectedAnnotation.index];return"rect"===n.type?this.originalRect=Object.assign({},n.data[0]):"polygon"===n.type&&(this.originalPolygon=n.data.map(t=>({point:Object.assign({},t.point)}))),!0}resizeRect(t){if(!this.selectedAnnotation||"rect"!==this.selectedAnnotation.type||!this.originalRect||!this.activeHandle)return!1;const e=this.recordList[this.selectedAnnotation.index].data[0],n=this.originalRect,i=n.start.x,s=n.start.x+n.width,a=n.start.y,o=n.start.y+n.height;let r,h;switch(this.activeHandle.index){case 0:r=s,h=o;break;case 1:r=i,h=o;break;case 2:r=s,h=a;break;case 3:r=i,h=a;break;default:return!1}const l=Math.min(t.x,r),c=Math.max(t.x,r),d=Math.min(t.y,h),g=Math.max(t.y,h);e.start.x=l,e.start.y=d,e.width=c-l,e.height=g-d;const x=t.x<r,f=t.y<h;return 0===this.activeHandle.index?x||f?!x&&f?this.activeHandle.index=1:x&&!f&&(this.activeHandle.index=2):this.activeHandle.index=3:1===this.activeHandle.index?x&&!f?this.activeHandle.index=2:x&&f?this.activeHandle.index=0:x||f||(this.activeHandle.index=3):2===this.activeHandle.index?!x&&f?this.activeHandle.index=1:x&&f?this.activeHandle.index=0:x||f||(this.activeHandle.index=3):3===this.activeHandle.index&&(x&&f?this.activeHandle.index=0:!x&&f?this.activeHandle.index=1:x&&!f&&(this.activeHandle.index=2)),this.originalRect=Object.assign({},e),!0}resizePolygon(t){if(!this.selectedAnnotation||"polygon"!==this.selectedAnnotation.type||!this.activeHandle)return!1;const e=this.recordList[this.selectedAnnotation.index].data,n=this.activeHandle.index;return n>=0&&n<e.length&&(e[n].point.x=t.x,e[n].point.y=t.y,!0)}getAnnotationAtPoint(t){for(let i=this.recordList.length-1;i>=0;i--){const s=this.recordList[i];if("rect"===s.type){if(e(t,s.data[0]))return{index:i,type:"rect"}}else if("polygon"===s.type){if(n(t,s.data))return{index:i,type:"polygon"}}}return null}getHandleAtPoint(t,e){if(!this.selectedAnnotation)return null;const n=this.recordList[this.selectedAnnotation.index],i=this.selectionStyle.handleSize/2;if("rect"===n.type){const s=n.data[0],a=this.viewport.offset.x+s.start.x*this.viewport.scale,o=this.viewport.offset.y+s.start.y*this.viewport.scale,r=s.width*this.viewport.scale,h=s.height*this.viewport.scale,l=[{x:a,y:o,index:0},{x:a+r,y:o,index:1},{x:a,y:o+h,index:2},{x:a+r,y:o+h,index:3}];for(const n of l)if(t>=n.x-i&&t<=n.x+i&&e>=n.y-i&&e<=n.y+i)return{type:"rect-corner",index:n.index}}else if("polygon"===n.type){const s=n.data;for(let n=0;n<s.length;n++){const a=s[n],o=this.viewport.offset.x+a.point.x*this.viewport.scale,r=this.viewport.offset.y+a.point.y*this.viewport.scale;if(t>=o-i&&t<=o+i&&e>=r-i&&e<=r+i)return{type:"polygon-vertex",index:n}}}return null}clear(){this.recordList=[],this.operate={type:"rect",data:[],status:"pending"},this.isDrawing=!1,this.tempPolygonPoint=null,this.deselectAnnotation(),this.deleteHistory=[]}getAnnotations(){return[...this.recordList]}getSelectedAnnotation(){if(!this.selectedAnnotation)return null;const t=this.recordList[this.selectedAnnotation.index];return t?{index:this.selectedAnnotation.index,type:this.selectedAnnotation.type,data:t}:null}}class l{constructor(t,e,n,i){this.viewport=t,this.container=e,this.ctx=n,this.renderCallback=i,this.textAnnotations=[],this.editingTextIndex=null,this.currentEditingIndex=null,this.isTextMoving=!1,this.textMoveStart={x:0,y:0},this.textBeforeEditing="",this.selectedTextIndex=null,this.textInput=null,this.deleteHistory=[],this.defaultInputStyle={border:"2px solid #00D9FF",borderRadius:"6px",padding:"6px 10px",fontSize:"16px",fontFamily:"Arial, sans-serif",backgroundColor:"#ffffff",color:"#333",boxShadow:"0 4px 12px rgba(0,0,0,0.3)",minWidth:"60px",maxWidth:"200px"},this.textStyle={font:"16px Arial",color:"#FFD700",padding:6,backgroundColor:"rgba(0,0,0,0.6)",borderRadius:4,selectedBorderColor:"#00D9FF",selectedBackgroundColor:"rgba(0,217,255,0.15)",inputStyle:Object.assign({},this.defaultInputStyle)},this.createTextInput()}createTextInput(){this.textInput=document.createElement("input"),this.textInput.type="text",this.textInput.style.position="absolute",this.textInput.style.zIndex="99999",this.textInput.style.display="none",this.textInput.style.outline="none",this.textInput.style.width="fit-content",this.textInput.placeholder="输入文字...",this.applyInputStyle(),this.textInput.addEventListener("blur",()=>{this.finishEditing(),this.renderCallback&&this.renderCallback()}),this.textInput.addEventListener("keydown",t=>{t.stopPropagation(),"Enter"===t.key?(this.finishEditing(),this.renderCallback&&this.renderCallback()):"Escape"===t.key?(t.preventDefault(),this.cancelEditing()):"Delete"===t.key&&""===this.textInput.value&&(t.preventDefault(),this.deleteEditingAnnotation())}),this.textInput.addEventListener("input",()=>{this.adjustInputWidth()}),this.container.appendChild(this.textInput)}applyInputStyle(){if(!this.textInput)return;const t=Object.assign(Object.assign({},this.defaultInputStyle),this.textStyle.inputStyle);this.textInput.style.border=t.border,this.textInput.style.borderRadius=t.borderRadius,this.textInput.style.padding=t.padding,this.textInput.style.fontSize=t.fontSize,this.textInput.style.fontFamily=t.fontFamily,this.textInput.style.backgroundColor=t.backgroundColor,this.textInput.style.color=t.color,this.textInput.style.boxShadow=t.boxShadow,this.textInput.style.minWidth=t.minWidth,this.textInput.style.maxWidth=t.maxWidth}setTextStyle(t){this.textStyle=Object.assign(Object.assign({},this.textStyle),t),this.applyInputStyle()}setInputStyle(t){this.textStyle.inputStyle=Object.assign(Object.assign({},this.textStyle.inputStyle),t),this.applyInputStyle()}setSelectionStyle(t){void 0!==t.selectedBorderColor&&(this.textStyle.selectedBorderColor=t.selectedBorderColor),void 0!==t.selectedBackgroundColor&&(this.textStyle.selectedBackgroundColor=t.selectedBackgroundColor)}getCurrentStyle(){return{font:this.textStyle.font,color:this.textStyle.color,backgroundColor:this.textStyle.backgroundColor}}addTextAnnotation(t,e,n=""){var i;this.ctx.font=this.textStyle.font;const s=n||"输入文字...",a=this.ctx.measureText(s);let o;if(a.actualBoundingBoxAscent&&a.actualBoundingBoxDescent)o=a.actualBoundingBoxAscent+a.actualBoundingBoxDescent;else{o=1.2*parseInt((null===(i=this.textStyle.font.match(/\d+/))||void 0===i?void 0:i[0])||"16")}const r={position:{x:t,y:e},text:n,style:this.getCurrentStyle(),width:Math.max(a.width,60),height:o};this.textAnnotations.push(r);const h=this.textAnnotations.length-1;return this.deleteHistory=[],this.startEditing(h),h}updateTextAnnotation(t,e){if(t<0||t>=this.textAnnotations.length)return!1;const n=this.textAnnotations[t];n.text=e,this.ctx.font=this.textStyle.font;const i=this.ctx.measureText(e);return n.width=i.width,n.height=1.2*parseInt(this.textStyle.font),!0}moveTextAnnotation(t,e,n){return!(t<0||t>=this.textAnnotations.length)&&(this.textAnnotations[t].position={x:e,y:n},!0)}removeTextAnnotation(t){if(t<0||t>=this.textAnnotations.length)return!1;const e=this.textAnnotations[t];return this.deleteHistory.push({annotation:Object.assign({},e),index:t}),this.textAnnotations.splice(t,1),this.editingTextIndex===t?this.cancelEditing():null!==this.editingTextIndex&&this.editingTextIndex>t&&this.editingTextIndex--,!0}clearTextAnnotations(){this.textAnnotations=[],this.selectedTextIndex=null,this.cancelEditing(),this.deleteHistory=[]}startEditing(t){if(!this.textInput||t<0||t>=this.textAnnotations.length)return!1;const e=this.textAnnotations[t];this.editingTextIndex=t,this.currentEditingIndex=t,this.textBeforeEditing=e.text;const n=this.viewport.offset.x+e.position.x*this.viewport.scale,i=this.viewport.offset.y+e.position.y*this.viewport.scale;return this.textInput.value=e.text,this.textInput.style.left=n-this.textStyle.padding+"px",this.textInput.style.top=i-this.textStyle.padding+"px",this.textInput.style.display="block",this.adjustInputWidth(),setTimeout(()=>{this.textInput&&(this.textInput.focus(),this.textInput.select())},10),!0}finishEditing(){if(!this.textInput)return!1;const t=this.currentEditingIndex,e=this.textInput.value.trim();if(null===t||t<0||t>=this.textAnnotations.length)return this.resetEditingState(),!1;if(""===e)this.textAnnotations.splice(t,1);else{const n=this.textAnnotations[t];n.text=e,this.ctx.font=this.textStyle.font;const i=this.ctx.measureText(n.text);n.width=i.width,n.height=1.2*parseInt(this.textStyle.font)}return this.resetEditingState(),!0}cancelEditing(){if(!this.textInput||null===this.editingTextIndex)return!1;this.textInput.value=this.textBeforeEditing;const t=this.editingTextIndex;if(t>=0&&t<this.textAnnotations.length){""===this.textAnnotations[t].text&&""===this.textBeforeEditing&&this.textAnnotations.splice(t,1)}return this.resetEditingState(),!0}deleteEditingAnnotation(){if(null===this.editingTextIndex)return!1;const t=this.editingTextIndex;return this.textInput&&(this.textInput.style.display="none"),t>=0&&t<this.textAnnotations.length&&this.textAnnotations.splice(t,1),this.resetEditingState(),!0}resetEditingState(){this.textInput&&(this.textInput.style.display="none"),this.editingTextIndex=null,this.currentEditingIndex=null,this.textBeforeEditing=""}adjustInputWidth(){if(!this.textInput||null===this.editingTextIndex)return;this.ctx.font=this.textStyle.font;const t=this.textInput.value||this.textInput.placeholder||"",e=this.ctx.measureText(t).width,n=Math.min(Math.max(e+24,60),200);this.textInput.style.width=`${n}px`}startMoving(t,e){return!(e<0||e>=this.textAnnotations.length)&&(this.editingTextIndex=e,this.isTextMoving=!0,this.textMoveStart={x:t.clientX,y:t.clientY},!0)}moveAnnotation(t){if(!this.isTextMoving||null===this.editingTextIndex)return!1;const e=t.clientX-this.textMoveStart.x,n=t.clientY-this.textMoveStart.y,i=this.textAnnotations[this.editingTextIndex];return!!i&&(i.position.x+=e/this.viewport.scale,i.position.y+=n/this.viewport.scale,this.textMoveStart={x:t.clientX,y:t.clientY},!0)}finishMoving(){this.isTextMoving=!1}checkTextClick(t,e){for(let n=this.textAnnotations.length-1;n>=0;n--){const i=this.textAnnotations[n],s=this.viewport.offset.x+i.position.x*this.viewport.scale,a=this.viewport.offset.y+i.position.y*this.viewport.scale;if(t>=s-this.textStyle.padding&&t<=s+i.width+this.textStyle.padding&&e>=a-this.textStyle.padding&&e<=a+i.height+this.textStyle.padding)return n}return null}checkTextClickForMove(t){const e=this.checkTextClick(t.offsetX,t.offsetY);return null!==e?(this.startMoving(t,e),{index:e,handled:!0}):{index:-1,handled:!1}}getTextAnnotations(){return[...this.textAnnotations]}withdraw(){if(this.deleteHistory.length>0){const{annotation:t,index:e}=this.deleteHistory.pop();return this.textAnnotations.splice(e,0,t),!0}return this.textAnnotations.length>0&&(this.textAnnotations.pop(),!0)}selectTextAnnotation(t){return t<0||t>=this.textAnnotations.length?(this.deselectTextAnnotation(),!1):(this.selectedTextIndex=t,null!==this.editingTextIndex&&this.finishEditing(),!0)}deselectTextAnnotation(){this.selectedTextIndex=null}deleteSelectedTextAnnotation(){if(null===this.selectedTextIndex)return!1;const t=this.selectedTextIndex,e=this.textAnnotations[t];return this.deleteHistory.push({annotation:Object.assign({},e),index:t}),this.textAnnotations.splice(t,1),this.selectedTextIndex=null,null!==this.editingTextIndex&&this.editingTextIndex>t&&this.editingTextIndex--,!0}getSelectedTextAnnotation(){if(null===this.selectedTextIndex)return null;const t=this.textAnnotations[this.selectedTextIndex];return t?{index:this.selectedTextIndex,data:t}:null}destroy(){this.textInput&&this.textInput.parentNode&&this.textInput.parentNode.removeChild(this.textInput)}}class c{constructor(t,e,n,i,s){this.ctx=t,this.viewport=e,this.annotationManager=n,this.textManager=i,this.canvas=s}render(t){this.ctx.clearRect(0,0,this.viewport.width,this.viewport.height),t&&this.drawBackgroundImage(t),this.drawAnnotations(),this.drawTextAnnotations()}drawBackgroundImage(t){this.ctx.drawImage(t,this.viewport.offset.x,this.viewport.offset.y,this.viewport.originalWidth*this.viewport.scale,this.viewport.originalHeight*this.viewport.scale)}drawAnnotations(){this.annotationManager.recordList.forEach((t,e)=>{var n;const i=this.annotationManager.getAnnotationStyle(t);this.ctx.strokeStyle=i.strokeColor,this.ctx.lineWidth=i.lineWidth,this.setLineDash(i.lineStyle||"solid"),this.ctx.fillStyle=this.ctx.strokeStyle;const s=(null===(n=this.annotationManager.selectedAnnotation)||void 0===n?void 0:n.index)===e;if("rect"===t.type)this.drawRect(t.data[0],s);else if("polygon"===t.type){const e=i.vertexStyle||this.annotationManager.getVertexStyle();this.drawPolygon(t.data,"fullfilled"===t.status,e)}}),this.annotationManager.operate.data.length>0&&(this.ctx.strokeStyle=this.annotationManager.strokeStyle,this.ctx.lineWidth=this.annotationManager.lineWidth,this.ctx.fillStyle=this.ctx.strokeStyle,this.setLineDash(this.annotationManager.lineStyle),"rect"===this.annotationManager.operate.type?this.drawRect(this.annotationManager.operate.data[0],!1):"polygon"===this.annotationManager.operate.type&&this.drawCurrentPolygon()),this.ctx.setLineDash([]),this.drawSelectionHighlight()}setLineDash(t){switch(t){case"dashed":this.ctx.setLineDash([10,5]);break;case"dotted":this.ctx.setLineDash([2,4]);break;default:this.ctx.setLineDash([])}}drawRect(t,e=!1){this.ctx.strokeRect(this.viewport.offset.x+t.start.x*this.viewport.scale,this.viewport.offset.y+t.start.y*this.viewport.scale,t.width*this.viewport.scale,t.height*this.viewport.scale)}drawPolygon(t,e,n){if(0!==t.length){if(this.ctx.beginPath(),t.forEach((t,e)=>{const n=Math.round(this.viewport.offset.x+t.point.x*this.viewport.scale),i=Math.round(this.viewport.offset.y+t.point.y*this.viewport.scale);0===e?this.ctx.moveTo(n,i):this.ctx.lineTo(n,i)}),e&&t.length>1){const e=t[0];this.ctx.lineTo(this.viewport.offset.x+e.point.x*this.viewport.scale,this.viewport.offset.y+e.point.y*this.viewport.scale)}this.ctx.stroke(),this.drawPolygonVertices(t,n)}}drawPolygonVertices(t,e){const n=e||this.annotationManager.getVertexStyle(),i=n.size/2;t.forEach(t=>{const e=Math.round(this.viewport.offset.x+t.point.x*this.viewport.scale),s=Math.round(this.viewport.offset.y+t.point.y*this.viewport.scale);switch(this.ctx.fillStyle=n.fillColor,this.ctx.strokeStyle=n.strokeColor,this.ctx.lineWidth=n.strokeWidth,this.ctx.beginPath(),n.shape){case"square":this.ctx.rect(e-i,s-i,2*i,2*i);break;case"diamond":this.ctx.moveTo(e,s-i),this.ctx.lineTo(e+i,s),this.ctx.lineTo(e,s+i),this.ctx.lineTo(e-i,s),this.ctx.closePath();break;default:this.ctx.arc(e,s,i,0,2*Math.PI)}this.ctx.fill(),n.strokeWidth>0&&this.ctx.stroke()})}drawCurrentPolygon(){const t=this.annotationManager.operate.data,e=this.annotationManager.tempPolygonPoint;if(this.ctx.beginPath(),t.forEach((t,e)=>{const n=Math.round(this.viewport.offset.x+t.point.x*this.viewport.scale),i=Math.round(this.viewport.offset.y+t.point.y*this.viewport.scale);0===e?this.ctx.moveTo(n,i):this.ctx.lineTo(n,i)}),e){const t=Math.round(this.viewport.offset.x+e.x*this.viewport.scale),n=Math.round(this.viewport.offset.y+e.y*this.viewport.scale);this.ctx.lineTo(t,n)}if(this.ctx.stroke(),this.drawPolygonVertices(t,this.annotationManager.getVertexStyle()),e){const t=this.annotationManager.getVertexStyle(),n=Math.round(this.viewport.offset.x+e.x*this.viewport.scale),i=Math.round(this.viewport.offset.y+e.y*this.viewport.scale),s=t.size/2;switch(this.ctx.fillStyle=t.fillColor,this.ctx.strokeStyle=t.strokeColor,this.ctx.lineWidth=t.strokeWidth,this.ctx.beginPath(),t.shape){case"square":this.ctx.rect(n-s,i-s,2*s,2*s);break;case"diamond":this.ctx.moveTo(n,i-s),this.ctx.lineTo(n+s,i),this.ctx.lineTo(n,i+s),this.ctx.lineTo(n-s,i),this.ctx.closePath();break;default:this.ctx.arc(n,i,s,0,2*Math.PI)}this.ctx.fill(),t.strokeWidth>0&&this.ctx.stroke()}}drawSelectionHighlight(){const t=this.annotationManager.selectedAnnotation;if(!t)return;const e=this.annotationManager.recordList[t.index];if(!e)return;const n=this.annotationManager.selectionStyle;this.ctx.save(),"rect"===e.type?this.drawRectSelectionHighlight(e.data[0],n):"polygon"===e.type&&this.drawPolygonSelectionHighlight(e.data,n),this.ctx.restore()}drawRectSelectionHighlight(t,e){const n=this.viewport.offset.x+t.start.x*this.viewport.scale,i=this.viewport.offset.y+t.start.y*this.viewport.scale,s=t.width*this.viewport.scale,a=t.height*this.viewport.scale;this.ctx.fillStyle=e.fillColor,this.ctx.fillRect(n,i,s,a),this.ctx.strokeStyle=e.strokeColor,this.ctx.lineWidth=2,this.ctx.setLineDash([5,5]),this.ctx.strokeRect(n,i,s,a);const o=e.handleSize;this.ctx.fillStyle=e.handleColor,this.ctx.setLineDash([]),this.ctx.fillRect(n-o/2,i-o/2,o,o),this.ctx.fillRect(n+s-o/2,i-o/2,o,o),this.ctx.fillRect(n-o/2,i+a-o/2,o,o),this.ctx.fillRect(n+s-o/2,i+a-o/2,o,o)}drawPolygonSelectionHighlight(t,e){this.ctx.beginPath(),t.forEach((t,e)=>{const n=this.viewport.offset.x+t.point.x*this.viewport.scale,i=this.viewport.offset.y+t.point.y*this.viewport.scale;0===e?this.ctx.moveTo(n,i):this.ctx.lineTo(n,i)}),this.ctx.closePath(),this.ctx.fillStyle=e.fillColor,this.ctx.fill(),this.ctx.strokeStyle=e.strokeColor,this.ctx.lineWidth=2,this.ctx.setLineDash([5,5]),this.ctx.stroke();const n=e.handleSize;this.ctx.fillStyle=e.handleColor,this.ctx.setLineDash([]),t.forEach(t=>{const e=Math.round(this.viewport.offset.x+t.point.x*this.viewport.scale),i=Math.round(this.viewport.offset.y+t.point.y*this.viewport.scale);this.ctx.fillRect(e-n/2,i-n/2,n,n)})}drawTextAnnotations(){const t=this.textManager.textStyle;this.textManager.textAnnotations.forEach((e,n)=>{if(this.textManager.editingTextIndex===n&&this.textManager.textInput&&"none"!==this.textManager.textInput.style.display)return;if(!e.text)return;const i=e.style||{font:t.font,color:t.color,backgroundColor:t.backgroundColor},s=this.viewport.offset.x+e.position.x*this.viewport.scale,a=this.viewport.offset.y+e.position.y*this.viewport.scale,o=this.textManager.selectedTextIndex===n;this.ctx.font=i.font;const r=this.ctx.measureText(e.text),h=r.actualBoundingBoxAscent||.8*e.height,l=h+(r.actualBoundingBoxDescent||.2*e.height),c=t.borderRadius,d=s-t.padding,g=e.width+2*t.padding,x=l+2*t.padding,f=a-h-t.padding;this.ctx.fillStyle=i.backgroundColor,this.ctx.beginPath(),this.ctx.roundRect(d,f,g,x,c),this.ctx.fill(),o&&(this.ctx.strokeStyle=t.selectedBorderColor,this.ctx.lineWidth=2,this.ctx.setLineDash([5,5]),this.ctx.beginPath(),this.ctx.roundRect(d,f,g,x,c),this.ctx.stroke(),this.ctx.setLineDash([])),this.ctx.fillStyle=i.color,this.ctx.fillText(e.text,s,a)})}drawForExport(t,e,n,i){t.drawImage(e,0,0,n,i),this.annotationManager.recordList.forEach(e=>{const n=this.annotationManager.getAnnotationStyle(e);if(t.strokeStyle=n.strokeColor,t.lineWidth=n.lineWidth,"rect"===e.type){const n=e.data[0];t.strokeRect(n.start.x,n.start.y,n.width,n.height)}else if("polygon"===e.type&&"fullfilled"===e.status){t.beginPath(),e.data.forEach((e,n)=>{0===n?t.moveTo(e.point.x,e.point.y):t.lineTo(e.point.x,e.point.y)});const i=e.data[0];t.lineTo(i.point.x,i.point.y),t.stroke(),e.data.forEach(e=>{t.beginPath(),t.arc(e.point.x,e.point.y,4,0,2*Math.PI),t.fillStyle=n.strokeColor,t.fill()})}}),this.textManager.textAnnotations.forEach(e=>{const n=this.textManager.textStyle,i=e.style||{font:n.font,color:n.color,backgroundColor:n.backgroundColor};t.font=i.font;const s=t.measureText(e.text),a=s.actualBoundingBoxAscent||.8*e.height,o=a+(s.actualBoundingBoxDescent||.2*e.height),r=e.position.x-n.padding,h=e.width+2*n.padding,l=o+2*n.padding,c=e.position.y-a-n.padding,d=n.borderRadius;t.fillStyle=i.backgroundColor,t.roundRect?(t.roundRect(r,c,h,l,d),t.fill()):t.fillRect(r,c,h,l),t.fillStyle=i.color,t.fillText(e.text,e.position.x,e.position.y)})}}class d{constructor(t,e,n,i,s,a,o,r,h){this.canvas=t,this.viewport=e,this.annotationManager=n,this.textManager=i,this.renderer=s,this.getDrawType=a,this.getBgImage=o,this.renderCallback=r,this.setDrawTypeCallback=h,this.isDragging=!1,this.dragStart={x:0,y:0},this.justFinishedMove=!1,this.justDeselected=!1}handleWheel(t){if(t.preventDefault(),!this.getBgImage())return;const e=t.deltaY>0?-.02:.02,n=this.viewport.scale,i=Math.max(this.viewport.minScale,Math.min(n+e,this.viewport.maxScale)),s=this.canvas.getBoundingClientRect(),a=t.clientX-s.left,o=t.clientY-s.top,r=(a-this.viewport.offset.x)/n,h=(o-this.viewport.offset.y)/n;this.viewport.scale=i;const l=r*i+this.viewport.offset.x,c=h*i+this.viewport.offset.y;this.viewport.offset.x+=a-l,this.viewport.offset.y+=o-c,this.viewport.calculateBoundaries(),this.viewport.constrainViewport(),this.viewport.isInitialScale=this.viewport.scale===this.viewport.initialScale,this.renderCallback()}handleMouseDown(t){var e,n;if(0!==t.button)return;const i=this.getDrawType(),s=this.getBgImage();if(this.annotationManager.selectedAnnotation&&s){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY),n=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(n)return this.annotationManager.startResizing(n,e),void this.renderCallback()}if("text"===i){const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return this.textManager.selectedTextIndex===e?this.textManager.startMoving(t,e):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(e)),void this.renderCallback();if(s){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY),n=this.annotationManager.getAnnotationAtPoint(e);if("rect"===(null==n?void 0:n.type))return this.annotationManager.selectAnnotation(n.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();const i=this.annotationManager.getAnnotationAtPoint(e);if("polygon"===(null==i?void 0:i.type))return this.annotationManager.selectAnnotation(i.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();if(this.annotationManager.selectedAnnotation)return this.annotationManager.deselectAnnotation(),void this.renderCallback()}return null!==this.textManager.selectedTextIndex?(this.textManager.deselectTextAnnotation(),void this.renderCallback()):void this.handleTextModeClick(t)}if(!s)return;const a=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);if("rect"===i){if("rect"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type)){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return this.annotationManager.startResizing(e,a),void this.renderCallback();const n=this.annotationManager.getAnnotationAtPoint(a);return n&&"rect"===n.type?(this.annotationManager.selectAnnotation(n.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback()):(this.annotationManager.deselectAnnotation(),this.justDeselected=!0,void this.renderCallback())}const n=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==n)return this.textManager.selectedTextIndex===n?this.textManager.startMoving(t,n):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(n)),void this.renderCallback();const i=this.annotationManager.getAnnotationAtPoint(a);if("polygon"===(null==i?void 0:i.type))return this.annotationManager.selectAnnotation(i.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();const s=this.annotationManager.getAnnotationAtPoint(a);if("rect"===(null==s?void 0:s.type))return this.annotationManager.selectAnnotation(s.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();this.annotationManager.startRectDrawing(a)}else if("polygon"===i){if("polygon"===(null===(n=this.annotationManager.selectedAnnotation)||void 0===n?void 0:n.type)){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e&&"polygon-vertex"===e.type)return this.annotationManager.startResizing(e,a),void this.renderCallback();const n=this.annotationManager.getAnnotationAtPoint(a);return n&&"polygon"===n.type?(this.annotationManager.selectAnnotation(n.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback()):(this.annotationManager.deselectAnnotation(),this.justDeselected=!0,void this.renderCallback())}const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return this.textManager.selectedTextIndex===e?this.textManager.startMoving(t,e):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(e)),void this.renderCallback();const i=this.annotationManager.getAnnotationAtPoint(a);if("rect"===(null==i?void 0:i.type))return this.annotationManager.selectAnnotation(i.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();const s=this.annotationManager.getAnnotationAtPoint(a);if("polygon"===(null==s?void 0:s.type))return this.annotationManager.selectAnnotation(s.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();this.annotationManager.isDrawing?this.checkPolygonPointValid(a)&&this.annotationManager.addPolygonPoint(a):this.annotationManager.startPolygonDrawing(a),this.annotationManager.updatePolygonTempPoint(a),this.renderCallback()}else if("drag"===i){if(this.annotationManager.selectedAnnotation){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return this.annotationManager.startResizing(e,a),void this.renderCallback()}const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return this.textManager.selectedTextIndex===e?this.textManager.startMoving(t,e):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(e)),void this.renderCallback();if(this.handleRectModeClick(t,a))return;if(this.handlePolygonModeClick(t,a))return;this.isDragging=!0,this.dragStart={x:t.clientX,y:t.clientY},this.canvas.style.cursor="grabbing"}else""===i&&this.handleNoModeClick(t,a)}checkTextClickInDragMode(t){return!!this.textManager.checkTextClickForMove(t).handled&&(this.canvas.style.cursor="grabbing",!0)}handleTextModeClick(t){const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return this.textManager.startMoving(t,e),this.canvas.style.cursor="grabbing",this.renderCallback(),!0;const n=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);return this.textManager.addTextAnnotation(n.x,n.y),!0}handleRectModeClick(t,e){var n,i;if("rect"===(null===(n=this.annotationManager.selectedAnnotation)||void 0===n?void 0:n.type)){const n=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(n)return this.annotationManager.startResizing(n,e),this.renderCallback(),!0}const s=this.annotationManager.getAnnotationAtPoint(e);return s&&"rect"===s.type?(this.annotationManager.selectAnnotation(s.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback(),!0):"rect"===(null===(i=this.annotationManager.selectedAnnotation)||void 0===i?void 0:i.type)&&(this.annotationManager.deselectAnnotation(),this.renderCallback(),!0)}handlePolygonModeClick(t,e){var n,i;if("polygon"===(null===(n=this.annotationManager.selectedAnnotation)||void 0===n?void 0:n.type)){const n=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(n&&"polygon-vertex"===n.type)return this.annotationManager.startResizing(n,e),this.renderCallback(),!0}const s=this.annotationManager.getAnnotationAtPoint(e);return s&&"polygon"===s.type?(this.annotationManager.selectAnnotation(s.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback(),!0):"polygon"===(null===(i=this.annotationManager.selectedAnnotation)||void 0===i?void 0:i.type)&&(this.annotationManager.deselectAnnotation(),this.renderCallback(),!0)}handleRectModeClickForMove(t,e){const n=this.annotationManager.getAnnotationAtPoint(e);return!(!n||"rect"!==n.type)&&(this.annotationManager.selectAnnotation(n.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback(),!0)}handlePolygonModeClickForMove(t,e){const n=this.annotationManager.getAnnotationAtPoint(e);return!(!n||"polygon"!==n.type)&&(this.annotationManager.selectAnnotation(n.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback(),!0)}handleNoModeClick(t,e){const n=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==n)return this.textManager.selectedTextIndex===n?this.textManager.startMoving(t,n):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(n)),void this.renderCallback();this.textManager.deselectTextAnnotation();const i=this.annotationManager.getAnnotationAtPoint(e);i?(this.annotationManager.selectAnnotation(i.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback()):this.annotationManager.deselectAnnotation()}checkPolygonPointValid(t){const e=this.annotationManager.operate.data.length>0?this.annotationManager.operate.data[this.annotationManager.operate.data.length-1]:null;return!e||Math.abs(e.point.x-t.x)>5||Math.abs(e.point.y-t.y)>5}handleMouseMove(t){if(!this.getBgImage())return;const e=this.getDrawType();if(this.isDragging&&"drag"===e){const e=t.clientX-this.dragStart.x,n=t.clientY-this.dragStart.y;return this.viewport.updateOffset(e,n),this.dragStart={x:t.clientX,y:t.clientY},void this.renderCallback()}if(this.textManager.isTextMoving&&null!==this.textManager.editingTextIndex)this.textManager.moveAnnotation(t)&&this.renderCallback();else if((this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing)&&this.annotationManager.selectedAnnotation)this.handleAnnotationMove(t);else{if(this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing||this.textManager.isTextMoving||this.updateCursor(t),this.annotationManager.isDrawing&&"rect"===this.annotationManager.operate.type){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.annotationManager.updateRectDrawing(e),this.renderCallback()}if("polygon"===e&&this.annotationManager.isDrawing){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.annotationManager.updatePolygonTempPoint(e),this.renderCallback()}}}handleAnnotationMove(t){var e,n;if(this.annotationManager.isResizing&&this.annotationManager.activeHandle){const i=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);return"rect"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type)?this.annotationManager.resizeRect(i):"polygon"===(null===(n=this.annotationManager.selectedAnnotation)||void 0===n?void 0:n.type)&&this.annotationManager.resizePolygon(i),void this.renderCallback()}if(!this.annotationManager.isMovingAnnotation||!this.annotationManager.selectedAnnotation)return;const i=(t.clientX-this.annotationManager.annotationMoveStart.x)/this.viewport.scale,s=(t.clientY-this.annotationManager.annotationMoveStart.y)/this.viewport.scale;this.annotationManager.moveSelectedAnnotation(i,s),this.annotationManager.annotationMoveStart={x:t.clientX,y:t.clientY},this.renderCallback()}updateCursor(t){const e=this.getDrawType();if("text"===e){if(this.getBgImage()){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);if(this.annotationManager.selectedAnnotation){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return void("rect-corner"===e.type?this.canvas.style.cursor=0===e.index||3===e.index?"nwse-resize":"nesw-resize":this.canvas.style.cursor="crosshair")}if(this.annotationManager.getAnnotationAtPoint(e))return void(this.canvas.style.cursor="move")}const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);return void(this.canvas.style.cursor=null!==e?"move":"crosshair")}"rect"!==e?"polygon"!==e?"drag"!==e?""!==e?this.canvas.style.cursor="default":this.updateNoModeCursor(t):this.updateDragModeCursor(t):this.updatePolygonModeCursor(t):this.updateRectModeCursor(t)}updateRectModeCursor(t){var e;if("rect"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type)){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return void(this.canvas.style.cursor=0===e.index||3===e.index?"nwse-resize":"nesw-resize")}const n=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.annotationManager.getAnnotationAtPoint(n)?this.canvas.style.cursor="move":null===this.textManager.checkTextClick(t.offsetX,t.offsetY)?this.canvas.style.cursor="crosshair":this.canvas.style.cursor="move"}updatePolygonModeCursor(t){var e;if("polygon"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type)){if(this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY))return void(this.canvas.style.cursor="crosshair")}const n=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.annotationManager.getAnnotationAtPoint(n)?this.canvas.style.cursor="move":null===this.textManager.checkTextClick(t.offsetX,t.offsetY)?this.canvas.style.cursor="crosshair":this.canvas.style.cursor="move"}updateDragModeCursor(t){if(this.annotationManager.selectedAnnotation){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return void("rect-corner"===e.type?this.canvas.style.cursor=0===e.index||3===e.index?"nwse-resize":"nesw-resize":this.canvas.style.cursor="crosshair")}if(null!==this.textManager.checkTextClick(t.offsetX,t.offsetY))return void(this.canvas.style.cursor="move");const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY),n=this.annotationManager.getAnnotationAtPoint(e);this.canvas.style.cursor=n?"move":"grab"}updateNoModeCursor(t){if(this.annotationManager.selectedAnnotation){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return void("rect-corner"===e.type?this.canvas.style.cursor=0===e.index||3===e.index?"nwse-resize":"nesw-resize":this.canvas.style.cursor="crosshair")}if(null!==this.textManager.checkTextClick(t.offsetX,t.offsetY))return void(this.canvas.style.cursor="move");const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY),n=this.annotationManager.getAnnotationAtPoint(e);this.canvas.style.cursor=n?"move":"default"}handleMouseUp(t){if(this.isDragging){this.isDragging=!1;const t=this.getDrawType();return void(this.canvas.style.cursor="drag"===t?"grab":"default")}if(this.textManager.isTextMoving){this.justFinishedMove=!0,this.textManager.finishMoving();const t=this.getDrawType();"drag"===t?this.canvas.style.cursor="grab":""===t?this.canvas.style.cursor="move":(this.textManager.editingTextIndex=null,this.canvas.style.cursor="default")}(this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing)&&(this.justFinishedMove=!0,this.annotationManager.finishMovingAnnotation(),this.canvas.style.cursor=this.annotationManager.selectedAnnotation?"move":"default"),this.annotationManager.isDrawing&&"rect"===this.annotationManager.operate.type&&(this.annotationManager.finishRectDrawing(),this.renderCallback())}handleMouseLeave(t){(this.isDragging||this.textManager.isTextMoving||this.annotationManager.isResizing||this.annotationManager.isMovingAnnotation)&&this.handleMouseUp(t)}handleDoubleClick(t){if(!this.getBgImage())return;this.justFinishedMove=!1;const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return void this.textManager.startEditing(e);if("polygon"===this.getDrawType()){if(this.annotationManager.isDrawing&&this.annotationManager.operate.data.length<=2)return;this.annotationManager.finishPolygonDrawing(),this.renderCallback()}}handleClick(t){var e;const n=this.getDrawType();if(!this.getBgImage()||"polygon"!==n||this.isDragging||this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing||this.textManager.isTextMoving)return;if(this.justDeselected)return void(this.justDeselected=!1);if("polygon"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type))return;const i=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.checkPolygonPointValid(i)&&(this.annotationManager.isDrawing?this.annotationManager.addPolygonPoint(i):this.annotationManager.startPolygonDrawing(i)),this.annotationManager.updatePolygonTempPoint(i),this.renderCallback()}handleKeyDown(t){if("z"===t.key&&(t.ctrlKey||t.metaKey))return this.annotationManager.withdraw()||this.textManager.withdraw(),void this.renderCallback();if(" "===t.key)return this.viewport.resetToInitial(),void this.renderCallback();if("Escape"!==t.key){if("Delete"===t.key||"Backspace"===t.key){if(null!==this.textManager.editingTextIndex)return this.textManager.removeTextAnnotation(this.textManager.editingTextIndex),void this.renderCallback();if(null!==this.textManager.selectedTextIndex)return this.textManager.deleteSelectedTextAnnotation(),void this.renderCallback();this.annotationManager.selectedAnnotation&&(this.annotationManager.deleteSelectedAnnotation(),this.renderCallback())}}else this.annotationManager.isDrawing?(this.annotationManager.cancelDrawing(),this.renderCallback()):this.annotationManager.selectedAnnotation&&(this.annotationManager.deselectAnnotation(),this.renderCallback())}}class g{constructor(t){this.canvas=document.createElement("canvas"),this.bgImage=null,this.bgImageSrc="",this.bgImageExt="jpeg",this.drawType="";const{drawType:e,useEvents:n=!0,id:i="container",annotationColor:s,lineStyle:a,vertexStyle:o,textStyle:g}=t,x=document.getElementById(i);if(!x)throw new Error(`Container with id "${i}" not found`);this.container=x,"absolute"!==x.style.position&&"fixed"!==x.style.position&&"relative"!==x.style.position&&(x.style.position="relative"),this.ctx=this.canvas.getContext("2d"),this.viewport=new r,this.viewport.setSize(x.clientWidth,x.clientHeight),this.canvas.width=this.viewport.width,this.canvas.height=this.viewport.height,this.canvas.style.width=`${this.viewport.width}px`,this.canvas.style.height=`${this.viewport.height}px`,this.canvas.style.cursor="default",this.canvas.style.display="block",x.appendChild(this.canvas),this.annotationManager=new h(this.viewport),s&&this.annotationManager.setColorConfig(s),a&&this.annotationManager.setLineStyle(a),o&&this.annotationManager.setVertexStyle(o),this.textManager=new l(this.viewport,this.container,this.ctx,()=>this.render()),g&&this.textManager.setTextStyle(g),this.renderer=new c(this.ctx,this.viewport,this.annotationManager,this.textManager,this.canvas),this.eventHandler=new d(this.canvas,this.viewport,this.annotationManager,this.textManager,this.renderer,()=>this.drawType,()=>this.bgImage,()=>this.render(),t=>this.setDrawType(t)),this.events={wheel:t=>this.eventHandler.handleWheel(t),mousedown:t=>this.eventHandler.handleMouseDown(t),mousemove:t=>this.eventHandler.handleMouseMove(t),mouseup:t=>this.eventHandler.handleMouseUp(t),mouseleave:t=>this.eventHandler.handleMouseLeave(t),dblclick:t=>this.eventHandler.handleDoubleClick(t),click:t=>this.eventHandler.handleClick(t),keydown:t=>this.eventHandler.handleKeyDown(t)},this.setDrawType(e||""),n&&this.addEventListeners()}setDrawType(t){"polygon"===this.drawType&&this.annotationManager.isDrawing&&this.annotationManager.operate.data.length>0?console.warn("Cannot switch mode while polygon is being drawn. Please complete or cancel the polygon first."):this.drawType!==t&&(this.annotationManager.operate.data=[],this.annotationManager.isDrawing=!1,this.annotationManager.tempPolygonPoint=null,""!==t&&this.deselectAnnotation(),this.annotationManager.operate.type=t,this.drawType=t,this.canvas.style.cursor="drag"===t?"grab":"text"===t?"text":"default")}drawImage(t){this.bgImageSrc=t,this.bgImageExt=i(t);const e=new Image;e.crossOrigin="anonymous",e.src=t,e.onload=()=>{this.bgImage=e,this.viewport.setOriginalSize(e.width,e.height),this.viewport.calculateInitialView(),this.render()}}render(){this.renderer.render(this.bgImage)}clear(t=""){this.annotationManager.clear(),this.textManager.clearTextAnnotations(),this.setDrawType(t),this.render()}withdraw(){const t=this.annotationManager.withdraw(),e=this.textManager.withdraw();(t||e)&&this.render()}resize(){const t=this.canvas.parentElement;t&&(this.viewport.setSize(t.clientWidth,t.clientHeight),this.canvas.width=this.viewport.width,this.canvas.height=this.viewport.height,this.canvas.style.width=`${this.viewport.width}px`,this.canvas.style.height=`${this.viewport.height}px`,this.render())}addEventListeners(){this.canvas.addEventListener("mousedown",this.events.mousedown),this.canvas.addEventListener("mousemove",this.events.mousemove),this.canvas.addEventListener("mouseup",this.events.mouseup),this.canvas.addEventListener("mouseleave",this.events.mouseleave),this.canvas.addEventListener("wheel",this.events.wheel),this.canvas.addEventListener("contextmenu",t=>t.preventDefault()),document.addEventListener("keydown",this.events.keydown),this.handleClickEventListener()}handleClickEventListener(){let t=null,e=0;this.canvas.addEventListener("click",n=>{if(this.eventHandler.isDragging||this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing||this.textManager.isTextMoving)return;const i=this.eventHandler.justFinishedMove;i&&(this.eventHandler.justFinishedMove=!1),e++,1===e?t=window.setTimeout(()=>{1!==e||i||this.events.click(n),e=0,t=null},300):2===e&&(this.eventHandler.justFinishedMove=!1,this.events.dblclick(n),e=0,null!==t&&(clearTimeout(t),t=null))})}changeScale(t){if(!this.bgImage)return;const e=o(this.viewport.scale,t),n=this.viewport.width/2,i=this.viewport.height/2,s=this.viewport.scale+e;this.viewport.updateScale(s,n,i),this.render()}clearCanvas(t=!1,e=!0){this.annotationManager.clear(),this.textManager.clearTextAnnotations(),this.textManager.deselectTextAnnotation(),!t&&e&&(this.bgImage=null,this.bgImageSrc="",this.viewport.setOriginalSize(0,0),this.viewport.reset()),this.ctx.clearRect(0,0,this.viewport.width,this.viewport.height),t&&this.bgImage&&this.render(),"drag"===this.drawType&&(this.canvas.style.cursor="default")}clearAnnotations(){this.clearCanvas(!0,!1)}getAnnotations(){return this.annotationManager.getAnnotations()}getTextAnnotations(){return this.textManager.getTextAnnotations()}exportAnnotationImage(){return new Promise((t,e)=>{if(!this.bgImage)return void e(new Error("No background image loaded"));const n=document.createElement("canvas");n.width=this.viewport.originalWidth,n.height=this.viewport.originalHeight;const i=n.getContext("2d");if(!i)return void e(new Error("Could not create offscreen canvas context"));const s=new Image;s.src=this.bgImageSrc,s.crossOrigin="anonymous",s.onload=()=>{try{this.renderer.drawForExport(i,s,this.viewport.originalWidth,this.viewport.originalHeight);const e=n.toDataURL(`image/${this.bgImageExt}`,.7);t(e)}catch(n){this.exportCurrentViewImage().then(t).catch(t=>e(new Error(`Both methods failed: ${t.message}`)))}},s.onerror=()=>{e(new Error("Failed to load image for export"))}})}exportCurrentViewImage(){return new Promise((t,e)=>{try{t(this.canvas.toDataURL(`image/${this.bgImageExt}`))}catch(t){e(new Error(`Failed to export current view: ${t.message}`))}})}base64ToFile(t,e){return s(t,e)}static base64ToBlob(t){return a(t)}addTextAnnotation(t,e,n=""){this.textManager.addTextAnnotation(t,e,n),this.render()}updateTextAnnotation(t,e){this.textManager.updateTextAnnotation(t,e)&&this.render()}moveTextAnnotation(t,e,n){this.textManager.moveTextAnnotation(t,e,n)&&this.render()}removeTextAnnotation(t){this.textManager.removeTextAnnotation(t)&&this.render()}clearTextAnnotations(){this.textManager.clearTextAnnotations(),this.render()}selectAnnotation(t){this.annotationManager.selectAnnotation(t)&&this.render()}deselectAnnotation(){this.annotationManager.deselectAnnotation(),this.textManager.deselectTextAnnotation(),this.textManager.editingTextIndex=null,this.render()}getSelectedAnnotation(){const t=this.annotationManager.getSelectedAnnotation();if(t)return t;const e=this.textManager.getSelectedTextAnnotation();return e?{index:e.index,type:"text",data:e.data}:null}deleteSelectedAnnotation(){if(null!==this.textManager.selectedTextIndex)return this.textManager.deleteSelectedTextAnnotation(),void this.render();this.annotationManager.deleteSelectedAnnotation()&&this.render()}moveSelectedAnnotation(t,e){this.annotationManager.moveSelectedAnnotation(t,e)&&this.render()}setAnnotationColor(t){this.annotationManager.setColorConfig(t),this.render()}getAnnotationColor(){return this.annotationManager.getColorConfig()}updateSelectedAnnotationStyle(){const t=this.annotationManager.selectedAnnotation;if(!t)return!1;const e=this.annotationManager.recordList[t.index];return!!e&&(e.style=this.annotationManager.getCurrentStyle(),this.render(),!0)}setLineStyle(t){this.annotationManager.setLineStyle(t),this.render()}getLineStyle(){return this.annotationManager.getLineStyle()}setVertexStyle(t){this.annotationManager.setVertexStyle(t),this.render()}getVertexStyle(){return this.annotationManager.getVertexStyle()}setTextStyle(t){this.textManager.setTextStyle(t),this.render()}setTextInputStyle(t){this.textManager.setInputStyle(t)}setTextSelectionStyle(t){this.textManager.setSelectionStyle(t),this.render()}destroy(){this.textManager.destroy(),this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas)}}t.AnnotationManager=h,t.Drawer=g,t.EventHandler=d,t.Renderer=c,t.TextAnnotationManager=l,t.ViewportManager=r,t.base64ToBlob=a,t.base64ToFile=s,t.default=g,t.getImageTypeFromUrl=i,t.getZoomDelta=o,t.isPointInPolygon=n,t.isPointInRect=e,Object.defineProperty(t,"__esModule",{value:!0})}),"undefined"!=typeof ImageAnnotationDrawer&&ImageAnnotationDrawer.default&&(ImageAnnotationDrawer=ImageAnnotationDrawer.default);
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).ImageAnnotationDrawer=t.ImageAnnotationDrawer||{})}(this,function(t){"use strict";function e(t,e){const i=e.width>=0?e.start.x:e.start.x+e.width,n=e.height>=0?e.start.y:e.start.y+e.height,s=Math.abs(e.width),a=Math.abs(e.height);return t.x>=i&&t.x<=i+s&&t.y>=n&&t.y<=n+a}function i(t,e){if(e.length<3)return!1;let i=!1;for(let n=0,s=e.length-1;n<e.length;s=n++){const a=e[n].point.x,o=e[n].point.y,r=e[s].point.x,h=e[s].point.y;o>t.y!=h>t.y&&t.x<(r-a)*(t.y-o)/(h-o)+a&&(i=!i)}return i}function n(t){try{const e=new URL(t).pathname.toLowerCase();return e.endsWith(".png")?"png":e.endsWith(".jpg")?"jpg":e.endsWith(".jpeg")?"jpeg":e.endsWith(".webp")?"webp":e.endsWith(".gif")?"gif":e.endsWith(".bmp")?"bmp":e.endsWith(".svg")?"svg":"jpeg"}catch(t){return"jpeg"}}function s(t,e){const i=t.split(";base64,"),n=i[0].split(":")[1],s=window.atob(i[1]),a=s.length,o=new Uint8Array(a);for(let t=0;t<a;++t)o[t]=s.charCodeAt(t);const r=new Blob([o],{type:n});return new File([r],e,{type:n})}function a(t){return new Promise((e,i)=>{const n=t.split(";base64,");if(n.length<2)return void i(new Error("Invalid base64 string"));const s=n[0].split(":")[1],a=atob(n[1]),o=new ArrayBuffer(a.length),r=new Uint8Array(o);for(let t=0;t<a.length;t++)r[t]=a.charCodeAt(t);e(new Blob([o],{type:s}))})}function o(t,e){return t<2?e?.1:-.1:t<5?e?.05:-.05:e?.02:-.02}class r{constructor(){this.width=0,this.height=0,this.originalWidth=0,this.originalHeight=0,this.scale=1,this.minScale=1,this.maxScale=10,this.offset={x:0,y:0},this.initialScale=1,this.initialOffset={x:0,y:0},this.isInitialScale=!0,this.minOffsetX=0,this.minOffsetY=0,this.maxOffsetX=0,this.maxOffsetY=0}setSize(t,e){this.width=t,this.height=e}setOriginalSize(t,e){this.originalWidth=t,this.originalHeight=e}calculateInitialView(){if(0===this.originalWidth||0===this.originalHeight)return;const t=this.width/this.originalWidth,e=this.height/this.originalHeight;this.scale=Math.min(t,e),this.initialScale=this.scale,this.minScale=this.initialScale,this.offset={x:(this.width-this.originalWidth*this.scale)/2,y:(this.height-this.originalHeight*this.scale)/2},this.initialOffset=Object.assign({},this.offset),this.calculateBoundaries(),this.isInitialScale=!0}calculateBoundaries(){const t=this.originalWidth*this.scale,e=this.originalHeight*this.scale;this.maxOffsetX=0,this.maxOffsetY=0,this.minOffsetX=this.width-t,this.minOffsetY=this.height-e,this.minOffsetX>this.maxOffsetX&&([this.minOffsetX,this.maxOffsetX]=[this.maxOffsetX,this.minOffsetX]),this.minOffsetY>this.maxOffsetY&&([this.minOffsetY,this.maxOffsetY]=[this.maxOffsetY,this.minOffsetY])}constrainViewport(){this.offset.x>this.maxOffsetX?this.offset.x=this.maxOffsetX:this.offset.x<this.minOffsetX&&(this.offset.x=this.minOffsetX),this.offset.y>this.maxOffsetY?this.offset.y=this.maxOffsetY:this.offset.y<this.minOffsetY&&(this.offset.y=this.minOffsetY)}toImageCoordinates(t,e){return{x:(t-this.offset.x)/this.scale,y:(e-this.offset.y)/this.scale}}toCanvasCoordinates(t,e){return{x:this.offset.x+t*this.scale,y:this.offset.y+e*this.scale}}updateScale(t,e,i){const n=this.scale,s=(e-this.offset.x)/n,a=(i-this.offset.y)/n;this.scale=Math.max(this.minScale,Math.min(t,this.maxScale));const o=s*this.scale+this.offset.x,r=a*this.scale+this.offset.y;this.offset.x+=e-o,this.offset.y+=i-r,this.calculateBoundaries(),this.constrainViewport(),this.isInitialScale=this.scale===this.initialScale}resetToInitial(){this.scale=this.initialScale,this.offset=Object.assign({},this.initialOffset),this.calculateBoundaries(),this.isInitialScale=!0}updateOffset(t,e){this.offset.x+=t,this.offset.y+=e,this.constrainViewport()}reset(){this.scale=1,this.minScale=1,this.offset={x:0,y:0},this.initialScale=1,this.initialOffset={x:0,y:0},this.isInitialScale=!0,this.minOffsetX=0,this.minOffsetY=0,this.maxOffsetX=0,this.maxOffsetY=0}}class h{constructor(t,e,i){this.viewport=t,this.container=e,this.renderCallback=i,this.recordList=[],this.operate={type:"rect",data:[],status:"pending"},this.isDrawing=!1,this.drawStartPoint={x:0,y:0},this.tempPolygonPoint=null,this.selectedAnnotation=null,this.isMovingAnnotation=!1,this.isResizing=!1,this.activeHandle=null,this.annotationMoveStart={x:0,y:0},this.originalRect=null,this.originalPolygon=null,this.deleteHistory=[],this.editingTitleIndex=null,this.titleInput=null,this.strokeStyle="red",this.lineWidth=5,this.lineStyle="solid",this.vertexStyle={size:8,fillColor:"red",strokeColor:"white",strokeWidth:2,shape:"circle"},this.selectionStyle={strokeColor:"#00D9FF",fillColor:"rgba(0,217,255,0.15)",handleSize:12,handleColor:"#00D9FF"},this.colorConfig={rect:"red",polygon:"red",default:"red"},this.titleStyle={font:"12px Arial",color:"#FFFFFF",backgroundColor:"rgba(0, 0, 0, 0.7)",paddingX:6,paddingY:3,borderRadius:4,placeholder:"输入标题..."},this.titlePosition={vertical:"top",align:"center",offsetX:0,offsetY:0},this.enableTitle=!1,this.applyColorConfig()}initTitleSupport(){this.container&&this.enableTitle&&this.createTitleInput()}setColorConfig(t){this.colorConfig="string"==typeof t?{rect:t,polygon:t,default:t}:Object.assign(Object.assign({},this.colorConfig),t),this.applyColorConfig()}getColorConfig(){return Object.assign({},this.colorConfig)}applyColorConfig(){this.strokeStyle=this.colorConfig.default||"red"}getColorForType(t){return this.colorConfig[t]||this.colorConfig.default||"red"}getCurrentStyle(){return{strokeColor:this.strokeStyle,lineWidth:this.lineWidth,lineStyle:this.lineStyle,vertexStyle:Object.assign({},this.vertexStyle)}}setLineStyle(t){this.lineStyle=t}setVertexStyle(t){this.vertexStyle=Object.assign(Object.assign({},this.vertexStyle),t),t.fillColor&&(this.strokeStyle=t.fillColor)}getLineStyle(){return this.lineStyle}getVertexStyle(){return Object.assign({},this.vertexStyle)}getAnnotationStyle(t){return t.style||this.getCurrentStyle()}startRectDrawing(t){this.isDrawing=!0,this.drawStartPoint=t,this.operate={type:"rect",data:[{start:Object.assign({},t),width:0,height:0}],status:"pending"}}updateRectDrawing(t){if(!this.isDrawing||"rect"!==this.operate.type)return;const e=this.operate.data[0];e.width=t.x-this.drawStartPoint.x,e.height=t.y-this.drawStartPoint.y}finishRectDrawing(){if(!this.isDrawing||"rect"!==this.operate.type)return!1;const t=this.operate.data[0];if(Math.abs(t.width)>5&&Math.abs(t.height)>5){const t=Object.assign(Object.assign({},this.operate),{style:this.getCurrentStyle()});this.recordList.push(t),this.deleteHistory=[]}return this.operate.data=[],this.isDrawing=!1,!0}startPolygonDrawing(t){this.isDrawing=!0,this.operate={type:"polygon",data:[{point:t}],status:"pending"},this.tempPolygonPoint=t}addPolygonPoint(t){if(!this.isDrawing||"polygon"!==this.operate.type)return!1;const e=this.operate.data.length>0?this.operate.data[this.operate.data.length-1]:null;return!(e&&Math.abs(e.point.x-t.x)<=5&&Math.abs(e.point.y-t.y)<=5)&&(this.operate.data.push({point:t}),!0)}updatePolygonTempPoint(t){this.tempPolygonPoint=t}finishPolygonDrawing(){if(!this.isDrawing||"polygon"!==this.operate.type)return!1;if(this.operate.data.length>=3){this.operate.status="fullfilled";const t=Object.assign(Object.assign({},this.operate),{style:this.getCurrentStyle()});this.recordList.push(t),this.deleteHistory=[]}return this.operate={type:"polygon",data:[],status:"pending"},this.isDrawing=!1,this.tempPolygonPoint=null,!0}cancelDrawing(){if(this.isDrawing&&"polygon"===this.operate.type&&this.operate.data.length>=2){this.operate.status="fullfilled";const t=Object.assign(Object.assign({},this.operate),{style:this.getCurrentStyle()});this.recordList.push(t)}this.operate.data=[],this.isDrawing=!1,this.tempPolygonPoint=null}withdraw(){if(this.operate.data.length>0)return"polygon"===this.operate.type?(this.operate.data.pop(),0===this.operate.data.length&&(this.isDrawing=!1)):(this.operate.data=[],this.isDrawing=!1),!0;if(this.deleteHistory.length>0){const{annotation:t,index:e}=this.deleteHistory.pop();return this.recordList.splice(e,0,t),!0}return this.recordList.length>0&&(this.recordList.pop(),!0)}selectAnnotation(t){if(t<0||t>=this.recordList.length)return this.deselectAnnotation(),!1;const e=this.recordList[t];return this.selectedAnnotation={index:t,type:e.type},!0}deselectAnnotation(){this.selectedAnnotation=null,this.isMovingAnnotation=!1,this.isResizing=!1,this.activeHandle=null,this.originalRect=null,this.originalPolygon=null}deleteSelectedAnnotation(){if(!this.selectedAnnotation)return!1;const t=this.selectedAnnotation.index,e=this.recordList[t];return this.deleteHistory.push({annotation:Object.assign({},e),index:t}),this.recordList.splice(t,1),this.deselectAnnotation(),!0}startMovingAnnotation(t){return!!this.selectedAnnotation&&(this.isMovingAnnotation=!0,this.annotationMoveStart={x:t.clientX,y:t.clientY},!0)}moveSelectedAnnotation(t,e){if(!this.selectedAnnotation)return!1;const i=this.recordList[this.selectedAnnotation.index];if(!i)return!1;if("rect"===i.type){const n=i.data[0];n.start.x+=t,n.start.y+=e}else if("polygon"===i.type){i.data.forEach(i=>{i.point.x+=t,i.point.y+=e})}return!0}finishMovingAnnotation(){this.isMovingAnnotation=!1,this.isResizing=!1,this.activeHandle=null,this.originalRect=null,this.originalPolygon=null}startResizing(t,e){if(!this.selectedAnnotation)return!1;this.activeHandle=t,this.isResizing=!0;const i=this.recordList[this.selectedAnnotation.index];return"rect"===i.type?this.originalRect=Object.assign({},i.data[0]):"polygon"===i.type&&(this.originalPolygon=i.data.map(t=>({point:Object.assign({},t.point)}))),!0}resizeRect(t){if(!this.selectedAnnotation||"rect"!==this.selectedAnnotation.type||!this.originalRect||!this.activeHandle)return!1;const e=this.recordList[this.selectedAnnotation.index].data[0],i=this.originalRect,n=i.start.x,s=i.start.x+i.width,a=i.start.y,o=i.start.y+i.height;let r,h;switch(this.activeHandle.index){case 0:r=s,h=o;break;case 1:r=n,h=o;break;case 2:r=s,h=a;break;case 3:r=n,h=a;break;default:return!1}const l=Math.min(t.x,r),c=Math.max(t.x,r),d=Math.min(t.y,h),g=Math.max(t.y,h);e.start.x=l,e.start.y=d,e.width=c-l,e.height=g-d;const x=t.x<r,f=t.y<h;return 0===this.activeHandle.index?x||f?!x&&f?this.activeHandle.index=1:x&&!f&&(this.activeHandle.index=2):this.activeHandle.index=3:1===this.activeHandle.index?x&&!f?this.activeHandle.index=2:x&&f?this.activeHandle.index=0:x||f||(this.activeHandle.index=3):2===this.activeHandle.index?!x&&f?this.activeHandle.index=1:x&&f?this.activeHandle.index=0:x||f||(this.activeHandle.index=3):3===this.activeHandle.index&&(x&&f?this.activeHandle.index=0:!x&&f?this.activeHandle.index=1:x&&!f&&(this.activeHandle.index=2)),this.originalRect=Object.assign({},e),!0}resizePolygon(t){if(!this.selectedAnnotation||"polygon"!==this.selectedAnnotation.type||!this.activeHandle)return!1;const e=this.recordList[this.selectedAnnotation.index].data,i=this.activeHandle.index;return i>=0&&i<e.length&&(e[i].point.x=t.x,e[i].point.y=t.y,!0)}getAnnotationAtPoint(t){for(let n=this.recordList.length-1;n>=0;n--){const s=this.recordList[n];if("rect"===s.type){if(e(t,s.data[0]))return{index:n,type:"rect"}}else if("polygon"===s.type){if(i(t,s.data))return{index:n,type:"polygon"}}}return null}getHandleAtPoint(t,e){if(!this.selectedAnnotation)return null;const i=this.recordList[this.selectedAnnotation.index],n=this.selectionStyle.handleSize/2;if("rect"===i.type){const s=i.data[0],a=this.viewport.offset.x+s.start.x*this.viewport.scale,o=this.viewport.offset.y+s.start.y*this.viewport.scale,r=s.width*this.viewport.scale,h=s.height*this.viewport.scale,l=[{x:a,y:o,index:0},{x:a+r,y:o,index:1},{x:a,y:o+h,index:2},{x:a+r,y:o+h,index:3}];for(const i of l)if(t>=i.x-n&&t<=i.x+n&&e>=i.y-n&&e<=i.y+n)return{type:"rect-corner",index:i.index}}else if("polygon"===i.type){const s=i.data;for(let i=0;i<s.length;i++){const a=s[i],o=this.viewport.offset.x+a.point.x*this.viewport.scale,r=this.viewport.offset.y+a.point.y*this.viewport.scale;if(t>=o-n&&t<=o+n&&e>=r-n&&e<=r+n)return{type:"polygon-vertex",index:i}}}return null}clear(){this.recordList=[],this.operate={type:"rect",data:[],status:"pending"},this.isDrawing=!1,this.tempPolygonPoint=null,this.deselectAnnotation(),this.deleteHistory=[]}getAnnotations(){return[...this.recordList]}getSelectedAnnotation(){if(!this.selectedAnnotation)return null;const t=this.recordList[this.selectedAnnotation.index];return t?{index:this.selectedAnnotation.index,type:this.selectedAnnotation.type,data:t}:null}setTitleStyle(t){this.titleStyle=Object.assign(Object.assign({},this.titleStyle),t)}getTitleStyle(){return Object.assign({},this.titleStyle)}setTitlePosition(t){this.titlePosition=Object.assign(Object.assign({},this.titlePosition),t)}getTitlePosition(){return Object.assign({},this.titlePosition)}getEffectiveTitleStyle(t){const e=this.getAnnotationStyle(t);return Object.assign(Object.assign({},this.titleStyle),e.titleStyle||{})}getEffectiveTitlePosition(t){const e=this.getAnnotationStyle(t);return Object.assign(Object.assign({},this.titlePosition),e.titlePosition||{})}captureTitleStyleSnapshot(t){t.style||(t.style=this.getCurrentStyle()),t.style.titleStyle=Object.assign({},this.titleStyle),t.style.titlePosition=Object.assign({},this.titlePosition)}setAnnotationTitleStyle(t,e){if(t<0||t>=this.recordList.length)return!1;const i=this.recordList[t];return i.style||(i.style=this.getCurrentStyle()),i.style.titleStyle=Object.assign(Object.assign({},i.style.titleStyle||this.titleStyle),e),!0}setAnnotationTitlePosition(t,e){if(t<0||t>=this.recordList.length)return!1;const i=this.recordList[t];return i.style||(i.style=this.getCurrentStyle()),i.style.titlePosition=Object.assign(Object.assign({},i.style.titlePosition||this.titlePosition),e),!0}createTitleInput(){this.container&&(this.titleInput=document.createElement("input"),this.titleInput.type="text",this.titleInput.style.position="absolute",this.titleInput.style.zIndex="99998",this.titleInput.style.display="none",this.titleInput.style.outline="none",this.titleInput.style.border="1px solid #00D9FF",this.titleInput.style.borderRadius="4px",this.titleInput.style.padding="4px 8px",this.titleInput.style.fontSize="13px",this.titleInput.style.fontFamily="Arial, sans-serif",this.titleInput.style.backgroundColor="#ffffff",this.titleInput.style.color="#333",this.titleInput.style.boxShadow="0 2px 8px rgba(0,0,0,0.3)",this.titleInput.style.minWidth="80px",this.titleInput.style.maxWidth="200px",this.titleInput.placeholder=this.titleStyle.placeholder||"输入标题...",this.titleInput.addEventListener("blur",()=>{this.finishTitleEditing()}),this.titleInput.addEventListener("keydown",t=>{t.stopPropagation(),"Enter"===t.key?this.finishTitleEditing():"Escape"===t.key&&(t.preventDefault(),this.cancelTitleEditing())}),this.container.appendChild(this.titleInput))}setTitle(t,e){if(t<0||t>=this.recordList.length)return!1;const i=this.recordList[t];return i.title=e||void 0,e&&this.captureTitleStyleSnapshot(i),!0}getTitle(t){if(!(t<0||t>=this.recordList.length))return this.recordList[t].title}startTitleEditing(t){if(!this.enableTitle||!this.titleInput||t<0||t>=this.recordList.length)return!1;const e=this.recordList[t];let i,n;if(this.editingTitleIndex=t,this.deselectAnnotation(),"rect"===e.type){const t=e.data[0];i=this.viewport.offset.x+t.start.x*this.viewport.scale,n=this.viewport.offset.y+t.start.y*this.viewport.scale}else{const t=e.data[0];i=this.viewport.offset.x+t.point.x*this.viewport.scale,n=this.viewport.offset.y+t.point.y*this.viewport.scale}return this.titleInput.value=e.title||"",this.titleInput.placeholder=this.getEffectiveTitleStyle(e).placeholder||"输入标题...",this.titleInput.style.left=`${i}px`,this.titleInput.style.top=n-32+"px",this.titleInput.style.display="block",setTimeout(()=>{this.titleInput&&(this.titleInput.focus(),this.titleInput.select())},10),!0}finishTitleEditing(){if(!this.titleInput||null===this.editingTitleIndex)return!1;const t=this.editingTitleIndex,e=this.titleInput.value.trim();if(t>=0&&t<this.recordList.length){const i=this.recordList[t];i.title=e||void 0,e&&this.captureTitleStyleSnapshot(i),this.selectAnnotation(t)}return this.resetTitleEditingState(),this.renderCallback&&this.renderCallback(),!0}cancelTitleEditing(){if(!this.titleInput||null===this.editingTitleIndex)return!1;const t=this.editingTitleIndex;return t>=0&&t<this.recordList.length&&this.selectAnnotation(t),this.resetTitleEditingState(),this.renderCallback&&this.renderCallback(),!0}resetTitleEditingState(){this.titleInput&&(this.titleInput.style.display="none"),this.editingTitleIndex=null}destroyTitleInput(){this.titleInput&&this.titleInput.parentNode&&(this.titleInput.parentNode.removeChild(this.titleInput),this.titleInput=null)}}class l{constructor(t,e,i,n){this.viewport=t,this.container=e,this.ctx=i,this.renderCallback=n,this.textAnnotations=[],this.editingTextIndex=null,this.currentEditingIndex=null,this.isTextMoving=!1,this.textMoveStart={x:0,y:0},this.textBeforeEditing="",this.selectedTextIndex=null,this.textInput=null,this.deleteHistory=[],this.defaultInputStyle={border:"2px solid #00D9FF",borderRadius:"6px",padding:"6px 10px",fontSize:"16px",fontFamily:"Arial, sans-serif",backgroundColor:"#ffffff",color:"#333",boxShadow:"0 4px 12px rgba(0,0,0,0.3)",minWidth:"60px",maxWidth:"200px"},this.textStyle={font:"16px Arial",color:"#FFD700",padding:6,backgroundColor:"rgba(0,0,0,0.6)",borderRadius:4,selectedBorderColor:"#00D9FF",selectedBackgroundColor:"rgba(0,217,255,0.15)",inputStyle:Object.assign({},this.defaultInputStyle)},this.createTextInput()}createTextInput(){this.textInput=document.createElement("input"),this.textInput.type="text",this.textInput.style.position="absolute",this.textInput.style.zIndex="99999",this.textInput.style.display="none",this.textInput.style.outline="none",this.textInput.style.width="fit-content",this.textInput.placeholder="输入文字...",this.applyInputStyle(),this.textInput.addEventListener("blur",()=>{this.finishEditing(),this.renderCallback&&this.renderCallback()}),this.textInput.addEventListener("keydown",t=>{t.stopPropagation(),"Enter"===t.key?(this.finishEditing(),this.renderCallback&&this.renderCallback()):"Escape"===t.key?(t.preventDefault(),this.cancelEditing()):"Delete"===t.key&&""===this.textInput.value&&(t.preventDefault(),this.deleteEditingAnnotation())}),this.textInput.addEventListener("input",()=>{this.adjustInputWidth()}),this.container.appendChild(this.textInput)}applyInputStyle(){if(!this.textInput)return;const t=Object.assign(Object.assign({},this.defaultInputStyle),this.textStyle.inputStyle);this.textInput.style.border=t.border,this.textInput.style.borderRadius=t.borderRadius,this.textInput.style.padding=t.padding,this.textInput.style.fontSize=t.fontSize,this.textInput.style.fontFamily=t.fontFamily,this.textInput.style.backgroundColor=t.backgroundColor,this.textInput.style.color=t.color,this.textInput.style.boxShadow=t.boxShadow,this.textInput.style.minWidth=t.minWidth,this.textInput.style.maxWidth=t.maxWidth}setTextStyle(t){this.textStyle=Object.assign(Object.assign({},this.textStyle),t),this.applyInputStyle()}setInputStyle(t){this.textStyle.inputStyle=Object.assign(Object.assign({},this.textStyle.inputStyle),t),this.applyInputStyle()}setSelectionStyle(t){void 0!==t.selectedBorderColor&&(this.textStyle.selectedBorderColor=t.selectedBorderColor),void 0!==t.selectedBackgroundColor&&(this.textStyle.selectedBackgroundColor=t.selectedBackgroundColor)}getCurrentStyle(){return{font:this.textStyle.font,color:this.textStyle.color,backgroundColor:this.textStyle.backgroundColor}}updateTextAnnotationStyle(t,e){if(t<0||t>=this.textAnnotations.length)return!1;const i=this.textAnnotations[t];return i.style=Object.assign(Object.assign({},i.style||this.getCurrentStyle()),e),!0}addTextAnnotation(t,e,i=""){var n;this.ctx.font=this.textStyle.font;const s=i||"输入文字...",a=this.ctx.measureText(s);let o;if(a.actualBoundingBoxAscent&&a.actualBoundingBoxDescent)o=a.actualBoundingBoxAscent+a.actualBoundingBoxDescent;else{o=1.2*parseInt((null===(n=this.textStyle.font.match(/\d+/))||void 0===n?void 0:n[0])||"16")}const r={position:{x:t,y:e},text:i,style:this.getCurrentStyle(),width:Math.max(a.width,60),height:o};this.textAnnotations.push(r);const h=this.textAnnotations.length-1;return this.deleteHistory=[],this.startEditing(h),h}updateTextAnnotation(t,e){if(t<0||t>=this.textAnnotations.length)return!1;const i=this.textAnnotations[t];i.text=e,this.ctx.font=this.textStyle.font;const n=this.ctx.measureText(e);return i.width=n.width,i.height=1.2*parseInt(this.textStyle.font),!0}moveTextAnnotation(t,e,i){return!(t<0||t>=this.textAnnotations.length)&&(this.textAnnotations[t].position={x:e,y:i},!0)}removeTextAnnotation(t){if(t<0||t>=this.textAnnotations.length)return!1;const e=this.textAnnotations[t];return this.deleteHistory.push({annotation:Object.assign({},e),index:t}),this.textAnnotations.splice(t,1),this.editingTextIndex===t?this.cancelEditing():null!==this.editingTextIndex&&this.editingTextIndex>t&&this.editingTextIndex--,!0}clearTextAnnotations(){this.textAnnotations=[],this.selectedTextIndex=null,this.cancelEditing(),this.deleteHistory=[]}startEditing(t){if(!this.textInput||t<0||t>=this.textAnnotations.length)return!1;const e=this.textAnnotations[t];this.editingTextIndex=t,this.currentEditingIndex=t,this.textBeforeEditing=e.text;const i=this.viewport.offset.x+e.position.x*this.viewport.scale,n=this.viewport.offset.y+e.position.y*this.viewport.scale;return this.textInput.value=e.text,this.textInput.style.left=`${i}px`,this.textInput.style.top=`${n}px`,this.textInput.style.display="block",this.adjustInputWidth(),setTimeout(()=>{this.textInput&&(this.textInput.focus(),this.textInput.select())},10),!0}finishEditing(){if(!this.textInput)return!1;const t=this.currentEditingIndex,e=this.textInput.value.trim();if(null===t||t<0||t>=this.textAnnotations.length)return this.resetEditingState(),!1;if(""===e)this.textAnnotations.splice(t,1);else{const i=this.textAnnotations[t];i.text=e,this.ctx.font=this.textStyle.font;const n=this.ctx.measureText(i.text);i.width=n.width,i.height=1.2*parseInt(this.textStyle.font)}return this.resetEditingState(),!0}cancelEditing(){if(!this.textInput||null===this.editingTextIndex)return!1;this.textInput.value=this.textBeforeEditing;const t=this.editingTextIndex;if(t>=0&&t<this.textAnnotations.length){""===this.textAnnotations[t].text&&""===this.textBeforeEditing&&this.textAnnotations.splice(t,1)}return this.resetEditingState(),!0}deleteEditingAnnotation(){if(null===this.editingTextIndex)return!1;const t=this.editingTextIndex;return this.textInput&&(this.textInput.style.display="none"),t>=0&&t<this.textAnnotations.length&&this.textAnnotations.splice(t,1),this.resetEditingState(),!0}resetEditingState(){this.textInput&&(this.textInput.style.display="none"),this.editingTextIndex=null,this.currentEditingIndex=null,this.textBeforeEditing=""}adjustInputWidth(){if(!this.textInput||null===this.editingTextIndex)return;this.ctx.font=this.textStyle.font;const t=this.textInput.value||this.textInput.placeholder||"",e=this.ctx.measureText(t).width,i=Math.min(Math.max(e+24,60),200);this.textInput.style.width=`${i}px`}startMoving(t,e){return!(e<0||e>=this.textAnnotations.length)&&(this.editingTextIndex=e,this.isTextMoving=!0,this.textMoveStart={x:t.clientX,y:t.clientY},!0)}moveAnnotation(t){if(!this.isTextMoving||null===this.editingTextIndex)return!1;const e=t.clientX-this.textMoveStart.x,i=t.clientY-this.textMoveStart.y,n=this.textAnnotations[this.editingTextIndex];return!!n&&(n.position.x+=e/this.viewport.scale,n.position.y+=i/this.viewport.scale,this.textMoveStart={x:t.clientX,y:t.clientY},!0)}finishMoving(){this.isTextMoving=!1}checkTextClick(t,e){for(let i=this.textAnnotations.length-1;i>=0;i--){const n=this.textAnnotations[i],s=this.viewport.offset.x+n.position.x*this.viewport.scale,a=this.viewport.offset.y+n.position.y*this.viewport.scale,o=Math.max(n.width,60)+2*this.textStyle.padding,r=n.height+2*this.textStyle.padding;if(t>=s&&t<=s+o&&e>=a&&e<=a+r)return i}return null}checkTextClickForMove(t){const e=this.checkTextClick(t.offsetX,t.offsetY);return null!==e?(this.startMoving(t,e),{index:e,handled:!0}):{index:-1,handled:!1}}getTextAnnotations(){return[...this.textAnnotations]}withdraw(){if(this.deleteHistory.length>0){const{annotation:t,index:e}=this.deleteHistory.pop();return this.textAnnotations.splice(e,0,t),!0}return this.textAnnotations.length>0&&(this.textAnnotations.pop(),!0)}selectTextAnnotation(t){return t<0||t>=this.textAnnotations.length?(this.deselectTextAnnotation(),!1):(this.selectedTextIndex=t,null!==this.editingTextIndex&&this.finishEditing(),!0)}deselectTextAnnotation(){this.selectedTextIndex=null}deleteSelectedTextAnnotation(){if(null===this.selectedTextIndex)return!1;const t=this.selectedTextIndex,e=this.textAnnotations[t];return this.deleteHistory.push({annotation:Object.assign({},e),index:t}),this.textAnnotations.splice(t,1),this.selectedTextIndex=null,null!==this.editingTextIndex&&this.editingTextIndex>t&&this.editingTextIndex--,!0}getSelectedTextAnnotation(){if(null===this.selectedTextIndex)return null;const t=this.textAnnotations[this.selectedTextIndex];return t?{index:this.selectedTextIndex,data:t}:null}destroy(){this.textInput&&this.textInput.parentNode&&this.textInput.parentNode.removeChild(this.textInput)}}class c{constructor(t,e,i,n,s){this.ctx=t,this.viewport=e,this.annotationManager=i,this.textManager=n,this.canvas=s}render(t){this.ctx.clearRect(0,0,this.viewport.width,this.viewport.height),t&&this.drawBackgroundImage(t),this.drawAnnotations(),this.drawTextAnnotations()}drawBackgroundImage(t){this.ctx.drawImage(t,this.viewport.offset.x,this.viewport.offset.y,this.viewport.originalWidth*this.viewport.scale,this.viewport.originalHeight*this.viewport.scale)}drawAnnotations(){this.annotationManager.recordList.forEach((t,e)=>{var i;const n=this.annotationManager.getAnnotationStyle(t);this.ctx.strokeStyle=n.strokeColor,this.ctx.lineWidth=n.lineWidth,this.setLineDash(n.lineStyle||"solid"),this.ctx.fillStyle=this.ctx.strokeStyle;const s=(null===(i=this.annotationManager.selectedAnnotation)||void 0===i?void 0:i.index)===e;if("rect"===t.type)this.drawRect(t.data[0],s);else if("polygon"===t.type){const e=n.vertexStyle||this.annotationManager.getVertexStyle();this.drawPolygon(t.data,"fullfilled"===t.status,e)}this.annotationManager.enableTitle&&t.title&&this.drawAnnotationTitle(t)}),this.annotationManager.operate.data.length>0&&(this.ctx.strokeStyle=this.annotationManager.strokeStyle,this.ctx.lineWidth=this.annotationManager.lineWidth,this.ctx.fillStyle=this.ctx.strokeStyle,this.setLineDash(this.annotationManager.lineStyle),"rect"===this.annotationManager.operate.type?this.drawRect(this.annotationManager.operate.data[0],!1):"polygon"===this.annotationManager.operate.type&&this.drawCurrentPolygon()),this.ctx.setLineDash([]),this.drawSelectionHighlight()}setLineDash(t){switch(t){case"dashed":this.ctx.setLineDash([10,5]);break;case"dotted":this.ctx.setLineDash([2,4]);break;default:this.ctx.setLineDash([])}}drawRect(t,e=!1){this.ctx.strokeRect(this.viewport.offset.x+t.start.x*this.viewport.scale,this.viewport.offset.y+t.start.y*this.viewport.scale,t.width*this.viewport.scale,t.height*this.viewport.scale)}drawPolygon(t,e,i){if(0!==t.length){if(this.ctx.beginPath(),t.forEach((t,e)=>{const i=Math.round(this.viewport.offset.x+t.point.x*this.viewport.scale),n=Math.round(this.viewport.offset.y+t.point.y*this.viewport.scale);0===e?this.ctx.moveTo(i,n):this.ctx.lineTo(i,n)}),e&&t.length>1){const e=t[0];this.ctx.lineTo(this.viewport.offset.x+e.point.x*this.viewport.scale,this.viewport.offset.y+e.point.y*this.viewport.scale)}this.ctx.stroke(),this.drawPolygonVertices(t,i)}}drawPolygonVertices(t,e){const i=e||this.annotationManager.getVertexStyle(),n=i.size/2;t.forEach(t=>{const e=Math.round(this.viewport.offset.x+t.point.x*this.viewport.scale),s=Math.round(this.viewport.offset.y+t.point.y*this.viewport.scale);switch(this.ctx.fillStyle=i.fillColor,this.ctx.strokeStyle=i.strokeColor,this.ctx.lineWidth=i.strokeWidth,this.ctx.beginPath(),i.shape){case"square":this.ctx.rect(e-n,s-n,2*n,2*n);break;case"diamond":this.ctx.moveTo(e,s-n),this.ctx.lineTo(e+n,s),this.ctx.lineTo(e,s+n),this.ctx.lineTo(e-n,s),this.ctx.closePath();break;default:this.ctx.arc(e,s,n,0,2*Math.PI)}this.ctx.fill(),i.strokeWidth>0&&this.ctx.stroke()})}drawCurrentPolygon(){const t=this.annotationManager.operate.data,e=this.annotationManager.tempPolygonPoint;if(this.ctx.beginPath(),t.forEach((t,e)=>{const i=Math.round(this.viewport.offset.x+t.point.x*this.viewport.scale),n=Math.round(this.viewport.offset.y+t.point.y*this.viewport.scale);0===e?this.ctx.moveTo(i,n):this.ctx.lineTo(i,n)}),e){const t=Math.round(this.viewport.offset.x+e.x*this.viewport.scale),i=Math.round(this.viewport.offset.y+e.y*this.viewport.scale);this.ctx.lineTo(t,i)}if(this.ctx.stroke(),this.drawPolygonVertices(t,this.annotationManager.getVertexStyle()),e){const t=this.annotationManager.getVertexStyle(),i=Math.round(this.viewport.offset.x+e.x*this.viewport.scale),n=Math.round(this.viewport.offset.y+e.y*this.viewport.scale),s=t.size/2;switch(this.ctx.fillStyle=t.fillColor,this.ctx.strokeStyle=t.strokeColor,this.ctx.lineWidth=t.strokeWidth,this.ctx.beginPath(),t.shape){case"square":this.ctx.rect(i-s,n-s,2*s,2*s);break;case"diamond":this.ctx.moveTo(i,n-s),this.ctx.lineTo(i+s,n),this.ctx.lineTo(i,n+s),this.ctx.lineTo(i-s,n),this.ctx.closePath();break;default:this.ctx.arc(i,n,s,0,2*Math.PI)}this.ctx.fill(),t.strokeWidth>0&&this.ctx.stroke()}}drawSelectionHighlight(){const t=this.annotationManager.selectedAnnotation;if(!t)return;const e=this.annotationManager.recordList[t.index];if(!e)return;const i=this.annotationManager.selectionStyle;this.ctx.save(),"rect"===e.type?this.drawRectSelectionHighlight(e.data[0],i):"polygon"===e.type&&this.drawPolygonSelectionHighlight(e.data,i),this.ctx.restore()}drawRectSelectionHighlight(t,e){const i=this.viewport.offset.x+t.start.x*this.viewport.scale,n=this.viewport.offset.y+t.start.y*this.viewport.scale,s=t.width*this.viewport.scale,a=t.height*this.viewport.scale;this.ctx.fillStyle=e.fillColor,this.ctx.fillRect(i,n,s,a),this.ctx.strokeStyle=e.strokeColor,this.ctx.lineWidth=2,this.ctx.setLineDash([5,5]),this.ctx.strokeRect(i,n,s,a);const o=e.handleSize;this.ctx.fillStyle=e.handleColor,this.ctx.setLineDash([]),this.ctx.fillRect(i-o/2,n-o/2,o,o),this.ctx.fillRect(i+s-o/2,n-o/2,o,o),this.ctx.fillRect(i-o/2,n+a-o/2,o,o),this.ctx.fillRect(i+s-o/2,n+a-o/2,o,o)}drawPolygonSelectionHighlight(t,e){this.ctx.beginPath(),t.forEach((t,e)=>{const i=this.viewport.offset.x+t.point.x*this.viewport.scale,n=this.viewport.offset.y+t.point.y*this.viewport.scale;0===e?this.ctx.moveTo(i,n):this.ctx.lineTo(i,n)}),this.ctx.closePath(),this.ctx.fillStyle=e.fillColor,this.ctx.fill(),this.ctx.strokeStyle=e.strokeColor,this.ctx.lineWidth=2,this.ctx.setLineDash([5,5]),this.ctx.stroke();const i=e.handleSize;this.ctx.fillStyle=e.handleColor,this.ctx.setLineDash([]),t.forEach(t=>{const e=Math.round(this.viewport.offset.x+t.point.x*this.viewport.scale),n=Math.round(this.viewport.offset.y+t.point.y*this.viewport.scale);this.ctx.fillRect(e-i/2,n-i/2,i,i)})}drawAnnotationTitle(t){var e,i,n;if(!t.title)return;const s=this.annotationManager.getEffectiveTitleStyle(t),a=this.annotationManager.getEffectiveTitlePosition(t),o=s.font||"12px Arial",r=s.color||"#FFFFFF",h=s.backgroundColor||"rgba(0, 0, 0, 0.7)",l=null!==(e=s.paddingX)&&void 0!==e?e:6,c=null!==(i=s.paddingY)&&void 0!==i?i:3,d=null!==(n=s.borderRadius)&&void 0!==n?n:4;this.ctx.save(),this.ctx.font=o;const g=this.ctx.measureText(t.title).width,x=o.match(/(\d+)px/),f=x?parseInt(x[1]):12;let p,u,y,v;if("rect"===t.type){const e=t.data[0];p=this.viewport.offset.x+e.start.x*this.viewport.scale,u=this.viewport.offset.y+e.start.y*this.viewport.scale,y=e.width*this.viewport.scale,v=e.height*this.viewport.scale}else{const e=t.data;if(0===e.length)return void this.ctx.restore();let i=1/0,n=1/0,s=-1/0,a=-1/0;e.forEach(t=>{const e=this.viewport.offset.x+t.point.x*this.viewport.scale,o=this.viewport.offset.y+t.point.y*this.viewport.scale;e<i&&(i=e),o<n&&(n=o),e>s&&(s=e),o>a&&(a=o)}),p=i,u=n,y=s-i,v=a-n}const M=g+2*l,w=f+2*c,A=a.vertical||"top",S=a.offsetX||0,T=a.offsetY||0;let b;switch(A){case"bottom":b=u+v+4+T;break;case"inside-top":b=u+4+T;break;default:b=u-w-4+T}let I;switch(a.align||"center"){case"left":I=p+S;break;case"right":I=p+y-M+S;break;default:I=p+y/2-M/2+S}this.ctx.fillStyle=h,this.ctx.setLineDash([]),this.ctx.beginPath(),this.ctx.roundRect(I,b,M,w,d),this.ctx.fill(),this.ctx.fillStyle=r,this.ctx.textAlign="left",this.ctx.textBaseline="top",this.ctx.fillText(t.title,I+l,b+c),this.ctx.restore()}drawTextAnnotations(){const t=this.textManager.textStyle;this.textManager.textAnnotations.forEach((e,i)=>{if(this.textManager.editingTextIndex===i&&this.textManager.textInput&&"none"!==this.textManager.textInput.style.display)return;if(!e.text)return;const n=e.style||{font:t.font,color:t.color,backgroundColor:t.backgroundColor},s=this.viewport.offset.x+e.position.x*this.viewport.scale,a=this.viewport.offset.y+e.position.y*this.viewport.scale,o=this.textManager.selectedTextIndex===i;this.ctx.font=n.font;const r=this.ctx.measureText(e.text),h=r.actualBoundingBoxAscent||.8*e.height,l=h+(r.actualBoundingBoxDescent||.2*e.height),c=t.borderRadius,d=s,g=a,x=e.width+2*t.padding,f=l+2*t.padding;this.ctx.fillStyle=n.backgroundColor,this.ctx.beginPath(),this.ctx.roundRect(d,g,x,f,c),this.ctx.fill(),o&&(this.ctx.strokeStyle=t.selectedBorderColor,this.ctx.lineWidth=2,this.ctx.setLineDash([5,5]),this.ctx.beginPath(),this.ctx.roundRect(d,g,x,f,c),this.ctx.stroke(),this.ctx.setLineDash([]));const p=s+t.padding,u=a+t.padding+h;this.ctx.fillStyle=n.color,this.ctx.fillText(e.text,p,u)})}drawForExport(t,e,i,n){t.drawImage(e,0,0,i,n),this.annotationManager.recordList.forEach(e=>{const i=this.annotationManager.getAnnotationStyle(e);if(t.strokeStyle=i.strokeColor,t.lineWidth=i.lineWidth,"rect"===e.type){const i=e.data[0];t.strokeRect(i.start.x,i.start.y,i.width,i.height)}else if("polygon"===e.type&&"fullfilled"===e.status){t.beginPath(),e.data.forEach((e,i)=>{0===i?t.moveTo(e.point.x,e.point.y):t.lineTo(e.point.x,e.point.y)});const n=e.data[0];t.lineTo(n.point.x,n.point.y),t.stroke(),e.data.forEach(e=>{t.beginPath(),t.arc(e.point.x,e.point.y,4,0,2*Math.PI),t.fillStyle=i.strokeColor,t.fill()})}}),this.textManager.textAnnotations.forEach(e=>{const i=this.textManager.textStyle,n=e.style||{font:i.font,color:i.color,backgroundColor:i.backgroundColor};t.font=n.font;const s=t.measureText(e.text),a=s.actualBoundingBoxAscent||.8*e.height,o=a+(s.actualBoundingBoxDescent||.2*e.height),r=e.position.x,h=e.position.y,l=e.width+2*i.padding,c=o+2*i.padding,d=i.borderRadius;t.fillStyle=n.backgroundColor,t.roundRect?(t.roundRect(r,h,l,c,d),t.fill()):t.fillRect(r,h,l,c),t.fillStyle=n.color,t.fillText(e.text,e.position.x+i.padding,e.position.y+i.padding+a)}),this.annotationManager.enableTitle&&this.annotationManager.recordList.forEach(e=>{var i,n,s;if(!e.title)return;const a=this.annotationManager.getEffectiveTitleStyle(e),o=this.annotationManager.getEffectiveTitlePosition(e),r=a.font||"12px Arial",h=a.color||"#FFFFFF",l=a.backgroundColor||"rgba(0, 0, 0, 0.7)",c=null!==(i=a.paddingX)&&void 0!==i?i:6,d=null!==(n=a.paddingY)&&void 0!==n?n:3,g=null!==(s=a.borderRadius)&&void 0!==s?s:4;t.font=r;const x=t.measureText(e.title).width,f=r.match(/(\d+)px/),p=f?parseInt(f[1]):12;let u,y,v,M;if("rect"===e.type){const t=e.data[0];u=t.start.x,y=t.start.y,v=t.width,M=t.height}else{const t=e.data;if(0===t.length)return;let i=1/0,n=1/0,s=-1/0,a=-1/0;t.forEach(t=>{t.point.x<i&&(i=t.point.x),t.point.y<n&&(n=t.point.y),t.point.x>s&&(s=t.point.x),t.point.y>a&&(a=t.point.y)}),u=i,y=n,v=s-i,M=a-n}const w=x+2*c,A=p+2*d,S=o.vertical||"top",T=o.offsetX||0,b=o.offsetY||0;let I;switch(S){case"bottom":I=y+M+4+b;break;case"inside-top":I=y+4+b;break;default:I=y-A-4+b}let m;switch(o.align||"center"){case"left":m=u+T;break;case"right":m=u+v-w+T;break;default:m=u+v/2-w/2+T}t.fillStyle=l,t.roundRect?(t.roundRect(m,I,w,A,g),t.fill()):t.fillRect(m,I,w,A),t.fillStyle=h,t.textAlign="left",t.textBaseline="top",t.fillText(e.title,m+c,I+d)})}}class d{constructor(t,e,i,n,s,a,o,r,h){this.canvas=t,this.viewport=e,this.annotationManager=i,this.textManager=n,this.renderer=s,this.getDrawType=a,this.getBgImage=o,this.renderCallback=r,this.setDrawTypeCallback=h,this.isDragging=!1,this.dragStart={x:0,y:0},this.justFinishedMove=!1,this.justDeselected=!1}handleWheel(t){if(t.preventDefault(),!this.getBgImage())return;const e=t.deltaY>0?-.02:.02,i=this.viewport.scale,n=Math.max(this.viewport.minScale,Math.min(i+e,this.viewport.maxScale)),s=this.canvas.getBoundingClientRect(),a=t.clientX-s.left,o=t.clientY-s.top,r=(a-this.viewport.offset.x)/i,h=(o-this.viewport.offset.y)/i;this.viewport.scale=n;const l=r*n+this.viewport.offset.x,c=h*n+this.viewport.offset.y;this.viewport.offset.x+=a-l,this.viewport.offset.y+=o-c,this.viewport.calculateBoundaries(),this.viewport.constrainViewport(),this.viewport.isInitialScale=this.viewport.scale===this.viewport.initialScale,this.renderCallback()}handleMouseDown(t){var e,i;if(0!==t.button)return;const n=this.getDrawType(),s=this.getBgImage();if(this.annotationManager.selectedAnnotation&&s){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY),i=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(i)return this.annotationManager.startResizing(i,e),void this.renderCallback()}if("text"===n){const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return this.textManager.selectedTextIndex===e?this.textManager.startMoving(t,e):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(e)),void this.renderCallback();if(s){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY),i=this.annotationManager.getAnnotationAtPoint(e);if("rect"===(null==i?void 0:i.type))return this.annotationManager.selectAnnotation(i.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();const n=this.annotationManager.getAnnotationAtPoint(e);if("polygon"===(null==n?void 0:n.type))return this.annotationManager.selectAnnotation(n.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();if(this.annotationManager.selectedAnnotation)return this.annotationManager.deselectAnnotation(),void this.renderCallback()}return null!==this.textManager.selectedTextIndex?(this.textManager.deselectTextAnnotation(),void this.renderCallback()):void this.handleTextModeClick(t)}if(!s)return;const a=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);if("rect"===n){if("rect"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type)){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return this.annotationManager.startResizing(e,a),void this.renderCallback();const i=this.annotationManager.getAnnotationAtPoint(a);return i&&"rect"===i.type?(this.annotationManager.selectAnnotation(i.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback()):(this.annotationManager.deselectAnnotation(),this.justDeselected=!0,void this.renderCallback())}const i=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==i)return this.textManager.selectedTextIndex===i?this.textManager.startMoving(t,i):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(i)),void this.renderCallback();const n=this.annotationManager.getAnnotationAtPoint(a);if("polygon"===(null==n?void 0:n.type))return this.annotationManager.selectAnnotation(n.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();const s=this.annotationManager.getAnnotationAtPoint(a);if("rect"===(null==s?void 0:s.type))return this.annotationManager.selectAnnotation(s.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();this.annotationManager.startRectDrawing(a)}else if("polygon"===n){if("polygon"===(null===(i=this.annotationManager.selectedAnnotation)||void 0===i?void 0:i.type)){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e&&"polygon-vertex"===e.type)return this.annotationManager.startResizing(e,a),void this.renderCallback();const i=this.annotationManager.getAnnotationAtPoint(a);return i&&"polygon"===i.type?(this.annotationManager.selectAnnotation(i.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback()):(this.annotationManager.deselectAnnotation(),this.justDeselected=!0,void this.renderCallback())}const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return this.textManager.selectedTextIndex===e?this.textManager.startMoving(t,e):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(e)),void this.renderCallback();const n=this.annotationManager.getAnnotationAtPoint(a);if("rect"===(null==n?void 0:n.type))return this.annotationManager.selectAnnotation(n.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();const s=this.annotationManager.getAnnotationAtPoint(a);if("polygon"===(null==s?void 0:s.type))return this.annotationManager.selectAnnotation(s.index),this.annotationManager.startMovingAnnotation(t),void this.renderCallback();this.annotationManager.isDrawing?this.checkPolygonPointValid(a)&&this.annotationManager.addPolygonPoint(a):this.annotationManager.startPolygonDrawing(a),this.annotationManager.updatePolygonTempPoint(a),this.renderCallback()}else if("drag"===n){if(this.annotationManager.selectedAnnotation){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return this.annotationManager.startResizing(e,a),void this.renderCallback()}const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return this.textManager.selectedTextIndex===e?this.textManager.startMoving(t,e):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(e)),void this.renderCallback();if(this.handleRectModeClick(t,a))return;if(this.handlePolygonModeClick(t,a))return;this.isDragging=!0,this.dragStart={x:t.clientX,y:t.clientY},this.canvas.style.cursor="grabbing"}else""===n&&this.handleNoModeClick(t,a)}checkTextClickInDragMode(t){return!!this.textManager.checkTextClickForMove(t).handled&&(this.canvas.style.cursor="grabbing",!0)}handleTextModeClick(t){const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return this.textManager.startMoving(t,e),this.canvas.style.cursor="grabbing",this.renderCallback(),!0;const i=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);return this.textManager.addTextAnnotation(i.x,i.y),!0}handleRectModeClick(t,e){var i,n;if("rect"===(null===(i=this.annotationManager.selectedAnnotation)||void 0===i?void 0:i.type)){const i=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(i)return this.annotationManager.startResizing(i,e),this.renderCallback(),!0}const s=this.annotationManager.getAnnotationAtPoint(e);return s&&"rect"===s.type?(this.annotationManager.selectAnnotation(s.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback(),!0):"rect"===(null===(n=this.annotationManager.selectedAnnotation)||void 0===n?void 0:n.type)&&(this.annotationManager.deselectAnnotation(),this.renderCallback(),!0)}handlePolygonModeClick(t,e){var i,n;if("polygon"===(null===(i=this.annotationManager.selectedAnnotation)||void 0===i?void 0:i.type)){const i=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(i&&"polygon-vertex"===i.type)return this.annotationManager.startResizing(i,e),this.renderCallback(),!0}const s=this.annotationManager.getAnnotationAtPoint(e);return s&&"polygon"===s.type?(this.annotationManager.selectAnnotation(s.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback(),!0):"polygon"===(null===(n=this.annotationManager.selectedAnnotation)||void 0===n?void 0:n.type)&&(this.annotationManager.deselectAnnotation(),this.renderCallback(),!0)}handleRectModeClickForMove(t,e){const i=this.annotationManager.getAnnotationAtPoint(e);return!(!i||"rect"!==i.type)&&(this.annotationManager.selectAnnotation(i.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback(),!0)}handlePolygonModeClickForMove(t,e){const i=this.annotationManager.getAnnotationAtPoint(e);return!(!i||"polygon"!==i.type)&&(this.annotationManager.selectAnnotation(i.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback(),!0)}handleNoModeClick(t,e){const i=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==i)return this.textManager.selectedTextIndex===i?this.textManager.startMoving(t,i):(this.annotationManager.deselectAnnotation(),this.textManager.selectTextAnnotation(i)),void this.renderCallback();this.textManager.deselectTextAnnotation();const n=this.annotationManager.getAnnotationAtPoint(e);n?(this.annotationManager.selectAnnotation(n.index),this.annotationManager.startMovingAnnotation(t),this.renderCallback()):this.annotationManager.deselectAnnotation()}checkPolygonPointValid(t){const e=this.annotationManager.operate.data.length>0?this.annotationManager.operate.data[this.annotationManager.operate.data.length-1]:null;return!e||Math.abs(e.point.x-t.x)>5||Math.abs(e.point.y-t.y)>5}handleMouseMove(t){if(!this.getBgImage())return;const e=this.getDrawType();if(this.isDragging&&"drag"===e){const e=t.clientX-this.dragStart.x,i=t.clientY-this.dragStart.y;return this.viewport.updateOffset(e,i),this.dragStart={x:t.clientX,y:t.clientY},void this.renderCallback()}if(this.textManager.isTextMoving&&null!==this.textManager.editingTextIndex)this.textManager.moveAnnotation(t)&&this.renderCallback();else if((this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing)&&this.annotationManager.selectedAnnotation)this.handleAnnotationMove(t);else{if(this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing||this.textManager.isTextMoving||this.updateCursor(t),this.annotationManager.isDrawing&&"rect"===this.annotationManager.operate.type){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.annotationManager.updateRectDrawing(e),this.renderCallback()}if("polygon"===e&&this.annotationManager.isDrawing){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.annotationManager.updatePolygonTempPoint(e),this.renderCallback()}}}handleAnnotationMove(t){var e,i;if(this.annotationManager.isResizing&&this.annotationManager.activeHandle){const n=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);return"rect"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type)?this.annotationManager.resizeRect(n):"polygon"===(null===(i=this.annotationManager.selectedAnnotation)||void 0===i?void 0:i.type)&&this.annotationManager.resizePolygon(n),void this.renderCallback()}if(!this.annotationManager.isMovingAnnotation||!this.annotationManager.selectedAnnotation)return;const n=(t.clientX-this.annotationManager.annotationMoveStart.x)/this.viewport.scale,s=(t.clientY-this.annotationManager.annotationMoveStart.y)/this.viewport.scale;this.annotationManager.moveSelectedAnnotation(n,s),this.annotationManager.annotationMoveStart={x:t.clientX,y:t.clientY},this.renderCallback()}updateCursor(t){const e=this.getDrawType();if("text"===e){if(this.getBgImage()){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);if(this.annotationManager.selectedAnnotation){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return void("rect-corner"===e.type?this.canvas.style.cursor=0===e.index||3===e.index?"nwse-resize":"nesw-resize":this.canvas.style.cursor="crosshair")}if(this.annotationManager.getAnnotationAtPoint(e))return void(this.canvas.style.cursor="move")}const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);return void(this.canvas.style.cursor=null!==e?"move":"crosshair")}"rect"!==e?"polygon"!==e?"drag"!==e?""!==e?this.canvas.style.cursor="default":this.updateNoModeCursor(t):this.updateDragModeCursor(t):this.updatePolygonModeCursor(t):this.updateRectModeCursor(t)}updateRectModeCursor(t){var e;if("rect"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type)){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return void(this.canvas.style.cursor=0===e.index||3===e.index?"nwse-resize":"nesw-resize")}const i=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.annotationManager.getAnnotationAtPoint(i)?this.canvas.style.cursor="move":null===this.textManager.checkTextClick(t.offsetX,t.offsetY)?this.canvas.style.cursor="crosshair":this.canvas.style.cursor="move"}updatePolygonModeCursor(t){var e;if("polygon"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type)){if(this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY))return void(this.canvas.style.cursor="crosshair")}const i=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.annotationManager.getAnnotationAtPoint(i)?this.canvas.style.cursor="move":null===this.textManager.checkTextClick(t.offsetX,t.offsetY)?this.canvas.style.cursor="crosshair":this.canvas.style.cursor="move"}updateDragModeCursor(t){if(this.annotationManager.selectedAnnotation){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return void("rect-corner"===e.type?this.canvas.style.cursor=0===e.index||3===e.index?"nwse-resize":"nesw-resize":this.canvas.style.cursor="crosshair")}if(null!==this.textManager.checkTextClick(t.offsetX,t.offsetY))return void(this.canvas.style.cursor="move");const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY),i=this.annotationManager.getAnnotationAtPoint(e);this.canvas.style.cursor=i?"move":"grab"}updateNoModeCursor(t){if(this.annotationManager.selectedAnnotation){const e=this.annotationManager.getHandleAtPoint(t.offsetX,t.offsetY);if(e)return void("rect-corner"===e.type?this.canvas.style.cursor=0===e.index||3===e.index?"nwse-resize":"nesw-resize":this.canvas.style.cursor="crosshair")}if(null!==this.textManager.checkTextClick(t.offsetX,t.offsetY))return void(this.canvas.style.cursor="move");const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY),i=this.annotationManager.getAnnotationAtPoint(e);this.canvas.style.cursor=i?"move":"default"}handleMouseUp(t){if(this.isDragging){this.isDragging=!1;const t=this.getDrawType();return void(this.canvas.style.cursor="drag"===t?"grab":"default")}if(this.textManager.isTextMoving){this.justFinishedMove=!0,this.textManager.finishMoving();const t=this.getDrawType();"drag"===t?this.canvas.style.cursor="grab":""===t?this.canvas.style.cursor="move":(this.textManager.editingTextIndex=null,this.canvas.style.cursor="default")}(this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing)&&(this.justFinishedMove=!0,this.annotationManager.finishMovingAnnotation(),this.canvas.style.cursor=this.annotationManager.selectedAnnotation?"move":"default"),this.annotationManager.isDrawing&&"rect"===this.annotationManager.operate.type&&(this.annotationManager.finishRectDrawing(),this.renderCallback())}handleMouseLeave(t){(this.isDragging||this.textManager.isTextMoving||this.annotationManager.isResizing||this.annotationManager.isMovingAnnotation)&&this.handleMouseUp(t)}handleDoubleClick(t){if(!this.getBgImage())return;this.justFinishedMove=!1;const e=this.textManager.checkTextClick(t.offsetX,t.offsetY);if(null!==e)return void this.textManager.startEditing(e);if(this.annotationManager.selectedAnnotation){const e=this.viewport.toImageCoordinates(t.offsetX,t.offsetY),i=this.annotationManager.getAnnotationAtPoint(e);if(i&&i.index===this.annotationManager.selectedAnnotation.index)return void this.annotationManager.startTitleEditing(i.index)}if("polygon"===this.getDrawType()){if(this.annotationManager.isDrawing&&this.annotationManager.operate.data.length<=2)return;this.annotationManager.finishPolygonDrawing(),this.renderCallback()}}handleClick(t){var e;const i=this.getDrawType();if(!this.getBgImage()||"polygon"!==i||this.isDragging||this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing||this.textManager.isTextMoving)return;if(this.justDeselected)return void(this.justDeselected=!1);if("polygon"===(null===(e=this.annotationManager.selectedAnnotation)||void 0===e?void 0:e.type))return;const n=this.viewport.toImageCoordinates(t.offsetX,t.offsetY);this.checkPolygonPointValid(n)&&(this.annotationManager.isDrawing?this.annotationManager.addPolygonPoint(n):this.annotationManager.startPolygonDrawing(n)),this.annotationManager.updatePolygonTempPoint(n),this.renderCallback()}handleKeyDown(t){const e=t.target;if(!e||"INPUT"!==e.tagName&&"TEXTAREA"!==e.tagName&&"SELECT"!==e.tagName&&!e.isContentEditable){if("z"===t.key&&(t.ctrlKey||t.metaKey))return this.annotationManager.withdraw()||this.textManager.withdraw(),void this.renderCallback();if(" "===t.key)return this.viewport.resetToInitial(),void this.renderCallback();if("Escape"!==t.key){if("Delete"===t.key||"Backspace"===t.key){if(null!==this.textManager.editingTextIndex)return this.textManager.removeTextAnnotation(this.textManager.editingTextIndex),void this.renderCallback();if(null!==this.textManager.selectedTextIndex)return this.textManager.deleteSelectedTextAnnotation(),void this.renderCallback();this.annotationManager.selectedAnnotation&&(this.annotationManager.deleteSelectedAnnotation(),this.renderCallback())}}else this.annotationManager.isDrawing?(this.annotationManager.cancelDrawing(),this.renderCallback()):this.annotationManager.selectedAnnotation&&(this.annotationManager.deselectAnnotation(),this.renderCallback())}}}class g{constructor(t){this.canvas=document.createElement("canvas"),this.bgImage=null,this.bgImageSrc="",this.bgImageExt="jpeg",this.drawType="";const{drawType:e,useEvents:i=!0,id:n="container",annotationColor:s,lineStyle:a,vertexStyle:o,textStyle:g,enableTitle:x=!1}=t,f=document.getElementById(n);if(!f)throw new Error(`Container with id "${n}" not found`);this.container=f,"absolute"!==f.style.position&&"fixed"!==f.style.position&&"relative"!==f.style.position&&(f.style.position="relative"),this.ctx=this.canvas.getContext("2d"),this.viewport=new r,this.viewport.setSize(f.clientWidth,f.clientHeight),this.canvas.width=this.viewport.width,this.canvas.height=this.viewport.height,this.canvas.style.width=`${this.viewport.width}px`,this.canvas.style.height=`${this.viewport.height}px`,this.canvas.style.cursor="default",this.canvas.style.display="block",f.appendChild(this.canvas),this.annotationManager=new h(this.viewport,this.container,()=>this.render()),s&&this.annotationManager.setColorConfig(s),a&&this.annotationManager.setLineStyle(a),o&&this.annotationManager.setVertexStyle(o),this.annotationManager.enableTitle=x,x&&this.annotationManager.initTitleSupport(),this.textManager=new l(this.viewport,this.container,this.ctx,()=>this.render()),g&&this.textManager.setTextStyle(g),this.renderer=new c(this.ctx,this.viewport,this.annotationManager,this.textManager,this.canvas),this.eventHandler=new d(this.canvas,this.viewport,this.annotationManager,this.textManager,this.renderer,()=>this.drawType,()=>this.bgImage,()=>this.render(),t=>this.setDrawType(t)),this.events={wheel:t=>this.eventHandler.handleWheel(t),mousedown:t=>this.eventHandler.handleMouseDown(t),mousemove:t=>this.eventHandler.handleMouseMove(t),mouseup:t=>this.eventHandler.handleMouseUp(t),mouseleave:t=>this.eventHandler.handleMouseLeave(t),dblclick:t=>this.eventHandler.handleDoubleClick(t),click:t=>this.eventHandler.handleClick(t),keydown:t=>this.eventHandler.handleKeyDown(t)},this.setDrawType(e||""),i&&this.addEventListeners()}setDrawType(t){"polygon"===this.drawType&&this.annotationManager.isDrawing&&this.annotationManager.operate.data.length>0?console.warn("Cannot switch mode while polygon is being drawn. Please complete or cancel the polygon first."):this.drawType!==t&&(this.annotationManager.operate.data=[],this.annotationManager.isDrawing=!1,this.annotationManager.tempPolygonPoint=null,""!==t&&this.deselectAnnotation(),this.annotationManager.operate.type=t,this.drawType=t,this.canvas.style.cursor="drag"===t?"grab":"text"===t?"text":"default")}drawImage(t){this.bgImageSrc=t,this.bgImageExt=n(t);const e=new Image;e.crossOrigin="anonymous",e.src=t,e.onload=()=>{this.bgImage=e,this.viewport.setOriginalSize(e.width,e.height),this.viewport.calculateInitialView(),this.render()}}render(){this.renderer.render(this.bgImage)}clear(t=""){this.annotationManager.clear(),this.textManager.clearTextAnnotations(),this.setDrawType(t),this.render()}withdraw(){const t=this.annotationManager.withdraw(),e=this.textManager.withdraw();(t||e)&&this.render()}resize(){const t=this.canvas.parentElement;t&&(this.viewport.setSize(t.clientWidth,t.clientHeight),this.canvas.width=this.viewport.width,this.canvas.height=this.viewport.height,this.canvas.style.width=`${this.viewport.width}px`,this.canvas.style.height=`${this.viewport.height}px`,this.render())}addEventListeners(){this.canvas.addEventListener("mousedown",this.events.mousedown),this.canvas.addEventListener("mousemove",this.events.mousemove),this.canvas.addEventListener("mouseup",this.events.mouseup),this.canvas.addEventListener("mouseleave",this.events.mouseleave),this.canvas.addEventListener("wheel",this.events.wheel),this.canvas.addEventListener("contextmenu",t=>t.preventDefault()),document.addEventListener("keydown",this.events.keydown),this.handleClickEventListener()}handleClickEventListener(){let t=null,e=0;this.canvas.addEventListener("click",i=>{if(this.eventHandler.isDragging||this.annotationManager.isMovingAnnotation||this.annotationManager.isResizing||this.textManager.isTextMoving)return;const n=this.eventHandler.justFinishedMove;n&&(this.eventHandler.justFinishedMove=!1),e++,1===e?t=window.setTimeout(()=>{1!==e||n||this.events.click(i),e=0,t=null},300):2===e&&(this.eventHandler.justFinishedMove=!1,this.events.dblclick(i),e=0,null!==t&&(clearTimeout(t),t=null))})}changeScale(t){if(!this.bgImage)return;const e=o(this.viewport.scale,t),i=this.viewport.width/2,n=this.viewport.height/2,s=this.viewport.scale+e;this.viewport.updateScale(s,i,n),this.render()}clearCanvas(t=!1,e=!0){this.annotationManager.clear(),this.textManager.clearTextAnnotations(),this.textManager.deselectTextAnnotation(),!t&&e&&(this.bgImage=null,this.bgImageSrc="",this.viewport.setOriginalSize(0,0),this.viewport.reset()),this.ctx.clearRect(0,0,this.viewport.width,this.viewport.height),t&&this.bgImage&&this.render(),"drag"===this.drawType&&(this.canvas.style.cursor="default")}clearAnnotations(){this.clearCanvas(!0,!1)}getAnnotations(){return this.annotationManager.getAnnotations()}setAnnotationTitle(t,e){const i=this.annotationManager.setTitle(t,e);return i&&this.render(),i}getAnnotationTitle(t){return this.annotationManager.getTitle(t)}setTitleStyle(t){this.annotationManager.setTitleStyle(t),this.render()}getTitleStyle(){return this.annotationManager.getTitleStyle()}setTitlePosition(t){this.annotationManager.setTitlePosition(t),this.render()}getTitlePosition(){return this.annotationManager.getTitlePosition()}setAnnotationTitleStyle(t,e){const i=this.annotationManager.setAnnotationTitleStyle(t,e);return i&&this.render(),i}setAnnotationTitlePosition(t,e){const i=this.annotationManager.setAnnotationTitlePosition(t,e);return i&&this.render(),i}getTextAnnotations(){return this.textManager.getTextAnnotations()}exportAnnotationImage(){return new Promise((t,e)=>{if(!this.bgImage)return void e(new Error("No background image loaded"));const i=document.createElement("canvas");i.width=this.viewport.originalWidth,i.height=this.viewport.originalHeight;const n=i.getContext("2d");if(!n)return void e(new Error("Could not create offscreen canvas context"));const s=new Image;s.src=this.bgImageSrc,s.crossOrigin="anonymous",s.onload=()=>{try{this.renderer.drawForExport(n,s,this.viewport.originalWidth,this.viewport.originalHeight);const e=i.toDataURL(`image/${this.bgImageExt}`,.7);t(e)}catch(i){this.exportCurrentViewImage().then(t).catch(t=>e(new Error(`Both methods failed: ${t.message}`)))}},s.onerror=()=>{e(new Error("Failed to load image for export"))}})}exportCurrentViewImage(){return new Promise((t,e)=>{try{t(this.canvas.toDataURL(`image/${this.bgImageExt}`))}catch(t){e(new Error(`Failed to export current view: ${t.message}`))}})}base64ToFile(t,e){return s(t,e)}static base64ToBlob(t){return a(t)}addTextAnnotation(t,e,i=""){this.textManager.addTextAnnotation(t,e,i),this.render()}updateTextAnnotation(t,e){this.textManager.updateTextAnnotation(t,e)&&this.render()}moveTextAnnotation(t,e,i){this.textManager.moveTextAnnotation(t,e,i)&&this.render()}removeTextAnnotation(t){this.textManager.removeTextAnnotation(t)&&this.render()}clearTextAnnotations(){this.textManager.clearTextAnnotations(),this.render()}selectAnnotation(t){this.annotationManager.selectAnnotation(t)&&this.render()}deselectAnnotation(){this.annotationManager.deselectAnnotation(),this.textManager.deselectTextAnnotation(),this.textManager.editingTextIndex=null,this.render()}getSelectedAnnotation(){const t=this.annotationManager.getSelectedAnnotation();if(t)return t;const e=this.textManager.getSelectedTextAnnotation();return e?{index:e.index,type:"text",data:e.data}:null}deleteSelectedAnnotation(){if(null!==this.textManager.selectedTextIndex)return this.textManager.deleteSelectedTextAnnotation(),void this.render();this.annotationManager.deleteSelectedAnnotation()&&this.render()}moveSelectedAnnotation(t,e){this.annotationManager.moveSelectedAnnotation(t,e)&&this.render()}setAnnotationColor(t){this.annotationManager.setColorConfig(t),this.render()}getAnnotationColor(){return this.annotationManager.getColorConfig()}updateSelectedAnnotationStyle(){const t=this.annotationManager.selectedAnnotation;if(!t)return!1;const e=this.annotationManager.recordList[t.index];return!!e&&(e.style=this.annotationManager.getCurrentStyle(),this.render(),!0)}setLineStyle(t){this.annotationManager.setLineStyle(t),this.render()}getLineStyle(){return this.annotationManager.getLineStyle()}setVertexStyle(t){this.annotationManager.setVertexStyle(t),this.render()}getVertexStyle(){return this.annotationManager.getVertexStyle()}setTextStyle(t){this.textManager.setTextStyle(t),this.render()}updateTextAnnotationStyle(t,e){const i=this.textManager.updateTextAnnotationStyle(t,e);return i&&this.render(),i}setTextInputStyle(t){this.textManager.setInputStyle(t)}setTextSelectionStyle(t){this.textManager.setSelectionStyle(t),this.render()}destroy(){this.textManager.destroy(),this.annotationManager.destroyTitleInput(),this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas)}}t.AnnotationManager=h,t.Drawer=g,t.EventHandler=d,t.Renderer=c,t.TextAnnotationManager=l,t.ViewportManager=r,t.base64ToBlob=a,t.base64ToFile=s,t.default=g,t.getImageTypeFromUrl=n,t.getZoomDelta=o,t.isPointInPolygon=i,t.isPointInRect=e,Object.defineProperty(t,"__esModule",{value:!0})}),"undefined"!=typeof ImageAnnotationDrawer&&ImageAnnotationDrawer.default&&(ImageAnnotationDrawer=ImageAnnotationDrawer.default);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "image-annotation-drawer",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"description": "A lightweight image annotation library based on HTML5 Canvas, supporting rectangles, polygons, text annotations, zoom and drag operations.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.esm.js",
|