dytools-canvas-engine 1.0.0 → 1.0.1

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.
@@ -0,0 +1,733 @@
1
+ /**
2
+ * Core event used throughout pipelines
3
+ */
4
+ interface CanvasEvent {
5
+ type: string;
6
+ targets?: InteractiveObject[];
7
+ data?: Record<string, any>;
8
+ stopPropagation?: boolean;
9
+ tool?: ToolPipelineNode;
10
+ }
11
+ /**
12
+ * Rectangular interactive zone with resize handles and move region.
13
+ */
14
+ declare class Zone implements InteractiveObject {
15
+ private _id;
16
+ private _rect;
17
+ private _name;
18
+ private _showName;
19
+ private _nameAnchor;
20
+ private _zIndex;
21
+ private _visible;
22
+ private _color;
23
+ private _selectable;
24
+ private _movable;
25
+ private _resizable;
26
+ get id(): string;
27
+ set id(value: string);
28
+ get rect(): Rectangle;
29
+ get name(): string | null;
30
+ set name(value: string | null);
31
+ get showName(): boolean;
32
+ set showName(value: boolean);
33
+ get nameAnchor(): "TopRight" | "TopLeft" | "BottomLeft" | "BottomRight" | "Center";
34
+ set nameAnchor(value: "TopRight" | "TopLeft" | "BottomLeft" | "BottomRight" | "Center");
35
+ get zIndex(): number;
36
+ set zIndex(value: number);
37
+ get visible(): boolean;
38
+ set visible(value: boolean | undefined);
39
+ get color(): string;
40
+ set color(value: string);
41
+ get selectable(): boolean;
42
+ set selectable(value: boolean);
43
+ get movable(): boolean;
44
+ set movable(value: boolean);
45
+ get resizable(): boolean;
46
+ set resizable(value: boolean);
47
+ private static readonly HANDLE_SIZE;
48
+ constructor(id: string, x: number, y: number, width: number, height: number, color?: string, isSelectable?: boolean, isMovable?: boolean, isResizable?: boolean, zIndex?: number);
49
+ constructor(id: string, rect: Rectangle, color?: string, isSelectable?: boolean, isMovable?: boolean, isResizable?: boolean, zIndex?: number);
50
+ /** Tight bounds of the zone body */
51
+ getBounds(): Rectangle;
52
+ /** Bounds including resize handles */
53
+ getOuterBounds(): Rectangle;
54
+ /**
55
+ * Reports which interactions this zone supports at the given point.
56
+ * If atPoint is null, returns all possible interactions.
57
+ */
58
+ getEventInteractions(atPoint: Point | null): EventInteraction[];
59
+ /**
60
+ * Returns eight candidate handle positions for resizing, adjusting
61
+ * their offset (inside, middle, or outside) based on object dimensions.
62
+ */
63
+ getResizeHandles(): Array<{
64
+ rect: Rectangle;
65
+ handleIndex: number;
66
+ }>;
67
+ /** No custom event handling; defaults to flags and tool logic */
68
+ handleEvent?(evt: CanvasEvent, emitExternal: (type: keyof EngineEventPayloads, payload: EngineEventPayloads[keyof EngineEventPayloads]) => void): void;
69
+ /** Render the zone with optional handles and selection styling */
70
+ render(ctx: CanvasRenderingContext2D, flags: Set<string>): void;
71
+ /**
72
+ * Return a rgba() string for any valid CSS color with a custom alpha.
73
+ * Uses the canvas context to normalize named colors, hex, hsl(), etc.
74
+ */
75
+ private withAlpha;
76
+ }
77
+ /**
78
+ * Rectangular interactive zone with resize handles and move region.
79
+ */
80
+ declare class CircleObject implements InteractiveObject {
81
+ private _id;
82
+ private _center;
83
+ private _radius;
84
+ private _name;
85
+ private _showName;
86
+ private _zIndex;
87
+ private _visible;
88
+ private _color;
89
+ private _selectable;
90
+ private _movable;
91
+ private _resizable;
92
+ get id(): string;
93
+ set id(value: string);
94
+ get center(): Point;
95
+ get name(): string | null;
96
+ set name(value: string | null);
97
+ get showName(): boolean;
98
+ set showName(value: boolean);
99
+ get zIndex(): number;
100
+ set zIndex(value: number);
101
+ get visible(): boolean;
102
+ set visible(value: boolean | undefined);
103
+ get color(): string;
104
+ set color(value: string);
105
+ get selectable(): boolean;
106
+ set selectable(value: boolean);
107
+ get movable(): boolean;
108
+ set movable(value: boolean);
109
+ get resizable(): boolean;
110
+ set resizable(value: boolean);
111
+ private static readonly HANDLE_SIZE;
112
+ constructor(id: string, x: number, y: number, radius: number, color?: string, isSelectable?: boolean, isMovable?: boolean, isResizable?: boolean, zIndex?: number);
113
+ constructor(id: string, center: Point, radius: number, color?: string, isSelectable?: boolean, isMovable?: boolean, isResizable?: boolean, zIndex?: number);
114
+ /** Tight bounds of the zone body */
115
+ getBounds(): Rectangle;
116
+ /** Bounds including resize handles */
117
+ getOuterBounds(): Rectangle;
118
+ /**
119
+ * Reports which interactions this zone supports at the given point.
120
+ * If atPoint is null, returns all possible interactions.
121
+ */
122
+ getEventInteractions(atPoint: Point | null): EventInteraction[];
123
+ /**
124
+ * Returns eight candidate handle positions for resizing, adjusting
125
+ * their offset (inside, middle, or outside) based on object dimensions.
126
+ */
127
+ getResizeHandles(): Array<{
128
+ rect: Rectangle;
129
+ handleIndex: number;
130
+ }>;
131
+ /** No custom event handling; defaults to flags and tool logic */
132
+ handleEvent?(evt: CanvasEvent, emitExternal: (type: keyof EngineEventPayloads, payload: EngineEventPayloads[keyof EngineEventPayloads]) => void): void;
133
+ /** Render the zone with optional handles and selection styling */
134
+ render(ctx: CanvasRenderingContext2D, flags: Set<string>): void;
135
+ /**
136
+ * Return a rgba() string for any valid CSS color with a custom alpha.
137
+ * Uses the canvas context to normalize named colors, hex, hsl(), etc.
138
+ */
139
+ private withAlpha;
140
+ }
141
+ /**
142
+ * PolygonObject: an InteractiveObject representing a polygon with arbitrary points.
143
+ */
144
+ declare class PolygonObject implements InteractiveObject {
145
+ id: string;
146
+ zIndex: number;
147
+ visible: boolean;
148
+ color: string;
149
+ selectable: boolean;
150
+ movable: boolean;
151
+ private _points;
152
+ /**
153
+ * @param id unique identifier
154
+ * @param points array of Point objects
155
+ * @param color stroke color (default "#000000")
156
+ * @param isSelectable whether selectable (default false)
157
+ * @param isMovable whether movable (default false)
158
+ * @param zIndex z-order (default 0)
159
+ */
160
+ constructor(id: string, points: Point[], color?: string, isSelectable?: boolean, isMovable?: boolean, zIndex?: number);
161
+ get points(): Point[];
162
+ /** Tight bounds of the polygon */
163
+ getBounds(): Rectangle;
164
+ /** For polygons, outer bounds = bounds (no handles) */
165
+ getOuterBounds(): Rectangle;
166
+ /**
167
+ * Reports which interactions this polygon supports at the given point.
168
+ * If atPoint is null, returns all possible interactions.
169
+ */
170
+ getEventInteractions(atPoint: Point | null): EventInteraction[];
171
+ /**
172
+ * Handles property and move events. (No resize support)
173
+ */
174
+ handleEvent?(evt: CanvasEvent, emitExternal: (type: keyof EngineEventPayloads, payload: EngineEventPayloads[keyof EngineEventPayloads]) => void): void;
175
+ /**
176
+ * Render the polygon with selection and hover styling.
177
+ */
178
+ render(ctx: CanvasRenderingContext2D, flags: Set<string>): void;
179
+ /**
180
+ * Return an rgba() string for any valid CSS color with a custom alpha.
181
+ */
182
+ private withAlpha;
183
+ }
184
+ /**
185
+ * EventSource that listens to DOM pointer and wheel events on a canvas
186
+ * and window resize events, emitting them into the engine.
187
+ */
188
+ declare class DomEventSource implements EventSource {
189
+ private listeners;
190
+ private canvas;
191
+ constructor(canvas: HTMLCanvasElement);
192
+ attach(emit: (evt: CanvasEvent) => void, engine: CanvasEngine): void;
193
+ detach(): void;
194
+ }
195
+ interface EventInteraction {
196
+ /** The semantic event prefix this region supports, e.g. “move”, “resize”, “select”
197
+ * but will also support resizeStart and resizeEnd if "resize" is included */
198
+ eventTypePrefix: string;
199
+ /**
200
+ * Which data properties a tool can count on being present on the event.
201
+ * E.g. “point” (world coords), “handleIndex” for resize, “delta” for drag, etc.
202
+ */
203
+ handleIndex?: number;
204
+ }
205
+ /**
206
+ * Basic interactive object contract
207
+ */
208
+ interface InteractiveObject {
209
+ id: string;
210
+ zIndex: number;
211
+ visible?: boolean;
212
+ selectable?: boolean;
213
+ /** The tight bounds of the object’s geometry (for hit-tests, layout) */
214
+ getBounds(): Rectangle;
215
+ /** The full interaction bounds (handles, halos, shadows, etc.) */
216
+ getOuterBounds(): Rectangle;
217
+ /** Optional hit-test for pointer events */
218
+ getEventInteractions(atPoint: Point | null): EventInteraction[];
219
+ /** Optional event handler for custom events */
220
+ handleEvent?(evt: CanvasEvent, emitExternal: (type: keyof EngineEventPayloads, payload: EngineEventPayloads[keyof EngineEventPayloads]) => void): void;
221
+ /** Render the object, given selection/hover flags */
222
+ render(ctx: CanvasRenderingContext2D, flags: Set<string>): void;
223
+ }
224
+ /**
225
+ * Converts raw inputs into semantic pipeline events
226
+ */
227
+ interface EventSource {
228
+ attach(emit: (evt: CanvasEvent) => void, engine: CanvasEngine): void;
229
+ detach?(): void;
230
+ }
231
+ /**
232
+ * Converts semantic events into higher-level gestures
233
+ */
234
+ interface GestureRecognizer {
235
+ process(evt: CanvasEvent, emit: (evt: CanvasEvent) => void): void;
236
+ }
237
+ /**
238
+ * Consumes semantic events and drives object interactions
239
+ */
240
+ interface ToolPipelineNode {
241
+ priority?: number;
242
+ processToolEvent(evt: CanvasEvent, emit: (evt: CanvasEvent) => void, emitExternal: (type: keyof EngineEventPayloads, payload: EngineEventPayloads[keyof EngineEventPayloads]) => void, engine: CanvasEngine, lock: ToolLock): void;
243
+ }
244
+ declare const EngineEventType: {
245
+ readonly ObjectPositionChanged: "object:positionchanged";
246
+ readonly ObjectResized: "object:resized";
247
+ readonly ObjectMoved: "object:moved";
248
+ readonly ObjectColorChanged: "object:colorchanged";
249
+ readonly ObjectZIndexChanged: "object:zindexchanged";
250
+ readonly ObjectSelectableChanged: "object:selectablechanged";
251
+ readonly ObjectMovableChanged: "object:movablechanged";
252
+ readonly ObjectResizableChanged: "object:resizablechanged";
253
+ readonly ObjectVisibleChanged: "object:visiblechanged";
254
+ readonly ObjectChanged: "object:changed";
255
+ readonly ObjectSelected: "object:selected";
256
+ readonly ObjectDeselected: "object:deselected";
257
+ readonly ObjectFlagsAdded: "object:flagsadded";
258
+ readonly ObjectFlagsRemoved: "object:flagsremoved";
259
+ readonly ObjectAdded: "object:added";
260
+ readonly ObjectRemoved: "object:removed";
261
+ readonly ViewportChanged: "viewport:changed";
262
+ readonly DrawingCompleted: "drawing:completed";
263
+ readonly EngineStarted: "engine:started";
264
+ readonly EngineError: "engine:error";
265
+ readonly EngineBackgroundChanged: "engine:backgroundchanged";
266
+ readonly EngineRenderStarted: "engine:renderstarted";
267
+ readonly EngineRenderCompleted: "engine:rendercompleted";
268
+ };
269
+ type EngineEventType = typeof EngineEventType[keyof typeof EngineEventType];
270
+ interface EngineEventPayloads {
271
+ [EngineEventType.ObjectPositionChanged]: {
272
+ object: InteractiveObject;
273
+ oldPosition: Rectangle;
274
+ newPosition: Rectangle;
275
+ };
276
+ [EngineEventType.ObjectResized]: {
277
+ object: InteractiveObject;
278
+ oldSize: Size;
279
+ newSize: Size;
280
+ };
281
+ [EngineEventType.ObjectMoved]: {
282
+ object: InteractiveObject;
283
+ oldPoint: Point;
284
+ newPoint: Point;
285
+ };
286
+ [EngineEventType.ObjectColorChanged]: {
287
+ object: InteractiveObject;
288
+ oldColor: string;
289
+ newColor: string;
290
+ };
291
+ [EngineEventType.ObjectZIndexChanged]: {
292
+ object: InteractiveObject;
293
+ oldZIndex: number;
294
+ newZIndex: number;
295
+ };
296
+ [EngineEventType.ObjectSelectableChanged]: {
297
+ object: InteractiveObject;
298
+ oldSelectable: boolean;
299
+ newSelectable: boolean;
300
+ };
301
+ [EngineEventType.ObjectMovableChanged]: {
302
+ object: InteractiveObject;
303
+ oldMovable: boolean;
304
+ newMovable: boolean;
305
+ };
306
+ [EngineEventType.ObjectResizableChanged]: {
307
+ object: InteractiveObject;
308
+ oldResizable: boolean;
309
+ newResizable: boolean;
310
+ };
311
+ [EngineEventType.ObjectVisibleChanged]: {
312
+ object: InteractiveObject;
313
+ oldVisible: boolean;
314
+ newVisible: boolean;
315
+ };
316
+ [EngineEventType.ObjectChanged]: {
317
+ object: InteractiveObject;
318
+ property: string;
319
+ oldValue: unknown;
320
+ newValue: unknown;
321
+ };
322
+ [EngineEventType.ObjectSelected]: {
323
+ object: InteractiveObject;
324
+ };
325
+ [EngineEventType.ObjectDeselected]: {
326
+ object: InteractiveObject;
327
+ };
328
+ [EngineEventType.ObjectFlagsAdded]: {
329
+ object: InteractiveObject;
330
+ addedFlags: string[];
331
+ currentFlags: string[];
332
+ };
333
+ [EngineEventType.ObjectFlagsRemoved]: {
334
+ object: InteractiveObject;
335
+ removedFlags: string[];
336
+ currentFlags: string[];
337
+ };
338
+ [EngineEventType.ObjectAdded]: {
339
+ object: InteractiveObject;
340
+ };
341
+ [EngineEventType.ObjectRemoved]: {
342
+ object: InteractiveObject;
343
+ };
344
+ [EngineEventType.DrawingCompleted]: {
345
+ rect: Rectangle;
346
+ maxZ: number;
347
+ };
348
+ [EngineEventType.ViewportChanged]: {
349
+ oldPan: Vector;
350
+ newPan: Vector;
351
+ oldZoom: number;
352
+ newZoom: number;
353
+ };
354
+ [EngineEventType.EngineStarted]: {};
355
+ [EngineEventType.EngineError]: {
356
+ error: Error;
357
+ message: string;
358
+ };
359
+ [EngineEventType.EngineRenderStarted]: {};
360
+ [EngineEventType.EngineRenderCompleted]: {};
361
+ [EngineEventType.EngineBackgroundChanged]: {
362
+ newImage: HTMLImageElement;
363
+ newWidth: number;
364
+ newHeight: number;
365
+ oldWidth: number;
366
+ oldHeight: number;
367
+ };
368
+ }
369
+ /**
370
+ * Renders overlays, handles, or decorations each frame
371
+ */
372
+ interface RenderPipelineNode {
373
+ renderBeforeAll?(ctx: CanvasRenderingContext2D, engine: CanvasEngine): void;
374
+ renderBeforeObject?(ctx: CanvasRenderingContext2D, engine: CanvasEngine, obj: InteractiveObject): void;
375
+ renderAfterObject?(ctx: CanvasRenderingContext2D, engine: CanvasEngine, obj: InteractiveObject): void;
376
+ renderAfterAll?(ctx: CanvasRenderingContext2D, engine: CanvasEngine): void;
377
+ }
378
+ interface InteractionEntry {
379
+ object: InteractiveObject;
380
+ interaction: EventInteraction;
381
+ }
382
+ interface CanvasEngineOptions {
383
+ initialZoom?: number;
384
+ initialPan?: {
385
+ x: number;
386
+ y: number;
387
+ };
388
+ defaultMode?: string;
389
+ addTimerEventSource?: boolean;
390
+ proxyObjectsForEvents?: boolean;
391
+ }
392
+ interface ModeConfig {
393
+ tools: string[];
394
+ renderers?: RenderPipelineNode[];
395
+ /**
396
+ * Optional per-event hook where a mode can inspect every CanvasEvent
397
+ * and imperatively switch modes by calling switchMode(name).
398
+ */
399
+ handleEvent?: (evt: CanvasEvent, engine: CanvasEngine) => void;
400
+ }
401
+ /**
402
+ * Main orchestrator: pipelines, objects, and flags
403
+ */
404
+ declare class CanvasEngine {
405
+ private readonly container;
406
+ private readonly canvas;
407
+ private readonly ctx;
408
+ private readonly defaultBackgroundImage;
409
+ private document;
410
+ private emitter;
411
+ private toolLockManager;
412
+ private eventSources;
413
+ private recognizers;
414
+ private tools;
415
+ private manualRenderers;
416
+ private modeRenderers;
417
+ private eventQueue;
418
+ private processingQueue;
419
+ private renderScheduled;
420
+ private flags;
421
+ private objects;
422
+ private needsRender;
423
+ private toolRegistry;
424
+ private modes;
425
+ private currentMode;
426
+ private options;
427
+ private readonly defaultOptions;
428
+ get pan(): Vector;
429
+ get zoom(): number;
430
+ _pan: Vector;
431
+ _zoom: number;
432
+ /** Convert screen coordinates to world coordinates */
433
+ screenToWorld(screen: Point): Point;
434
+ /** Convert world coordinates to screen coordinates */
435
+ worldToScreen(point: Point): Point;
436
+ constructor(selector: string, opts?: CanvasEngineOptions);
437
+ /** The engine is also a tool to allow certain events to be handled **/
438
+ processToolEvent(evt: CanvasEvent, emit: (evt: CanvasEvent) => void, emitExternal: (type: keyof EngineEventPayloads, payload: EngineEventPayloads[keyof EngineEventPayloads]) => void, engine: CanvasEngine, lock: ToolLock): void;
439
+ /** INTERNAL SETUP **/
440
+ private createOptions;
441
+ private setupDefaultEventSources;
442
+ private setupDefaultRecognizers;
443
+ private setupDefaultTools;
444
+ private setupDefaultModes;
445
+ private setupRenderNodes;
446
+ /** EXTERNAL EVENTS */
447
+ on: <K extends keyof EngineEventPayloads>(type: K, callback: (payload: EngineEventPayloads[K]) => void, options?: {
448
+ once?: boolean;
449
+ }) => void;
450
+ off: <K extends keyof EngineEventPayloads>(type: K, callback: (payload: EngineEventPayloads[K]) => void) => void;
451
+ private emitExternal;
452
+ private wrapInteractiveObject;
453
+ /** TOOL REGISTRY **/
454
+ /**
455
+ * Register a tool under a key.
456
+ * If `singleton=true`, the same instance is reused everywhere;
457
+ * otherwise `.factory()` is called each time you switch modes.
458
+ */
459
+ registerTool(key: string, factory: () => ToolPipelineNode, singleton?: boolean): void;
460
+ /** Grab an instance for a given key */
461
+ private resolveTool;
462
+ /** MODE MANAGEMENT **/
463
+ defineMode(name: string, config: ModeConfig): void;
464
+ get mode(): string;
465
+ switchMode(name: string): void;
466
+ get renderers(): RenderPipelineNode[];
467
+ /** Background / DocumentObject / canvas-background **/
468
+ get width(): number;
469
+ get height(): number;
470
+ get documentBlob(): Blob;
471
+ setBackgroundImage(imageOrUrl: string | File | Blob): Promise<void>;
472
+ private loadImageFromUrl;
473
+ private loadImage;
474
+ /** OBJECT MANAGEMENT **/
475
+ getObject(id: string): InteractiveObject | undefined;
476
+ addObject(obj: InteractiveObject): void;
477
+ removeObject(objOrId: string | InteractiveObject): void;
478
+ /**
479
+ * API helper: enqueue move to absolute position.
480
+ */
481
+ moveObjectToPoint(object: InteractiveObject, x: number, y: number): void;
482
+ moveObjectToPoint(id: string, x: number, y: number): void;
483
+ moveObjectToPoint(object: InteractiveObject, point: Point): void;
484
+ moveObjectToPoint(id: string, point: Point): void;
485
+ /**
486
+ * API helper: enqueue move by vector.
487
+ */
488
+ moveObjectByVector(object: InteractiveObject, dx: number, dy: number): void;
489
+ moveObjectByVector(id: string, dx: number, dy: number): void;
490
+ moveObjectByVector(object: InteractiveObject, vector: Vector): void;
491
+ moveObjectByVector(id: string, vector: Vector): void;
492
+ /**
493
+ * API helper: enqueue resize to absolute size.
494
+ */
495
+ resizeObjectToSize(object: InteractiveObject, width: number, height: number): void;
496
+ resizeObjectToSize(id: string, width: number, height: number): void;
497
+ resizeObjectToSize(object: InteractiveObject, size: Size): void;
498
+ resizeObjectToSize(id: string, size: Size): void;
499
+ /**
500
+ * API helper: enqueue resize by delta.
501
+ */
502
+ resizeObjectByVector(object: InteractiveObject, dw: number, dh: number): void;
503
+ resizeObjectByVector(id: string, dw: number, dh: number): void;
504
+ resizeObjectByVector(object: InteractiveObject, vector: Vector): void;
505
+ resizeObjectByVector(id: string, vector: Vector): void;
506
+ /**
507
+ * API helper: enqueue color change.
508
+ */
509
+ setObjectColor(objOrId: string | InteractiveObject, color: string): void;
510
+ /**
511
+ * API helper: enqueue Z-index change.
512
+ */
513
+ setObjectZIndex(objOrId: string | InteractiveObject, zIndex: number): void;
514
+ /**
515
+ * API helper: enqueue selectability change.
516
+ */
517
+ updateObjectSelectability(objOrId: string | InteractiveObject, isSelectable: boolean): void;
518
+ /**
519
+ * API helper: enqueue movability change.
520
+ */
521
+ updateObjectMovability(objOrId: string | InteractiveObject, isMovable: boolean): void;
522
+ /**
523
+ * API helper: enqueue resizability change.
524
+ */
525
+ updateObjectResizeability(objOrId: string | InteractiveObject, isResizable: boolean): void;
526
+ /**
527
+ * API helper: enqueue visibility change.
528
+ */
529
+ updateObjectVisibility(objOrId: string | InteractiveObject, visible: boolean): void;
530
+ /** FLAG MANAGEMENT **/
531
+ setFlag(objOrId: string | InteractiveObject, flag: string): void;
532
+ clearFlag(objOrId: string | InteractiveObject, flag: string): void;
533
+ hasFlag(objOrId: string | InteractiveObject, flag: string): boolean;
534
+ clearAllFlags(flag: string): void;
535
+ getObjectsWithFlag(flag: string): InteractiveObject[];
536
+ /** ZOOM AND PAN API */
537
+ setZoom(zoom: number, center?: Point): void;
538
+ setPan(x: number, y: number): void;
539
+ setPan(pan: Vector): void;
540
+ /** SELECTION HELPERS **/
541
+ setSelectedObjects(objOrIds: (string | InteractiveObject)[]): void;
542
+ getSelectedObjects(): InteractiveObject[];
543
+ /**
544
+ * Returns all interactive objects in this engine.
545
+ */
546
+ getAllObjects(): InteractiveObject[];
547
+ /**
548
+ * Returns a map from event-prefix → topmost object that supports it at (x,y).
549
+ */
550
+ getTopMostInteractionPerEventType(point: Point): Map<string, InteractionEntry>;
551
+ /** QUEUE HELPERS **/
552
+ /** Called by any EventSource to enqueue a raw event */
553
+ private enqueue;
554
+ /** Kick off the queue-processor if it isn’t already running */
555
+ private ensureEventLoop;
556
+ /** Drains the queue, running recognizers → tools → objects */
557
+ private processQueue;
558
+ /** PIPELINE REGISTRATION **/
559
+ addEventSource(src: EventSource): void;
560
+ removeEventSource(src: EventSource): void;
561
+ addRecognizer(node: GestureRecognizer): void;
562
+ addRenderNode(node: RenderPipelineNode): void;
563
+ /**
564
+ * Convenience: auto-register by interface
565
+ */
566
+ register(node: unknown): void;
567
+ /** RENDERING **/
568
+ redraw(): void;
569
+ private requestRender;
570
+ private doRender;
571
+ dispose(): void;
572
+ }
573
+ declare class ToolLockManager {
574
+ private currentTool;
575
+ get isLocked(): boolean;
576
+ getToolLock(tool: ToolPipelineNode): ToolLock;
577
+ }
578
+ interface ToolLock {
579
+ acquire(): boolean;
580
+ release(): void;
581
+ hasLock(): boolean;
582
+ }
583
+ declare class MoveTool implements ToolPipelineNode {
584
+ private objectsBeingMoved;
585
+ private lastDelta;
586
+ processToolEvent(evt: CanvasEvent, emit: (e: CanvasEvent) => void, emitExternal: (type: keyof EngineEventPayloads, payload: EngineEventPayloads[keyof EngineEventPayloads]) => void, engine: CanvasEngine, lock: ToolLock): void;
587
+ }
588
+ /**
589
+ * Tool that handles click-based selection of interactive objects.
590
+ */
591
+ declare class SelectTool implements ToolPipelineNode {
592
+ /** Higher priority to process clicks before other tools */
593
+ priority: number;
594
+ processToolEvent(evt: CanvasEvent, emit: (evt: CanvasEvent) => void, emitExternal: (type: keyof EngineEventPayloads, payload: EngineEventPayloads[keyof EngineEventPayloads]) => void, engine: CanvasEngine, lock: ToolLock): void;
595
+ }
596
+ /**
597
+ * Tool that marks all objects under the pointer as hovered on each move.
598
+ */
599
+ declare class HoverTool implements ToolPipelineNode {
600
+ /** Lower priority so selection and other tools run first */
601
+ priority: number;
602
+ processToolEvent(evt: CanvasEvent, emit: (evt: CanvasEvent) => void, emitExternal: (type: keyof EngineEventPayloads, payload: EngineEventPayloads[keyof EngineEventPayloads]) => void, engine: CanvasEngine, lock: ToolLock): void;
603
+ }
604
+ declare class ResizeTool implements ToolPipelineNode {
605
+ private activeHandleIndex;
606
+ private objectsBeingResized;
607
+ private lastDelta;
608
+ processToolEvent(evt: CanvasEvent, emit: (e: CanvasEvent) => void, emitExternal: (type: keyof EngineEventPayloads, payload: EngineEventPayloads[keyof EngineEventPayloads]) => void, engine: CanvasEngine, lock: ToolLock): void;
609
+ }
610
+ /**
611
+ * Tool to draw new rectangular zones via drag gestures.
612
+ */
613
+ declare class DrawingTool implements ToolPipelineNode, RenderPipelineNode {
614
+ private drawing;
615
+ private startPt;
616
+ private currentPt;
617
+ private defaultColor;
618
+ constructor(defaultColor?: string);
619
+ processToolEvent(evt: CanvasEvent, emit: (evt: CanvasEvent) => void, emitExternal: (type: keyof EngineEventPayloads, payload: EngineEventPayloads[keyof EngineEventPayloads]) => void, engine: CanvasEngine, lock: ToolLock): void;
620
+ renderAfterAll(ctx: CanvasRenderingContext2D, engine: CanvasEngine): void;
621
+ }
622
+ /**
623
+ * Ray-casting algorithm to test if a point is inside a polygon.
624
+ */
625
+ /**
626
+ * Tool to draw a freeform lasso and select all objects fully inside the polygon.
627
+ */
628
+ declare class LassoTool implements ToolPipelineNode, RenderPipelineNode {
629
+ private lassoing;
630
+ private points;
631
+ processToolEvent(evt: CanvasEvent, emit: (evt: CanvasEvent) => void, emitExternal: (type: keyof EngineEventPayloads, payload: EngineEventPayloads[keyof EngineEventPayloads]) => void, engine: CanvasEngine, lock: ToolLock): void;
632
+ renderAfterAll(ctx: CanvasRenderingContext2D, engine: CanvasEngine): void;
633
+ }
634
+ /**
635
+ * Translates high‐level API calls into the standard semantic events
636
+ * so they flow through the same move/resize/remove pipelines.
637
+ */
638
+ declare class ApiTool implements ToolPipelineNode {
639
+ processToolEvent(evt: CanvasEvent, emit: (evt: CanvasEvent) => void, emitExternal: (type: keyof EngineEventPayloads, payload: EngineEventPayloads[keyof EngineEventPayloads]) => void, engine: CanvasEngine, lock: ToolLock): void;
640
+ }
641
+ /**
642
+ * Recognizes drag gestures: pointerdown, pointermove, pointerup,
643
+ * emitting dragstart, drag, and dragEnd events with world and screen data.
644
+ */
645
+ declare class DragGestureRecognizer implements GestureRecognizer {
646
+ private activePointerIds;
647
+ private isDragging;
648
+ private buttonStart;
649
+ private origin;
650
+ private moveThreshold;
651
+ constructor(moveThreshold?: number);
652
+ process(evt: CanvasEvent, emit: (evt: CanvasEvent) => void): void;
653
+ private handleStart;
654
+ private handleMove;
655
+ private handleEnd;
656
+ private getTouches;
657
+ private findActiveTouch;
658
+ }
659
+ declare class ClickGestureRecognizer implements GestureRecognizer {
660
+ private activePointerIds;
661
+ private origin;
662
+ private moveThreshold;
663
+ constructor(moveThreshold?: number);
664
+ process(evt: CanvasEvent, emit: (evt: CanvasEvent) => void): void;
665
+ private handleStart;
666
+ private handleMove;
667
+ private handleEnd;
668
+ private getTouches;
669
+ private findActiveTouch;
670
+ }
671
+ declare class Point {
672
+ x: number;
673
+ y: number;
674
+ constructor(x: number, y: number);
675
+ equals(other: Point): boolean;
676
+ static equals(a: Point, b: Point): boolean;
677
+ getDeltaFromPoint(other: Point): Vector;
678
+ getDeltaToPoint(other: Point): Vector;
679
+ add(vector: Vector): Point;
680
+ asVector(): Vector;
681
+ subtract(vector: Vector): Point;
682
+ isInside(rect: Rectangle): boolean;
683
+ clone(): Point;
684
+ isInPolygon(poly: Polygon): boolean;
685
+ }
686
+ declare class Size {
687
+ width: number;
688
+ height: number;
689
+ constructor(width: number, height: number);
690
+ equals(other: Size): boolean;
691
+ static equals(a: Size, b: Size): boolean;
692
+ clone(): Size;
693
+ }
694
+ declare class Rectangle {
695
+ origin: Point;
696
+ size: Size;
697
+ constructor(x: number, y: number, width: number, height: number);
698
+ constructor(origin: Point, size: Size);
699
+ equals(other: Rectangle): boolean;
700
+ static equals(a: Rectangle, b: Rectangle): boolean;
701
+ contains(point: Point): boolean;
702
+ clone(): Rectangle;
703
+ center(): Point;
704
+ normalized(): Rectangle;
705
+ isNormalized(): boolean;
706
+ normalize(): void;
707
+ expand(rect: Rectangle): Rectangle;
708
+ move(vector: Vector): Rectangle;
709
+ toPolygon(): Polygon;
710
+ }
711
+ declare class Vector {
712
+ x: number;
713
+ y: number;
714
+ constructor(x: number, y: number);
715
+ equals(other: Vector): boolean;
716
+ static equals(a: Vector, b: Vector): boolean;
717
+ add(other: Vector): Vector;
718
+ subtract(other: Vector): Vector;
719
+ scale(factor: number): Vector;
720
+ length(): number;
721
+ normalize(): Vector;
722
+ clone(): Vector;
723
+ }
724
+ declare class Polygon {
725
+ points: Point[];
726
+ constructor(points: Point[]);
727
+ equals(other: Polygon): boolean;
728
+ static equals(a: Polygon, b: Polygon): boolean;
729
+ containsPoint(pt: Point): boolean;
730
+ containsRect(rect: Rectangle): boolean;
731
+ }
732
+
733
+ export { ApiTool, CanvasEngine, type CanvasEvent, CircleObject, ClickGestureRecognizer, DomEventSource, DragGestureRecognizer, DrawingTool, type EngineEventPayloads, EngineEventType, type EventSource, type GestureRecognizer, HoverTool, type InteractiveObject, LassoTool, MoveTool, Point, Polygon, PolygonObject, Rectangle, type RenderPipelineNode, ResizeTool, SelectTool, Size, ToolLockManager, type ToolPipelineNode, Vector, Zone };