/** * Shape Module — interactive drawing of predefined shapes: * • Circle — perfect circle (uniform scaling enforced) * • Ellipse — independent width / height * • Square — perfect square (uniform scaling enforced) * • Rectangle — independent width / height * • Arrow — line with arrowhead, endpoints individually editable * * Behaviour: * 1. activate(type) puts the canvas into drag-to-draw mode for that * shape. The user presses-and-drags to size the shape; on mouseup * the shape is finalised, registered as an `_rpAnnotation`, and * selected so resize handles are visible immediately. * 2. All shapes are stored as plain Fabric objects so they participate * in undo / redo, JSON serialisation and the standard eraser flow. * 3. Circle and Square lock uniform scaling so the user can only resize * them proportionally — they can never be turned into ellipses / * rectangles. * 4. The Arrow uses a custom `fabric.Object` subclass with two custom * control handles at the start- and end-points so the user can * drag either tip to reshape the arrow (changing its direction and * length). The whole arrow can still be dragged to reposition. * * The module does NOT modify anything outside its own state; it only * registers / un-registers a few canvas listeners during the active * draw-gesture. */ import { fabric } from 'fabric'; import { ShapeType } from '../types/index.js'; /** * Internal arrow object — extends fabric.Object with two endpoints * (x1, y1) and (x2, y2) expressed in **canvas coordinates** (the same * coordinate system as `left` / `top`). The bounding box is recomputed * from the endpoints on every change. */ export interface RpArrow extends fabric.Object { x1: number; y1: number; x2: number; y2: number; arrowheadSize: number; _updateBBox(): void; } /** * Internal polyline object — a chain of `points` expressed in **canvas * coordinates**. The user builds it up with successive clicks, and once * finalised each vertex is individually draggable via a custom control. */ export interface RpPolyline extends fabric.Object { points: { x: number; y: number; }[]; _rpClosed?: boolean; _updateBBox(): void; } export declare class ShapeModule { private canvas; private isActive; private activeShape; private strokeColor; private strokeWidth; private isDrawing; private startX; private startY; private currentObject; constructor(canvas: fabric.Canvas); /** * Activate drag-to-draw for the requested primitive. * Replaces any currently-active shape tool. */ activate(shape: ShapeType): void; deactivate(): void; /** Set stroke colour for shapes drawn from this point onward. Also * updates the currently-selected shape (if any) so it matches the * global color-picker behaviour used by draw/text/callout. */ setStrokeColor(color: string): void; setStrokeWidth(width: number): void; getIsActive(): boolean; setPlacementBoundsProvider(provider: (() => { left: number; top: number; right: number; bottom: number; } | null) | null): void; private handleMouseDown; private handleMouseMove; private handleMouseUp; /** Double-click while drawing a polyline → finalise it. */ private handleDblClick; /** Enter → finalise; Escape → cancel — only while building a polyline. */ private handleKeyDown; private finalisePolyline; /** * Is the given pointer close enough to the polyline's first vertex to * count as a "close-shape" click? Only true once we have ≥ 3 committed * vertices (i.e. points.length ≥ 4, counting the trailing tracker). * Threshold is 12 screen pixels regardless of zoom. */ private isNearFirstVertex; private cancelPolyline; /** * Keep a polyline's vertices in sync with its left/top as the user drags * the whole shape. Mirrors `wireArrowDragSync`. */ private wirePolylineDragSync; private createShape; /** * Keep an arrow's endpoints in sync with its left/top as the user * drags the whole arrow. Fabric updates `left`/`top` during the * `moving` event but the arrow stores its endpoints in canvas coords, * so we shift them by the per-frame delta. */ private wireArrowDragSync; private updateShapeDuringDraw; private isShapeTooSmall; }