@deepnoid/canvas 0.1.58 → 0.1.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +202 -3
  2. package/dist/engine/annotation/annotationTypes.d.ts +21 -0
  3. package/dist/engine/annotation/annotationTypes.js +6 -0
  4. package/dist/engine/annotation/rectangle/rectangleController.d.ts +26 -0
  5. package/dist/engine/annotation/rectangle/rectangleController.js +115 -0
  6. package/dist/engine/annotation/rectangle/rectangleHitTest.d.ts +7 -0
  7. package/dist/engine/annotation/rectangle/rectangleHitTest.js +102 -0
  8. package/dist/engine/annotation/rectangle/rectangleInteraction.d.ts +17 -0
  9. package/dist/engine/annotation/rectangle/rectangleInteraction.js +30 -0
  10. package/dist/engine/annotation/rectangle/rectangleMath.d.ts +10 -0
  11. package/dist/engine/annotation/rectangle/rectangleMath.js +29 -0
  12. package/dist/engine/annotation/rectangle/rectangleRenderer.d.ts +5 -0
  13. package/dist/engine/annotation/rectangle/rectangleRenderer.js +88 -0
  14. package/dist/engine/annotation/rectangle/rectangleTransform.d.ts +14 -0
  15. package/dist/engine/annotation/rectangle/rectangleTransform.js +65 -0
  16. package/dist/{enum/common.d.ts → engine/annotation/rectangle/rectangleTypes.d.ts} +0 -3
  17. package/dist/{enum/common.js → engine/annotation/rectangle/rectangleTypes.js} +0 -4
  18. package/dist/engine/history.d.ts +11 -0
  19. package/dist/{components/AnnotationCanvas/_utils/createHistory.js → engine/history.js} +4 -4
  20. package/dist/engine/interaction/drawModeRouter.d.ts +3 -0
  21. package/dist/engine/interaction/drawModeRouter.js +56 -0
  22. package/dist/engine/interaction/interactionController.d.ts +13 -0
  23. package/dist/engine/interaction/interactionController.js +53 -0
  24. package/dist/engine/interaction/interface.d.ts +15 -0
  25. package/dist/engine/interaction/panZoomInteraction.d.ts +3 -0
  26. package/dist/engine/interaction/panZoomInteraction.js +29 -0
  27. package/dist/engine/interaction/pointerInteraction.d.ts +16 -0
  28. package/dist/engine/interaction/pointerInteraction.js +48 -0
  29. package/dist/engine/pan-zoom/panZoomController.d.ts +26 -0
  30. package/dist/engine/pan-zoom/panZoomController.js +148 -0
  31. package/dist/engine/pan-zoom/panZoomUtils.d.ts +10 -0
  32. package/dist/engine/pan-zoom/panZoomUtils.js +24 -0
  33. package/dist/engine/public/annotationEngine.d.ts +75 -0
  34. package/dist/engine/public/annotationEngine.js +263 -0
  35. package/dist/engine/renderer/drawCross.d.ts +2 -0
  36. package/dist/engine/renderer/drawCross.js +19 -0
  37. package/dist/engine/renderer/interface.d.ts +6 -0
  38. package/dist/engine/renderer/interface.js +1 -0
  39. package/dist/{types/index.d.ts → engine/types.d.ts} +12 -21
  40. package/dist/engine/types.js +1 -0
  41. package/dist/engine/utils/mousePoint.d.ts +3 -0
  42. package/dist/engine/utils/mousePoint.js +52 -0
  43. package/dist/index.d.ts +4 -5
  44. package/dist/index.js +2 -2
  45. package/dist/{components/AnnotationCanvas/index.d.ts → react/AnnotationCanvas.d.ts} +7 -6
  46. package/dist/react/AnnotationCanvas.js +110 -0
  47. package/dist/{components → react}/index.d.ts +1 -1
  48. package/dist/{components → react}/index.js +1 -1
  49. package/package.json +1 -1
  50. package/dist/components/AnnotationCanvas/_hooks/useImagePanZoom.d.ts +0 -24
  51. package/dist/components/AnnotationCanvas/_hooks/useImagePanZoom.js +0 -143
  52. package/dist/components/AnnotationCanvas/_utils/createHistory.d.ts +0 -11
  53. package/dist/components/AnnotationCanvas/_utils/panZoom.d.ts +0 -10
  54. package/dist/components/AnnotationCanvas/_utils/panZoom.js +0 -29
  55. package/dist/components/AnnotationCanvas/index.js +0 -96
  56. package/dist/components/AnnotationLayer/_hooks/drawEvents/rectangle.d.ts +0 -5
  57. package/dist/components/AnnotationLayer/_hooks/drawEvents/rectangle.js +0 -88
  58. package/dist/components/AnnotationLayer/_hooks/drawEvents/rectangleUtils.d.ts +0 -28
  59. package/dist/components/AnnotationLayer/_hooks/drawEvents/rectangleUtils.js +0 -204
  60. package/dist/components/AnnotationLayer/_hooks/drawEvents/useDrawEvents.d.ts +0 -25
  61. package/dist/components/AnnotationLayer/_hooks/drawEvents/useDrawEvents.js +0 -43
  62. package/dist/components/AnnotationLayer/_hooks/useCanvasDraw.d.ts +0 -13
  63. package/dist/components/AnnotationLayer/_hooks/useCanvasDraw.js +0 -115
  64. package/dist/components/AnnotationLayer/index.d.ts +0 -14
  65. package/dist/components/AnnotationLayer/index.js +0 -122
  66. package/dist/utils/canvas.d.ts +0 -3
  67. package/dist/utils/canvas.js +0 -37
  68. package/dist/utils/pointTransform.d.ts +0 -2
  69. package/dist/utils/pointTransform.js +0 -46
  70. /package/dist/{types/index.js → engine/interaction/interface.js} +0 -0
  71. /package/dist/{utils/common → engine/utils}/cloneDeep.d.ts +0 -0
  72. /package/dist/{utils/common → engine/utils}/cloneDeep.js +0 -0
  73. /package/dist/{utils/common → engine/utils}/deepEqual.d.ts +0 -0
  74. /package/dist/{utils/common → engine/utils}/deepEqual.js +0 -0
  75. /package/dist/{utils/common → engine/utils}/isEqualWith.d.ts +0 -0
  76. /package/dist/{utils/common → engine/utils}/isEqualWith.js +0 -0
  77. /package/dist/{utils → engine/utils}/mouseActions.d.ts +0 -0
  78. /package/dist/{utils → engine/utils}/mouseActions.js +0 -0
  79. /package/dist/{hooks → react/hooks}/useDebounce.d.ts +0 -0
  80. /package/dist/{hooks → react/hooks}/useDebounce.js +0 -0
  81. /package/dist/{components/AnnotationLayer/_hooks → react/hooks}/useHotkeys.d.ts +0 -0
  82. /package/dist/{components/AnnotationLayer/_hooks → react/hooks}/useHotkeys.js +0 -0
  83. /package/dist/{hooks → react/hooks}/useResizeObserver.d.ts +0 -0
  84. /package/dist/{hooks → react/hooks}/useResizeObserver.js +0 -0
@@ -0,0 +1,14 @@
1
+ import { Point } from '../../types';
2
+ import { Annotation } from '../annotationTypes';
3
+ import { RectangleAnchor } from './rectangleTypes';
4
+ type RectangleParams = {
5
+ annotations: Annotation[];
6
+ dw: number;
7
+ dh: number;
8
+ mousePoint: Point;
9
+ prevMousePoint: Point;
10
+ rectangleAnchor: RectangleAnchor | null;
11
+ };
12
+ export declare function applyRectangleMove({ annotations, dw, dh, mousePoint, prevMousePoint }: RectangleParams): void;
13
+ export declare function applyRectangleResize({ annotations, dw, dh, mousePoint, prevMousePoint, rectangleAnchor, }: RectangleParams): void;
14
+ export {};
@@ -0,0 +1,65 @@
1
+ import { RectangleAnchor } from './rectangleTypes';
2
+ export function applyRectangleMove({ annotations, dw, dh, mousePoint, prevMousePoint }) {
3
+ if (!mousePoint || !prevMousePoint)
4
+ return;
5
+ const findIndex = annotations.findIndex((annotation) => annotation.selected);
6
+ const findAnnotation = annotations[findIndex];
7
+ if (findIndex < 0 || !findAnnotation)
8
+ return;
9
+ const dx = mousePoint.x - prevMousePoint.x;
10
+ const dy = mousePoint.y - prevMousePoint.y;
11
+ const newX = findAnnotation.x + dx;
12
+ const newY = findAnnotation.y + dy;
13
+ findAnnotation.x = Math.max(0, Math.min(newX, dw - findAnnotation.width));
14
+ findAnnotation.y = Math.max(0, Math.min(newY, dh - findAnnotation.height));
15
+ }
16
+ export function applyRectangleResize({ annotations, dw, dh, mousePoint, prevMousePoint, rectangleAnchor, }) {
17
+ const findIndex = annotations.findIndex((annotation) => annotation.selected);
18
+ const findAnnotation = annotations[findIndex];
19
+ if (findIndex >= 0 && findAnnotation) {
20
+ const dx = mousePoint.x - prevMousePoint.x;
21
+ const dy = mousePoint.y - prevMousePoint.y;
22
+ switch (rectangleAnchor) {
23
+ case RectangleAnchor.LEFT_TOP:
24
+ findAnnotation.x += dx;
25
+ findAnnotation.y += dy;
26
+ findAnnotation.width -= dx;
27
+ findAnnotation.height -= dy;
28
+ break;
29
+ case RectangleAnchor.RIGHT_TOP:
30
+ findAnnotation.y += dy;
31
+ findAnnotation.width += dx;
32
+ findAnnotation.height -= dy;
33
+ break;
34
+ case RectangleAnchor.LEFT_BOTTOM:
35
+ findAnnotation.x += dx;
36
+ findAnnotation.width -= dx;
37
+ findAnnotation.height += dy;
38
+ break;
39
+ case RectangleAnchor.RIGHT_BOTTOM:
40
+ findAnnotation.width += dx;
41
+ findAnnotation.height += dy;
42
+ break;
43
+ case RectangleAnchor.TOP:
44
+ findAnnotation.y += dy;
45
+ findAnnotation.height -= dy;
46
+ break;
47
+ case RectangleAnchor.BOTTOM:
48
+ findAnnotation.height += dy;
49
+ break;
50
+ case RectangleAnchor.LEFT:
51
+ findAnnotation.x += dx;
52
+ findAnnotation.width -= dx;
53
+ break;
54
+ case RectangleAnchor.RIGHT:
55
+ findAnnotation.width += dx;
56
+ break;
57
+ default:
58
+ break;
59
+ }
60
+ findAnnotation.x = Math.max(0, Math.min(findAnnotation.x, dw - findAnnotation.width));
61
+ findAnnotation.y = Math.max(0, Math.min(findAnnotation.y, dh - findAnnotation.height));
62
+ findAnnotation.width = Math.max(0, Math.min(findAnnotation.width, dw - findAnnotation.x));
63
+ findAnnotation.height = Math.max(0, Math.min(findAnnotation.height, dh - findAnnotation.y));
64
+ }
65
+ }
@@ -1,6 +1,3 @@
1
- export declare enum DrawMode {
2
- RECTANGLE = "RECTANGLE"
3
- }
4
1
  export declare enum RectangleAnchor {
5
2
  LEFT_TOP = "LEFT_TOP",
6
3
  RIGHT_TOP = "RIGHT_TOP",
@@ -1,7 +1,3 @@
1
- export var DrawMode;
2
- (function (DrawMode) {
3
- DrawMode["RECTANGLE"] = "RECTANGLE";
4
- })(DrawMode || (DrawMode = {}));
5
1
  export var RectangleAnchor;
6
2
  (function (RectangleAnchor) {
7
3
  RectangleAnchor["LEFT_TOP"] = "LEFT_TOP";
@@ -0,0 +1,11 @@
1
+ import { Annotation } from './annotation/annotationTypes';
2
+ export type History = {
3
+ getHistory: () => Annotation[][];
4
+ init: (annotations?: Annotation[]) => void;
5
+ getHistoryIndex: () => number;
6
+ addInit: () => void;
7
+ add: (annotations: Annotation[]) => void;
8
+ undo: () => Annotation[] | undefined;
9
+ redo: () => Annotation[] | undefined;
10
+ };
11
+ export declare const createHistory: () => History;
@@ -4,17 +4,17 @@ export const createHistory = () => {
4
4
  return {
5
5
  getHistory: () => history,
6
6
  getHistoryIndex: () => historyIndex,
7
- init: (coordinates = []) => {
8
- history = [coordinates];
7
+ init: (annotations = []) => {
8
+ history = [annotations];
9
9
  historyIndex = 0;
10
10
  },
11
11
  addInit: () => {
12
12
  history.push([]);
13
13
  historyIndex++;
14
14
  },
15
- add: (coordinates) => {
15
+ add: (annotations) => {
16
16
  history.splice(historyIndex + 1);
17
- history.push(coordinates);
17
+ history.push(annotations);
18
18
  historyIndex++;
19
19
  },
20
20
  undo: () => {
@@ -0,0 +1,3 @@
1
+ import { AnnotationEngine } from '../public/annotationEngine';
2
+ import { Interaction } from './interface';
3
+ export declare function drawModeRouter(engine: AnnotationEngine): Interaction;
@@ -0,0 +1,56 @@
1
+ import { RectangleController } from '../annotation/rectangle/rectangleController';
2
+ import { RectangleInteraction } from '../annotation/rectangle/rectangleInteraction';
3
+ import { DrawMode } from '../annotation/annotationTypes';
4
+ export function drawModeRouter(engine) {
5
+ const rectangle = new RectangleInteraction(new RectangleController(engine));
6
+ return {
7
+ getType() {
8
+ const drawMode = engine.getDrawing().mode;
9
+ if (drawMode)
10
+ return drawMode;
11
+ return false;
12
+ },
13
+ getRenderState() {
14
+ const drawMode = engine.getDrawing().mode;
15
+ if (drawMode === DrawMode.RECTANGLE) {
16
+ return rectangle.getRenderState();
17
+ }
18
+ return null;
19
+ },
20
+ getRedrawLayers() {
21
+ if (engine.getDrawing().mode === DrawMode.RECTANGLE) {
22
+ return rectangle.getRedrawLayers();
23
+ }
24
+ return {
25
+ imageCanvas: false,
26
+ annotationsCanvas: false,
27
+ };
28
+ },
29
+ handleMouseDown(e) {
30
+ if (!engine.isEditable())
31
+ return false;
32
+ if (engine.getDrawing().mode === DrawMode.RECTANGLE) {
33
+ return rectangle.handleMouseDown(e);
34
+ }
35
+ return false;
36
+ },
37
+ handleMouseMove(e) {
38
+ if (engine.getDrawing().mode === DrawMode.RECTANGLE) {
39
+ return rectangle.handleMouseMove(e);
40
+ }
41
+ return false;
42
+ },
43
+ handleMouseUp(e) {
44
+ if (engine.getDrawing().mode === DrawMode.RECTANGLE) {
45
+ return rectangle.handleMouseUp(e);
46
+ }
47
+ return false;
48
+ },
49
+ handleMouseLeave(e) {
50
+ if (engine.getDrawing().mode === DrawMode.RECTANGLE) {
51
+ return rectangle.handleMouseLeave(e);
52
+ }
53
+ return false;
54
+ },
55
+ };
56
+ }
@@ -0,0 +1,13 @@
1
+ import { DrawMode } from '../annotation/annotationTypes';
2
+ import { Interaction, RedrawLayers } from '../interaction/interface';
3
+ export declare class InteractionController {
4
+ private interactions;
5
+ constructor(interactions: Interaction[]);
6
+ getInteractions(): Interaction[];
7
+ getRenderState(type: DrawMode): unknown | null;
8
+ handleMouseDown: (e: MouseEvent) => void;
9
+ handleMouseMove: (e: MouseEvent) => RedrawLayers;
10
+ handleMouseUp: (e: MouseEvent) => void;
11
+ handleMouseLeave: (e: MouseEvent) => void;
12
+ handleWheel: (e: WheelEvent) => void;
13
+ }
@@ -0,0 +1,53 @@
1
+ export class InteractionController {
2
+ constructor(interactions) {
3
+ this.interactions = interactions;
4
+ this.handleMouseDown = (e) => {
5
+ for (const interaction of this.interactions) {
6
+ if (interaction.handleMouseDown?.(e))
7
+ break;
8
+ }
9
+ };
10
+ this.handleMouseMove = (e) => {
11
+ const layers = {
12
+ imageCanvas: false,
13
+ annotationsCanvas: false,
14
+ };
15
+ for (const interaction of this.interactions) {
16
+ const handled = interaction.handleMouseMove?.(e);
17
+ const interactionLayers = interaction.getRedrawLayers?.();
18
+ if (interactionLayers) {
19
+ layers.imageCanvas = layers.imageCanvas || interactionLayers.imageCanvas;
20
+ layers.annotationsCanvas = layers.annotationsCanvas || interactionLayers.annotationsCanvas;
21
+ }
22
+ if (handled)
23
+ break;
24
+ }
25
+ return layers;
26
+ };
27
+ this.handleMouseUp = (e) => {
28
+ for (const interaction of this.interactions) {
29
+ if (interaction.handleMouseUp?.(e))
30
+ break;
31
+ }
32
+ };
33
+ this.handleMouseLeave = (e) => {
34
+ for (const interaction of this.interactions) {
35
+ if (interaction.handleMouseLeave?.(e))
36
+ break;
37
+ }
38
+ };
39
+ this.handleWheel = (e) => {
40
+ for (const interaction of this.interactions) {
41
+ if (interaction.handleWheel?.(e))
42
+ break;
43
+ }
44
+ };
45
+ }
46
+ getInteractions() {
47
+ return this.interactions;
48
+ }
49
+ getRenderState(type) {
50
+ const interaction = this.interactions.find((i) => i.getType?.() === type);
51
+ return interaction?.getRenderState?.() ?? null;
52
+ }
53
+ }
@@ -0,0 +1,15 @@
1
+ import { DrawMode } from '../annotation/annotationTypes';
2
+ export type RedrawLayers = {
3
+ imageCanvas: boolean;
4
+ annotationsCanvas: boolean;
5
+ };
6
+ export interface Interaction {
7
+ getType?(): boolean | DrawMode;
8
+ getRenderState?(): unknown;
9
+ getRedrawLayers?: () => RedrawLayers;
10
+ handleMouseDown?(e: MouseEvent): boolean | void;
11
+ handleMouseMove?(e: MouseEvent): boolean | void;
12
+ handleMouseUp?(e: MouseEvent): boolean | void;
13
+ handleMouseLeave?(e: MouseEvent): boolean | void;
14
+ handleWheel?(e: WheelEvent): boolean | void;
15
+ }
@@ -0,0 +1,3 @@
1
+ import { PanZoomController } from '../pan-zoom/panZoomController';
2
+ import { Interaction } from './interface';
3
+ export declare const panZoomInteraction: (panZoom: PanZoomController) => Interaction;
@@ -0,0 +1,29 @@
1
+ export const panZoomInteraction = (panZoom) => ({
2
+ getRedrawLayers() {
3
+ const isDragging = panZoom.getIsDragging();
4
+ return {
5
+ imageCanvas: isDragging,
6
+ annotationsCanvas: isDragging,
7
+ };
8
+ },
9
+ handleMouseDown(e) {
10
+ panZoom.handleMouseDown(e);
11
+ return panZoom.getIsDragging();
12
+ },
13
+ handleMouseMove(e) {
14
+ panZoom.handleMouseMove(e);
15
+ return panZoom.getIsDragging();
16
+ },
17
+ handleMouseUp(e) {
18
+ panZoom.handleMouseUp(e);
19
+ return false;
20
+ },
21
+ handleMouseLeave(e) {
22
+ panZoom.handleMouseLeave(e);
23
+ return false;
24
+ },
25
+ handleWheel(e) {
26
+ panZoom.handleWheel(e);
27
+ return true;
28
+ },
29
+ });
@@ -0,0 +1,16 @@
1
+ import { AnnotationEngine } from '../public/annotationEngine';
2
+ import { Point } from '../types';
3
+ import { Interaction, RedrawLayers } from './interface';
4
+ export declare class PointerInteraction implements Interaction {
5
+ private engine;
6
+ private mousePoint;
7
+ private needsRedraw;
8
+ constructor(engine: AnnotationEngine);
9
+ getRenderState(): {
10
+ mousePoint: Point | null;
11
+ };
12
+ getRedrawLayers(): RedrawLayers;
13
+ handleMouseMove(e: MouseEvent): false | undefined;
14
+ handleMouseLeave(): false | undefined;
15
+ private updateCursor;
16
+ }
@@ -0,0 +1,48 @@
1
+ import { DrawMode } from '../annotation/annotationTypes';
2
+ import { getRectangleCursor } from '../annotation/rectangle/rectangleRenderer';
3
+ import { getCanvasMousePoint } from '../utils/mousePoint';
4
+ export class PointerInteraction {
5
+ constructor(engine) {
6
+ this.engine = engine;
7
+ this.mousePoint = null;
8
+ this.needsRedraw = false;
9
+ }
10
+ getRenderState() {
11
+ return {
12
+ mousePoint: this.mousePoint,
13
+ };
14
+ }
15
+ getRedrawLayers() {
16
+ return {
17
+ imageCanvas: false,
18
+ annotationsCanvas: this.needsRedraw,
19
+ };
20
+ }
21
+ handleMouseMove(e) {
22
+ if (!this.engine.isEditable())
23
+ return;
24
+ this.mousePoint = getCanvasMousePoint(e, this.engine.getImageCanvas(), this.engine.getImageCanvasState());
25
+ this.updateCursor();
26
+ this.needsRedraw = this.mousePoint !== null;
27
+ return false;
28
+ }
29
+ handleMouseLeave() {
30
+ if (!this.engine.isEditable())
31
+ return;
32
+ this.mousePoint = null;
33
+ this.needsRedraw = true;
34
+ return false;
35
+ }
36
+ updateCursor() {
37
+ const canvas = this.engine.getAnnotationsCanvas();
38
+ const annotation = this.engine.getSelectedAnnotation();
39
+ const { zoom } = this.engine.getImageCanvasState();
40
+ const { mode } = this.engine.getDrawing();
41
+ let cursor = 'default';
42
+ if (annotation && this.mousePoint) {
43
+ if (mode === DrawMode.RECTANGLE)
44
+ cursor = getRectangleCursor(this.mousePoint, annotation, zoom);
45
+ }
46
+ canvas.style.cursor = cursor;
47
+ }
48
+ }
@@ -0,0 +1,26 @@
1
+ import { AnnotationCanvasOptionsZoom, ImageCanvasState } from '../types';
2
+ export declare class PanZoomController {
3
+ private canvas;
4
+ private image;
5
+ private getImageCanvasState;
6
+ private setImageCanvasState;
7
+ private options;
8
+ private isDragging;
9
+ private startMousePoint?;
10
+ constructor(canvas: HTMLCanvasElement | null, image: HTMLImageElement | null, getImageCanvasState: () => ImageCanvasState, setImageCanvasState: (state: ImageCanvasState) => void, options: {
11
+ enabled: boolean;
12
+ zoom?: AnnotationCanvasOptionsZoom;
13
+ editable: boolean;
14
+ });
15
+ destroy(): void;
16
+ setEnabled(enabled: boolean): void;
17
+ setEditable(editable: boolean): void;
18
+ resetZoomAndPosition(): void;
19
+ preserveZoomAndPosition(): void;
20
+ getIsDragging(): boolean;
21
+ handleMouseDown(event: MouseEvent): void;
22
+ handleMouseUp: (event: MouseEvent) => void;
23
+ handleMouseLeave: (event: MouseEvent) => void;
24
+ handleMouseMove(event: MouseEvent): void;
25
+ handleWheel(event: WheelEvent): void;
26
+ }
@@ -0,0 +1,148 @@
1
+ import { isMouseClickAction, isMouseDragAction, MouseAction } from '../utils/mouseActions';
2
+ import { getMousePointTransform } from '../utils/mousePoint';
3
+ import { calculateInitZoom, getCenteredImageOffset, getZoomPivotFromCanvasCenter, limitZoom } from './panZoomUtils';
4
+ export class PanZoomController {
5
+ constructor(canvas, image, getImageCanvasState, setImageCanvasState, options) {
6
+ this.canvas = canvas;
7
+ this.image = image;
8
+ this.getImageCanvasState = getImageCanvasState;
9
+ this.setImageCanvasState = setImageCanvasState;
10
+ this.options = options;
11
+ this.isDragging = false;
12
+ this.handleMouseUp = (event) => {
13
+ if (!this.options.enabled || !this.canvas)
14
+ return;
15
+ this.isDragging = false;
16
+ event.preventDefault();
17
+ };
18
+ this.handleMouseLeave = (event) => {
19
+ if (!this.options.enabled || !this.canvas)
20
+ return;
21
+ this.isDragging = false;
22
+ event.preventDefault();
23
+ };
24
+ }
25
+ destroy() {
26
+ this.isDragging = false;
27
+ this.startMousePoint = undefined;
28
+ this.canvas = null;
29
+ this.image = null;
30
+ }
31
+ setEnabled(enabled) {
32
+ this.options.enabled = enabled;
33
+ if (!enabled) {
34
+ this.isDragging = false;
35
+ }
36
+ }
37
+ setEditable(editable) {
38
+ this.options.editable = editable;
39
+ this.isDragging = false;
40
+ }
41
+ resetZoomAndPosition() {
42
+ if (!this.canvas || !this.image)
43
+ return;
44
+ const { dx, dy } = getCenteredImageOffset(this.canvas, this.image);
45
+ const newInitZoom = calculateInitZoom(this.canvas, this.image);
46
+ const { zoomX, zoomY } = getZoomPivotFromCanvasCenter(this.canvas, 0, 0, dx, dy);
47
+ this.setImageCanvasState({
48
+ moveX: 0,
49
+ moveY: 0,
50
+ zoomX,
51
+ zoomY,
52
+ zoom: newInitZoom,
53
+ initZoom: newInitZoom,
54
+ dx,
55
+ dy,
56
+ dw: this.image.naturalWidth,
57
+ dh: this.image.naturalHeight,
58
+ });
59
+ }
60
+ preserveZoomAndPosition() {
61
+ if (!this.canvas || !this.image)
62
+ return;
63
+ const { moveX, moveY, zoom, initZoom } = this.getImageCanvasState();
64
+ const zoomRatio = initZoom > 0 ? zoom / initZoom : 1;
65
+ const { dx, dy } = getCenteredImageOffset(this.canvas, this.image);
66
+ const newInitZoom = calculateInitZoom(this.canvas, this.image);
67
+ const { zoomX, zoomY } = getZoomPivotFromCanvasCenter(this.canvas, moveX, moveY, dx, dy);
68
+ this.setImageCanvasState({
69
+ moveX,
70
+ moveY,
71
+ zoomX,
72
+ zoomY,
73
+ zoom: newInitZoom * zoomRatio,
74
+ dx,
75
+ dy,
76
+ dw: this.image.naturalWidth,
77
+ dh: this.image.naturalHeight,
78
+ initZoom: newInitZoom,
79
+ });
80
+ }
81
+ getIsDragging() {
82
+ return this.isDragging;
83
+ }
84
+ handleMouseDown(event) {
85
+ if (!this.options.enabled || !this.canvas)
86
+ return;
87
+ if (this.options.editable && isMouseClickAction(event.button, MouseAction.LEFT))
88
+ return;
89
+ this.isDragging = true;
90
+ const { moveX, moveY, zoomX, zoomY, dx, dy, zoom } = this.getImageCanvasState();
91
+ const canvas = this.canvas;
92
+ const rect = canvas.getBoundingClientRect();
93
+ const mouseX = event.clientX - rect.left;
94
+ const mouseY = event.clientY - rect.top;
95
+ const mouse = getMousePointTransform({ x: mouseX, y: mouseY }, { x: moveX, y: moveY }, { x: zoomX, y: zoomY }, { x: dx, y: dy }, zoom || 1, canvas);
96
+ this.startMousePoint = { x: mouse.x, y: mouse.y };
97
+ event.preventDefault();
98
+ }
99
+ handleMouseMove(event) {
100
+ if (!this.options.enabled || !this.canvas || !this.isDragging)
101
+ return;
102
+ if (this.options.editable && isMouseDragAction(event.button, MouseAction.LEFT))
103
+ return;
104
+ const canvasState = this.getImageCanvasState();
105
+ const { moveX, moveY, zoomX, zoomY, dx, dy, zoom } = canvasState;
106
+ const canvas = this.canvas;
107
+ const rect = canvas.getBoundingClientRect();
108
+ const mouseX = event.clientX - rect.left;
109
+ const mouseY = event.clientY - rect.top;
110
+ const mouse = getMousePointTransform({ x: mouseX, y: mouseY }, { x: moveX, y: moveY }, { x: zoomX, y: zoomY }, { x: dx, y: dy }, zoom || 1, canvas);
111
+ let x = mouse.x;
112
+ let y = mouse.y;
113
+ if (this.startMousePoint) {
114
+ x -= this.startMousePoint.x;
115
+ y -= this.startMousePoint.y;
116
+ }
117
+ const { zoomX: newZoomX, zoomY: newZoomY } = getZoomPivotFromCanvasCenter(canvas, moveX + x, moveY + y, dx, dy);
118
+ this.setImageCanvasState({
119
+ ...canvasState,
120
+ moveX: moveX + x,
121
+ moveY: moveY + y,
122
+ zoomX: newZoomX,
123
+ zoomY: newZoomY,
124
+ });
125
+ event.preventDefault();
126
+ }
127
+ handleWheel(event) {
128
+ if (!this.options.enabled || !this.canvas || !this.image)
129
+ return;
130
+ const canvasState = this.getImageCanvasState();
131
+ const { zoom, initZoom, moveX, moveY, dx, dy } = canvasState;
132
+ if (initZoom <= 0)
133
+ return;
134
+ const zoomStep = this.options.zoom?.step ?? 0.9;
135
+ const minZoom = (this.options.zoom?.min ?? 0) * initZoom;
136
+ const maxZoom = (this.options.zoom?.max ?? Infinity) * initZoom;
137
+ const nextZoom = limitZoom(event.deltaY < 0 ? zoom * (1 / zoomStep) : zoom * zoomStep, minZoom, maxZoom);
138
+ if (nextZoom === zoom)
139
+ return;
140
+ const { zoomX, zoomY } = getZoomPivotFromCanvasCenter(this.canvas, moveX, moveY, dx, dy);
141
+ this.setImageCanvasState({
142
+ ...canvasState,
143
+ zoom: nextZoom,
144
+ zoomX,
145
+ zoomY,
146
+ });
147
+ }
148
+ }
@@ -0,0 +1,10 @@
1
+ export declare const getCenteredImageOffset: (canvas: HTMLCanvasElement, image: HTMLImageElement) => {
2
+ dx: number;
3
+ dy: number;
4
+ };
5
+ export declare const calculateInitZoom: (canvas: HTMLCanvasElement, image: HTMLImageElement) => number;
6
+ export declare const getZoomPivotFromCanvasCenter: (canvas: HTMLCanvasElement, moveX: number, moveY: number, dx: number, dy: number) => {
7
+ zoomX: number;
8
+ zoomY: number;
9
+ };
10
+ export declare const limitZoom: (zoom: number, min?: number, max?: number) => number;
@@ -0,0 +1,24 @@
1
+ export const getCenteredImageOffset = (canvas, image) => {
2
+ return {
3
+ dx: (canvas.clientWidth - image.naturalWidth) / 2,
4
+ dy: (canvas.clientHeight - image.naturalHeight) / 2,
5
+ };
6
+ };
7
+ export const calculateInitZoom = (canvas, image) => {
8
+ if (!canvas || !image.naturalWidth)
9
+ return 1;
10
+ const canvasWidth = canvas.clientWidth;
11
+ const canvasHeight = canvas.clientHeight;
12
+ const imageWidth = image.naturalWidth;
13
+ const imageHeight = image.naturalHeight;
14
+ const scaleX = canvasWidth / imageWidth;
15
+ const scaleY = canvasHeight / imageHeight;
16
+ return Math.min(scaleX, scaleY);
17
+ };
18
+ export const getZoomPivotFromCanvasCenter = (canvas, moveX, moveY, dx, dy) => {
19
+ return {
20
+ zoomX: canvas.clientWidth / 2 - dx - moveX,
21
+ zoomY: canvas.clientHeight / 2 - dy - moveY,
22
+ };
23
+ };
24
+ export const limitZoom = (zoom, min, max) => min !== undefined && zoom < min ? min : max !== undefined && zoom > max ? max : zoom;
@@ -0,0 +1,75 @@
1
+ import { AnnotationCanvasDrawing, AnnotationCanvasOptionsZoom, ImageCanvasState } from '../types';
2
+ import { Annotation } from '../annotation/annotationTypes';
3
+ export declare class AnnotationEngine {
4
+ private imageCanvas;
5
+ private image;
6
+ private imageCanvasState;
7
+ private annotationsCanvas;
8
+ private annotations;
9
+ private setAnnotationsCallback;
10
+ private drawing;
11
+ private history;
12
+ private enableHotkeys;
13
+ private editable;
14
+ private onChange?;
15
+ private showSelectedOnly;
16
+ private panZoom;
17
+ private interactionController;
18
+ private readonly renderers;
19
+ constructor(params: {
20
+ imageCanvas: HTMLCanvasElement;
21
+ image: HTMLImageElement;
22
+ imageCanvasState: ImageCanvasState;
23
+ annotationsCanvas: HTMLCanvasElement;
24
+ annotations: Annotation[];
25
+ setAnnotations: (annotations: Annotation[]) => void;
26
+ drawing: AnnotationCanvasDrawing;
27
+ enableHotkeys: boolean;
28
+ editable?: boolean;
29
+ panZoomEnabled?: boolean;
30
+ zoom?: AnnotationCanvasOptionsZoom;
31
+ onChange?: () => void;
32
+ });
33
+ getImageCanvas(): HTMLCanvasElement;
34
+ getImageCanvasState(): ImageCanvasState;
35
+ getAnnotationsCanvas(): HTMLCanvasElement;
36
+ getDrawing(): AnnotationCanvasDrawing;
37
+ setDrawing(drawing: AnnotationCanvasDrawing): void;
38
+ isEditable(): boolean;
39
+ setEditable(editable: boolean): void;
40
+ setPanZoomEnabled(enabled: boolean): void;
41
+ getEnableHotkeys(): boolean;
42
+ getShowSelectedOnly(): boolean;
43
+ destroy(): void;
44
+ getZoomRatioLabel(): string;
45
+ initImageCanvas(resetZoom: boolean): void;
46
+ private setupCanvasResolution;
47
+ private drawCanvasAll;
48
+ private drawImageCanvas;
49
+ private drawAnnotationsCanvas;
50
+ private drawCanvas;
51
+ clearCanvasAll(): void;
52
+ private clearCanvas;
53
+ resetCanvas(): void;
54
+ getAnnotations(): import("../annotation/annotationTypes").RectangleAnnotation[];
55
+ setAnnotations(annotation: Annotation[]): void;
56
+ appendAnnotation(annotation: Annotation): void;
57
+ private syncAnnotations;
58
+ private drawAnnotations;
59
+ getSelectedAnnotation(): Annotation | null;
60
+ setSelectedAnnotation(target: Annotation | null): void;
61
+ getPointerRenderState(): {
62
+ mousePoint: import("../types").Point | null;
63
+ } | null;
64
+ getDrawRenderState(): unknown;
65
+ onMouseDown(e: MouseEvent): void;
66
+ onMouseMove(e: MouseEvent): void;
67
+ onMouseUp(e: MouseEvent): void;
68
+ onMouseLeave(e: MouseEvent): void;
69
+ onWheel(e: WheelEvent): void;
70
+ initHistory(annotations: Annotation[]): void;
71
+ commitHistory(checkInit?: boolean): void;
72
+ deleteSelected(): void;
73
+ toggleShowSelectedOnly(): void;
74
+ undoRedo(isRedo: boolean): void;
75
+ }