@ue-too/board 0.9.5 → 0.10.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.
Files changed (57) hide show
  1. package/README.md +66 -2
  2. package/boardify/index.d.ts +280 -9
  3. package/camera/base.d.ts +364 -68
  4. package/camera/camera-edge-auto-input.d.ts +105 -0
  5. package/camera/camera-mux/animation-and-lock/animation-and-lock.d.ts +316 -14
  6. package/camera/camera-mux/animation-and-lock/index.d.ts +27 -0
  7. package/camera/camera-mux/animation-and-lock/pan-control-state-machine.d.ts +143 -60
  8. package/camera/camera-mux/animation-and-lock/rotation-control-state-machine.d.ts +143 -55
  9. package/camera/camera-mux/animation-and-lock/zoom-control-state-machine.d.ts +205 -58
  10. package/camera/camera-mux/index.d.ts +26 -0
  11. package/camera/camera-mux/interface.d.ts +161 -5
  12. package/camera/camera-mux/relay.d.ts +79 -16
  13. package/camera/camera-rig/camera-rig.d.ts +536 -94
  14. package/camera/camera-rig/index.d.ts +26 -1
  15. package/camera/camera-rig/pan-handler.d.ts +508 -48
  16. package/camera/camera-rig/rotation-handler.d.ts +353 -31
  17. package/camera/camera-rig/zoom-handler.d.ts +369 -32
  18. package/camera/default-camera.d.ts +173 -26
  19. package/camera/index.d.ts +20 -0
  20. package/camera/interface.d.ts +202 -2
  21. package/camera/update-publisher.d.ts +128 -38
  22. package/camera/utils/coordinate-conversion.d.ts +323 -26
  23. package/camera/utils/index.d.ts +22 -0
  24. package/camera/utils/matrix.d.ts +217 -14
  25. package/camera/utils/position.d.ts +249 -11
  26. package/camera/utils/rotation.d.ts +139 -9
  27. package/camera/utils/zoom.d.ts +72 -4
  28. package/index.d.ts +37 -0
  29. package/index.js +2 -4796
  30. package/index.js.map +39 -38
  31. package/input-interpretation/index.d.ts +29 -0
  32. package/input-interpretation/input-orchestrator.d.ts +197 -0
  33. package/input-interpretation/input-state-machine/index.d.ts +18 -0
  34. package/input-interpretation/input-state-machine/kmt-input-context.d.ts +191 -38
  35. package/input-interpretation/input-state-machine/kmt-input-state-machine.d.ts +201 -85
  36. package/input-interpretation/input-state-machine/touch-input-context.d.ts +76 -10
  37. package/input-interpretation/input-state-machine/touch-input-state-machine.d.ts +138 -17
  38. package/input-interpretation/raw-input-parser/index.d.ts +19 -0
  39. package/input-interpretation/raw-input-parser/vanilla-kmt-event-parser.d.ts +107 -21
  40. package/input-interpretation/raw-input-parser/vanilla-touch-event-parser.d.ts +71 -8
  41. package/input-interpretation/raw-input-publisher/index.d.ts +18 -0
  42. package/input-interpretation/raw-input-publisher/raw-input-publisher.d.ts +133 -37
  43. package/package.json +3 -3
  44. package/utils/canvas-position-dimension.d.ts +282 -1
  45. package/utils/coordinate-conversions/canvas-viewport.d.ts +79 -0
  46. package/utils/coordinate-conversions/viewport-world.d.ts +101 -0
  47. package/utils/coordinate-conversions/window-canvas.d.ts +90 -0
  48. package/utils/coorindate-conversion.d.ts +91 -0
  49. package/utils/drawing.d.ts +151 -3
  50. package/utils/index.d.ts +21 -0
  51. package/utils/observable.d.ts +179 -0
  52. package/utils/ruler.d.ts +36 -0
  53. package/utils/zoomlevel-adjustment.d.ts +144 -8
  54. package/camera/camera-rig/update-batcher/index.d.ts +0 -3
  55. package/camera/camera-rig/update-batcher/position-update-batcher.d.ts +0 -58
  56. package/camera/camera-rig/update-batcher/rotation-update-batcher.d.ts +0 -54
  57. package/camera/camera-rig/update-batcher/zoom-udpate-batcher.d.ts +0 -60
package/README.md CHANGED
@@ -168,9 +168,73 @@ To see detail of each component navigate to the respective readme in the subdire
168
168
 
169
169
  It's recommended to start with the [Board Camera](https://github.com/ue-too/ue-too/tree/main/packages/board/src/camera) since the other parts are built on top of it.
170
170
 
171
- Below is a diagram showing from the user input to how the camera is updated and everything in the middle.
171
+ Below is a diagram showing the data flow from user input to camera updates.
172
+
173
+ ```mermaid
174
+ flowchart TB
175
+ subgraph Input ["Input Layer"]
176
+ CE["🖼️ Canvas Element"]
177
+ CDP["📐 Canvas Proxy"]
178
+ CEP["🎯 Event Parsers<br/><small>KMT + Touch</small>"]
179
+ end
180
+
181
+ subgraph Interpretation ["Input Interpretation"]
182
+ ISM["🔄 Input State Machine<br/><small>interprets user intent</small>"]
183
+ IT["📋 Input Tracker<br/><small>cursor position, canvas info</small>"]
184
+ end
185
+
186
+ subgraph Orchestration ["Input Orchestration"]
187
+ IO["🎛️ Input Orchestrator<br/><small>central routing hub</small>"]
188
+ end
189
+
190
+ subgraph Publishing ["Raw Input Publishing"]
191
+ RIP["📡 Raw Input Publisher"]
192
+ RIO["👂 User Callbacks<br/><small>onInput handlers</small>"]
193
+ end
194
+
195
+ subgraph CameraControl ["Camera Control"]
196
+ CM["🚦 Camera Mux<br/><small>permission control</small>"]
197
+ OCIS["🎬 Other Input Sources<br/><small>animations, programmatic</small>"]
198
+ CR["🎮 Camera Rig<br/><small>restrictions & clamping</small>"]
199
+ end
200
+
201
+ subgraph Camera ["Camera"]
202
+ OC["📷 Observable Camera"]
203
+ ACMO["👂 Camera Observers<br/><small>on handlers</small>"]
204
+ end
205
+
206
+ %% Canvas setup
207
+ CDP -.->|"tracks dimensions"| CE
208
+ CE -->|"DOM events"| CEP
209
+ CDP -->|"canvas info"| IT
210
+
211
+ %% Input interpretation
212
+ CEP -->|"state machine events"| ISM
213
+ ISM <-->|"read/update context"| IT
214
+ ISM -->|"pan, zoom, rotate"| IO
215
+
216
+ %% Orchestrator routing (parallel paths)
217
+ IO -->|"always publish"| RIP
218
+ RIP --> RIO
219
+ IO -->|"ask permission"| CM
220
+
221
+ %% Camera Mux
222
+ OCIS -->|"request input"| CM
223
+ CM -->|"allowPassThrough?"| IO
224
+
225
+ %% Camera execution
226
+ IO -->|"if allowed"| CR
227
+ CR --> OC
228
+ OC --> ACMO
229
+ ```
172
230
 
173
- ![data-flow](https://ue-too.github.io/ue-too/assets/doc-media/entire-process.png)
231
+ **Key concepts:**
232
+ - **Event Parsers**: Register listeners on canvas (works with vanilla, pixi.js, fabric.js, konva)
233
+ - **Input State Machine**: Interprets raw events into camera intents (pan/zoom/rotate)
234
+ - **Input Orchestrator**: Routes outputs in parallel — always publishes raw input, and asks CameraMux for permission
235
+ - **Camera Mux**: Controls input priority (e.g., user input can cancel animations). Returns `{allowPassThrough: true/false}`
236
+ - **Camera Rig**: Applies movement restrictions and clamping before updating camera
237
+ - **Observable Camera**: Final camera state with change observers
174
238
 
175
239
  ## TODO
176
240
  - [x] Add a canvas position dimension publisher that can be used to get the position and dimension of the canvas.
@@ -6,9 +6,59 @@ import { CameraEventMap, CameraState, UnSubscribe } from '../camera/update-publi
6
6
  import { UnsubscribeToUserRawInput, RawUserInputEventMap } from '../input-interpretation/raw-input-publisher';
7
7
  import { CameraMux } from '../camera/camera-mux';
8
8
  import { CameraRig } from '../camera/camera-rig';
9
- import { EdgeAutoCameraInput } from '../camera/camera-edge-auto-input';
10
9
  /**
11
- * Usage
10
+ * Main user-facing API class that provides an infinite canvas with pan, zoom, and rotate capabilities.
11
+ *
12
+ * The Board class is the primary entry point for using the board package. It integrates all subsystems
13
+ * including camera management, input handling, and state machines into a simple, unified API for
14
+ * creating interactive 2D canvases with advanced camera controls.
15
+ *
16
+ * @remarks
17
+ * ## Architecture Overview
18
+ *
19
+ * The Board class orchestrates several subsystems:
20
+ *
21
+ * - **Camera System**: Manages viewport transformations (pan/zoom/rotate) through {@link ObservableBoardCamera}.
22
+ * The camera can be configured with boundaries, zoom limits, and various movement constraints.
23
+ *
24
+ * - **Input System**: Processes user input through state machines for both mouse/keyboard/trackpad (KMT)
25
+ * and touch events. Input is parsed, interpreted, and translated into camera movements.
26
+ *
27
+ * - **Camera Rig**: Enforces constraints and restrictions on camera movement (boundaries, zoom limits,
28
+ * clamping behavior). See {@link CameraRig} for details.
29
+ *
30
+ * - **Camera Multiplexer**: Coordinates between different camera control sources (user input, animations,
31
+ * programmatic control) to ensure smooth transitions. See {@link CameraMux} for details.
32
+ *
33
+ * ## Coordinate Systems
34
+ *
35
+ * The Board supports three coordinate systems:
36
+ *
37
+ * 1. **World Coordinates**: The infinite canvas space where your content lives. When the camera is at
38
+ * position (0, 0) with no zoom or rotation, world coordinates map directly to viewport coordinates.
39
+ *
40
+ * 2. **Viewport Coordinates**: The visible area of the canvas relative to the camera center. The camera
41
+ * center is at (0, 0) in viewport space, with coordinates extending in both directions based on the
42
+ * canvas size.
43
+ *
44
+ * 3. **Window/Canvas Coordinates**: The browser's coordinate system, with (0, 0) at the top-left corner
45
+ * of the canvas element. Use {@link convertWindowPoint2WorldCoord} to convert from window to world space.
46
+ *
47
+ * By default, {@link alignCoordinateSystem} is `true`, which means the Y-axis points down (standard HTML
48
+ * canvas orientation). Set it to `false` to use a mathematical coordinate system where Y points up.
49
+ *
50
+ * ## Main Features
51
+ *
52
+ * - **Camera Control**: Pan, zoom, and rotate the viewport through user input or programmatic API
53
+ * - **Boundaries**: Define world-space boundaries to constrain camera movement
54
+ * - **Zoom Limits**: Set minimum and maximum zoom levels
55
+ * - **Input Modes**: Support for mouse/keyboard/trackpad and touch input with customizable parsers
56
+ * - **Event System**: Subscribe to camera events (pan, zoom, rotate) and input events
57
+ * - **Coordinate Conversion**: Convert between window and world coordinates
58
+ * - **Flexible Configuration**: Extensive options for restricting/clamping camera movement
59
+ *
60
+ * @example
61
+ * Basic setup with drawing
12
62
  * ```typescript
13
63
  * const canvasElement = document.querySelector("canvas") as HTMLCanvasElement;
14
64
  * const board = new Board(canvasElement);
@@ -16,13 +66,14 @@ import { EdgeAutoCameraInput } from '../camera/camera-edge-auto-input';
16
66
  * function draw(timestamp: number) {
17
67
  * board.step(timestamp);
18
68
  *
19
- * // because board can be initialized without a canvas element, the context can be undefined until the canvas is attached
69
+ * // Because board can be initialized without a canvas element,
70
+ * // the context can be undefined until the canvas is attached
20
71
  * if(board.context == undefined) {
21
72
  * return;
22
73
  * }
23
74
  *
24
- * // draw after the board has stepped
25
- * // the coordinate system is the same as before; just that (0, 0) is at the center of the canvas when the camera position is at (0, 0)
75
+ * // Draw after the board has stepped
76
+ * // The coordinate system has (0, 0) at the center of the canvas when camera position is at (0, 0)
26
77
  * board.context.beginPath();
27
78
  * board.context.rect(0, 0, 100, 100);
28
79
  * board.context.fill();
@@ -30,9 +81,81 @@ import { EdgeAutoCameraInput } from '../camera/camera-edge-auto-input';
30
81
  * requestAnimationFrame(draw);
31
82
  * }
32
83
  *
84
+ * requestAnimationFrame(draw);
85
+ * ```
86
+ *
87
+ * @example
88
+ * Handling camera and input events
89
+ * ```typescript
90
+ * const board = new Board(canvasElement);
91
+ *
92
+ * // Listen to camera pan events
93
+ * board.on('pan', (event, cameraState) => {
94
+ * console.log('Camera panned to:', cameraState.position);
95
+ * });
96
+ *
97
+ * // Listen to camera zoom events
98
+ * board.on('zoom', (event, cameraState) => {
99
+ * console.log('Camera zoom level:', cameraState.zoomLevel);
100
+ * });
101
+ *
102
+ * // Listen to raw input events (before camera movement)
103
+ * board.onInput('pan', (event) => {
104
+ * console.log('User is panning');
105
+ * });
33
106
  * ```
34
- * @category Board
35
107
  *
108
+ * @example
109
+ * Configuring boundaries and zoom limits
110
+ * ```typescript
111
+ * const board = new Board(canvasElement);
112
+ *
113
+ * // Set world boundaries
114
+ * board.camera.boundaries = {
115
+ * min: { x: -1000, y: -1000 },
116
+ * max: { x: 1000, y: 1000 }
117
+ * };
118
+ *
119
+ * // Set zoom limits
120
+ * board.camera.setMinZoomLevel(0.1);
121
+ * board.camera.setMaxZoomLevel(5.0);
122
+ *
123
+ * // Ensure entire viewport stays within boundaries
124
+ * board.limitEntireViewPort = true;
125
+ *
126
+ * // Clamp camera position to boundaries
127
+ * board.clampTranslation = true;
128
+ * board.clampZoom = true;
129
+ * ```
130
+ *
131
+ * @example
132
+ * Converting window coordinates to world coordinates
133
+ * ```typescript
134
+ * const board = new Board(canvasElement);
135
+ *
136
+ * canvasElement.addEventListener('click', (event) => {
137
+ * const windowPoint = { x: event.clientX, y: event.clientY };
138
+ * const worldPoint = board.convertWindowPoint2WorldCoord(windowPoint);
139
+ * console.log('Clicked at world position:', worldPoint);
140
+ * });
141
+ * ```
142
+ *
143
+ * @example
144
+ * Using fullscreen mode
145
+ * ```typescript
146
+ * const board = new Board();
147
+ * board.fullScreen = true; // Canvas will resize with window
148
+ *
149
+ * // Attach canvas later
150
+ * const canvasElement = document.createElement('canvas');
151
+ * document.body.appendChild(canvasElement);
152
+ * board.attach(canvasElement);
153
+ * ```
154
+ *
155
+ * @category Board
156
+ * @see {@link ObservableBoardCamera} for camera API details
157
+ * @see {@link CameraRig} for camera constraint configuration
158
+ * @see {@link CameraMux} for camera control coordination
36
159
  */
37
160
  export default class Board {
38
161
  private _context?;
@@ -43,14 +166,161 @@ export default class Board {
43
166
  private _alignCoordinateSystem;
44
167
  private _fullScreen;
45
168
  private cameraRig;
169
+ private _cameraMux;
46
170
  private boardInputPublisher;
47
- private _edgeAutoCameraInput;
48
171
  private _observableInputTracker;
49
172
  private _touchInputTracker;
50
- private _canvasSizeUpdateQueue;
173
+ private _inputOrchestrator;
51
174
  private lastUpdateTime;
175
+ /**
176
+ * Creates a new Board instance with an optional canvas element.
177
+ *
178
+ * The constructor initializes all subsystems including the camera, input parsers, state machines,
179
+ * and event publishers. The board can be created with or without a canvas element - if no canvas
180
+ * is provided, you can attach one later using {@link attach}.
181
+ *
182
+ * @param canvas - Optional HTMLCanvasElement to attach to the board. If provided, the board will
183
+ * immediately initialize with this canvas. If omitted, you must call {@link attach} before the
184
+ * board can be used.
185
+ * @param debug - Optional debug flag that enables `willReadFrequently` hint on the canvas context,
186
+ * which optimizes the canvas for frequent readback operations. Default is `false`. Only use this
187
+ * if you need to frequently read pixel data from the canvas.
188
+ *
189
+ * @remarks
190
+ * ## Initialization Sequence
191
+ *
192
+ * When the constructor is called, it performs the following initialization:
193
+ *
194
+ * 1. **Camera Setup**: Creates a {@link DefaultBoardCamera} with default boundaries of ±50,000 units
195
+ * in both X and Y directions. This provides a large working area for most use cases.
196
+ *
197
+ * 2. **Canvas Proxy**: Initializes a {@link CanvasProxy} that observes canvas dimension changes and
198
+ * automatically updates the camera's viewport dimensions.
199
+ *
200
+ * 3. **Camera Rig**: Creates a {@link CameraRig} with default configuration:
201
+ * - `limitEntireViewPort: true` - Entire viewport is constrained within boundaries
202
+ * - `clampTranslation: true` - Camera position is clamped to boundaries
203
+ * - `clampZoom: true` - Zoom level is clamped to min/max limits
204
+ * - All translation restrictions are disabled by default
205
+ *
206
+ * 4. **Input System**: Initializes both keyboard/mouse/trackpad (KMT) and touch input parsers,
207
+ * state machines, and the input orchestrator that coordinates camera control.
208
+ *
209
+ * 5. **Canvas Attachment** (if canvas provided): If a canvas element is provided, it's immediately
210
+ * attached and the viewport dimensions are synchronized with the canvas size.
211
+ *
212
+ * ## Default Configuration
213
+ *
214
+ * The board is created with sensible defaults:
215
+ * - World boundaries: (-50000, -50000) to (50000, 50000)
216
+ * - Coordinate system: Aligned with HTML canvas (Y-axis points down)
217
+ * - Camera position: (0, 0)
218
+ * - Zoom level: 1.0
219
+ * - Rotation: 0 radians
220
+ * - Full screen: disabled
221
+ *
222
+ * You can customize these defaults after construction by setting properties on the board or camera.
223
+ *
224
+ * @example
225
+ * Create board with canvas element
226
+ * ```typescript
227
+ * const canvas = document.querySelector('canvas') as HTMLCanvasElement;
228
+ * const board = new Board(canvas);
229
+ * // Board is ready to use immediately
230
+ * ```
231
+ *
232
+ * @example
233
+ * Create board without canvas, attach later
234
+ * ```typescript
235
+ * const board = new Board();
236
+ * // ... later, when canvas is ready
237
+ * const canvas = document.createElement('canvas');
238
+ * document.body.appendChild(canvas);
239
+ * board.attach(canvas);
240
+ * ```
241
+ *
242
+ * @example
243
+ * Enable debug mode for pixel readback
244
+ * ```typescript
245
+ * const board = new Board(canvas, true);
246
+ * // Now getImageData() and similar operations will be optimized
247
+ * ```
248
+ *
249
+ * @group LifeCycle
250
+ * @see {@link attach} for attaching a canvas after construction
251
+ * @see {@link tearDown} for cleanup when done with the board
252
+ */
52
253
  constructor(canvas?: HTMLCanvasElement, debug?: boolean);
53
254
  private syncViewPortDimensions;
255
+ /**
256
+ * Attaches a canvas element to the board, enabling rendering and input handling.
257
+ *
258
+ * This method connects a canvas element to the board's rendering and input systems. It must be
259
+ * called before the board can be used if no canvas was provided to the constructor. If a canvas
260
+ * was already attached, this method will replace it with the new canvas.
261
+ *
262
+ * @param canvas - The HTMLCanvasElement to attach to the board. This canvas will be used for
263
+ * rendering and will receive all input events.
264
+ * @param debug - Optional debug flag that enables `willReadFrequently` hint on the canvas context.
265
+ * Default is `false`. Set to `true` if you need to frequently read pixel data from the canvas,
266
+ * which will optimize the context for readback operations.
267
+ *
268
+ * @remarks
269
+ * When a canvas is attached, the following happens:
270
+ *
271
+ * 1. **Context Creation**: A 2D rendering context is obtained from the canvas with the specified
272
+ * debug settings.
273
+ *
274
+ * 2. **Input Parser Attachment**: Both KMT (keyboard/mouse/trackpad) and touch input parsers are
275
+ * attached to the canvas to begin receiving input events.
276
+ *
277
+ * 3. **Canvas Proxy Attachment**: The canvas proxy begins observing the canvas for dimension changes,
278
+ * automatically updating the camera's viewport dimensions when the canvas is resized.
279
+ *
280
+ * 4. **Zoom Level Synchronization**: If {@link limitEntireViewPort} is enabled, the minimum zoom
281
+ * level is calculated and set to ensure the entire viewport can fit within the camera boundaries.
282
+ *
283
+ * 5. **Coordinate System Setup**: Both standard and Y-reversed rendering contexts are created to
284
+ * support both coordinate system modes (see {@link alignCoordinateSystem}).
285
+ *
286
+ * @example
287
+ * Attach canvas during construction
288
+ * ```typescript
289
+ * const canvas = document.querySelector('canvas') as HTMLCanvasElement;
290
+ * const board = new Board(canvas);
291
+ * // No need to call attach() - already attached
292
+ * ```
293
+ *
294
+ * @example
295
+ * Attach canvas after construction
296
+ * ```typescript
297
+ * const board = new Board();
298
+ *
299
+ * // Later, when canvas is ready...
300
+ * const canvas = document.createElement('canvas');
301
+ * canvas.width = 800;
302
+ * canvas.height = 600;
303
+ * document.body.appendChild(canvas);
304
+ *
305
+ * board.attach(canvas);
306
+ * // Board is now ready to use
307
+ * ```
308
+ *
309
+ * @example
310
+ * Switch to a different canvas
311
+ * ```typescript
312
+ * const board = new Board(canvas1);
313
+ *
314
+ * // Later, switch to a different canvas
315
+ * const canvas2 = document.querySelector('#other-canvas') as HTMLCanvasElement;
316
+ * board.attach(canvas2);
317
+ * // Board is now rendering to canvas2
318
+ * ```
319
+ *
320
+ * @group LifeCycle
321
+ * @see {@link tearDown} for detaching and cleaning up
322
+ * @see {@link context} for accessing the rendering context
323
+ */
54
324
  attach(canvas: HTMLCanvasElement, debug?: boolean): void;
55
325
  /**
56
326
  * @group LifeCycle
@@ -104,13 +374,13 @@ export default class Board {
104
374
  set camera(camera: ObservableBoardCamera);
105
375
  get cameraMux(): CameraMux;
106
376
  set cameraMux(cameraMux: CameraMux);
107
- get cameraMovementOnMouseEdge(): EdgeAutoCameraInput;
108
377
  /**
109
378
  * @description This is the step function that is called in the animation frame. This function is responsible for updating the canvas context and the camera state.
110
379
  * @param timestamp
111
380
  */
112
381
  step(timestamp: number): void;
113
382
  /**
383
+ * TODO add the option to make the camera position to be at the top left corner of the canvas; or better yet any point in the viewport (within the viewport boundaries)
114
384
  * @description Converts a point from window coordinates to world coordinates.
115
385
  * @param clickPointInWindow The point in window coordinates to convert.
116
386
  * @returns The converted point in world coordinates.
@@ -171,4 +441,5 @@ export default class Board {
171
441
  get clampRotation(): boolean;
172
442
  set clampRotation(value: boolean);
173
443
  getCameraRig(): CameraRig;
444
+ setInputMode(mode: 'kmt' | 'trackpad'): void;
174
445
  }