@ue-too/board 0.11.0 → 0.13.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/index.js.map CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/camera/default-camera.ts", "../src/utils/observable.ts", "../src/camera/update-publisher.ts", "../src/camera/utils/coordinate-conversion.ts", "../src/camera/utils/matrix.ts", "../src/utils/coordinate-conversions/canvas-viewport.ts", "../src/utils/coordinate-conversions/viewport-world.ts", "../src/camera/utils/position.ts", "../src/camera/utils/zoom.ts", "../src/camera/utils/rotation.ts", "../src/camera/base.ts", "../src/input-interpretation/raw-input-parser/vanilla-kmt-event-parser.ts", "../src/input-interpretation/raw-input-parser/vanilla-touch-event-parser.ts", "../src/utils/coordinate-conversions/window-canvas.ts", "../src/utils/coorindate-conversion.ts", "../src/utils/ruler.ts", "../src/utils/handler-pipeline.ts", "../src/utils/canvas-position-dimension.ts", "../src/utils/drawing-utils.ts", "../src/utils/drawing.ts", "../src/utils/zoomlevel-adjustment.ts", "../src/boardify/index.ts", "../src/input-interpretation/raw-input-publisher/raw-input-publisher.ts", "../src/camera/camera-mux/relay.ts", "../src/camera/camera-mux/animation-and-lock/pan-control-state-machine.ts", "../src/camera/camera-mux/animation-and-lock/zoom-control-state-machine.ts", "../src/camera/camera-mux/animation-and-lock/rotation-control-state-machine.ts", "../src/camera/camera-rig/zoom-handler.ts", "../src/camera/camera-rig/pan-handler.ts", "../src/camera/camera-rig/rotation-handler.ts", "../src/camera/camera-rig/camera-rig.ts", "../src/camera/camera-mux/animation-and-lock/animation-and-lock.ts", "../src/input-interpretation/input-state-machine/kmt-input-context.ts", "../src/input-interpretation/input-state-machine/touch-input-context.ts", "../src/input-interpretation/input-state-machine/touch-input-state-machine.ts", "../src/input-interpretation/input-state-machine/kmt-input-state-machine.ts", "../src/input-interpretation/input-orchestrator.ts", "../src/camera/camera-edge-auto-input.ts"],
3
+ "sources": ["../src/camera/default-camera.ts", "../src/utils/observable.ts", "../src/camera/update-publisher.ts", "../src/camera/utils/coordinate-conversion.ts", "../src/camera/utils/matrix.ts", "../src/utils/coordinate-conversions/canvas-viewport.ts", "../src/utils/coordinate-conversions/viewport-world.ts", "../src/camera/utils/position.ts", "../src/camera/utils/zoom.ts", "../src/camera/utils/rotation.ts", "../src/camera/base.ts", "../src/input-interpretation/raw-input-parser/vanilla-kmt-event-parser.ts", "../src/input-interpretation/raw-input-parser/vanilla-touch-event-parser.ts", "../src/utils/coordinate-conversions/window-canvas.ts", "../src/utils/coorindate-conversion.ts", "../src/utils/ruler.ts", "../src/utils/handler-pipeline.ts", "../src/utils/canvas-position-dimension.ts", "../src/utils/drawing-utils.ts", "../src/utils/drawing.ts", "../src/utils/zoomlevel-adjustment.ts", "../src/boardify/index.ts", "../src/input-interpretation/raw-input-publisher/raw-input-publisher.ts", "../src/camera/camera-mux/relay.ts", "../src/camera/camera-mux/animation-and-lock/pan-control-state-machine.ts", "../src/camera/camera-mux/animation-and-lock/zoom-control-state-machine.ts", "../src/camera/camera-mux/animation-and-lock/rotation-control-state-machine.ts", "../src/camera/camera-mux/animation-and-lock/animation-and-lock.ts", "../src/camera/camera-rig/zoom-handler.ts", "../src/camera/camera-rig/pan-handler.ts", "../src/camera/camera-rig/rotation-handler.ts", "../src/camera/camera-rig/camera-rig.ts", "../src/input-interpretation/input-state-machine/kmt-input-context.ts", "../src/input-interpretation/input-state-machine/touch-input-context.ts", "../src/input-interpretation/input-state-machine/touch-input-state-machine.ts", "../src/input-interpretation/input-state-machine/kmt-input-state-machine.ts", "../src/input-interpretation/input-orchestrator.ts", "../src/camera/camera-edge-auto-input.ts"],
4
4
  "sourcesContent": [
5
- "import { Point, PointCal } from '@ue-too/math';\nimport { Boundaries } from './utils/position';\nimport { TransformationMatrix } from './utils/matrix';\nimport { CameraUpdatePublisher, UnSubscribe } from './update-publisher';\nimport { ZoomLevelLimits } from './utils/zoom';\nimport { RotationLimits } from './utils/rotation';\nimport { convert2WorldSpaceAnchorAtCenter, convert2ViewPortSpaceAnchorAtCenter } from './utils/coordinate-conversion';\nimport { CameraEventMap, CameraState } from './update-publisher';\nimport { ObservableBoardCamera } from './interface';\nimport BaseCamera from './base';\nimport { SubscriptionOptions } from '../utils/observable';\n\n/** Default viewport width in CSS pixels */\nexport const DEFAULT_BOARD_CAMERA_VIEWPORT_WIDTH = 1000;\n\n/** Default viewport height in CSS pixels */\nexport const DEFAULT_BOARD_CAMERA_VIEWPORT_HEIGHT = 1000;\n\n/** Default zoom level constraints (0.1x to 10x) */\nexport const DEFAULT_BOARD_CAMERA_ZOOM_BOUNDARIES: ZoomLevelLimits = {min: 0.1, max: 10};\n\n/** Default position boundaries (±10000 on both axes) */\nexport const DEFAULT_BOARD_CAMERA_BOUNDARIES: Boundaries = {min: {x: -10000, y: -10000}, max: {x: 10000, y: 10000}};\n\n/** Default rotation boundaries (unrestricted) */\nexport const DEFAULT_BOARD_CAMERA_ROTATION_BOUNDARIES: RotationLimits | undefined = undefined;\n\n/**\n * Observable camera implementation that extends {@link BaseCamera} with event notification.\n * This is the recommended camera class for most applications.\n *\n * @remarks\n * DefaultBoardCamera wraps {@link BaseCamera} and adds an event system via {@link CameraUpdatePublisher}.\n * All camera state changes (pan, zoom, rotate) trigger corresponding events that observers can subscribe to.\n *\n * Use this class when you need to:\n * - React to camera changes in your UI or game logic\n * - Synchronize multiple systems with camera state\n * - Implement camera-dependent features (minimap, LOD, culling)\n *\n * For a non-observable camera without event overhead, use {@link BaseCamera} directly.\n *\n * @example\n * ```typescript\n * const camera = new DefaultBoardCamera(1920, 1080);\n *\n * // Subscribe to camera events\n * camera.on('zoom', (event, state) => {\n * console.log(`Zoomed by ${event.deltaZoomAmount}`);\n * console.log(`New zoom level: ${state.zoomLevel}`);\n * });\n *\n * camera.on('pan', (event, state) => {\n * console.log(`Panned by (${event.diff.x}, ${event.diff.y})`);\n * });\n *\n * // Camera updates trigger events\n * camera.setZoomLevel(2.0);\n * camera.setPosition({ x: 100, y: 200 });\n * ```\n *\n * @category Camera\n * @see {@link BaseCamera} for non-observable camera\n * @see {@link ObservableBoardCamera} for the interface definition\n */\nexport default class DefaultBoardCamera implements ObservableBoardCamera {\n\n private _baseCamera: BaseCamera;\n private _observer: CameraUpdatePublisher;\n /**\n * Creates a new observable camera with event notification capabilities.\n *\n * @param viewPortWidth - Width of the viewport in CSS pixels (default: 1000)\n * @param viewPortHeight - Height of the viewport in CSS pixels (default: 1000)\n * @param position - Initial camera position in world coordinates (default: {x: 0, y: 0})\n * @param rotation - Initial rotation in radians (default: 0)\n * @param zoomLevel - Initial zoom level (default: 1.0)\n * @param boundaries - Position constraints (default: ±10000 on both axes)\n * @param zoomLevelBoundaries - Zoom constraints (default: 0.1 to 10)\n * @param rotationBoundaries - Optional rotation constraints (default: unrestricted)\n *\n * @example\n * ```typescript\n * // Camera with default settings\n * const camera1 = new DefaultBoardCamera();\n *\n * // Camera with custom viewport\n * const camera2 = new DefaultBoardCamera(1920, 1080);\n *\n * // Camera with all options\n * const camera3 = new DefaultBoardCamera(\n * 1920, 1080,\n * { x: 0, y: 0 },\n * 0,\n * 1.0,\n * { min: { x: -5000, y: -5000 }, max: { x: 5000, y: 5000 } },\n * { min: 0.5, max: 4 },\n * { start: 0, end: Math.PI * 2 }\n * );\n * ```\n */\n constructor(viewPortWidth: number = DEFAULT_BOARD_CAMERA_VIEWPORT_WIDTH, viewPortHeight: number = DEFAULT_BOARD_CAMERA_VIEWPORT_HEIGHT, position: Point = {x: 0, y: 0}, rotation: number = 0, zoomLevel: number = 1, boundaries: Boundaries = DEFAULT_BOARD_CAMERA_BOUNDARIES, zoomLevelBoundaries: ZoomLevelLimits = DEFAULT_BOARD_CAMERA_ZOOM_BOUNDARIES, rotationBoundaries: RotationLimits | undefined = DEFAULT_BOARD_CAMERA_ROTATION_BOUNDARIES){\n this._baseCamera = new BaseCamera(viewPortWidth, viewPortHeight, position, rotation, zoomLevel, boundaries, zoomLevelBoundaries, rotationBoundaries);\n this._observer = new CameraUpdatePublisher();\n }\n\n /**\n * @description The boundaries of the camera in the world coordinate system.\n * \n * @category Camera\n */\n get boundaries(): Boundaries | undefined{\n return this._baseCamera.boundaries;\n }\n\n set boundaries(boundaries: Boundaries | undefined){\n this._baseCamera.boundaries = boundaries;\n }\n\n /**\n * @description The width of the viewport. (The width of the canvas in css pixels)\n * \n * @category Camera\n */\n get viewPortWidth(): number{\n return this._baseCamera.viewPortWidth;\n }\n\n set viewPortWidth(width: number){\n this._baseCamera.viewPortWidth = width;\n }\n\n /**\n * @description The height of the viewport. (The height of the canvas in css pixels)\n * \n * @category Camera\n */\n get viewPortHeight(): number{\n return this._baseCamera.viewPortHeight;\n }\n\n set viewPortHeight(height: number){\n this._baseCamera.viewPortHeight = height;\n }\n\n /**\n * @description The position of the camera in the world coordinate system.\n * \n * @category Camera\n */\n get position(): Point{\n return this._baseCamera.position;\n }\n\n /**\n * Sets the camera position and notifies observers if successful.\n *\n * @param destination - Target position in world coordinates\n * @returns True if position was updated, false if rejected by boundaries or negligible change\n *\n * @remarks\n * If the position changes, a 'pan' event is triggered with the position delta and new camera state.\n * All 'pan' and 'all' event subscribers will be notified.\n *\n * @example\n * ```typescript\n * camera.on('pan', (event, state) => {\n * console.log(`Camera moved by (${event.diff.x}, ${event.diff.y})`);\n * });\n *\n * camera.setPosition({ x: 100, y: 200 }); // Triggers pan event\n * ```\n */\n setPosition(destination: Point){\n const currentPosition = {...this._baseCamera.position};\n if(!this._baseCamera.setPosition(destination)){\n return false;\n }\n this._observer.notifyPan({diff: PointCal.subVector(destination, currentPosition)}, {position: this._baseCamera.position, rotation: this._baseCamera.rotation, zoomLevel: this._baseCamera.zoomLevel});\n return true;\n }\n\n /**\n * @description The zoom level of the camera.\n * \n * @category Camera\n */\n get zoomLevel(): number{\n return this._baseCamera.zoomLevel;\n }\n\n /**\n * @description The boundaries of the zoom level of the camera.\n * \n * @category Camera\n */\n get zoomBoundaries(): ZoomLevelLimits | undefined{\n return this._baseCamera.zoomBoundaries;\n }\n\n set zoomBoundaries(zoomBoundaries: ZoomLevelLimits | undefined){\n this._baseCamera.zoomBoundaries = zoomBoundaries;\n }\n\n setMaxZoomLevel(maxZoomLevel: number){\n const currentZoomLevel = this._baseCamera.zoomLevel;\n if(!this._baseCamera.setMaxZoomLevel(maxZoomLevel)){\n return false;\n }\n this._observer.notifyZoom({deltaZoomAmount: maxZoomLevel - currentZoomLevel}, {position: this._baseCamera.position, rotation: this._baseCamera.rotation, zoomLevel: this._baseCamera.zoomLevel});\n return true;\n }\n\n setMinZoomLevel(minZoomLevel: number){\n if(!this._baseCamera.setMinZoomLevel(minZoomLevel)){\n return false;\n }\n return true;\n }\n\n /**\n * Sets the camera zoom level and notifies observers if successful.\n *\n * @param zoomLevel - Target zoom level (1.0 = 100%, 2.0 = 200%, etc.)\n * @returns True if zoom was updated, false if outside boundaries or already at limit\n *\n * @remarks\n * If the zoom changes, a 'zoom' event is triggered with the zoom delta and new camera state.\n * All 'zoom' and 'all' event subscribers will be notified.\n *\n * @example\n * ```typescript\n * camera.on('zoom', (event, state) => {\n * console.log(`Zoom changed by ${event.deltaZoomAmount}`);\n * console.log(`New zoom: ${state.zoomLevel}`);\n * });\n *\n * camera.setZoomLevel(2.0); // Triggers zoom event\n * ```\n */\n setZoomLevel(zoomLevel: number){\n const currentZoomLevel = this._baseCamera.zoomLevel;\n if(!this._baseCamera.setZoomLevel(zoomLevel)){\n return false;\n }\n this._observer.notifyZoom({deltaZoomAmount: this._baseCamera.zoomLevel - currentZoomLevel}, {position: this._baseCamera.position, rotation: this._baseCamera.rotation, zoomLevel: this._baseCamera.zoomLevel});\n return true;\n }\n\n /**\n * Gets the current camera rotation in radians.\n *\n * @returns Current rotation angle (0 to 2π)\n */\n get rotation(): number{\n return this._baseCamera.rotation;\n }\n\n /**\n * @description The boundaries of the rotation of the camera.\n * \n * @category Camera\n */\n get rotationBoundaries(): RotationLimits | undefined{\n return this._baseCamera.rotationBoundaries;\n }\n\n set rotationBoundaries(rotationBoundaries: RotationLimits | undefined){\n this._baseCamera.rotationBoundaries = rotationBoundaries;\n }\n\n /**\n * @description The order of the transformation is as follows:\n * 1. Scale (scale the context using the device pixel ratio)\n * 2. Translation (move the origin of the context to the center of the canvas)\n * 3. Rotation (rotate the context negatively the rotation of the camera)\n * 4. Zoom (scale the context using the zoom level of the camera)\n * 5. Translation (move the origin of the context to the position of the camera in the context coordinate system)\n * \n * @param devicePixelRatio The device pixel ratio of the canvas\n * @param alignCoorindate Whether to align the coordinate system to the camera's position\n * @returns The transformation matrix\n */\n getTransform(devicePixelRatio: number, alignCoorindate: boolean = true): TransformationMatrix {\n return this._baseCamera.getTransform(devicePixelRatio, alignCoorindate);\n }\n\n /**\n * Sets the camera rotation and notifies observers if successful.\n *\n * @param rotation - Target rotation in radians\n * @returns True if rotation was updated, false if outside boundaries or already at limit\n *\n * @remarks\n * If the rotation changes, a 'rotate' event is triggered with the rotation delta and new camera state.\n * All 'rotate' and 'all' event subscribers will be notified.\n * Rotation is automatically normalized to 0-2π range.\n *\n * @example\n * ```typescript\n * camera.on('rotate', (event, state) => {\n * console.log(`Camera rotated by ${event.deltaRotation} radians`);\n * });\n *\n * camera.setRotation(Math.PI / 4); // Triggers rotate event\n * ```\n */\n setRotation(rotation: number){\n const currentRotation = this._baseCamera.rotation;\n if(!this._baseCamera.setRotation(rotation)){\n return false;\n }\n this._observer.notifyRotate({deltaRotation: rotation - currentRotation}, {position: this._baseCamera.position, rotation: this._baseCamera.rotation, zoomLevel: this._baseCamera.zoomLevel});\n return true;\n }\n\n /**\n * @description The origin of the camera in the window coordinate system.\n * @deprecated\n * \n * @param centerInWindow The center of the camera in the window coordinate system.\n * @returns The origin of the camera in the window coordinate system.\n */\n getCameraOriginInWindow(centerInWindow: Point): Point{\n return centerInWindow;\n }\n\n /**\n * @description Converts a point from the viewport coordinate system to the world coordinate system.\n * \n * @param point The point in the viewport coordinate system.\n * @returns The point in the world coordinate system.\n */\n convertFromViewPort2WorldSpace(point: Point): Point{\n return convert2WorldSpaceAnchorAtCenter(point, this._baseCamera.position, this._baseCamera.zoomLevel, this._baseCamera.rotation);\n }\n\n /**\n * @description Converts a point from the world coordinate system to the viewport coordinate system.\n * \n * @param point The point in the world coordinate system.\n * @returns The point in the viewport coordinate system.\n */\n convertFromWorld2ViewPort(point: Point): Point{\n return convert2ViewPortSpaceAnchorAtCenter(point, this._baseCamera.position, this._baseCamera.zoomLevel, this._baseCamera.rotation);\n }\n\n /**\n * @description Inverts a point from the world coordinate system to the viewport coordinate system.\n * \n * @param point The point in the world coordinate system.\n * @returns The point in the viewport coordinate system.\n */\n invertFromWorldSpace2ViewPort(point: Point): Point{\n let cameraFrameCenter = {x: this._baseCamera.viewPortWidth / 2, y: this._baseCamera.viewPortHeight / 2};\n let delta2Point = PointCal.subVector(point, this._baseCamera.position);\n delta2Point = PointCal.rotatePoint(delta2Point, -this._baseCamera.rotation);\n delta2Point = PointCal.multiplyVectorByScalar(delta2Point, this._baseCamera.zoomLevel);\n return PointCal.addVector(cameraFrameCenter, delta2Point);\n }\n\n setHorizontalBoundaries(min: number, max: number){\n if (min > max){\n let temp = max;\n max = min;\n min = temp;\n }\n if(this._baseCamera.boundaries == undefined){\n this._baseCamera.boundaries = {min: undefined, max: undefined};\n }\n if(this._baseCamera.boundaries.min == undefined){\n this._baseCamera.boundaries.min = {x: undefined, y: undefined};\n }\n if(this._baseCamera.boundaries.max == undefined){\n this._baseCamera.boundaries.max = {x: undefined, y: undefined};\n }\n this._baseCamera.boundaries.min.x = min;\n this._baseCamera.boundaries.max.x = max;\n //NOTE leave for future optimization when setting the boundaries if the camera lies outside the boundaries clamp the position of the camera\n // if(!this.withinBoundaries(this.position)){\n // this.position = this.clampPoint(this.position);\n // }\n }\n\n setVerticalBoundaries(min: number, max: number){\n if (min > max){\n let temp = max;\n max = min;\n min = temp;\n }\n if(this._baseCamera.boundaries == undefined){\n this._baseCamera.boundaries = {min: undefined, max: undefined};\n }\n if(this._baseCamera.boundaries.min == undefined){\n this._baseCamera.boundaries.min = {x: undefined, y: undefined};\n }\n if(this._baseCamera.boundaries.max == undefined){\n this._baseCamera.boundaries.max = {x: undefined, y: undefined};\n }\n this._baseCamera.boundaries.min.y = min;\n this._baseCamera.boundaries.max.y = max;\n }\n\n /**\n * Subscribes to camera events with optional AbortController for cancellation.\n *\n * @typeParam K - The event type key from CameraEventMap\n * @param eventName - Event type to listen for: 'pan', 'zoom', 'rotate', or 'all'\n * @param callback - Function called when event occurs, receives event data and camera state\n * @param options - Optional subscription configuration including AbortController signal\n * @returns Function to unsubscribe from this event\n *\n * @remarks\n * Available events:\n * - 'pan': Triggered when camera position changes\n * - 'zoom': Triggered when zoom level changes\n * - 'rotate': Triggered when rotation changes\n * - 'all': Triggered for any camera change (pan, zoom, or rotate)\n *\n * Use the AbortController pattern to manage multiple subscriptions:\n *\n * @example\n * ```typescript\n * // Basic subscription\n * const unsubscribe = camera.on('pan', (event, state) => {\n * console.log(`Panned by (${event.diff.x}, ${event.diff.y})`);\n * console.log(`New position: (${state.position.x}, ${state.position.y})`);\n * });\n *\n * // Later: unsubscribe\n * unsubscribe();\n *\n * // Subscribe to all events\n * camera.on('all', (event, state) => {\n * if (event.type === 'pan') {\n * console.log('Pan event:', event.diff);\n * } else if (event.type === 'zoom') {\n * console.log('Zoom event:', event.deltaZoomAmount);\n * } else if (event.type === 'rotate') {\n * console.log('Rotate event:', event.deltaRotation);\n * }\n * });\n *\n * // Using AbortController for batch unsubscribe\n * const controller = new AbortController();\n * camera.on('pan', handlePan, { signal: controller.signal });\n * camera.on('zoom', handleZoom, { signal: controller.signal });\n * camera.on('rotate', handleRotate, { signal: controller.signal });\n *\n * // Unsubscribe all at once\n * controller.abort();\n * ```\n */\n on<K extends keyof CameraEventMap>(eventName: K, callback: (event: CameraEventMap[K], cameraState: CameraState)=>void, options?: SubscriptionOptions): UnSubscribe {\n return this._observer.on(eventName, callback, options);\n }\n\n getTRS(devicePixelRatio: number, alignCoordinateSystem: boolean): {scale: {x: number, y: number}, rotation: number, translation: {x: number, y: number}} {\n return this._baseCamera.getTRS(devicePixelRatio, alignCoordinateSystem);\n }\n\n viewPortInWorldSpace(alignCoordinate: boolean = true): {top: {left: Point, right: Point}, bottom: {left: Point, right: Point}}{\n return this._baseCamera.viewPortInWorldSpace(alignCoordinate);\n }\n\n viewPortAABB(alignCoordinate: boolean = true): {min: Point, max: Point}{\n return this._baseCamera.viewPortAABB(alignCoordinate);\n }\n}\n",
5
+ "import { Point, PointCal } from '@ue-too/math';\nimport { Boundaries } from './utils/position';\nimport { TransformationMatrix } from './utils/matrix';\nimport { CameraUpdatePublisher, UnSubscribe } from './update-publisher';\nimport { ZoomLevelLimits } from './utils/zoom';\nimport { RotationLimits } from './utils/rotation';\nimport { convert2WorldSpaceAnchorAtCenter, convert2ViewPortSpaceAnchorAtCenter } from './utils/coordinate-conversion';\nimport { CameraEventMap, CameraState } from './update-publisher';\nimport { ObservableBoardCamera } from './interface';\nimport BaseCamera from './base';\nimport { SubscriptionOptions } from '../utils/observable';\n\n/** Default viewport width in CSS pixels */\nexport const DEFAULT_BOARD_CAMERA_VIEWPORT_WIDTH = 1000;\n\n/** Default viewport height in CSS pixels */\nexport const DEFAULT_BOARD_CAMERA_VIEWPORT_HEIGHT = 1000;\n\n/** Default zoom level constraints (0.1x to 10x) */\nexport const DEFAULT_BOARD_CAMERA_ZOOM_BOUNDARIES: ZoomLevelLimits = {min: 0.1, max: 10};\n\n/** Default position boundaries (±10000 on both axes) */\nexport const DEFAULT_BOARD_CAMERA_BOUNDARIES: Boundaries = {min: {x: -10000, y: -10000}, max: {x: 10000, y: 10000}};\n\n/** Default rotation boundaries (unrestricted) */\nexport const DEFAULT_BOARD_CAMERA_ROTATION_BOUNDARIES: RotationLimits | undefined = undefined;\n\n/**\n * Observable camera implementation that extends {@link BaseCamera} with event notification.\n * This is the recommended camera class for most applications.\n *\n * @remarks\n * DefaultBoardCamera wraps {@link BaseCamera} and adds an event system via {@link CameraUpdatePublisher}.\n * All camera state changes (pan, zoom, rotate) trigger corresponding events that observers can subscribe to.\n *\n * Use this class when you need to:\n * - React to camera changes in your UI or game logic\n * - Synchronize multiple systems with camera state\n * - Implement camera-dependent features (minimap, LOD, culling)\n *\n * For a non-observable camera without event overhead, use {@link BaseCamera} directly.\n *\n * @example\n * ```typescript\n * const camera = new DefaultBoardCamera(1920, 1080);\n *\n * // Subscribe to camera events\n * camera.on('zoom', (event, state) => {\n * console.log(`Zoomed by ${event.deltaZoomAmount}`);\n * console.log(`New zoom level: ${state.zoomLevel}`);\n * });\n *\n * camera.on('pan', (event, state) => {\n * console.log(`Panned by (${event.diff.x}, ${event.diff.y})`);\n * });\n *\n * // Camera updates trigger events\n * camera.setZoomLevel(2.0);\n * camera.setPosition({ x: 100, y: 200 });\n * ```\n *\n * @category Camera\n * @see {@link BaseCamera} for non-observable camera\n * @see {@link ObservableBoardCamera} for the interface definition\n */\nexport default class DefaultBoardCamera implements ObservableBoardCamera {\n\n private _baseCamera: BaseCamera;\n private _observer: CameraUpdatePublisher;\n /**\n * Creates a new observable camera with event notification capabilities.\n *\n * @param viewPortWidth - Width of the viewport in CSS pixels (default: 1000)\n * @param viewPortHeight - Height of the viewport in CSS pixels (default: 1000)\n * @param position - Initial camera position in world coordinates (default: {x: 0, y: 0})\n * @param rotation - Initial rotation in radians (default: 0)\n * @param zoomLevel - Initial zoom level (default: 1.0)\n * @param boundaries - Position constraints (default: ±10000 on both axes)\n * @param zoomLevelBoundaries - Zoom constraints (default: 0.1 to 10)\n * @param rotationBoundaries - Optional rotation constraints (default: unrestricted)\n *\n * @example\n * ```typescript\n * // Camera with default settings\n * const camera1 = new DefaultBoardCamera();\n *\n * // Camera with custom viewport\n * const camera2 = new DefaultBoardCamera(1920, 1080);\n *\n * // Camera with all options\n * const camera3 = new DefaultBoardCamera(\n * 1920, 1080,\n * { x: 0, y: 0 },\n * 0,\n * 1.0,\n * { min: { x: -5000, y: -5000 }, max: { x: 5000, y: 5000 } },\n * { min: 0.5, max: 4 },\n * { start: 0, end: Math.PI * 2 }\n * );\n * ```\n */\n constructor(viewPortWidth: number = DEFAULT_BOARD_CAMERA_VIEWPORT_WIDTH, viewPortHeight: number = DEFAULT_BOARD_CAMERA_VIEWPORT_HEIGHT, position: Point = {x: 0, y: 0}, rotation: number = 0, zoomLevel: number = 1, boundaries: Boundaries = DEFAULT_BOARD_CAMERA_BOUNDARIES, zoomLevelBoundaries: ZoomLevelLimits = DEFAULT_BOARD_CAMERA_ZOOM_BOUNDARIES, rotationBoundaries: RotationLimits | undefined = DEFAULT_BOARD_CAMERA_ROTATION_BOUNDARIES){\n this._baseCamera = new BaseCamera(viewPortWidth, viewPortHeight, position, rotation, zoomLevel, boundaries, zoomLevelBoundaries, rotationBoundaries);\n this._observer = new CameraUpdatePublisher();\n }\n\n /**\n * @description The boundaries of the camera in the world coordinate system.\n * \n * @category Camera\n */\n get boundaries(): Boundaries | undefined{\n return this._baseCamera.boundaries;\n }\n\n set boundaries(boundaries: Boundaries | undefined){\n this._baseCamera.boundaries = boundaries;\n }\n\n /**\n * @description The width of the viewport. (The width of the canvas in css pixels)\n * \n * @category Camera\n */\n get viewPortWidth(): number{\n return this._baseCamera.viewPortWidth;\n }\n\n set viewPortWidth(width: number){\n this._baseCamera.viewPortWidth = width;\n }\n\n /**\n * @description The height of the viewport. (The height of the canvas in css pixels)\n * \n * @category Camera\n */\n get viewPortHeight(): number{\n return this._baseCamera.viewPortHeight;\n }\n\n set viewPortHeight(height: number){\n this._baseCamera.viewPortHeight = height;\n }\n\n /**\n * @description The position of the camera in the world coordinate system.\n * \n * @category Camera\n */\n get position(): Point{\n return this._baseCamera.position;\n }\n\n /**\n * Sets the camera position and notifies observers if successful.\n *\n * @param destination - Target position in world coordinates\n * @returns True if position was updated, false if rejected by boundaries or negligible change\n *\n * @remarks\n * If the position changes, a 'pan' event is triggered with the position delta and new camera state.\n * All 'pan' and 'all' event subscribers will be notified.\n *\n * @example\n * ```typescript\n * camera.on('pan', (event, state) => {\n * console.log(`Camera moved by (${event.diff.x}, ${event.diff.y})`);\n * });\n *\n * camera.setPosition({ x: 100, y: 200 }); // Triggers pan event\n * ```\n */\n setPosition(destination: Point){\n const currentPosition = {...this._baseCamera.position};\n if(!this._baseCamera.setPosition(destination)){\n return false;\n }\n this._observer.notifyPan({diff: PointCal.subVector(destination, currentPosition)}, {position: this._baseCamera.position, rotation: this._baseCamera.rotation, zoomLevel: this._baseCamera.zoomLevel});\n return true;\n }\n\n /**\n * @description The zoom level of the camera.\n * \n * @category Camera\n */\n get zoomLevel(): number{\n return this._baseCamera.zoomLevel;\n }\n\n /**\n * @description The boundaries of the zoom level of the camera.\n * \n * @category Camera\n */\n get zoomBoundaries(): ZoomLevelLimits | undefined{\n return this._baseCamera.zoomBoundaries;\n }\n\n set zoomBoundaries(zoomBoundaries: ZoomLevelLimits | undefined){\n this._baseCamera.zoomBoundaries = zoomBoundaries;\n }\n\n setMaxZoomLevel(maxZoomLevel: number){\n const currentZoomLevel = this._baseCamera.zoomLevel;\n if(!this._baseCamera.setMaxZoomLevel(maxZoomLevel)){\n return false;\n }\n this._observer.notifyZoom({deltaZoomAmount: maxZoomLevel - currentZoomLevel}, {position: this._baseCamera.position, rotation: this._baseCamera.rotation, zoomLevel: this._baseCamera.zoomLevel});\n return true;\n }\n\n setMinZoomLevel(minZoomLevel: number){\n if(!this._baseCamera.setMinZoomLevel(minZoomLevel)){\n return false;\n }\n return true;\n }\n\n /**\n * Sets the camera zoom level and notifies observers if successful.\n *\n * @param zoomLevel - Target zoom level (1.0 = 100%, 2.0 = 200%, etc.)\n * @returns True if zoom was updated, false if outside boundaries or already at limit\n *\n * @remarks\n * If the zoom changes, a 'zoom' event is triggered with the zoom delta and new camera state.\n * All 'zoom' and 'all' event subscribers will be notified.\n *\n * @example\n * ```typescript\n * camera.on('zoom', (event, state) => {\n * console.log(`Zoom changed by ${event.deltaZoomAmount}`);\n * console.log(`New zoom: ${state.zoomLevel}`);\n * });\n *\n * camera.setZoomLevel(2.0); // Triggers zoom event\n * ```\n */\n setZoomLevel(zoomLevel: number){\n const currentZoomLevel = this._baseCamera.zoomLevel;\n if(!this._baseCamera.setZoomLevel(zoomLevel)){\n return false;\n }\n this._observer.notifyZoom({deltaZoomAmount: this._baseCamera.zoomLevel - currentZoomLevel}, {position: this._baseCamera.position, rotation: this._baseCamera.rotation, zoomLevel: this._baseCamera.zoomLevel});\n return true;\n }\n\n /**\n * Gets the current camera rotation in radians.\n *\n * @returns Current rotation angle (0 to 2π)\n */\n get rotation(): number{\n return this._baseCamera.rotation;\n }\n\n /**\n * @description The boundaries of the rotation of the camera.\n * \n * @category Camera\n */\n get rotationBoundaries(): RotationLimits | undefined{\n return this._baseCamera.rotationBoundaries;\n }\n\n set rotationBoundaries(rotationBoundaries: RotationLimits | undefined){\n this._baseCamera.rotationBoundaries = rotationBoundaries;\n }\n\n /**\n * @description The order of the transformation is as follows:\n * 1. Scale (scale the context using the device pixel ratio)\n * 2. Translation (move the origin of the context to the center of the canvas)\n * 3. Rotation (rotate the context negatively the rotation of the camera)\n * 4. Zoom (scale the context using the zoom level of the camera)\n * 5. Translation (move the origin of the context to the position of the camera in the context coordinate system)\n * \n * @param devicePixelRatio The device pixel ratio of the canvas\n * @param alignCoorindate Whether to align the coordinate system to the camera's position\n * @returns The transformation matrix\n */\n getTransform(devicePixelRatio: number = 1, alignCoorindate: boolean = true): TransformationMatrix {\n return this._baseCamera.getTransform(devicePixelRatio, alignCoorindate);\n }\n\n /**\n * Sets the camera rotation and notifies observers if successful.\n *\n * @param rotation - Target rotation in radians\n * @returns True if rotation was updated, false if outside boundaries or already at limit\n *\n * @remarks\n * If the rotation changes, a 'rotate' event is triggered with the rotation delta and new camera state.\n * All 'rotate' and 'all' event subscribers will be notified.\n * Rotation is automatically normalized to 0-2π range.\n *\n * @example\n * ```typescript\n * camera.on('rotate', (event, state) => {\n * console.log(`Camera rotated by ${event.deltaRotation} radians`);\n * });\n *\n * camera.setRotation(Math.PI / 4); // Triggers rotate event\n * ```\n */\n setRotation(rotation: number){\n const currentRotation = this._baseCamera.rotation;\n if(!this._baseCamera.setRotation(rotation)){\n return false;\n }\n this._observer.notifyRotate({deltaRotation: rotation - currentRotation}, {position: this._baseCamera.position, rotation: this._baseCamera.rotation, zoomLevel: this._baseCamera.zoomLevel});\n return true;\n }\n\n /**\n * @description The origin of the camera in the window coordinate system.\n * @deprecated\n * \n * @param centerInWindow The center of the camera in the window coordinate system.\n * @returns The origin of the camera in the window coordinate system.\n */\n getCameraOriginInWindow(centerInWindow: Point): Point{\n return centerInWindow;\n }\n\n /**\n * @description Converts a point from the viewport coordinate system to the world coordinate system.\n * \n * @param point The point in the viewport coordinate system.\n * @returns The point in the world coordinate system.\n */\n convertFromViewPort2WorldSpace(point: Point): Point{\n return convert2WorldSpaceAnchorAtCenter(point, this._baseCamera.position, this._baseCamera.zoomLevel, this._baseCamera.rotation);\n }\n\n /**\n * @description Converts a point from the world coordinate system to the viewport coordinate system.\n * \n * @param point The point in the world coordinate system.\n * @returns The point in the viewport coordinate system.\n */\n convertFromWorld2ViewPort(point: Point): Point{\n return convert2ViewPortSpaceAnchorAtCenter(point, this._baseCamera.position, this._baseCamera.zoomLevel, this._baseCamera.rotation);\n }\n\n /**\n * @description Inverts a point from the world coordinate system to the viewport coordinate system.\n * \n * @param point The point in the world coordinate system.\n * @returns The point in the viewport coordinate system.\n */\n invertFromWorldSpace2ViewPort(point: Point): Point{\n let cameraFrameCenter = {x: this._baseCamera.viewPortWidth / 2, y: this._baseCamera.viewPortHeight / 2};\n let delta2Point = PointCal.subVector(point, this._baseCamera.position);\n delta2Point = PointCal.rotatePoint(delta2Point, -this._baseCamera.rotation);\n delta2Point = PointCal.multiplyVectorByScalar(delta2Point, this._baseCamera.zoomLevel);\n return PointCal.addVector(cameraFrameCenter, delta2Point);\n }\n\n setHorizontalBoundaries(min: number, max: number){\n if (min > max){\n let temp = max;\n max = min;\n min = temp;\n }\n if(this._baseCamera.boundaries == undefined){\n this._baseCamera.boundaries = {min: undefined, max: undefined};\n }\n if(this._baseCamera.boundaries.min == undefined){\n this._baseCamera.boundaries.min = {x: undefined, y: undefined};\n }\n if(this._baseCamera.boundaries.max == undefined){\n this._baseCamera.boundaries.max = {x: undefined, y: undefined};\n }\n this._baseCamera.boundaries.min.x = min;\n this._baseCamera.boundaries.max.x = max;\n //NOTE leave for future optimization when setting the boundaries if the camera lies outside the boundaries clamp the position of the camera\n // if(!this.withinBoundaries(this.position)){\n // this.position = this.clampPoint(this.position);\n // }\n }\n\n setVerticalBoundaries(min: number, max: number){\n if (min > max){\n let temp = max;\n max = min;\n min = temp;\n }\n if(this._baseCamera.boundaries == undefined){\n this._baseCamera.boundaries = {min: undefined, max: undefined};\n }\n if(this._baseCamera.boundaries.min == undefined){\n this._baseCamera.boundaries.min = {x: undefined, y: undefined};\n }\n if(this._baseCamera.boundaries.max == undefined){\n this._baseCamera.boundaries.max = {x: undefined, y: undefined};\n }\n this._baseCamera.boundaries.min.y = min;\n this._baseCamera.boundaries.max.y = max;\n }\n\n /**\n * Subscribes to camera events with optional AbortController for cancellation.\n *\n * @typeParam K - The event type key from CameraEventMap\n * @param eventName - Event type to listen for: 'pan', 'zoom', 'rotate', or 'all'\n * @param callback - Function called when event occurs, receives event data and camera state\n * @param options - Optional subscription configuration including AbortController signal\n * @returns Function to unsubscribe from this event\n *\n * @remarks\n * Available events:\n * - 'pan': Triggered when camera position changes\n * - 'zoom': Triggered when zoom level changes\n * - 'rotate': Triggered when rotation changes\n * - 'all': Triggered for any camera change (pan, zoom, or rotate)\n *\n * Use the AbortController pattern to manage multiple subscriptions:\n *\n * @example\n * ```typescript\n * // Basic subscription\n * const unsubscribe = camera.on('pan', (event, state) => {\n * console.log(`Panned by (${event.diff.x}, ${event.diff.y})`);\n * console.log(`New position: (${state.position.x}, ${state.position.y})`);\n * });\n *\n * // Later: unsubscribe\n * unsubscribe();\n *\n * // Subscribe to all events\n * camera.on('all', (event, state) => {\n * if (event.type === 'pan') {\n * console.log('Pan event:', event.diff);\n * } else if (event.type === 'zoom') {\n * console.log('Zoom event:', event.deltaZoomAmount);\n * } else if (event.type === 'rotate') {\n * console.log('Rotate event:', event.deltaRotation);\n * }\n * });\n *\n * // Using AbortController for batch unsubscribe\n * const controller = new AbortController();\n * camera.on('pan', handlePan, { signal: controller.signal });\n * camera.on('zoom', handleZoom, { signal: controller.signal });\n * camera.on('rotate', handleRotate, { signal: controller.signal });\n *\n * // Unsubscribe all at once\n * controller.abort();\n * ```\n */\n on<K extends keyof CameraEventMap>(eventName: K, callback: (event: CameraEventMap[K], cameraState: CameraState)=>void, options?: SubscriptionOptions): UnSubscribe {\n return this._observer.on(eventName, callback, options);\n }\n\n getTRS(devicePixelRatio: number = 1, alignCoordinateSystem: boolean = true): {scale: {x: number, y: number}, rotation: number, translation: {x: number, y: number}, cached: boolean} {\n return this._baseCamera.getTRS(devicePixelRatio, alignCoordinateSystem);\n }\n\n setUsingTransformationMatrix(transformationMatrix: TransformationMatrix, devicePixelRatio: number = 1){\n this._baseCamera.setUsingTransformationMatrix(transformationMatrix, devicePixelRatio);\n }\n\n viewPortInWorldSpace(alignCoordinate: boolean = true): {top: {left: Point, right: Point}, bottom: {left: Point, right: Point}}{\n return this._baseCamera.viewPortInWorldSpace(alignCoordinate);\n }\n\n viewPortAABB(alignCoordinate: boolean = true): {min: Point, max: Point}{\n return this._baseCamera.viewPortAABB(alignCoordinate);\n }\n}\n",
6
6
  "/**\n * Type definition for an observer callback function.\n *\n * @typeParam T - Tuple type of arguments passed to the observer\n *\n * @remarks\n * Observers are callbacks that get notified when an Observable emits data.\n * The generic type T is a tuple representing the arguments passed to the callback.\n *\n * @example\n * ```typescript\n * // Observer that receives a single string\n * const stringObserver: Observer<[string]> = (message) => {\n * console.log(message);\n * };\n *\n * // Observer that receives multiple arguments\n * const multiObserver: Observer<[number, string, boolean]> = (num, str, flag) => {\n * console.log(num, str, flag);\n * };\n * ```\n *\n * @category Observable Pattern\n */\nexport type Observer<T extends any[]> = (...data: T) => void;\n\n/**\n * Options for subscribing to an Observable.\n *\n * @property signal - Optional AbortSignal for automatic unsubscription\n *\n * @remarks\n * Subscription options allow for automatic cleanup of subscriptions using\n * the AbortController API. When the signal is aborted, the subscription\n * is automatically removed.\n *\n * @example\n * ```typescript\n * const controller = new AbortController();\n *\n * observable.subscribe(\n * (data) => console.log(data),\n * { signal: controller.signal }\n * );\n *\n * // Later, abort to unsubscribe\n * controller.abort();\n * ```\n *\n * @category Observable Pattern\n */\nexport interface SubscriptionOptions {\n signal?: AbortSignal;\n}\n\n/**\n * Interface for the Observable pattern implementation.\n *\n * @typeParam T - Tuple type of data emitted to observers\n *\n * @remarks\n * Observables allow multiple observers to subscribe and receive notifications\n * when data is emitted. This is the pub-sub pattern for event handling.\n *\n * Implementations can be synchronous or asynchronous:\n * - {@link SynchronousObservable}: Notifies observers immediately\n * - {@link AsyncObservable}: Notifies observers via microtasks\n *\n * @category Observable Pattern\n */\nexport interface Observable<T extends any[]> {\n subscribe(observer: Observer<T>, options?: SubscriptionOptions): () => void;\n notify(...data: T): void;\n}\n\n/**\n * Asynchronous Observable implementation that notifies observers via microtasks.\n *\n * @typeParam T - Tuple type of data emitted to observers\n *\n * @remarks\n * This Observable uses `queueMicrotask` to defer observer notifications,\n * ensuring they execute after the current execution context completes but\n * before the next task. This prevents recursive notification issues and\n * allows the notifier to complete before observers run.\n *\n * Use AsyncObservable when:\n * - You want to prevent recursion issues in notifications\n * - Observer execution should not block the notifier\n * - You need guaranteed async behavior\n *\n * @example\n * ```typescript\n * const observable = new AsyncObservable<[string]>();\n *\n * observable.subscribe((message) => {\n * console.log('Observer received:', message);\n * });\n *\n * console.log('Before notify');\n * observable.notify('Hello');\n * console.log('After notify');\n *\n * // Output:\n * // Before notify\n * // After notify\n * // Observer received: Hello\n * ```\n *\n * @category Observable Pattern\n * @see {@link SynchronousObservable} for synchronous notifications\n */\nexport class AsyncObservable<T extends any[]> implements Observable<T> {\n private observers: Observer<T>[] = [];\n\n /**\n * Subscribes an observer to receive notifications.\n *\n * @param observer - The callback function to be notified\n * @param options - Optional subscription options including AbortSignal\n * @returns Unsubscribe function to remove this observer\n *\n * @remarks\n * If an AbortSignal is provided and is already aborted, the observer\n * is not added and the returned unsubscribe function is a no-op.\n */\n subscribe(observer: Observer<T>, options?: SubscriptionOptions): () => void {\n this.observers.push(observer);\n\n // Handle AbortSignal\n if (options?.signal) {\n // If signal is already aborted, don't add the observer\n if (options.signal.aborted) {\n this.observers = this.observers.filter(o => o !== observer);\n return () => {};\n }\n\n // Add abort handler\n const abortHandler = () => {\n this.observers = this.observers.filter(o => o !== observer);\n options.signal?.removeEventListener('abort', abortHandler);\n };\n\n options.signal.addEventListener('abort', abortHandler);\n }\n\n // Return unsubscribe function\n return () => {\n this.observers = this.observers.filter(o => o !== observer);\n };\n }\n\n /**\n * Notifies all observers with the provided data asynchronously.\n *\n * @param data - The data to pass to all observers\n *\n * @remarks\n * Each observer is called via `queueMicrotask`, ensuring async execution.\n * This method returns immediately; observers run later in the event loop.\n */\n notify(...data: T): void {\n this.observers.forEach(observer => queueMicrotask(() => observer(...data)));\n }\n}\n\n/**\n * Synchronous Observable implementation that notifies observers immediately.\n *\n * @typeParam T - Tuple type of data emitted to observers\n *\n * @remarks\n * This Observable calls all observers synchronously and immediately when\n * `notify()` is called. The notify method doesn't return until all observers\n * have executed.\n *\n * Use SynchronousObservable when:\n * - You need immediate, guaranteed execution of observers\n * - Observer execution order matters and must be predictable\n * - You're in a performance-critical path (no async overhead)\n *\n * Caution: Can lead to recursion issues if observers trigger notifications.\n *\n * @example\n * ```typescript\n * const observable = new SynchronousObservable<[string]>();\n *\n * observable.subscribe((message) => {\n * console.log('Observer received:', message);\n * });\n *\n * console.log('Before notify');\n * observable.notify('Hello');\n * console.log('After notify');\n *\n * // Output:\n * // Before notify\n * // Observer received: Hello\n * // After notify\n * ```\n *\n * @category Observable Pattern\n * @see {@link AsyncObservable} for asynchronous notifications\n */\nexport class SynchronousObservable<T extends any[]> implements Observable<T> {\n private observers: Observer<T>[] = [];\n\n /**\n * Subscribes an observer to receive notifications.\n *\n * @param observer - The callback function to be notified\n * @param options - Optional subscription options including AbortSignal\n * @returns Unsubscribe function to remove this observer\n *\n * @remarks\n * If an AbortSignal is provided and is already aborted, the observer\n * is not added and the returned unsubscribe function is a no-op.\n */\n subscribe(observer: Observer<T>, options?: SubscriptionOptions): () => void {\n this.observers.push(observer);\n\n // Handle AbortSignal\n if (options?.signal) {\n // If signal is already aborted, don't add the observer\n if (options.signal.aborted) {\n this.observers = this.observers.filter(o => o !== observer);\n return () => {};\n }\n\n // Add abort handler\n const abortHandler = () => {\n this.observers = this.observers.filter(o => o !== observer);\n options.signal?.removeEventListener('abort', abortHandler);\n };\n\n options.signal.addEventListener('abort', abortHandler);\n }\n\n // Return unsubscribe function\n return () => {\n this.observers = this.observers.filter(o => o !== observer);\n };\n }\n\n /**\n * Notifies all observers with the provided data synchronously.\n *\n * @param data - The data to pass to all observers\n *\n * @remarks\n * Each observer is called immediately in order. This method blocks until\n * all observers have completed execution.\n */\n notify(...data: T): void {\n this.observers.forEach(observer => observer(...data));\n }\n}\n\n// Usage example\n// const observable = new Observable<[string]>();\n\n// Create an AbortController\n// const controller = new AbortController();\n\n// Subscribe with AbortSignal\n// const unsubscribe = observable.subscribe(\n// (data) => console.log('Received:', data),\n// { signal: controller.signal }\n// );\n\n// Example notifications\n// observable.notify('Hello!'); // Observer will receive this\n\n// Abort the subscription\n// controller.abort();\n\n// Observer won't receive this notification\n// observable.notify('World!');\n\n// Alternative way to unsubscribe using the returned function\n// unsubscribe();\n",
7
7
  "import { Point } from \"@ue-too/math\";\nimport { AsyncObservable, Observable, Observer, SubscriptionOptions } from \"../utils/observable\";\n\n/**\n * Payload for camera pan (position change) events.\n *\n * @property diff - The displacement vector from previous to new position\n *\n * @category Camera\n */\nexport type CameraPanEventPayload = {\n /** Movement delta in world coordinates */\n diff: Point;\n}\n\n/**\n * Payload for camera zoom (scale change) events.\n *\n * @property deltaZoomAmount - Change in zoom level (positive = zoom in, negative = zoom out)\n *\n * @category Camera\n */\nexport type CameraZoomEventPayload = {\n /** Change in zoom level from previous value */\n deltaZoomAmount: number;\n}\n\n/**\n * Payload for camera rotation events.\n *\n * @property deltaRotation - Change in rotation angle in radians\n *\n * @category Camera\n */\nexport type CameraRotateEventPayload = {\n /** Change in rotation from previous value, in radians */\n deltaRotation: number;\n}\n\n/**\n * Mapping of camera event names to their payload types.\n * Used for type-safe event subscription.\n *\n * @category Camera\n */\nexport type CameraEventMap = {\n /** Position change event */\n \"pan\": CameraPanEventPayload,\n /** Zoom level change event */\n \"zoom\": CameraZoomEventPayload,\n /** Rotation change event */\n \"rotate\": CameraRotateEventPayload,\n /** Any camera change event (union of pan, zoom, rotate) */\n \"all\": AllCameraEventPayload,\n}\n\n/**\n * Rotation event with discriminated type field for 'all' event handling.\n * Includes type discriminator and rotation payload.\n *\n * @category Camera\n */\nexport type CameraRotateEvent = {\n /** Event type discriminator */\n type: \"rotate\",\n} & CameraRotateEventPayload;\n\n/**\n * Pan event with discriminated type field for 'all' event handling.\n * Includes type discriminator and pan payload.\n *\n * @category Camera\n */\nexport type CameraPanEvent = {\n /** Event type discriminator */\n type: \"pan\",\n} & CameraPanEventPayload;\n\n/**\n * Zoom event with discriminated type field for 'all' event handling.\n * Includes type discriminator and zoom payload.\n *\n * @category Camera\n */\nexport type CameraZoomEvent = {\n /** Event type discriminator */\n type: \"zoom\",\n} & CameraZoomEventPayload;\n\n/**\n * Snapshot of camera state at the time an event occurs.\n * Passed to all event callbacks alongside the event payload.\n *\n * @category Camera\n */\nexport type CameraState = {\n /** Camera position in world coordinates */\n position: Point;\n /** Current zoom level */\n zoomLevel: number;\n /** Current rotation in radians */\n rotation: number;\n}\n\n/**\n * Union type of all camera event payloads with type discriminators.\n * Used for the 'all' event which fires for any camera change.\n *\n * @category Camera\n */\nexport type AllCameraEventPayload = CameraRotateEvent | CameraPanEvent | CameraZoomEvent;\n\n/**\n * Generic callback function type for camera events.\n *\n * @typeParam K - The event type key from CameraEventMap\n * @param event - The event payload specific to this event type\n * @param cameraState - Current camera state snapshot at the time of the event\n *\n * @category Camera\n */\nexport type Callback<K extends keyof CameraEventMap> = (event: CameraEventMap[K], cameraState: CameraState)=>void;\n\n/**\n * Callback function type specifically for the 'all' camera event.\n * Receives a discriminated union of all camera events.\n *\n * @category Camera\n */\nexport type ConslidateCallback = (payload: AllCameraEventPayload, cameraState: CameraState) => void;\n\n/**\n * Function returned by event subscriptions that unsubscribes the callback when called.\n *\n * @category Camera\n */\nexport type UnSubscribe = () => void;\n\n/**\n * Callback type for pan (position change) events.\n *\n * @category Camera\n */\nexport type PanObserver = Callback<\"pan\">;\n\n/**\n * Callback type for zoom (scale change) events.\n *\n * @category Camera\n */\nexport type ZoomObserver = Callback<\"zoom\">;\n\n/**\n * Callback type for rotation events.\n *\n * @category Camera\n */\nexport type RotateObserver = Callback<\"rotate\">;\n\n/**\n * Callback type for the 'all' event that fires on any camera change.\n *\n * @category Camera\n */\nexport type AllObserver = Callback<\"all\">;\n\n/**\n * Event publisher for camera state changes using the Observable pattern.\n * Manages subscriptions and notifications for pan, zoom, and rotate events.\n *\n * @remarks\n * This class is used internally by {@link DefaultBoardCamera} to implement the event system.\n * You typically don't instantiate this directly unless building custom camera implementations.\n *\n * Each specific event (pan, zoom, rotate) also triggers the 'all' event, allowing\n * listeners to subscribe to any camera change with a single handler.\n *\n * @example\n * ```typescript\n * const publisher = new CameraUpdatePublisher();\n *\n * // Subscribe to pan events\n * publisher.on('pan', (event, state) => {\n * console.log('Camera panned:', event.diff);\n * });\n *\n * // Notify subscribers of a pan event\n * publisher.notifyPan(\n * { diff: { x: 10, y: 20 } },\n * { position: { x: 100, y: 200 }, zoomLevel: 1, rotation: 0 }\n * );\n * ```\n *\n * @category Camera\n * @see {@link DefaultBoardCamera} for the primary consumer of this class\n */\nexport class CameraUpdatePublisher {\n\n private pan: Observable<Parameters<Callback<\"pan\">>>;\n private zoom: Observable<Parameters<Callback<\"zoom\">>>;\n private rotate: Observable<Parameters<Callback<\"rotate\">>>;\n private all: Observable<Parameters<Callback<\"all\">>>;\n\n /**\n * Creates a new camera event publisher with async observables for each event type.\n */\n constructor() {\n this.pan = new AsyncObservable<Parameters<Callback<\"pan\">>>();\n this.zoom = new AsyncObservable<Parameters<Callback<\"zoom\">>>();\n this.rotate = new AsyncObservable<Parameters<Callback<\"rotate\">>>();\n this.all = new AsyncObservable<Parameters<Callback<\"all\">>>();\n }\n\n /**\n * Notifies all pan event subscribers.\n * Also triggers the 'all' event with type discrimination.\n *\n * @param event - Pan event payload containing position delta\n * @param cameraState - Current camera state snapshot\n */\n notifyPan(event: CameraEventMap[\"pan\"], cameraState: CameraState): void {\n this.pan.notify(event, cameraState);\n this.all.notify({type: \"pan\", diff: event.diff}, cameraState);\n }\n\n /**\n * Notifies all zoom event subscribers.\n * Also triggers the 'all' event with type discrimination.\n *\n * @param event - Zoom event payload containing zoom delta\n * @param cameraState - Current camera state snapshot\n */\n notifyZoom(event: CameraEventMap[\"zoom\"], cameraState: CameraState): void {\n this.zoom.notify(event, cameraState);\n this.all.notify({type: \"zoom\", deltaZoomAmount: event.deltaZoomAmount}, cameraState);\n }\n\n /**\n * Notifies all rotation event subscribers.\n * Also triggers the 'all' event with type discrimination.\n *\n * @param event - Rotation event payload containing rotation delta\n * @param cameraState - Current camera state snapshot\n */\n notifyRotate(event: CameraEventMap[\"rotate\"], cameraState: CameraState): void {\n this.rotate.notify(event, cameraState);\n this.all.notify({type: \"rotate\", deltaRotation: event.deltaRotation}, cameraState);\n }\n \n /**\n * Subscribes to camera events with type-safe callbacks and optional AbortController support.\n *\n * @typeParam K - The event type key from CameraEventMap\n * @param eventName - Event type to subscribe to ('pan', 'zoom', 'rotate', or 'all')\n * @param callback - Function called when the event occurs\n * @param options - Optional subscription options including AbortController signal\n * @returns Function that unsubscribes this callback when called\n *\n * @throws Error if an invalid event name is provided\n *\n * @remarks\n * Use the AbortController pattern for managing multiple subscriptions:\n *\n * @example\n * ```typescript\n * // Basic subscription\n * const unsubscribe = publisher.on('pan', (event, state) => {\n * console.log(`Panned by (${event.diff.x}, ${event.diff.y})`);\n * });\n *\n * // Later: unsubscribe\n * unsubscribe();\n *\n * // Using AbortController for batch management\n * const controller = new AbortController();\n * publisher.on('pan', handlePan, { signal: controller.signal });\n * publisher.on('zoom', handleZoom, { signal: controller.signal });\n *\n * // Unsubscribe all at once\n * controller.abort();\n *\n * // Subscribe to all events with type discrimination\n * publisher.on('all', (event, state) => {\n * switch (event.type) {\n * case 'pan':\n * console.log('Pan:', event.diff);\n * break;\n * case 'zoom':\n * console.log('Zoom:', event.deltaZoomAmount);\n * break;\n * case 'rotate':\n * console.log('Rotate:', event.deltaRotation);\n * break;\n * }\n * });\n * ```\n */\n on<K extends keyof CameraEventMap>(eventName: K, callback: (event: CameraEventMap[K], cameraState: CameraState)=>void, options?: SubscriptionOptions): UnSubscribe {\n switch (eventName){\n case \"pan\":\n return this.pan.subscribe(callback as Observer<Parameters<Callback<\"pan\">>>, options);\n case \"zoom\":\n return this.zoom.subscribe(callback as Observer<Parameters<Callback<\"zoom\">>>, options);\n case \"rotate\":\n return this.rotate.subscribe(callback as Observer<Parameters<Callback<\"rotate\">>>, options);\n case \"all\":\n return this.all.subscribe(callback as Observer<Parameters<Callback<\"all\">>>, options);\n default:\n throw new Error(`Invalid event name: ${eventName}`);\n }\n }\n}\n",
8
8
  "import { Point, PointCal } from \"@ue-too/math\";\nimport { multiplyMatrix, TransformationMatrix } from \"./matrix\";\nimport { convertFromCanvas2ViewPort, convertFromViewPort2Canvas } from \"../../utils/coordinate-conversions/canvas-viewport\";\nimport { convertFromViewport2World, convertFromWorld2Viewport } from \"../../utils/coordinate-conversions/viewport-world\";\n\n/**\n * Converts a viewport point to world space with respect to a hypothetical camera position.\n * \"WRT\" = \"With Respect To\" - calculates where a viewport point would be in world space\n * if the camera were at the target position.\n *\n * @param targetPosition - Hypothetical camera position in world coordinates\n * @param interestPoint - Point in canvas coordinates (origin at bottom-left)\n * @param viewPortWidth - Viewport width in CSS pixels\n * @param viewPortHeight - Viewport height in CSS pixels\n * @param cameraZoomLevel - Zoom level to apply\n * @param cameraRotation - Rotation to apply in radians\n * @returns World space coordinates of the interest point\n *\n * @remarks\n * This is useful for \"what-if\" calculations, such as:\n * - Predicting where a viewport corner would land if camera moves to a position\n * - Checking if moving to a position would show certain world objects\n *\n * The interest point uses canvas coordinates (bottom-left origin), not viewport coordinates (center origin).\n *\n * @example\n * ```typescript\n * // Where would the top-left viewport corner be in world space\n * // if camera moved to (100, 100)?\n * const worldCorner = convert2WorldSpaceWRT(\n * { x: 100, y: 100 }, // target camera position\n * { x: 0, y: 1080 }, // top-left in canvas coords\n * 1920, 1080, // viewport size\n * 1.0, // zoom\n * 0 // rotation\n * );\n * ```\n *\n * @category Camera\n */\nexport function convert2WorldSpaceWRT(targetPosition: Point, interestPoint: Point, viewPortWidth: number, viewPortHeight: number, cameraZoomLevel: number, cameraRotation: number): Point{\n const interestPointInViewPort = convertFromCanvas2ViewPort(interestPoint, {x: viewPortWidth / 2, y: viewPortHeight / 2}, false);\n return convertFromViewport2World(interestPointInViewPort, targetPosition, cameraZoomLevel, cameraRotation, false);\n}\n\n/**\n * Converts a canvas point to world space using current camera state.\n *\n * @param point - Point in canvas coordinates (origin at bottom-left)\n * @param viewPortWidth - Viewport width in CSS pixels\n * @param viewPortHeight - Viewport height in CSS pixels\n * @param cameraPosition - Current camera position in world coordinates\n * @param cameraZoomLevel - Current camera zoom level\n * @param cameraRotation - Current camera rotation in radians\n * @returns World space coordinates of the point\n *\n * @remarks\n * Input coordinates use canvas space with origin at bottom-left.\n * This is useful when working with canvas element coordinates directly.\n *\n * For points already in viewport space (origin at center), use\n * {@link convert2WorldSpaceAnchorAtCenter} instead.\n *\n * @example\n * ```typescript\n * // Convert bottom-left corner of canvas to world coords\n * const worldPos = convert2WorldSpace(\n * { x: 0, y: 0 },\n * 1920, 1080,\n * { x: 100, y: 200 }, // camera position\n * 1.5, // zoom\n * 0 // rotation\n * );\n * ```\n *\n * @category Camera\n */\nexport function convert2WorldSpace(point: Point, viewPortWidth: number, viewPortHeight: number, cameraPosition: Point, cameraZoomLevel: number, cameraRotation: number): Point{\n const pointInViewPort = convertFromCanvas2ViewPort(point, {x: viewPortWidth / 2, y: viewPortHeight / 2}, false);\n return convertFromViewport2World(pointInViewPort, cameraPosition, cameraZoomLevel, cameraRotation, false);\n}\n\n/**\n * Converts a viewport point (center-anchored) to world space.\n * This is the most commonly used viewport-to-world conversion function.\n *\n * @param point - Point in viewport coordinates (origin at viewport center)\n * @param cameraPosition - Camera position in world coordinates\n * @param cameraZoomLevel - Camera zoom level\n * @param cameraRotation - Camera rotation in radians\n * @returns World space coordinates of the point\n *\n * @remarks\n * Viewport coordinates have the origin at the center of the viewport, with:\n * - Positive x to the right\n * - Positive y upward\n * - Point (0, 0) is the center of the viewport\n *\n * This is the standard coordinate system for camera operations.\n *\n * @example\n * ```typescript\n * // Convert viewport center (0,0) to world space\n * const worldCenter = convert2WorldSpaceAnchorAtCenter(\n * { x: 0, y: 0 },\n * { x: 500, y: 300 }, // camera at world (500, 300)\n * 1.0,\n * 0\n * );\n * // worldCenter will be { x: 500, y: 300 }\n *\n * // Convert point 100 pixels right of center\n * const rightPoint = convert2WorldSpaceAnchorAtCenter(\n * { x: 100, y: 0 },\n * { x: 500, y: 300 },\n * 2.0, // 2x zoom\n * 0\n * );\n * // At 2x zoom, 100 viewport pixels = 50 world units\n * // Result: { x: 550, y: 300 }\n * ```\n *\n * @category Camera\n */\nexport function convert2WorldSpaceAnchorAtCenter(point: Point, cameraPosition: Point, cameraZoomLevel: number, cameraRotation: number): Point{\n return convertFromViewport2World(point, cameraPosition, cameraZoomLevel, cameraRotation, false);\n}\n\n/**\n * Converts a world point to viewport space (center-anchored).\n * Inverse of {@link convert2WorldSpaceAnchorAtCenter}.\n *\n * @param point - Point in world coordinates\n * @param cameraPosition - Camera position in world coordinates\n * @param cameraZoomLevel - Camera zoom level\n * @param cameraRotation - Camera rotation in radians\n * @returns Viewport coordinates (origin at center, in CSS pixels)\n *\n * @remarks\n * Use this to find where a world object appears on screen.\n * Result is in viewport space with origin at center, useful for:\n * - Positioning UI elements over world objects\n * - Checking if objects are on screen\n * - Converting click positions\n *\n * @example\n * ```typescript\n * // Where does world point (600, 300) appear in viewport?\n * const viewportPos = convert2ViewPortSpaceAnchorAtCenter(\n * { x: 600, y: 300 }, // world position\n * { x: 500, y: 300 }, // camera position\n * 1.0,\n * 0\n * );\n * // Result: { x: 100, y: 0 } (100 pixels right of center)\n *\n * // Position a DOM element at this world object\n * element.style.left = `${viewportPos.x + canvas.width/2}px`;\n * element.style.top = `${-viewportPos.y + canvas.height/2}px`;\n * ```\n *\n * @category Camera\n */\nexport function convert2ViewPortSpaceAnchorAtCenter(point: Point, cameraPosition: Point, cameraZoomLevel: number, cameraRotation: number): Point{\n return convertFromWorld2Viewport(point, cameraPosition, cameraZoomLevel, cameraRotation, false);\n}\n\n/**\n * Converts a world point to canvas coordinates (bottom-left origin).\n *\n * @param point - Point in world coordinates\n * @param viewPortWidth - Viewport width in CSS pixels\n * @param viewPortHeight - Viewport height in CSS pixels\n * @param cameraPosition - Camera position in world coordinates\n * @param cameraZoomLevel - Camera zoom level\n * @param cameraRotation - Camera rotation in radians\n * @returns Canvas coordinates (origin at bottom-left, in CSS pixels)\n *\n * @remarks\n * \"Invert\" in the function name refers to inverting the forward transformation\n * (world → viewport → canvas). The result uses canvas coordinates where:\n * - (0, 0) is at the bottom-left corner\n * - x increases to the right\n * - y increases upward\n *\n * @example\n * ```typescript\n * const canvasPos = invertFromWorldSpace(\n * { x: 500, y: 300 }, // world position\n * 1920, 1080,\n * { x: 500, y: 300 }, // camera at same position\n * 1.0,\n * 0\n * );\n * // Result: { x: 960, y: 540 } (center of 1920x1080 canvas)\n * ```\n *\n * @category Camera\n */\nexport function invertFromWorldSpace(point: Point, viewPortWidth: number, viewPortHeight: number, cameraPosition: Point, cameraZoomLevel: number, cameraRotation: number): Point{\n const pointInViewPort = convertFromWorld2Viewport(point, cameraPosition, cameraZoomLevel, cameraRotation, false);\n return convertFromViewPort2Canvas(pointInViewPort, {x: viewPortWidth / 2, y: viewPortHeight / 2}, false);\n}\n\n/**\n * Checks if a world point is currently visible in the viewport.\n *\n * @param point - Point in world coordinates\n * @param viewPortWidth - Viewport width in CSS pixels\n * @param viewPortHeight - Viewport height in CSS pixels\n * @param cameraPosition - Camera position in world coordinates\n * @param cameraZoomLevel - Camera zoom level\n * @param cameraRotation - Camera rotation in radians\n * @returns True if point is visible in viewport, false otherwise\n *\n * @remarks\n * A point is visible if it falls within the rectangular viewport bounds.\n * This uses canvas coordinates for the visibility check (0 to width/height).\n *\n * @example\n * ```typescript\n * const isVisible = pointIsInViewPort(\n * { x: 550, y: 300 }, // world point\n * 1920, 1080,\n * { x: 500, y: 300 }, // camera position\n * 1.0,\n * 0\n * );\n * // Returns true if point is within viewport bounds\n * ```\n *\n * @category Camera\n */\nexport function pointIsInViewPort(point: Point, viewPortWidth: number, viewPortHeight: number, cameraPosition: Point, cameraZoomLevel: number, cameraRotation: number): boolean{\n const pointInCameraFrame = invertFromWorldSpace(point, viewPortWidth, viewPortHeight, cameraPosition, cameraZoomLevel, cameraRotation);\n if(pointInCameraFrame.x < 0 || pointInCameraFrame.x > viewPortWidth || pointInCameraFrame.y < 0 || pointInCameraFrame.y > viewPortHeight){\n return false;\n }\n return true;\n}\n\n/**\n * Converts a displacement vector from viewport space to world space.\n * Use this for converting movement deltas, not absolute positions.\n *\n * @param delta - Displacement vector in viewport space (CSS pixels)\n * @param cameraZoomLevel - Camera zoom level\n * @param cameraRotation - Camera rotation in radians\n * @returns Displacement vector in world coordinates\n *\n * @remarks\n * This transforms a *relative* displacement, not an absolute point.\n * The conversion accounts for:\n * - Rotation: Delta is rotated by camera rotation\n * - Zoom: Delta is scaled by 1/zoom (viewport pixels → world units)\n *\n * Note: Camera position is NOT needed for delta transformations.\n *\n * @example\n * ```typescript\n * // User dragged 100 pixels to the right in viewport\n * const viewportDelta = { x: 100, y: 0 };\n * const worldDelta = convertDeltaInViewPortToWorldSpace(\n * viewportDelta,\n * 2.0, // 2x zoom\n * 0 // no rotation\n * );\n * // Result: { x: 50, y: 0 } (100 viewport pixels = 50 world units at 2x zoom)\n * ```\n *\n * @category Camera\n */\nexport function convertDeltaInViewPortToWorldSpace(delta: Point, cameraZoomLevel: number, cameraRotation: number): Point{\n return PointCal.multiplyVectorByScalar(PointCal.rotatePoint(delta, cameraRotation), 1 / cameraZoomLevel);\n}\n\n/**\n * Converts a displacement vector from world space to viewport space.\n * Use this for converting movement deltas, not absolute positions.\n * Inverse of {@link convertDeltaInViewPortToWorldSpace}.\n *\n * @param delta - Displacement vector in world coordinates\n * @param cameraZoomLevel - Camera zoom level\n * @param cameraRotation - Camera rotation in radians\n * @returns Displacement vector in viewport space (CSS pixels)\n *\n * @remarks\n * This transforms a *relative* displacement, not an absolute point.\n * The conversion accounts for:\n * - Rotation: Delta is rotated by -camera rotation\n * - Zoom: Delta is scaled by zoom (world units → viewport pixels)\n *\n * @example\n * ```typescript\n * // Object moved 50 units right in world space\n * const worldDelta = { x: 50, y: 0 };\n * const viewportDelta = convertDeltaInWorldToViewPortSpace(\n * worldDelta,\n * 2.0, // 2x zoom\n * 0 // no rotation\n * );\n * // Result: { x: 100, y: 0 } (50 world units = 100 viewport pixels at 2x zoom)\n * ```\n *\n * @category Camera\n */\nexport function convertDeltaInWorldToViewPortSpace(delta: Point, cameraZoomLevel: number, cameraRotation: number): Point{\n return PointCal.multiplyVectorByScalar(PointCal.rotatePoint(delta, -cameraRotation), cameraZoomLevel);\n}\n\n/**\n * Calculates the camera position needed to place a world point at a specific viewport location.\n * Useful for implementing \"zoom to point\" or \"focus on object\" features.\n *\n * @param pointInWorld - The world point to focus on\n * @param toPointInViewPort - Where in the viewport this point should appear (origin at center)\n * @param cameraZoomLevel - Target zoom level\n * @param cameraRotation - Target rotation in radians\n * @returns Camera position that achieves the desired framing\n *\n * @remarks\n * This is particularly useful for:\n * - Zoom-to-cursor: Make clicked point stay under cursor while zooming\n * - Pan-and-zoom: Smoothly navigate to show a specific object\n * - Focus features: Center camera on a world object\n *\n * The viewport point is in viewport coordinates (center origin).\n * To center on a world point, use toPointInViewPort = {x: 0, y: 0}.\n *\n * @example\n * ```typescript\n * // Center camera on world point (1000, 500)\n * const newCameraPos = cameraPositionToGet(\n * { x: 1000, y: 500 }, // world point to focus on\n * { x: 0, y: 0 }, // center of viewport\n * 2.0, // zoom level\n * 0 // rotation\n * );\n * camera.setPosition(newCameraPos);\n *\n * // Zoom to cursor position\n * // Keep world point under cursor at (viewportX, viewportY)\n * const cursorViewport = {\n * x: mouseX - canvas.width/2,\n * y: mouseY - canvas.height/2\n * };\n * const worldAtCursor = camera.convertFromViewPort2WorldSpace(cursorViewport);\n * const newPos = cameraPositionToGet(worldAtCursor, cursorViewport, newZoom, rotation);\n * camera.setPosition(newPos);\n * camera.setZoomLevel(newZoom);\n * ```\n *\n * @category Camera\n */\nexport function cameraPositionToGet(pointInWorld: Point, toPointInViewPort: Point, cameraZoomLevel: number, cameraRotation: number): Point {\n const scaled = PointCal.multiplyVectorByScalar(toPointInViewPort, 1 / cameraZoomLevel);\n const rotated = PointCal.rotatePoint(scaled, cameraRotation);\n return PointCal.subVector(pointInWorld, rotated);\n}\n\n/**\n * Creates a transformation matrix from camera parameters.\n * Combines position, zoom, and rotation into a single transform.\n *\n * @param cameraPosition - Camera position in world coordinates\n * @param cameraZoomLevel - Camera zoom level\n * @param cameraRotation - Camera rotation in radians\n * @returns Transformation matrix for viewport-to-world conversion\n *\n * @remarks\n * The resulting matrix can be used with {@link convert2WorldSpaceWithTransformationMatrix}\n * for efficient batch transformations when camera state doesn't change.\n *\n * Matrix composition order: Translation → Rotation → Scale(1/zoom)\n *\n * @category Camera\n */\nexport function transformationMatrixFromCamera(cameraPosition: Point, cameraZoomLevel: number, cameraRotation: number): TransformationMatrix{\n const cos = Math.cos(cameraRotation);\n const sin = Math.sin(cameraRotation);\n const trMatrix = multiplyMatrix({\n a: 1,\n b: 0,\n c: 0,\n d: 1,\n e: cameraPosition.x,\n f: cameraPosition.y\n }, {\n a: cos,\n b: sin,\n c: -sin,\n d: cos,\n e: 0,\n f: 0\n });\n const trsMatrix = multiplyMatrix(trMatrix, {\n a: 1 / cameraZoomLevel,\n b: 0,\n c: 0,\n d: 1 / cameraZoomLevel,\n e: 0,\n f: 0\n });\n return trsMatrix;\n}\n\n/**\n * Transforms a viewport point to world space using a precomputed transformation matrix.\n * Faster than repeated function calls when transforming many points with the same camera state.\n *\n * @param point - Point in viewport coordinates (origin at center)\n * @param transformationMatrix - Precomputed transformation matrix from {@link transformationMatrixFromCamera}\n * @returns World space coordinates of the point\n *\n * @remarks\n * Use this for batch transformations when the camera state is constant:\n * 1. Create matrix once with {@link transformationMatrixFromCamera}\n * 2. Transform many points with this function\n *\n * This avoids recalculating sin/cos and matrix operations for each point.\n *\n * @example\n * ```typescript\n * // Transform many points efficiently\n * const matrix = transformationMatrixFromCamera(\n * { x: 100, y: 200 },\n * 1.5,\n * Math.PI / 4\n * );\n *\n * const worldPoints = viewportPoints.map(vp =>\n * convert2WorldSpaceWithTransformationMatrix(vp, matrix)\n * );\n * ```\n *\n * @category Camera\n * @see {@link transformationMatrixFromCamera} to create the matrix\n */\nexport function convert2WorldSpaceWithTransformationMatrix(point: Point, transformationMatrix: TransformationMatrix): Point{\n return {\n x: point.x * transformationMatrix.a + point.y * transformationMatrix.c + transformationMatrix.e,\n y: point.x * transformationMatrix.b + point.y * transformationMatrix.d + transformationMatrix.f\n }\n}\n",
@@ -12,36 +12,36 @@
12
12
  "import { Point, PointCal } from \"@ue-too/math\";\nimport { convert2WorldSpaceWRT } from \"./coordinate-conversion\";\n\n/**\n * Position boundaries for camera movement in world space.\n * Allows optional constraints on x and y axes independently.\n *\n * @property min - Minimum position constraints (both x and y are optional)\n * @property max - Maximum position constraints (both x and y are optional)\n *\n * @remarks\n * All coordinates are in world space. Each axis (x, y) can be:\n * - Fully constrained: both min and max defined\n * - Partially constrained: only min or max defined\n * - Unconstrained: neither min nor max defined\n *\n * This allows for flexible boundary configurations like:\n * - Horizontal-only boundaries (x constrained, y free)\n * - Vertical-only boundaries (y constrained, x free)\n * - One-sided boundaries (e.g., minimum x but no maximum)\n *\n * @example\n * ```typescript\n * // Fully constrained rectangular boundary\n * const rect: Boundaries = {\n * min: { x: -1000, y: -1000 },\n * max: { x: 1000, y: 1000 }\n * };\n *\n * // Horizontal constraints only\n * const horizontal: Boundaries = {\n * min: { x: -500 },\n * max: { x: 500 }\n * };\n *\n * // One-sided constraint (can't go below y=0)\n * const floor: Boundaries = {\n * min: { y: 0 }\n * };\n * ```\n *\n * @category Camera\n */\nexport type Boundaries = {\n min?: {x?: number, y?: number};\n max?: {x?: number, y?: number};\n}\n\n/**\n * Checks if a point is within the specified boundaries.\n *\n * @param point - Point to check in world coordinates\n * @param boundaries - Optional boundary constraints\n * @returns True if point is within boundaries or no boundaries specified, false otherwise\n *\n * @remarks\n * Returns true if:\n * - No boundaries are defined (undefined)\n * - Point satisfies all defined constraints\n *\n * Each axis is checked independently. A missing constraint on an axis means\n * that axis is unbounded.\n *\n * @example\n * ```typescript\n * const bounds: Boundaries = {\n * min: { x: -100, y: -50 },\n * max: { x: 100, y: 50 }\n * };\n *\n * withinBoundaries({ x: 0, y: 0 }, bounds); // true (inside)\n * withinBoundaries({ x: 150, y: 0 }, bounds); // false (x too large)\n * withinBoundaries({ x: 0, y: -100 }, bounds); // false (y too small)\n * withinBoundaries({ x: 100, y: 50 }, bounds); // true (on boundary)\n * withinBoundaries({ x: 0, y: 0 }, undefined); // true (no bounds)\n * ```\n *\n * @category Camera\n */\nexport function withinBoundaries(point: Point, boundaries: Boundaries | undefined): boolean{\n if(boundaries == undefined){\n // no boundaries \n return true;\n }\n let leftSide = false;\n let rightSide = false;\n let topSide = false;\n let bottomSide = false;\n // check within boundaries horizontally\n if(boundaries.max == undefined || boundaries.max.x == undefined || point.x <= boundaries.max.x){\n rightSide = true;\n }\n if(boundaries.min == undefined || boundaries.min.x == undefined || point.x >= boundaries.min.x){\n leftSide = true;\n }\n if(boundaries.max == undefined || boundaries.max.y == undefined || point.y <= boundaries.max.y){\n topSide = true;\n }\n if(boundaries.min == undefined || boundaries.min.y == undefined || point.y >= boundaries.min.y){\n bottomSide = true;\n }\n return leftSide && rightSide && topSide && bottomSide;\n}\n\n/**\n * Validates that boundaries are logically consistent.\n *\n * @param boundaries - The boundaries to validate\n * @returns True if boundaries are valid or undefined, false if min >= max on any axis\n *\n * @remarks\n * Returns false if:\n * - On any axis, both min and max are defined AND min >= max\n *\n * Returns true if:\n * - Boundaries are undefined\n * - Only min or max is defined on an axis\n * - Both are defined and min < max on all axes\n *\n * @example\n * ```typescript\n * isValidBoundaries({ min: { x: 0, y: 0 }, max: { x: 100, y: 100 } }); // true\n * isValidBoundaries({ min: { x: 100 }, max: { x: 0 } }); // false (min > max)\n * isValidBoundaries({ min: { x: 50, y: 50 }, max: { x: 50, y: 60 } }); // false (x min == max)\n * isValidBoundaries({ min: { x: 0 } }); // true (partial)\n * isValidBoundaries(undefined); // true\n * ```\n *\n * @category Camera\n */\nexport function isValidBoundaries(boundaries: Boundaries | undefined): boolean{\n if(boundaries == undefined){\n return true;\n }\n const minX = boundaries.min?.x;\n const maxX = boundaries.max?.x;\n if (minX != undefined && maxX != undefined && minX >= maxX){\n return false;\n }\n const minY = boundaries.min?.y;\n const maxY = boundaries.max?.y;\n if (minY != undefined && maxY != undefined && minY >= maxY){\n return false;\n }\n return true;\n}\n\n/**\n * Checks if boundaries have all four constraints (min/max for both x and y) defined.\n *\n * @param boundaries - The boundaries to check\n * @returns True if all four constraints are defined, false otherwise\n *\n * @remarks\n * Returns true only if boundaries define a complete rectangular region:\n * - min.x, min.y, max.x, and max.y are all defined\n *\n * @example\n * ```typescript\n * boundariesFullyDefined({\n * min: { x: 0, y: 0 },\n * max: { x: 100, y: 100 }\n * }); // true\n *\n * boundariesFullyDefined({\n * min: { x: 0, y: 0 },\n * max: { x: 100 } // missing max.y\n * }); // false\n *\n * boundariesFullyDefined({ min: { x: 0 } }); // false\n * boundariesFullyDefined(undefined); // false\n * ```\n *\n * @category Camera\n */\nexport function boundariesFullyDefined(boundaries: Boundaries | undefined): boolean{\n if(boundaries == undefined){\n return false;\n }\n if(boundaries.max == undefined || boundaries.min == undefined){\n return false;\n }\n if(boundaries.max.x == undefined || boundaries.max.y == undefined || boundaries.min.x == undefined || boundaries.min.y == undefined){\n return false;\n }\n return true;\n}\n\n/**\n * Clamps a point to stay within specified boundaries.\n *\n * @param point - Point to clamp in world coordinates\n * @param boundaries - Optional boundary constraints\n * @returns Clamped point, or original if already within bounds or no boundaries\n *\n * @remarks\n * Each axis is clamped independently:\n * - If a min constraint exists on an axis, ensures point >= min\n * - If a max constraint exists on an axis, ensures point <= max\n * - If no constraint exists on an axis, that axis is unchanged\n *\n * @example\n * ```typescript\n * const bounds: Boundaries = {\n * min: { x: -100, y: -50 },\n * max: { x: 100, y: 50 }\n * };\n *\n * clampPoint({ x: 0, y: 0 }, bounds); // { x: 0, y: 0 } (inside)\n * clampPoint({ x: 150, y: 0 }, bounds); // { x: 100, y: 0 } (clamped x)\n * clampPoint({ x: 0, y: -100 }, bounds); // { x: 0, y: -50 } (clamped y)\n * clampPoint({ x: 200, y: -200 }, bounds); // { x: 100, y: -50 } (both clamped)\n * clampPoint({ x: 0, y: 0 }, undefined); // { x: 0, y: 0 } (no bounds)\n * ```\n *\n * @category Camera\n */\nexport function clampPoint(point: Point, boundaries: Boundaries | undefined): Point{\n if(withinBoundaries(point, boundaries) || boundaries == undefined){\n return point;\n }\n let manipulatePoint = {x: point.x, y: point.y};\n let limit = boundaries.min;\n if (limit != undefined){\n if(limit.x != undefined){\n manipulatePoint.x = Math.max(manipulatePoint.x, limit.x);\n }\n if(limit.y != undefined){\n manipulatePoint.y = Math.max(manipulatePoint.y, limit.y);\n }\n }\n limit = boundaries.max;\n if(limit != undefined){\n if(limit.x != undefined){\n manipulatePoint.x = Math.min(manipulatePoint.x, limit.x);\n }\n if(limit.y != undefined){\n manipulatePoint.y = Math.min(manipulatePoint.y, limit.y);\n }\n }\n return manipulatePoint;\n}\n\n/**\n * Calculates the width (x-axis span) of the boundaries.\n *\n * @param boundaries - The boundaries to measure\n * @returns Width in world units, or undefined if x boundaries are not fully defined\n *\n * @remarks\n * Returns undefined if boundaries don't have both min.x and max.x defined.\n * Result is always non-negative for valid boundaries (max.x - min.x).\n *\n * @example\n * ```typescript\n * translationWidthOf({\n * min: { x: -100, y: -50 },\n * max: { x: 100, y: 50 }\n * }); // 200\n *\n * translationWidthOf({ min: { x: 0 } }); // undefined (no max.x)\n * translationWidthOf(undefined); // undefined\n * ```\n *\n * @category Camera\n */\nexport function translationWidthOf(boundaries: Boundaries | undefined): number | undefined{\n if(boundaries == undefined || boundaries.min == undefined || boundaries.max == undefined || boundaries.min.x == undefined || boundaries.max.x == undefined){\n return undefined;\n }\n return boundaries.max.x - boundaries.min.x;\n}\n\n/**\n * Calculates half the width (x-axis half-span) of the boundaries.\n *\n * @param boundaries - The boundaries to measure\n * @returns Half-width in world units, or undefined if x boundaries are not fully defined\n *\n * @remarks\n * Useful for calculating radius or offset from center for x-axis.\n * Equivalent to `translationWidthOf(boundaries) / 2`.\n *\n * @example\n * ```typescript\n * halfTranslationWidthOf({\n * min: { x: -100, y: -50 },\n * max: { x: 100, y: 50 }\n * }); // 100\n * ```\n *\n * @category Camera\n */\nexport function halfTranslationWidthOf(boundaries: Boundaries | undefined): number | undefined{\n const translationWidth = translationWidthOf(boundaries);\n return translationWidth != undefined ? translationWidth / 2 : undefined;\n}\n\n/**\n * Calculates the height (y-axis span) of the boundaries.\n *\n * @param boundaries - The boundaries to measure\n * @returns Height in world units, or undefined if y boundaries are not fully defined\n *\n * @remarks\n * Returns undefined if boundaries don't have both min.y and max.y defined.\n * Result is always non-negative for valid boundaries (max.y - min.y).\n *\n * @example\n * ```typescript\n * translationHeightOf({\n * min: { x: -100, y: -50 },\n * max: { x: 100, y: 50 }\n * }); // 100\n *\n * translationHeightOf({ min: { y: 0 } }); // undefined (no max.y)\n * translationHeightOf(undefined); // undefined\n * ```\n *\n * @category Camera\n */\nexport function translationHeightOf(boundaries: Boundaries | undefined): number | undefined{\n if(boundaries == undefined || boundaries.min == undefined || boundaries.max == undefined || boundaries.min.y == undefined || boundaries.max.y == undefined){\n return undefined;\n }\n return boundaries.max.y - boundaries.min.y;\n}\n\n/**\n * Calculates half the height (y-axis half-span) of the boundaries.\n *\n * @param boundaries - The boundaries to measure\n * @returns Half-height in world units, or undefined if y boundaries are not fully defined\n *\n * @remarks\n * Useful for calculating radius or offset from center for y-axis.\n * Equivalent to `translationHeightOf(boundaries) / 2`.\n *\n * @example\n * ```typescript\n * halfTranslationHeightOf({\n * min: { x: -100, y: -50 },\n * max: { x: 100, y: 50 }\n * }); // 50\n * ```\n *\n * @category Camera\n */\nexport function halfTranslationHeightOf(boundaries: Boundaries | undefined): number | undefined{\n const translationHeight = translationHeightOf(boundaries);\n return translationHeight != undefined ? translationHeight / 2 : undefined;\n}\n\n/**\n * Clamps camera position to ensure the entire viewport stays within boundaries.\n * More restrictive than {@link clampPoint} as it considers viewport size and rotation.\n *\n * @param point - Proposed camera position in world coordinates\n * @param viewPortWidth - Width of the viewport in CSS pixels\n * @param viewPortHeight - Height of the viewport in CSS pixels\n * @param boundaries - Optional boundary constraints in world space\n * @param cameraZoomLevel - Current camera zoom level\n * @param cameraRotation - Current camera rotation in radians\n * @returns Adjusted camera position that keeps entire viewport within boundaries\n *\n * @remarks\n * This function ensures no part of the viewport extends outside the boundaries.\n * It accounts for:\n * - Viewport dimensions (width/height)\n * - Camera rotation (viewport corners rotate around camera center)\n * - Zoom level (affects world-space size of viewport)\n *\n * The algorithm:\n * 1. Calculates all four viewport corners in world space\n * 2. Clamps each corner to boundaries\n * 3. Finds the maximum displacement needed across all corners\n * 4. Adjusts camera position by that displacement\n *\n * Use this for \"edge-stop\" behavior where viewport cannot scroll past boundaries.\n * For \"center-stop\" behavior, use {@link clampPoint} instead.\n *\n * @example\n * ```typescript\n * const bounds: Boundaries = {\n * min: { x: 0, y: 0 },\n * max: { x: 1000, y: 1000 }\n * };\n *\n * // Camera at center of bounds, viewport extends outside\n * const adjusted = clampPointEntireViewPort(\n * { x: 100, y: 100 }, // camera position\n * 800, 600, // viewport size\n * bounds,\n * 1.0, // zoom\n * 0 // rotation\n * );\n * // Returns position that prevents viewport from exceeding bounds\n * ```\n *\n * @category Camera\n * @see {@link clampPoint} for clamping camera center only\n */\nexport function clampPointEntireViewPort(point: Point, viewPortWidth: number, viewPortHeight: number, boundaries: Boundaries | undefined, cameraZoomLevel: number, cameraRotation: number): Point{\n if(boundaries == undefined){\n return point;\n }\n let topLeftCorner = convert2WorldSpaceWRT(point, {x: 0, y: viewPortHeight}, viewPortWidth, viewPortHeight, cameraZoomLevel, cameraRotation);\n let bottomLeftCorner = convert2WorldSpaceWRT(point, {x: 0, y: 0}, viewPortWidth, viewPortHeight, cameraZoomLevel, cameraRotation);\n let topRightCorner = convert2WorldSpaceWRT(point, {x: viewPortWidth, y: viewPortHeight}, viewPortWidth, viewPortHeight, cameraZoomLevel, cameraRotation);\n let bottomRightCorner = convert2WorldSpaceWRT(point, {x: viewPortWidth, y: 0}, viewPortWidth, viewPortHeight, cameraZoomLevel, cameraRotation);\n let topLeftCornerClamped = clampPoint(topLeftCorner, boundaries);\n let topRightCornerClamped = clampPoint(topRightCorner, boundaries);\n let bottomLeftCornerClamped = clampPoint(bottomLeftCorner, boundaries);\n let bottomRightCornerClamped = clampPoint(bottomRightCorner, boundaries);\n let topLeftCornerDiff = PointCal.subVector(topLeftCornerClamped, topLeftCorner);\n let topRightCornerDiff = PointCal.subVector(topRightCornerClamped, topRightCorner);\n let bottomLeftCornerDiff = PointCal.subVector(bottomLeftCornerClamped, bottomLeftCorner);\n let bottomRightCornerDiff = PointCal.subVector(bottomRightCornerClamped, bottomRightCorner);\n let diffs = [topLeftCornerDiff, topRightCornerDiff, bottomLeftCornerDiff, bottomRightCornerDiff];\n let maxXDiff = Math.abs(diffs[0].x);\n let maxYDiff = Math.abs(diffs[0].y);\n let delta = diffs[0];\n diffs.forEach((diff)=>{\n if(Math.abs(diff.x) > maxXDiff){\n maxXDiff = Math.abs(diff.x);\n delta.x = diff.x;\n }\n if(Math.abs(diff.y) > maxYDiff){\n maxYDiff = Math.abs(diff.y);\n delta.y = diff.y;\n }\n });\n return PointCal.addVector(point, delta);\n}\n",
13
13
  "/**\n * Constraints for camera zoom level with optional minimum and maximum bounds.\n *\n * @property min - Minimum allowed zoom level (optional, e.g., 0.1 for 10% zoom)\n * @property max - Maximum allowed zoom level (optional, e.g., 10 for 1000% zoom)\n *\n * @remarks\n * Zoom level of 1.0 represents 100% (no zoom), values >1 zoom in, values <1 zoom out.\n * If both min and max are undefined, no constraints are applied.\n *\n * @category Camera\n */\nexport type ZoomLevelLimits = {min?: number, max?: number};\n\n/**\n * Validates that zoom level limits are logically consistent.\n *\n * @param zoomLevelLimits - The zoom limits to validate\n * @returns True if limits are valid or undefined, false if min > max\n *\n * @remarks\n * Returns true if:\n * - Limits are undefined (no constraints)\n * - Only min or max is defined\n * - Both are defined and min ≤ max\n *\n * @example\n * ```typescript\n * isValidZoomLevelLimits({ min: 0.5, max: 5 }); // true\n * isValidZoomLevelLimits({ min: 5, max: 0.5 }); // false\n * isValidZoomLevelLimits({ min: 0.5 }); // true\n * isValidZoomLevelLimits(undefined); // true\n * ```\n *\n * @category Camera\n */\nexport function isValidZoomLevelLimits(zoomLevelLimits: ZoomLevelLimits | undefined): boolean{\n if(zoomLevelLimits === undefined){\n return true;\n }\n if(zoomLevelLimits.min !== undefined && zoomLevelLimits.max !== undefined && zoomLevelLimits.min > zoomLevelLimits.max){\n return false;\n }\n return true;\n}\n\n/**\n * Clamps a zoom level to stay within specified limits.\n *\n * @param zoomLevel - The zoom level to clamp\n * @param zoomLevelLimits - Optional zoom constraints\n * @returns The clamped zoom level, or original value if already within limits\n *\n * @remarks\n * If the zoom level is already within limits, returns it unchanged.\n * If no limits are specified, returns the original value.\n *\n * @example\n * ```typescript\n * const limits = { min: 0.5, max: 4 };\n *\n * clampZoomLevel(2.0, limits); // 2.0 (within bounds)\n * clampZoomLevel(0.1, limits); // 0.5 (clamped to min)\n * clampZoomLevel(10, limits); // 4.0 (clamped to max)\n * clampZoomLevel(2.0); // 2.0 (no limits)\n * ```\n *\n * @category Camera\n */\nexport function clampZoomLevel(zoomLevel: number, zoomLevelLimits?: ZoomLevelLimits): number{\n if(zoomLevelWithinLimits(zoomLevel, zoomLevelLimits) || zoomLevelLimits === undefined){\n return zoomLevel;\n }\n if(zoomLevelLimits.max){\n zoomLevel = Math.min(zoomLevelLimits.max, zoomLevel);\n }\n if(zoomLevelLimits.min){\n zoomLevel = Math.max(zoomLevelLimits.min, zoomLevel);\n }\n return zoomLevel;\n}\n\n/**\n * Checks if a zoom level is within specified limits.\n *\n * @param zoomLevel - The zoom level to check\n * @param zoomLevelLimits - Optional zoom constraints\n * @returns True if zoom level is valid and within limits, false otherwise\n *\n * @remarks\n * Returns false if:\n * - Zoom level is ≤ 0 (invalid zoom)\n * - Zoom level exceeds maximum limit (if defined)\n * - Zoom level is below minimum limit (if defined)\n *\n * Returns true if no limits are defined or zoom is within bounds.\n *\n * @example\n * ```typescript\n * const limits = { min: 0.5, max: 4 };\n *\n * zoomLevelWithinLimits(2.0, limits); // true\n * zoomLevelWithinLimits(0.1, limits); // false (below min)\n * zoomLevelWithinLimits(10, limits); // false (above max)\n * zoomLevelWithinLimits(-1, limits); // false (negative zoom)\n * zoomLevelWithinLimits(0, limits); // false (zero zoom)\n * zoomLevelWithinLimits(2.0); // true (no limits)\n * ```\n *\n * @category Camera\n */\nexport function zoomLevelWithinLimits(zoomLevel: number, zoomLevelLimits?: ZoomLevelLimits): boolean{\n if(zoomLevelLimits === undefined){\n return true;\n }\n if(zoomLevel <= 0 || (zoomLevelLimits !== undefined &&\n ((zoomLevelLimits.max !== undefined && zoomLevelLimits.max < zoomLevel) ||\n (zoomLevelLimits.min !== undefined && zoomLevelLimits.min > zoomLevel)\n ))){\n return false;\n }\n return true;\n}\n",
14
14
  "/**\n * Constraints for camera rotation defining an angular range with direction.\n *\n * @property start - Starting angle of the allowed range in radians\n * @property end - Ending angle of the allowed range in radians\n * @property ccw - If true, the range is measured counter-clockwise from start to end. If false, clockwise\n * @property startAsTieBreaker - When clamping and distance to start equals distance to end, clamp to start if true, end if false\n *\n * @remarks\n * Rotation limits define an angular arc. The direction (ccw) determines which\n * way around the circle the range extends from start to end.\n *\n * For example:\n * - start=0, end=π/2, ccw=true: allows 0 to π/2 (0° to 90°)\n * - start=0, end=π/2, ccw=false: allows 0 to -3π/2 going clockwise (0° to 270° the other way)\n *\n * @category Camera\n */\nexport type RotationLimits = {start: number, end: number, ccw: boolean, startAsTieBreaker: boolean};\n\n/**\n * Experimental rotation boundary type with positive/negative direction semantics.\n *\n * @property start - Starting angle of the boundary in radians\n * @property end - Ending angle of the boundary in radians\n * @property positiveDirection - If true, range extends in positive angle direction. If false, negative direction\n * @property startAsTieBreaker - When equidistant from start and end, prefer start if true, end if false\n *\n * @remarks\n * This is an experimental alternative to {@link RotationLimits} with different direction semantics.\n *\n * @category Camera\n */\nexport type RotationBoundary = {start: number, end: number, positiveDirection: boolean, startAsTieBreaker: boolean};\n\n/**\n * Clamps a rotation angle to stay within specified angular limits.\n *\n * @param rotation - The rotation angle to clamp in radians\n * @param rotationLimits - Optional rotation constraints with direction\n * @returns The clamped rotation angle, or original if already within limits\n *\n * @remarks\n * If the rotation is outside the allowed arc, it's clamped to the nearest\n * boundary (start or end). When equidistant from both, the `startAsTieBreaker`\n * flag determines which boundary to use.\n *\n * The rotation is normalized to [0, 2π] before clamping.\n *\n * @example\n * ```typescript\n * const limits = { start: 0, end: Math.PI/2, ccw: true, startAsTieBreaker: true };\n *\n * clampRotation(Math.PI/4, limits); // π/4 (within range)\n * clampRotation(Math.PI, limits); // π/2 (clamped to end)\n * clampRotation(-0.1, limits); // 0 (clamped to start)\n * ```\n *\n * @category Camera\n */\nexport function clampRotation(rotation: number, rotationLimits?: RotationLimits): number{\n if(rotationWithinLimits(rotation, rotationLimits) || rotationLimits === undefined){\n return rotation;\n }\n rotation = normalizeAngleZero2TwoPI(rotation);\n const angleSpanFromStart = angleSpan(rotationLimits.start, rotation);\n const angleSpanFromEnd = angleSpan(rotationLimits.end, rotation);\n if((rotationLimits.ccw && (angleSpanFromStart < 0 || angleSpanFromEnd > 0)) || (!rotationLimits.ccw && (angleSpanFromStart > 0 || angleSpanFromEnd < 0))){\n // ccw out of bounds\n if(Math.abs(angleSpanFromStart) === Math.abs(angleSpanFromEnd)){\n // console.log(\"tie\", \"start:\", rotationLimits.start, \"end:\", rotationLimits.end, \"rotation:\", rotation);\n return rotationLimits.startAsTieBreaker ? rotationLimits.start : rotationLimits.end;\n }\n const closerToStart = Math.abs(angleSpanFromStart) < Math.abs(angleSpanFromEnd);\n return closerToStart ? rotationLimits.start : rotationLimits.end;\n }\n return rotation;\n}\n\n/**\n * Checks if a rotation angle is within specified angular limits.\n *\n * @param rotation - The rotation angle to check in radians\n * @param rotationLimits - Optional rotation constraints with direction\n * @returns True if rotation is within the allowed arc or no limits specified, false otherwise\n *\n * @remarks\n * Returns true if:\n * - No limits are specified (undefined)\n * - Start and end angles are effectively equal (full circle allowed)\n * - Rotation falls within the arc from start to end in the specified direction\n *\n * The rotation is normalized to [0, 2π] before checking.\n *\n * @example\n * ```typescript\n * const limits = { start: 0, end: Math.PI/2, ccw: true, startAsTieBreaker: true };\n *\n * rotationWithinLimits(Math.PI/4, limits); // true (within range)\n * rotationWithinLimits(Math.PI, limits); // false (outside range)\n * rotationWithinLimits(0, limits); // true (at start)\n * rotationWithinLimits(Math.PI/2, limits); // true (at end)\n * ```\n *\n * @category Camera\n */\nexport function rotationWithinLimits(rotation: number, rotationLimits?: RotationLimits): boolean{\n if(rotationLimits === undefined){\n return true;\n }\n if(normalizeAngleZero2TwoPI(rotationLimits.start) === normalizeAngleZero2TwoPI(rotationLimits.end)){\n return true;\n }\n if(normalizeAngleZero2TwoPI(rotationLimits.start + 0.01) === normalizeAngleZero2TwoPI(rotationLimits.end + 0.01)){\n return true;\n }\n const normalizedRotation = normalizeAngleZero2TwoPI(rotation);\n const angleSpanFromStart = angleSpan(rotationLimits.start, normalizedRotation);\n const angleSpanFromEnd = angleSpan(rotationLimits.end, normalizedRotation);\n if((rotationLimits.ccw && (angleSpanFromStart < 0 || angleSpanFromEnd > 0)) || (!rotationLimits.ccw && (angleSpanFromStart > 0 || angleSpanFromEnd < 0))){\n return false;\n }\n return true;\n}\n\n/**\n * Checks if a rotation angle is within an experimental rotation boundary.\n *\n * @param rotation - The rotation angle to check in radians\n * @param rotationBoundary - Rotation boundary with positive/negative direction\n * @returns True if rotation is within the boundary range, false otherwise\n *\n * @remarks\n * This is an experimental alternative to {@link rotationWithinLimits} using\n * positive/negative direction semantics instead of ccw/cw.\n *\n * @category Camera\n */\nexport function rotationWithinBoundary(rotation: number, rotationBoundary: RotationBoundary): boolean {\n if(normalizeAngleZero2TwoPI(rotationBoundary.start) === normalizeAngleZero2TwoPI(rotationBoundary.end)){\n return true;\n }\n if(normalizeAngleZero2TwoPI(rotationBoundary.start + 0.01) === normalizeAngleZero2TwoPI(rotationBoundary.end + 0.01)){\n return true;\n }\n const normalizedRotation = normalizeAngleZero2TwoPI(rotation);\n\n let angleFromStart = normalizedRotation - normalizeAngleZero2TwoPI(rotationBoundary.start);\n if (angleFromStart < 0){\n angleFromStart += (Math.PI * 2);\n }\n if (!rotationBoundary.positiveDirection && angleFromStart > 0){\n angleFromStart = Math.PI * 2 - angleFromStart;\n }\n\n let angleRange = normalizeAngleZero2TwoPI(rotationBoundary.end) - normalizeAngleZero2TwoPI(rotationBoundary.start);\n if(angleRange < 0){\n angleRange += (Math.PI * 2);\n }\n if(!rotationBoundary.positiveDirection && angleRange > 0){\n angleRange = Math.PI * 2 - angleRange;\n }\n\n return angleRange >= angleFromStart;\n}\n\n/**\n * Normalizes an angle to the range [0, 2π).\n *\n * @param angle - Angle in radians (can be any value)\n * @returns Equivalent angle in the range [0, 2π)\n *\n * @remarks\n * This function wraps angles to the standard [0, 2π) range. Useful for\n * ensuring consistent angle representation when comparing or storing angles.\n *\n * @example\n * ```typescript\n * normalizeAngleZero2TwoPI(0); // 0\n * normalizeAngleZero2TwoPI(Math.PI); // π\n * normalizeAngleZero2TwoPI(3 * Math.PI); // π (wraps around)\n * normalizeAngleZero2TwoPI(-Math.PI/2); // 3π/2 (negative becomes positive)\n * normalizeAngleZero2TwoPI(2 * Math.PI); // 0 (full rotation)\n * ```\n *\n * @category Camera\n */\nexport function normalizeAngleZero2TwoPI(angle: number){\n // reduce the angle\n angle = angle % (Math.PI * 2);\n\n // force it to be the positive remainder, so that 0 <= angle < 2 * Math.PI\n angle = (angle + Math.PI * 2) % (Math.PI * 2);\n return angle;\n}\n\n/**\n * Calculates the signed angular distance between two angles, taking the shorter path.\n *\n * @param from - Starting angle in radians\n * @param to - Target angle in radians\n * @returns Signed angular difference in radians, in the range (-π, π]\n *\n * @remarks\n * Returns the shortest angular path from `from` to `to`:\n * - Positive value: rotate counter-clockwise (positive direction)\n * - Negative value: rotate clockwise (negative direction)\n * - Always returns the smaller of the two possible paths\n *\n * @example\n * ```typescript\n * angleSpan(0, Math.PI/2); // π/2 (90° ccw)\n * angleSpan(Math.PI/2, 0); // -π/2 (90° cw)\n * angleSpan(0, 3*Math.PI/2); // -π/2 (shorter to go cw)\n * angleSpan(3*Math.PI/2, 0); // π/2 (shorter to go ccw)\n * angleSpan(0, Math.PI); // π (180°, ambiguous)\n * ```\n *\n * @category Camera\n */\nexport function angleSpan(from: number, to: number): number{\n // in radians\n from = normalizeAngleZero2TwoPI(from);\n to = normalizeAngleZero2TwoPI(to);\n let angleDiff = to - from;\n\n if(angleDiff > Math.PI){\n angleDiff = - (Math.PI * 2 - angleDiff);\n }\n\n if(angleDiff < -Math.PI){\n angleDiff += (Math.PI * 2);\n }\n return angleDiff;\n}\n\n/**\n * Converts degrees to radians.\n *\n * @param deg - Angle in degrees\n * @returns Equivalent angle in radians\n *\n * @example\n * ```typescript\n * deg2rad(0); // 0\n * deg2rad(90); // π/2\n * deg2rad(180); // π\n * deg2rad(360); // 2π\n * deg2rad(-45); // -π/4\n * ```\n *\n * @category Camera\n */\nexport function deg2rad(deg: number): number{\n return deg * Math.PI / 180;\n}\n\n/**\n * Converts radians to degrees.\n *\n * @param rad - Angle in radians\n * @returns Equivalent angle in degrees\n *\n * @example\n * ```typescript\n * rad2deg(0); // 0\n * rad2deg(Math.PI/2); // 90\n * rad2deg(Math.PI); // 180\n * rad2deg(2 * Math.PI); // 360\n * rad2deg(-Math.PI/4); // -45\n * ```\n *\n * @category Camera\n */\nexport function rad2deg(rad: number): number{\n return rad * 180 / Math.PI;\n}\n",
15
- "import { Point } from '@ue-too/math';\nimport { Boundaries, withinBoundaries } from './utils/position';\nimport { zoomLevelWithinLimits, ZoomLevelLimits, clampZoomLevel } from './utils/zoom';\nimport { RotationLimits, rotationWithinLimits, normalizeAngleZero2TwoPI, clampRotation } from './utils/rotation';\nimport { convert2WorldSpaceAnchorAtCenter, convert2ViewPortSpaceAnchorAtCenter } from './utils/coordinate-conversion';\nimport { PointCal } from '@ue-too/math';\nimport { BoardCamera } from './interface';\nimport { decomposeCameraMatrix, decomposeTRS, TransformationMatrix } from './utils/matrix';\n\n/**\n * Base camera implementation providing core functionality for an infinite canvas system.\n * This is the fundamental building block for camera management in the board package.\n *\n * @remarks\n * BaseCamera is non-observable and does not emit events when state changes.\n * For event-driven camera updates, use {@link DefaultBoardCamera} instead.\n *\n * The camera supports:\n * - Position, rotation, and zoom transformations\n * - Configurable boundaries for position, zoom, and rotation\n * - Coordinate conversion between viewport and world space\n * - Transformation matrix caching for performance\n * - High-DPI display support via devicePixelRatio\n *\n * @example\n * ```typescript\n * // Create a camera for a 1920x1080 viewport\n * const camera = new BaseCamera(1920, 1080, { x: 0, y: 0 }, 0, 1.0);\n *\n * // Set boundaries to constrain camera movement\n * camera.setHorizontalBoundaries(-5000, 5000);\n * camera.setVerticalBoundaries(-5000, 5000);\n *\n * // Update camera state\n * camera.setPosition({ x: 100, y: 200 });\n * camera.setZoomLevel(2.0);\n * camera.setRotation(Math.PI / 6);\n *\n * // Get transformation matrix for rendering\n * const transform = camera.getTransform(window.devicePixelRatio, true);\n * ctx.setTransform(transform.a, transform.b, transform.c, transform.d, transform.e, transform.f);\n * ```\n *\n * @category Camera\n * @see {@link DefaultBoardCamera} for observable camera with event support\n * @see {@link CameraRig} for high-level camera control with input handling\n */\nexport default class BaseCamera implements BoardCamera {\n\n private _position: Point;\n private _rotation: number;\n private _zoomLevel: number;\n\n private currentCachedTransform: {transform: {a: number, b: number, c: number, d: number, e: number, f: number}, position: Point, rotation: number, zoomLevel: number, alignCoorindate: boolean, devicePixelRatio: number, viewPortWidth: number, viewPortHeight: number} | undefined;\n\n private _viewPortWidth: number;\n private _viewPortHeight: number;\n\n private _boundaries?: Boundaries;\n private _zoomBoundaries?: ZoomLevelLimits;\n private _rotationBoundaries?: RotationLimits;\n\n /**\n * Creates a new BaseCamera instance with specified viewport size and optional constraints.\n *\n * @param viewPortWidth - Width of the viewport in CSS pixels (default: 1000)\n * @param viewPortHeight - Height of the viewport in CSS pixels (default: 1000)\n * @param position - Initial camera position in world coordinates (default: {x: 0, y: 0})\n * @param rotation - Initial rotation in radians (default: 0)\n * @param zoomLevel - Initial zoom level, where 1.0 = 100% (default: 1.0)\n * @param boundaries - Position constraints in world space (default: ±10000 on both axes)\n * @param zoomLevelBoundaries - Zoom constraints (default: 0.1 to 10)\n * @param rotationBoundaries - Optional rotation constraints (default: undefined, unrestricted)\n *\n * @example\n * ```typescript\n * // Basic camera with defaults\n * const camera = new BaseCamera();\n *\n * // Camera with custom viewport and position\n * const camera2 = new BaseCamera(\n * 1920, 1080,\n * { x: 500, y: 300 },\n * 0,\n * 1.5\n * );\n *\n * // Camera with all constraints\n * const camera3 = new BaseCamera(\n * 1920, 1080,\n * { x: 0, y: 0 },\n * 0,\n * 1.0,\n * { min: { x: -2000, y: -2000 }, max: { x: 2000, y: 2000 } },\n * { min: 0.5, max: 5 },\n * { start: 0, end: Math.PI / 2 }\n * );\n * ```\n */\n constructor(viewPortWidth: number = 1000, viewPortHeight: number = 1000, position: Point = {x: 0, y: 0}, rotation: number = 0, zoomLevel: number = 1, boundaries: Boundaries = {min: {x: -10000, y: -10000}, max: {x: 10000, y: 10000}}, zoomLevelBoundaries: ZoomLevelLimits = {min: 0.1, max: 10}, rotationBoundaries: RotationLimits | undefined = undefined){\n this._position = position;\n this._zoomLevel = zoomLevel;\n this._rotation = rotation;\n this._viewPortHeight = viewPortHeight;\n this._viewPortWidth = viewPortWidth;\n this._zoomBoundaries = zoomLevelBoundaries;\n this._rotationBoundaries = rotationBoundaries;\n this._boundaries = boundaries;\n }\n\n /**\n * Gets the current position boundaries that constrain camera movement in world coordinates.\n *\n * @returns The boundaries object or undefined if no boundaries are set\n */\n get boundaries(): Boundaries | undefined{\n return this._boundaries;\n }\n\n /**\n * Sets position boundaries to constrain camera movement in world coordinates.\n *\n * @param boundaries - Boundary constraints or undefined to remove all constraints\n */\n set boundaries(boundaries: Boundaries | undefined){\n this._boundaries = boundaries;\n }\n\n /**\n * Gets the viewport width in CSS pixels.\n *\n * @returns Current viewport width\n */\n get viewPortWidth(): number{\n return this._viewPortWidth;\n }\n\n /**\n * Sets the viewport width in CSS pixels.\n * Updates invalidate the cached transformation matrix.\n *\n * @param width - New viewport width in CSS pixels\n */\n set viewPortWidth(width: number){\n this._viewPortWidth = width;\n }\n\n /**\n * Gets the viewport height in CSS pixels.\n *\n * @returns Current viewport height\n */\n get viewPortHeight(): number{\n return this._viewPortHeight;\n }\n\n /**\n * Sets the viewport height in CSS pixels.\n * Updates invalidate the cached transformation matrix.\n *\n * @param height - New viewport height in CSS pixels\n */\n set viewPortHeight(height: number){\n this._viewPortHeight = height;\n }\n\n /**\n * Gets the current camera position in world coordinates.\n *\n * @returns A copy of the current position (center of viewport in world space)\n */\n get position(): Point{\n return {...this._position};\n }\n\n /**\n * Sets the camera position with boundary validation and floating-point jitter prevention.\n *\n * @param destination - Target position in world coordinates\n * @returns True if position was updated, false if rejected by boundaries or negligible change\n *\n * @remarks\n * Position updates are rejected if:\n * - The destination is outside the configured boundaries\n * - The change magnitude is less than 10E-10\n * - The change magnitude is less than 1/zoomLevel (prevents sub-pixel jitter)\n *\n * @example\n * ```typescript\n * camera.setHorizontalBoundaries(-1000, 1000);\n * camera.setVerticalBoundaries(-1000, 1000);\n *\n * camera.setPosition({ x: 500, y: 500 }); // returns true\n * camera.setPosition({ x: 2000, y: 0 }); // returns false (out of bounds)\n * ```\n */\n setPosition(destination: Point){\n if(!withinBoundaries(destination, this._boundaries)){\n return false;\n }\n const diff = PointCal.subVector(destination, this._position);\n if(PointCal.magnitude(diff) < 10E-10 && PointCal.magnitude(diff) < 1 / this._zoomLevel){\n return false;\n }\n this._position = destination;\n return true;\n }\n\n /**\n * Gets the current zoom level.\n *\n * @returns Current zoom level (1.0 = 100%, 2.0 = 200%, etc.)\n */\n get zoomLevel(): number{\n return this._zoomLevel;\n }\n\n /**\n * Gets the current zoom level constraints.\n *\n * @returns Zoom boundaries object or undefined if unconstrained\n */\n get zoomBoundaries(): ZoomLevelLimits | undefined{\n return this._zoomBoundaries;\n }\n\n /**\n * Sets zoom level constraints with automatic min/max swapping if needed.\n *\n * @param zoomBoundaries - Zoom constraints or undefined to remove constraints\n *\n * @remarks\n * If min > max, the values are automatically swapped.\n */\n set zoomBoundaries(zoomBoundaries: ZoomLevelLimits | undefined){\n const newZoomBoundaries = {...zoomBoundaries};\n if(newZoomBoundaries !== undefined && newZoomBoundaries.min !== undefined && newZoomBoundaries.max !== undefined && newZoomBoundaries.min > newZoomBoundaries.max){\n let temp = newZoomBoundaries.max;\n newZoomBoundaries.max = newZoomBoundaries.min;\n newZoomBoundaries.min = temp;\n }\n this._zoomBoundaries = newZoomBoundaries;\n }\n\n /**\n * Sets the maximum allowed zoom level.\n *\n * @param maxZoomLevel - New maximum zoom level\n * @returns True if successfully set, false if conflicts with existing min or current zoom\n *\n * @remarks\n * Returns false if:\n * - The new max is less than the current minimum boundary\n * - The current zoom level exceeds the new maximum\n */\n setMaxZoomLevel(maxZoomLevel: number){\n if(this._zoomBoundaries == undefined){\n this._zoomBoundaries = {min: undefined, max: undefined};\n }\n if((this._zoomBoundaries.min != undefined && this._zoomBoundaries.min > maxZoomLevel) || this._zoomLevel > maxZoomLevel){\n return false;\n }\n this._zoomBoundaries.max = maxZoomLevel;\n console.trace('setMaxZoomLevel', maxZoomLevel);\n return true\n }\n\n /**\n * Sets the minimum allowed zoom level.\n *\n * @param minZoomLevel - New minimum zoom level\n * @returns True if successfully set, false if conflicts with existing max\n *\n * @remarks\n * If the current zoom level is below the new minimum, the camera automatically\n * zooms in to match the minimum. Returns false if new min exceeds existing max boundary.\n */\n setMinZoomLevel(minZoomLevel: number){\n if(this._zoomBoundaries == undefined){\n this._zoomBoundaries = {min: undefined, max: undefined};\n }\n if((this._zoomBoundaries.max != undefined && this._zoomBoundaries.max < minZoomLevel)){\n return false;\n }\n this._zoomBoundaries.min = minZoomLevel;\n if(this._zoomLevel < minZoomLevel){\n this._zoomLevel = minZoomLevel;\n }\n console.trace('setMinZoomLevel', minZoomLevel);\n return true;\n }\n\n /**\n * Sets the camera zoom level with boundary validation.\n *\n * @param zoomLevel - Target zoom level (1.0 = 100%, 2.0 = 200%, etc.)\n * @returns True if zoom was updated, false if outside boundaries or already at limit\n *\n * @remarks\n * Returns false if:\n * - Zoom level is outside configured boundaries\n * - Already at maximum and trying to zoom beyond it\n * - Already at minimum and trying to zoom below it\n *\n * @example\n * ```typescript\n * camera.setZoomLevel(2.0); // 200% zoom\n * camera.setZoomLevel(0.5); // 50% zoom\n * ```\n */\n setZoomLevel(zoomLevel: number){\n if(!zoomLevelWithinLimits(zoomLevel, this._zoomBoundaries)){\n return false;\n }\n if(this._zoomBoundaries !== undefined && this._zoomBoundaries.max !== undefined && clampZoomLevel(zoomLevel, this._zoomBoundaries) == this._zoomBoundaries.max && this._zoomLevel == this._zoomBoundaries.max){\n return false;\n }\n if(this._zoomBoundaries !== undefined && this._zoomBoundaries.min !== undefined && clampZoomLevel(zoomLevel, this._zoomBoundaries) == this._zoomBoundaries.min && this._zoomLevel == this._zoomBoundaries.min){\n return false;\n }\n this._zoomLevel = zoomLevel;\n return true;\n }\n\n /**\n * Gets the current camera rotation in radians.\n *\n * @returns Current rotation angle (0 to 2π)\n */\n get rotation(): number{\n return this._rotation;\n }\n\n /**\n * Gets the current rotation constraints.\n *\n * @returns Rotation boundaries or undefined if unconstrained\n */\n get rotationBoundaries(): RotationLimits | undefined{\n return this._rotationBoundaries;\n }\n\n /**\n * Sets rotation constraints with automatic start/end swapping if needed.\n *\n * @param rotationBoundaries - Rotation limits or undefined to remove constraints\n *\n * @remarks\n * If start > end, the values are automatically swapped.\n */\n set rotationBoundaries(rotationBoundaries: RotationLimits | undefined){\n if(rotationBoundaries !== undefined && rotationBoundaries.start !== undefined && rotationBoundaries.end !== undefined && rotationBoundaries.start > rotationBoundaries.end){\n let temp = rotationBoundaries.end;\n rotationBoundaries.end = rotationBoundaries.start;\n rotationBoundaries.start = temp;\n }\n this._rotationBoundaries = rotationBoundaries;\n }\n\n /**\n * Computes the complete transformation matrix from world space to canvas pixel space.\n * Includes caching for performance optimization.\n *\n * @param devicePixelRatio - Device pixel ratio (typically window.devicePixelRatio)\n * @param alignCoorindate - If true, uses standard y-up coordinate system. If false, inverts y-axis\n * @returns Transformation matrix object {a, b, c, d, e, f} with optional cached flag\n *\n * @remarks\n * Transformation order applied:\n * 1. Scale by devicePixelRatio\n * 2. Translate to viewport center\n * 3. Rotate (negated if alignCoorindate is true)\n * 4. Scale by zoom level\n * 5. Translate by camera position\n *\n * The result is cached based on all parameters. Subsequent calls with identical parameters\n * return the cached matrix with `cached: true` flag.\n *\n * @example\n * ```typescript\n * const ctx = canvas.getContext('2d');\n * const transform = camera.getTransform(window.devicePixelRatio, true);\n * ctx.setTransform(transform.a, transform.b, transform.c, transform.d, transform.e, transform.f);\n *\n * // Now drawing at world coordinates (100, 200) appears correctly on canvas\n * ctx.fillRect(100, 200, 50, 50);\n * ```\n *\n * @see {@link getTRS} for decomposed transformation components\n */\n getTransform(devicePixelRatio: number, alignCoorindate: boolean) {\n if(this.currentCachedTransform !== undefined\n && this.currentCachedTransform.devicePixelRatio === devicePixelRatio\n && this.currentCachedTransform.alignCoorindate === alignCoorindate\n && this.currentCachedTransform.position.x === this._position.x\n && this.currentCachedTransform.position.y === this._position.y\n && this.currentCachedTransform.rotation === this._rotation\n && this.currentCachedTransform.zoomLevel === this._zoomLevel\n && this.currentCachedTransform.viewPortWidth === this._viewPortWidth\n && this.currentCachedTransform.viewPortHeight === this._viewPortHeight\n ){\n return {...this.currentCachedTransform.transform, cached: true};\n }\n\n const tx = devicePixelRatio * this._viewPortWidth / 2; // 0 if the camera position represents the position at the top left corner of the canvas in the world\n const ty = devicePixelRatio * this._viewPortHeight / 2; // 0 if the camera position represents the position at the top left corner of the canvas in the world\n\n const tx2 = -this._position.x;\n const ty2 = alignCoorindate ? -this._position.y : this._position.y;\n\n const s = devicePixelRatio;\n const s2 = this._zoomLevel;\n const θ = alignCoorindate ? -this._rotation : this._rotation;\n\n const sin = Math.sin(θ);\n const cos = Math.cos(θ);\n\n const a = s2 * s * cos;\n const b = s2 * s * sin;\n const c = -s * s2 * sin;\n const d = s2 * s * cos;\n const e = s * s2 * cos * tx2 - s * s2 * sin * ty2 + tx;\n const f = s * s2 * sin * tx2 + s * s2 * cos * ty2 + ty;\n this.currentCachedTransform = {transform: {a, b, c, d, e, f}, position: this._position, rotation: this._rotation, zoomLevel: this._zoomLevel, alignCoorindate, devicePixelRatio, viewPortWidth: this._viewPortWidth, viewPortHeight: this._viewPortHeight};\n return {a, b, c, d, e, f, cached: false};\n }\n\n /**\n * Decomposes the transformation matrix into Translation, Rotation, and Scale components.\n *\n * @param devicePixelRatio - Device pixel ratio for high-DPI displays\n * @param alignCoorindate - If true, uses standard y-up coordinate system. If false, inverts y-axis\n * @returns Object containing separate scale, rotation, and translation values\n *\n * @remarks\n * This is useful when you need individual transformation components rather than\n * the combined matrix. Internally calls {@link getTransform} and decomposes the result.\n */\n getTRS(devicePixelRatio: number, alignCoorindate: boolean){\n const transform = this.getTransform(devicePixelRatio, alignCoorindate);\n const decompositionRes = decomposeTRS(transform);\n return decompositionRes;\n }\n\n /**\n * Sets camera state by decomposing a transformation matrix.\n * Inverse operation of {@link getTransform}.\n *\n * @param transformationMatrix - 2D transformation matrix to decompose\n *\n * @remarks\n * The matrix is decomposed assuming the same transformation order as {@link getTransform}:\n * Scale(devicePixelRatio) → Translation(viewport center) → Rotation → Zoom → Translation(position)\n *\n * Extracted position, zoom, and rotation values are still validated against boundaries.\n *\n * @example\n * ```typescript\n * // Apply a transformation matrix from an external source\n * const matrix = { a: 2, b: 0, c: 0, d: 2, e: 100, f: 100 };\n * camera.setUsingTransformationMatrix(matrix);\n * ```\n */\n setUsingTransformationMatrix(transformationMatrix: TransformationMatrix){\n const decomposed = decomposeCameraMatrix(transformationMatrix, this._viewPortWidth, this._viewPortHeight, this._zoomLevel);\n\n // TODO clamp the attributes?\n this.setPosition(decomposed.position);\n this.setRotation(decomposed.rotation);\n this.setZoomLevel(decomposed.zoom);\n }\n\n /**\n * Sets the camera rotation with boundary validation and normalization.\n *\n * @param rotation - Target rotation in radians\n * @returns True if rotation was updated, false if outside boundaries or already at limit\n *\n * @remarks\n * Rotation is automatically normalized to 0-2π range. Returns false if:\n * - Rotation is outside configured boundaries\n * - Already at maximum boundary and trying to rotate beyond it\n * - Already at minimum boundary and trying to rotate below it\n *\n * @example\n * ```typescript\n * camera.setRotation(Math.PI / 4); // 45 degrees\n * camera.setRotation(Math.PI); // 180 degrees\n * ```\n */\n setRotation(rotation: number){\n if(!rotationWithinLimits(rotation, this._rotationBoundaries)){\n return false;\n }\n rotation = normalizeAngleZero2TwoPI(rotation);\n if(this._rotationBoundaries !== undefined && this._rotationBoundaries.end !== undefined && clampRotation(rotation, this._rotationBoundaries) == this._rotationBoundaries.end && this._rotation == this._rotationBoundaries.end){\n return false;\n }\n if(this._rotationBoundaries !== undefined && this._rotationBoundaries.start !== undefined && clampRotation(rotation, this._rotationBoundaries) == this._rotationBoundaries.start && this._rotation == this._rotationBoundaries.start){\n return false;\n }\n this._rotation = rotation;\n return true;\n }\n\n /**\n * Gets the camera origin in window coordinates.\n *\n * @deprecated This method is deprecated and will be removed in a future version.\n * Currently just returns the input unchanged.\n *\n * @param centerInWindow - Center point in window coordinates\n * @returns The same point (camera origin equals window center)\n */\n getCameraOriginInWindow(centerInWindow: Point): Point{\n return centerInWindow;\n }\n\n /**\n * Converts a point from viewport coordinates to world coordinates.\n *\n * @param point - Point in viewport space (relative to viewport center, in CSS pixels)\n * @returns Corresponding point in world coordinates\n *\n * @remarks\n * This accounts for camera position, zoom, and rotation. Useful for converting\n * mouse/touch input to world space.\n *\n * @example\n * ```typescript\n * // Convert mouse click to world position\n * const rect = canvas.getBoundingClientRect();\n * const viewportPoint = {\n * x: event.clientX - rect.left - rect.width / 2,\n * y: event.clientY - rect.top - rect.height / 2\n * };\n * const worldPoint = camera.convertFromViewPort2WorldSpace(viewportPoint);\n * ```\n */\n convertFromViewPort2WorldSpace(point: Point): Point{\n return convert2WorldSpaceAnchorAtCenter(point, this._position, this._zoomLevel, this._rotation);\n }\n\n /**\n * Converts a point from world coordinates to viewport coordinates.\n *\n * @param point - Point in world coordinates\n * @returns Corresponding point in viewport space (relative to viewport center, in CSS pixels)\n *\n * @remarks\n * This accounts for camera position, zoom, and rotation. Useful for positioning\n * UI elements at world object locations.\n *\n * @example\n * ```typescript\n * // Position a DOM element at a world object's location\n * const viewportPos = camera.convertFromWorld2ViewPort(objectWorldPos);\n * element.style.left = `${viewportPos.x + canvas.width / 2}px`;\n * element.style.top = `${viewportPos.y + canvas.height / 2}px`;\n * ```\n */\n convertFromWorld2ViewPort(point: Point): Point{\n return convert2ViewPortSpaceAnchorAtCenter(point, this._position, this._zoomLevel, this._rotation);\n }\n\n /**\n * Converts a point from world coordinates to viewport coordinates.\n * Alternative implementation of {@link convertFromWorld2ViewPort}.\n *\n * @param point - Point in world coordinates\n * @returns Corresponding point in viewport space (relative to viewport center, in CSS pixels)\n *\n * @remarks\n * This method provides an alternative calculation approach. In most cases,\n * prefer using {@link convertFromWorld2ViewPort} for consistency.\n */\n invertFromWorldSpace2ViewPort(point: Point): Point{\n let cameraFrameCenter = {x: this.viewPortWidth / 2, y: this._viewPortHeight / 2};\n let delta2Point = PointCal.subVector(point, this._position);\n delta2Point = PointCal.rotatePoint(delta2Point, -this._rotation);\n delta2Point = PointCal.multiplyVectorByScalar(delta2Point, this._zoomLevel);\n return PointCal.addVector(cameraFrameCenter, delta2Point);\n }\n\n /**\n * Sets horizontal (x-axis) position boundaries for camera movement.\n *\n * @param min - Minimum x coordinate in world space\n * @param max - Maximum x coordinate in world space\n *\n * @remarks\n * If min > max, the values are automatically swapped. The current camera position\n * is not automatically clamped when boundaries are set.\n *\n * @example\n * ```typescript\n * camera.setHorizontalBoundaries(-1000, 1000);\n * // Camera can now only move between x: -1000 and x: 1000\n * ```\n */\n setHorizontalBoundaries(min: number, max: number){\n if (min > max){\n let temp = max;\n max = min;\n min = temp;\n }\n if(this._boundaries == undefined){\n this._boundaries = {min: {x: undefined, y: undefined}, max: {x: undefined, y: undefined}};\n }\n if(this._boundaries.min == undefined){\n this._boundaries.min = {x: undefined, y: undefined};\n }\n if(this._boundaries.max == undefined){\n this._boundaries.max = {x: undefined, y: undefined};\n }\n this._boundaries.min.x = min;\n this._boundaries.max.x = max;\n //NOTE leave for future optimization when setting the boundaries if the camera lies outside the boundaries clamp the position of the camera\n // if(!this.withinBoundaries(this.position)){\n // this.position = this.clampPoint(this.position);\n // }\n }\n\n /**\n * Sets vertical (y-axis) position boundaries for camera movement.\n *\n * @param min - Minimum y coordinate in world space\n * @param max - Maximum y coordinate in world space\n *\n * @remarks\n * If min > max, the values are automatically swapped. The current camera position\n * is not automatically clamped when boundaries are set.\n *\n * @example\n * ```typescript\n * camera.setVerticalBoundaries(-500, 500);\n * // Camera can now only move between y: -500 and y: 500\n * ```\n */\n setVerticalBoundaries(min: number, max: number){\n if (min > max){\n let temp = max;\n max = min;\n min = temp;\n }\n if(this._boundaries == undefined){\n this._boundaries = {min: {x: undefined, y: undefined}, max: {x: undefined, y: undefined}};\n }\n if(this._boundaries.min == undefined){\n this._boundaries.min = {x: undefined, y: undefined};\n }\n if(this._boundaries.max == undefined){\n this._boundaries.max = {x: undefined, y: undefined};\n }\n this._boundaries.min.y = min;\n this._boundaries.max.y = max;\n }\n\n /**\n * Calculates the four corners of the viewport in world space, accounting for rotation.\n *\n * @param alignCoordinate - If true, uses standard y-up coordinate system. If false, inverts y-axis (default: true)\n * @returns Object containing the four corner points organized as top/bottom and left/right\n *\n * @remarks\n * Returns the actual rotated viewport corners. This is more precise than {@link viewPortAABB}\n * which returns the axis-aligned bounding box. Use this when you need the exact viewport bounds.\n *\n * @example\n * ```typescript\n * const corners = camera.viewPortInWorldSpace();\n * console.log(corners.top.left); // Top-left corner in world coords\n * console.log(corners.top.right); // Top-right corner\n * console.log(corners.bottom.left); // Bottom-left corner\n * console.log(corners.bottom.right);// Bottom-right corner\n * ```\n */\n viewPortInWorldSpace(alignCoordinate: boolean = true): {top: {left: Point, right: Point}, bottom: {left: Point, right: Point}}{\n const topLeftCorner = convert2WorldSpaceAnchorAtCenter({x: -this._viewPortWidth / 2, y: alignCoordinate ? -this._viewPortHeight / 2 : this._viewPortHeight / 2}, this._position, this._zoomLevel, this._rotation);\n const topRightCorner = convert2WorldSpaceAnchorAtCenter({x: this._viewPortWidth / 2, y: alignCoordinate ? -this._viewPortHeight / 2 : this._viewPortHeight / 2}, this._position, this._zoomLevel, this._rotation);\n const bottomLeftCorner = convert2WorldSpaceAnchorAtCenter({x: -this._viewPortWidth / 2, y: alignCoordinate ? this._viewPortHeight / 2 : -this._viewPortHeight / 2}, this._position, this._zoomLevel, this._rotation);\n const bottomRightCorner = convert2WorldSpaceAnchorAtCenter({x: this._viewPortWidth / 2, y: alignCoordinate ? this._viewPortHeight / 2 : -this._viewPortHeight / 2}, this._position, this._zoomLevel, this._rotation);\n\n return {\n top: {left: topLeftCorner, right: topRightCorner},\n bottom: {left: bottomLeftCorner, right: bottomRightCorner},\n }\n }\n\n /**\n * Calculates the axis-aligned bounding box (AABB) of the viewport in world space.\n *\n * @param alignCoordinate - If true, uses standard y-up coordinate system. If false, inverts y-axis\n * @returns Object with min and max points defining the AABB\n *\n * @remarks\n * This returns the smallest axis-aligned rectangle that contains the entire viewport.\n * When the camera is rotated, this AABB will be larger than the actual viewport.\n * For exact viewport bounds, use {@link viewPortInWorldSpace}.\n *\n * Useful for:\n * - Frustum culling (checking if objects are visible)\n * - Broad-phase collision detection\n * - Determining which tiles/chunks to load\n *\n * @example\n * ```typescript\n * const aabb = camera.viewPortAABB();\n * const isVisible = (\n * object.x >= aabb.min.x && object.x <= aabb.max.x &&\n * object.y >= aabb.min.y && object.y <= aabb.max.y\n * );\n * ```\n */\n viewPortAABB(alignCoordinate?: boolean): {min: Point, max: Point}{\n const {top: {left: topLeft, right: topRight}, bottom: {left: bottomLeft, right: bottomRight}} = this.viewPortInWorldSpace(alignCoordinate);\n\n return {\n min: {x: Math.min(topLeft.x, bottomLeft.x, topRight.x, bottomRight.x), y: Math.min(topLeft.y, bottomLeft.y, topRight.y, bottomRight.y)},\n max: {x: Math.max(topLeft.x, bottomLeft.x, topRight.x, bottomRight.x), y: Math.max(topLeft.y, bottomLeft.y, topRight.y, bottomRight.y)},\n };\n }\n}\n",
16
- "import type { EventArgs } from \"@ue-too/being\";\nimport type { KmtInputEventMapping, KmtInputStateMachine } from \"../../input-interpretation/input-state-machine\";\nimport type { InputOrchestrator } from \"../input-orchestrator\";\n\n/**\n * Interface for KMT (Keyboard/Mouse/Trackpad) event parsers.\n *\n * @remarks\n * Event parsers bridge the gap between DOM events and the state machine.\n * They listen for raw DOM events, convert them to state machine events,\n * and coordinate with the orchestrator for output processing.\n *\n * @category Raw Input Parser\n */\nexport interface KMTEventParser {\n /** Whether the parser is currently disabled */\n disabled: boolean;\n /** Initializes event listeners */\n setUp(): void;\n /** Removes event listeners and cleans up */\n tearDown(): void;\n /** Attaches to a new canvas element */\n attach(canvas: HTMLCanvasElement): void;\n /** The state machine that processes parsed events */\n stateMachine: KmtInputStateMachine;\n /** The orchestrator that handles state machine outputs */\n orchestrator: InputOrchestrator;\n}\n\n/**\n * Minimal pointer event interface for framework interoperability.\n *\n * @remarks\n * This subset of the DOM PointerEvent interface allows the parser to work with\n * both vanilla JavaScript PointerEvents and framework-wrapped events (e.g., PixiJS).\n *\n * @category Raw Input Parser\n */\nexport type MinimumPointerEvent = {\n /** Mouse button number (0=left, 1=middle, 2=right) */\n button: number;\n /** Pointer type (\"mouse\", \"pen\", \"touch\") */\n pointerType: string;\n /** X coordinate in window space */\n clientX: number;\n /** Y coordinate in window space */\n clientY: number;\n /** Bitmask of currently pressed buttons */\n buttons: number;\n}\n\n/**\n * Minimal wheel event interface for framework interoperability.\n *\n * @remarks\n * This subset of the DOM WheelEvent interface allows the parser to work with\n * both vanilla JavaScript WheelEvents and framework-wrapped events.\n *\n * @category Raw Input Parser\n */\nexport type MinimumWheelEvent = {\n /** Prevents default scroll behavior */\n preventDefault: () => void;\n /** Horizontal scroll delta */\n deltaX: number;\n /** Vertical scroll delta */\n deltaY: number;\n /** Whether Ctrl key is pressed (for zoom) */\n ctrlKey: boolean;\n /** X coordinate in window space */\n clientX: number;\n /** Y coordinate in window space */\n clientY: number;\n}\n\n/**\n * Minimal keyboard event interface for framework interoperability.\n *\n * @remarks\n * This subset of the DOM KeyboardEvent interface allows the parser to work with\n * both vanilla JavaScript KeyboardEvents and framework-wrapped events.\n *\n * @category Raw Input Parser\n */\nexport type MinimumKeyboardEvent = {\n /** Prevents default keyboard behavior */\n preventDefault: () => void;\n /** The key that was pressed */\n key: string;\n};\n\n/**\n * Minimal event target interface for framework interoperability.\n *\n * @remarks\n * This interface allows the parser to attach event listeners to different\n * types of event targets (HTMLElement, Canvas, PixiJS Container, etc.).\n *\n * @category Raw Input Parser\n */\nexport type EventTargetWithPointerEvents = {\n addEventListener: (type: string, listener: (event: any) => void, options?: {passive: boolean}) => void;\n removeEventListener: (type: string, listener: (event: any) => void) => void;\n};\n\n\n/**\n * DOM event parser for Keyboard/Mouse/Trackpad input.\n *\n * @remarks\n * This parser converts raw DOM events into state machine events and coordinates with\n * the orchestrator to process outputs. It serves as the entry point for all KMT input\n * in the input interpretation pipeline.\n *\n * **Event Flow**:\n * ```\n * DOM Events → Parser → State Machine → Parser → Orchestrator → Camera/Observers\n * ```\n *\n * **Responsibilities**:\n * 1. Listen for DOM pointer, wheel, and keyboard events\n * 2. Convert DOM events to state machine event format\n * 3. Send events to the state machine\n * 4. Forward state machine outputs to the orchestrator\n *\n * **Handled DOM Events**:\n * - pointerdown/up/move (canvas-scoped)\n * - wheel (canvas-scoped)\n * - keydown/up (window-scoped for spacebar)\n *\n * **Keyboard Handling**:\n * Keyboard events are only processed when `document.body` is the target,\n * preventing interference with text inputs and other UI elements.\n *\n * The parser can be disabled to temporarily stop input processing (e.g., during\n * modal dialogs or animations).\n *\n * @category Raw Input Parser\n *\n * @example\n * ```typescript\n * const canvasElement = document.getElementById(\"canvas\");\n * const stateMachine = createKmtInputStateMachine(context);\n * const orchestrator = new InputOrchestrator(cameraMux, cameraRig, publisher);\n * const parser = new VanillaKMTEventParser(stateMachine, orchestrator, canvasElement);\n *\n * parser.setUp(); // Starts listening for events\n *\n * // Later, to disable input temporarily\n * parser.disabled = true;\n *\n * // Cleanup when done\n * parser.tearDown();\n * ```\n */\nexport class VanillaKMTEventParser implements KMTEventParser {\n\n private _disabled: boolean = false;\n private _stateMachine: KmtInputStateMachine;\n private _orchestrator: InputOrchestrator;\n private _keyfirstPressed: Map<string, boolean>;\n private _abortController: AbortController;\n private _canvas?: HTMLCanvasElement | SVGSVGElement;\n\n\n constructor(kmtInputStateMachine: KmtInputStateMachine, orchestrator: InputOrchestrator, canvas?: HTMLCanvasElement | SVGSVGElement){\n this._canvas = canvas;\n this.bindFunctions();\n this._abortController = new AbortController();\n this._stateMachine = kmtInputStateMachine;\n this._orchestrator = orchestrator;\n this._keyfirstPressed = new Map();\n }\n\n get disabled(): boolean {\n return this._disabled;\n }\n\n set disabled(value: boolean){\n this._disabled = value;\n }\n\n get stateMachine(): KmtInputStateMachine {\n return this._stateMachine;\n }\n\n get orchestrator(): InputOrchestrator {\n return this._orchestrator;\n }\n\n addEventListeners(signal: AbortSignal){\n if(this._canvas == undefined){\n return;\n }\n this._canvas.addEventListener('pointerdown', this.pointerDownHandler as EventListener, {signal});\n this._canvas.addEventListener('pointerup', this.pointerUpHandler as EventListener, {signal});\n this._canvas.addEventListener('pointermove', this.pointerMoveHandler as EventListener, {signal});\n this._canvas.addEventListener('wheel', this.scrollHandler as EventListener, {signal});\n window.addEventListener('keydown', this.keypressHandler, {signal});\n window.addEventListener('keyup', this.keyupHandler, {signal});\n }\n \n setUp(): void {\n if(this._abortController.signal.aborted){\n this._abortController = new AbortController();\n }\n this.addEventListeners(this._abortController.signal);\n }\n\n tearDown(): void {\n this._abortController.abort();\n this._abortController = new AbortController();\n this._canvas = undefined;\n }\n\n bindFunctions(): void {\n this.pointerDownHandler = this.pointerDownHandler.bind(this);\n this.pointerUpHandler = this.pointerUpHandler.bind(this);\n this.pointerMoveHandler = this.pointerMoveHandler.bind(this);\n this.scrollHandler = this.scrollHandler.bind(this);\n this.keypressHandler = this.keypressHandler.bind(this);\n this.keyupHandler = this.keyupHandler.bind(this);\n }\n\n private processEvent<K extends keyof KmtInputEventMapping>(\n ...args: EventArgs<KmtInputEventMapping, K>\n ): void {\n const result = this._stateMachine.happens(...args);\n if (result.handled && result.output) {\n this._orchestrator.processInputEventOutput(result.output);\n }\n }\n\n pointerDownHandler(e: PointerEvent){\n if(this._disabled){\n return;\n }\n if(e.button === 0 && e.pointerType === \"mouse\"){\n this.processEvent(\"leftPointerDown\", {x: e.clientX, y: e.clientY});\n return;\n }\n if(e.button === 1 && e.pointerType === \"mouse\"){\n this.processEvent(\"middlePointerDown\", {x: e.clientX, y: e.clientY});\n return;\n }\n }\n\n pointerUpHandler(e: PointerEvent){\n if(this._disabled){\n return;\n }\n if(e.button === 0 && e.pointerType === \"mouse\"){\n this.processEvent(\"leftPointerUp\", {x: e.clientX, y: e.clientY});\n return;\n }\n if(e.button === 1 && e.pointerType === \"mouse\"){\n this.processEvent(\"middlePointerUp\", {x: e.clientX, y: e.clientY});\n return;\n }\n }\n\n pointerMoveHandler(e: PointerEvent){\n if(this._disabled){\n return;\n }\n if((e.buttons === 1) && e.pointerType === \"mouse\"){\n this.processEvent(\"leftPointerMove\", {x: e.clientX, y: e.clientY});\n return;\n }\n if((e.buttons === 4) && e.pointerType === \"mouse\"){\n this.processEvent(\"middlePointerMove\", {x: e.clientX, y: e.clientY});\n return;\n }\n this.processEvent(\"pointerMove\", {x: e.clientX, y: e.clientY});\n }\n\n scrollHandler(e: WheelEvent){\n if(this._disabled) return;\n e.preventDefault();\n if(e.ctrlKey){\n this.processEvent(\"scrollWithCtrl\", {x: e.clientX, y: e.clientY, deltaX: e.deltaX, deltaY: e.deltaY});\n } else {\n this.processEvent(\"scroll\", {x: e.clientX, y: e.clientY, deltaX: e.deltaX, deltaY: e.deltaY});\n }\n }\n\n keypressHandler(e: KeyboardEvent){\n if(e.target !== document.body){\n return;\n }\n if(this._keyfirstPressed.has(e.key)){\n return;\n }\n this._keyfirstPressed.set(e.key, true);\n if(e.key === \" \"){\n this.processEvent(\"spacebarDown\");\n }\n }\n\n keyupHandler(e: KeyboardEvent){\n if(this._keyfirstPressed.has(e.key)){\n this._keyfirstPressed.delete(e.key);\n }\n if(e.key === \" \"){\n this.processEvent(\"spacebarUp\");\n }\n }\n\n attach(canvas: HTMLCanvasElement){\n this.tearDown();\n this._canvas = canvas;\n this.setUp();\n }\n}\n",
17
- "import { TouchEventMapping, TouchInputStateMachine } from \"../../input-interpretation/input-state-machine/touch-input-state-machine\";\nimport { TouchPoints } from \"../../input-interpretation/input-state-machine/touch-input-context\";\nimport type { InputOrchestrator } from \"../input-orchestrator\";\nimport { EventArgs } from \"@ue-too/being\";\n\n/**\n * Interface for touch event parsers.\n *\n * @remarks\n * Touch event parsers bridge DOM TouchEvents and the touch state machine.\n * They provide granular control over which gesture types are enabled.\n *\n * @category Raw Input Parser\n */\nexport interface TouchEventParser {\n /** Whether all touch input is disabled */\n disabled: boolean;\n /** Whether pan gestures are disabled */\n panDisabled: boolean;\n /** Whether zoom gestures are disabled */\n zoomDisabled: boolean;\n /** Whether rotation gestures are disabled (currently unused) */\n rotateDisabled: boolean;\n /** Initializes event listeners */\n setUp(): void;\n /** Removes event listeners and cleans up */\n tearDown(): void;\n /** Attaches to a new canvas element */\n attach(canvas: HTMLCanvasElement): void;\n /** The state machine that processes parsed events */\n stateMachine: TouchInputStateMachine;\n /** The orchestrator that handles state machine outputs */\n orchestrator: InputOrchestrator;\n}\n\n/**\n * DOM event parser for touch input.\n *\n * @remarks\n * This parser converts raw DOM TouchEvents into state machine events and coordinates\n * with the orchestrator to process outputs. It serves as the entry point for all touch\n * input in the input interpretation pipeline.\n *\n * **Event Flow**:\n * ```\n * DOM TouchEvents → Parser → State Machine → Parser → Orchestrator → Camera/Observers\n * ```\n *\n * **Responsibilities**:\n * 1. Listen for DOM touch events (touchstart/move/end/cancel)\n * 2. Extract touch point data (identifier, x, y)\n * 3. Convert to state machine event format\n * 4. Send events to the state machine\n * 5. Forward state machine outputs to the orchestrator\n *\n * **Touch Point Extraction**:\n * - touchstart/touchend: Uses `changedTouches` (only new/removed touches)\n * - touchmove: Uses `targetTouches` (all touches on the canvas)\n *\n * **Gesture Control**:\n * Individual gesture types (pan, zoom, rotate) can be disabled independently,\n * though currently the state machine outputs are filtered by the orchestrator\n * rather than the parser.\n *\n * The parser prevents default touch behavior to avoid browser scroll/zoom\n * interfering with canvas gestures.\n *\n * @category Raw Input Parser\n *\n * @example\n * ```typescript\n * const canvasElement = document.getElementById(\"canvas\");\n * const stateMachine = createTouchInputStateMachine(context);\n * const orchestrator = new InputOrchestrator(cameraMux, cameraRig, publisher);\n * const parser = new VanillaTouchEventParser(stateMachine, orchestrator, canvasElement);\n *\n * parser.setUp(); // Starts listening for touch events\n *\n * // Disable zoom gestures temporarily\n * parser.zoomDisabled = true;\n *\n * // Cleanup when done\n * parser.tearDown();\n * ```\n */\nexport class VanillaTouchEventParser implements TouchEventParser {\n\n private _canvas?: HTMLCanvasElement;\n private _disabled: boolean;\n private _panDisabled: boolean = false;\n private _zoomDisabled: boolean = false;\n private _rotateDisabled: boolean = false;\n\n private _stateMachine: TouchInputStateMachine;\n private _orchestrator: InputOrchestrator;\n\n private _abortController: AbortController;\n\n constructor(touchInputStateMachine: TouchInputStateMachine, orchestrator: InputOrchestrator, canvas?: HTMLCanvasElement){\n this._canvas = canvas;\n this._disabled = false;\n this._stateMachine = touchInputStateMachine;\n this._orchestrator = orchestrator;\n this._abortController = new AbortController();\n\n this.bindListeners();\n }\n\n get stateMachine(): TouchInputStateMachine {\n return this._stateMachine;\n }\n\n get orchestrator(): InputOrchestrator {\n return this._orchestrator;\n }\n\n get touchStateMachine(): TouchInputStateMachine {\n return this._stateMachine;\n }\n\n bindListeners(): void{\n this.touchstartHandler = this.touchstartHandler.bind(this);\n this.touchendHandler = this.touchendHandler.bind(this);\n this.touchcancelHandler = this.touchcancelHandler.bind(this);\n this.touchmoveHandler = this.touchmoveHandler.bind(this);\n }\n\n enableStrategy(): void {\n this._disabled = false;\n }\n\n disableStrategy(): void {\n this._disabled = true;\n }\n\n setUp(): void {\n if(this._canvas == undefined){\n return;\n }\n if(this._abortController.signal.aborted){\n this._abortController = new AbortController();\n }\n this._canvas.addEventListener('touchstart', this.touchstartHandler, {signal: this._abortController.signal});\n this._canvas.addEventListener('touchend', this.touchendHandler, {signal: this._abortController.signal});\n this._canvas.addEventListener('touchcancel', this.touchcancelHandler, {signal: this._abortController.signal});\n this._canvas.addEventListener('touchmove', this.touchmoveHandler, {signal: this._abortController.signal});\n }\n\n tearDown(): void {\n this._abortController.abort();\n this._abortController = new AbortController();\n this._canvas = undefined;\n }\n\n get disabled(): boolean {\n return this._disabled;\n }\n\n get panDisabled(): boolean {\n return this._panDisabled;\n }\n\n set panDisabled(panDisabled: boolean){\n this._panDisabled = panDisabled;\n }\n\n get zoomDisabled(): boolean {\n return this._zoomDisabled;\n }\n\n set zoomDisabled(zoomDisabled: boolean){\n this._zoomDisabled = zoomDisabled;\n }\n\n get rotateDisabled(): boolean {\n return this._rotateDisabled;\n }\n\n set rotateDisabled(rotateDisabled: boolean){\n this._rotateDisabled = rotateDisabled;\n }\n\n private processEvent<K extends keyof TouchEventMapping>(\n ...args: EventArgs<TouchEventMapping, K>\n ): void {\n const result = this._stateMachine.happens(...args);\n if (result.handled && result.output) {\n this._orchestrator.processInputEventOutput(result.output);\n }\n }\n\n touchstartHandler(e: TouchEvent){\n if(this._disabled) {\n return;\n }\n\n const pointsAdded: TouchPoints[] = [];\n for (let i = 0; i < e.changedTouches.length; i++) {\n pointsAdded.push({ident: e.changedTouches[i].identifier, x: e.changedTouches[i].clientX, y: e.changedTouches[i].clientY});\n }\n this.processEvent(\"touchstart\", {points: pointsAdded});\n e.preventDefault();\n }\n\n touchcancelHandler(e: TouchEvent){\n if(this._disabled) {\n return;\n }\n const pointsRemoved: TouchPoints[] = [];\n for (let i = 0; i < e.changedTouches.length; i++) {\n pointsRemoved.push({ident: e.changedTouches[i].identifier, x: e.changedTouches[i].clientX, y: e.changedTouches[i].clientY});\n }\n this.processEvent(\"touchend\", {points: pointsRemoved});\n }\n\n touchendHandler(e: TouchEvent){\n if(this._disabled) {\n return;\n }\n const pointsRemoved: TouchPoints[] = [];\n for (let i = 0; i < e.changedTouches.length; i++) {\n pointsRemoved.push({ident: e.changedTouches[i].identifier, x: e.changedTouches[i].clientX, y: e.changedTouches[i].clientY});\n }\n this.processEvent(\"touchend\", {points: pointsRemoved});\n }\n\n touchmoveHandler(e: TouchEvent){\n if(this._disabled) {\n return;\n }\n e.preventDefault();\n const pointsMoved: TouchPoints[] = [];\n for (let i = 0; i < e.targetTouches.length; i++) {\n pointsMoved.push({ident: e.targetTouches[i].identifier, x: e.targetTouches[i].clientX, y: e.targetTouches[i].clientY});\n }\n this.processEvent(\"touchmove\", {points: pointsMoved});\n }\n\n attach(canvas: HTMLCanvasElement){\n this.tearDown();\n this._canvas = canvas;\n this.setUp();\n }\n}\n",
15
+ "import { Point } from '@ue-too/math';\nimport { Boundaries, withinBoundaries } from './utils/position';\nimport { zoomLevelWithinLimits, ZoomLevelLimits, clampZoomLevel } from './utils/zoom';\nimport { RotationLimits, rotationWithinLimits, normalizeAngleZero2TwoPI, clampRotation } from './utils/rotation';\nimport { convert2WorldSpaceAnchorAtCenter, convert2ViewPortSpaceAnchorAtCenter } from './utils/coordinate-conversion';\nimport { PointCal } from '@ue-too/math';\nimport { BoardCamera } from './interface';\nimport { decomposeCameraMatrix, decomposeTRS, TransformationMatrix } from './utils/matrix';\n\n/**\n * Base camera implementation providing core functionality for an infinite canvas system.\n * This is the fundamental building block for camera management in the board package.\n *\n * @remarks\n * BaseCamera is non-observable and does not emit events when state changes.\n * For event-driven camera updates, use {@link DefaultBoardCamera} instead.\n *\n * The camera supports:\n * - Position, rotation, and zoom transformations\n * - Configurable boundaries for position, zoom, and rotation\n * - Coordinate conversion between viewport and world space\n * - Transformation matrix caching for performance\n * - High-DPI display support via devicePixelRatio\n *\n * @example\n * ```typescript\n * // Create a camera for a 1920x1080 viewport\n * const camera = new BaseCamera(1920, 1080, { x: 0, y: 0 }, 0, 1.0);\n *\n * // Set boundaries to constrain camera movement\n * camera.setHorizontalBoundaries(-5000, 5000);\n * camera.setVerticalBoundaries(-5000, 5000);\n *\n * // Update camera state\n * camera.setPosition({ x: 100, y: 200 });\n * camera.setZoomLevel(2.0);\n * camera.setRotation(Math.PI / 6);\n *\n * // Get transformation matrix for rendering\n * const transform = camera.getTransform(window.devicePixelRatio, true);\n * ctx.setTransform(transform.a, transform.b, transform.c, transform.d, transform.e, transform.f);\n * ```\n *\n * @category Camera\n * @see {@link DefaultBoardCamera} for observable camera with event support\n * @see {@link CameraRig} for high-level camera control with input handling\n */\nexport default class BaseCamera implements BoardCamera {\n\n private _position: Point;\n private _rotation: number;\n private _zoomLevel: number;\n\n private currentCachedTransform: {transform: {a: number, b: number, c: number, d: number, e: number, f: number}, position: Point, rotation: number, zoomLevel: number, alignCoorindate: boolean, devicePixelRatio: number, viewPortWidth: number, viewPortHeight: number} | undefined;\n\n private currentCachedTRS: {scale: {x: number, y: number}, rotation: number, translation: {x: number, y: number}, transformMatrix: TransformationMatrix} | undefined;\n\n private _viewPortWidth: number;\n private _viewPortHeight: number;\n\n private _boundaries?: Boundaries;\n private _zoomBoundaries?: ZoomLevelLimits;\n private _rotationBoundaries?: RotationLimits;\n\n /**\n * Creates a new BaseCamera instance with specified viewport size and optional constraints.\n *\n * @param viewPortWidth - Width of the viewport in CSS pixels (default: 1000)\n * @param viewPortHeight - Height of the viewport in CSS pixels (default: 1000)\n * @param position - Initial camera position in world coordinates (default: {x: 0, y: 0})\n * @param rotation - Initial rotation in radians (default: 0)\n * @param zoomLevel - Initial zoom level, where 1.0 = 100% (default: 1.0)\n * @param boundaries - Position constraints in world space (default: ±10000 on both axes)\n * @param zoomLevelBoundaries - Zoom constraints (default: 0.1 to 10)\n * @param rotationBoundaries - Optional rotation constraints (default: undefined, unrestricted)\n *\n * @example\n * ```typescript\n * // Basic camera with defaults\n * const camera = new BaseCamera();\n *\n * // Camera with custom viewport and position\n * const camera2 = new BaseCamera(\n * 1920, 1080,\n * { x: 500, y: 300 },\n * 0,\n * 1.5\n * );\n *\n * // Camera with all constraints\n * const camera3 = new BaseCamera(\n * 1920, 1080,\n * { x: 0, y: 0 },\n * 0,\n * 1.0,\n * { min: { x: -2000, y: -2000 }, max: { x: 2000, y: 2000 } },\n * { min: 0.5, max: 5 },\n * { start: 0, end: Math.PI / 2 }\n * );\n * ```\n */\n constructor(viewPortWidth: number = 1000, viewPortHeight: number = 1000, position: Point = {x: 0, y: 0}, rotation: number = 0, zoomLevel: number = 1, boundaries: Boundaries = {min: {x: -10000, y: -10000}, max: {x: 10000, y: 10000}}, zoomLevelBoundaries: ZoomLevelLimits = {min: 0.1, max: 10}, rotationBoundaries: RotationLimits | undefined = undefined){\n this._position = position;\n this._zoomLevel = zoomLevel;\n this._rotation = rotation;\n this._viewPortHeight = viewPortHeight;\n this._viewPortWidth = viewPortWidth;\n this._zoomBoundaries = zoomLevelBoundaries;\n this._rotationBoundaries = rotationBoundaries;\n this._boundaries = boundaries;\n }\n\n /**\n * Gets the current position boundaries that constrain camera movement in world coordinates.\n *\n * @returns The boundaries object or undefined if no boundaries are set\n */\n get boundaries(): Boundaries | undefined{\n return this._boundaries;\n }\n\n /**\n * Sets position boundaries to constrain camera movement in world coordinates.\n *\n * @param boundaries - Boundary constraints or undefined to remove all constraints\n */\n set boundaries(boundaries: Boundaries | undefined){\n this._boundaries = boundaries;\n }\n\n /**\n * Gets the viewport width in CSS pixels.\n *\n * @returns Current viewport width\n */\n get viewPortWidth(): number{\n return this._viewPortWidth;\n }\n\n /**\n * Sets the viewport width in CSS pixels.\n * Updates invalidate the cached transformation matrix.\n *\n * @param width - New viewport width in CSS pixels\n */\n set viewPortWidth(width: number){\n this._viewPortWidth = width;\n }\n\n /**\n * Gets the viewport height in CSS pixels.\n *\n * @returns Current viewport height\n */\n get viewPortHeight(): number{\n return this._viewPortHeight;\n }\n\n /**\n * Sets the viewport height in CSS pixels.\n * Updates invalidate the cached transformation matrix.\n *\n * @param height - New viewport height in CSS pixels\n */\n set viewPortHeight(height: number){\n this._viewPortHeight = height;\n }\n\n /**\n * Gets the current camera position in world coordinates.\n *\n * @returns A copy of the current position (center of viewport in world space)\n */\n get position(): Point{\n return {...this._position};\n }\n\n /**\n * Sets the camera position with boundary validation and floating-point jitter prevention.\n *\n * @param destination - Target position in world coordinates\n * @returns True if position was updated, false if rejected by boundaries or negligible change\n *\n * @remarks\n * Position updates are rejected if:\n * - The destination is outside the configured boundaries\n * - The change magnitude is less than 10E-10\n * - The change magnitude is less than 1/zoomLevel (prevents sub-pixel jitter)\n *\n * @example\n * ```typescript\n * camera.setHorizontalBoundaries(-1000, 1000);\n * camera.setVerticalBoundaries(-1000, 1000);\n *\n * camera.setPosition({ x: 500, y: 500 }); // returns true\n * camera.setPosition({ x: 2000, y: 0 }); // returns false (out of bounds)\n * ```\n */\n setPosition(destination: Point){\n if(!withinBoundaries(destination, this._boundaries)){\n return false;\n }\n const diff = PointCal.subVector(destination, this._position);\n if(PointCal.magnitude(diff) < 10E-10 && PointCal.magnitude(diff) < 1 / this._zoomLevel){\n return false;\n }\n this._position = destination;\n return true;\n }\n\n /**\n * Gets the current zoom level.\n *\n * @returns Current zoom level (1.0 = 100%, 2.0 = 200%, etc.)\n */\n get zoomLevel(): number{\n return this._zoomLevel;\n }\n\n /**\n * Gets the current zoom level constraints.\n *\n * @returns Zoom boundaries object or undefined if unconstrained\n */\n get zoomBoundaries(): ZoomLevelLimits | undefined{\n return this._zoomBoundaries;\n }\n\n /**\n * Sets zoom level constraints with automatic min/max swapping if needed.\n *\n * @param zoomBoundaries - Zoom constraints or undefined to remove constraints\n *\n * @remarks\n * If min > max, the values are automatically swapped.\n */\n set zoomBoundaries(zoomBoundaries: ZoomLevelLimits | undefined){\n const newZoomBoundaries = {...zoomBoundaries};\n if(newZoomBoundaries !== undefined && newZoomBoundaries.min !== undefined && newZoomBoundaries.max !== undefined && newZoomBoundaries.min > newZoomBoundaries.max){\n let temp = newZoomBoundaries.max;\n newZoomBoundaries.max = newZoomBoundaries.min;\n newZoomBoundaries.min = temp;\n }\n this._zoomBoundaries = newZoomBoundaries;\n }\n\n /**\n * Sets the maximum allowed zoom level.\n *\n * @param maxZoomLevel - New maximum zoom level\n * @returns True if successfully set, false if conflicts with existing min or current zoom\n *\n * @remarks\n * Returns false if:\n * - The new max is less than the current minimum boundary\n * - The current zoom level exceeds the new maximum\n */\n setMaxZoomLevel(maxZoomLevel: number){\n if(this._zoomBoundaries == undefined){\n this._zoomBoundaries = {min: undefined, max: undefined};\n }\n if((this._zoomBoundaries.min != undefined && this._zoomBoundaries.min > maxZoomLevel) || this._zoomLevel > maxZoomLevel){\n return false;\n }\n this._zoomBoundaries.max = maxZoomLevel;\n console.trace('setMaxZoomLevel', maxZoomLevel);\n return true\n }\n\n /**\n * Sets the minimum allowed zoom level.\n *\n * @param minZoomLevel - New minimum zoom level\n * @returns True if successfully set, false if conflicts with existing max\n *\n * @remarks\n * If the current zoom level is below the new minimum, the camera automatically\n * zooms in to match the minimum. Returns false if new min exceeds existing max boundary.\n */\n setMinZoomLevel(minZoomLevel: number){\n if(this._zoomBoundaries == undefined){\n this._zoomBoundaries = {min: undefined, max: undefined};\n }\n if((this._zoomBoundaries.max != undefined && this._zoomBoundaries.max < minZoomLevel)){\n return false;\n }\n this._zoomBoundaries.min = minZoomLevel;\n if(this._zoomLevel < minZoomLevel){\n this._zoomLevel = minZoomLevel;\n }\n console.trace('setMinZoomLevel', minZoomLevel);\n return true;\n }\n\n /**\n * Sets the camera zoom level with boundary validation.\n *\n * @param zoomLevel - Target zoom level (1.0 = 100%, 2.0 = 200%, etc.)\n * @returns True if zoom was updated, false if outside boundaries or already at limit\n *\n * @remarks\n * Returns false if:\n * - Zoom level is outside configured boundaries\n * - Already at maximum and trying to zoom beyond it\n * - Already at minimum and trying to zoom below it\n *\n * @example\n * ```typescript\n * camera.setZoomLevel(2.0); // 200% zoom\n * camera.setZoomLevel(0.5); // 50% zoom\n * ```\n */\n setZoomLevel(zoomLevel: number){\n if(!zoomLevelWithinLimits(zoomLevel, this._zoomBoundaries)){\n return false;\n }\n if(this._zoomBoundaries !== undefined && this._zoomBoundaries.max !== undefined && clampZoomLevel(zoomLevel, this._zoomBoundaries) == this._zoomBoundaries.max && this._zoomLevel == this._zoomBoundaries.max){\n return false;\n }\n if(this._zoomBoundaries !== undefined && this._zoomBoundaries.min !== undefined && clampZoomLevel(zoomLevel, this._zoomBoundaries) == this._zoomBoundaries.min && this._zoomLevel == this._zoomBoundaries.min){\n return false;\n }\n this._zoomLevel = zoomLevel;\n return true;\n }\n\n /**\n * Gets the current camera rotation in radians.\n *\n * @returns Current rotation angle (0 to 2π)\n */\n get rotation(): number{\n return this._rotation;\n }\n\n /**\n * Gets the current rotation constraints.\n *\n * @returns Rotation boundaries or undefined if unconstrained\n */\n get rotationBoundaries(): RotationLimits | undefined{\n return this._rotationBoundaries;\n }\n\n /**\n * Sets rotation constraints with automatic start/end swapping if needed.\n *\n * @param rotationBoundaries - Rotation limits or undefined to remove constraints\n *\n * @remarks\n * If start > end, the values are automatically swapped.\n */\n set rotationBoundaries(rotationBoundaries: RotationLimits | undefined){\n if(rotationBoundaries !== undefined && rotationBoundaries.start !== undefined && rotationBoundaries.end !== undefined && rotationBoundaries.start > rotationBoundaries.end){\n let temp = rotationBoundaries.end;\n rotationBoundaries.end = rotationBoundaries.start;\n rotationBoundaries.start = temp;\n }\n this._rotationBoundaries = rotationBoundaries;\n }\n\n /**\n * Computes the complete transformation matrix from world space to canvas pixel space.\n * Includes caching for performance optimization.\n *\n * @param devicePixelRatio - Device pixel ratio (typically window.devicePixelRatio)\n * @param alignCoorindate - If true, uses standard y-up coordinate system. If false, inverts y-axis\n * @returns Transformation matrix object {a, b, c, d, e, f} with optional cached flag\n *\n * @remarks\n * Transformation order applied:\n * 1. Scale by devicePixelRatio\n * 2. Translate to viewport center\n * 3. Rotate (negated if alignCoorindate is true)\n * 4. Scale by zoom level\n * 5. Translate by camera position\n *\n * The result is cached based on all parameters. Subsequent calls with identical parameters\n * return the cached matrix with `cached: true` flag.\n *\n * @example\n * ```typescript\n * const ctx = canvas.getContext('2d');\n * const transform = camera.getTransform(window.devicePixelRatio, true);\n * ctx.setTransform(transform.a, transform.b, transform.c, transform.d, transform.e, transform.f);\n *\n * // Now drawing at world coordinates (100, 200) appears correctly on canvas\n * ctx.fillRect(100, 200, 50, 50);\n * ```\n *\n * @see {@link getTRS} for decomposed transformation components\n */\n getTransform(devicePixelRatio: number = 1, alignCoorindate: boolean = true) {\n if(this.currentCachedTransform !== undefined\n && this.currentCachedTransform.devicePixelRatio === devicePixelRatio\n && this.currentCachedTransform.alignCoorindate === alignCoorindate\n && this.currentCachedTransform.position.x === this._position.x\n && this.currentCachedTransform.position.y === this._position.y\n && this.currentCachedTransform.rotation === this._rotation\n && this.currentCachedTransform.zoomLevel === this._zoomLevel\n && this.currentCachedTransform.viewPortWidth === this._viewPortWidth\n && this.currentCachedTransform.viewPortHeight === this._viewPortHeight\n ){\n return {...this.currentCachedTransform.transform, cached: true};\n }\n\n const tx = devicePixelRatio * this._viewPortWidth / 2; // 0 if the camera position represents the position at the top left corner of the canvas in the world\n const ty = devicePixelRatio * this._viewPortHeight / 2; // 0 if the camera position represents the position at the top left corner of the canvas in the world\n\n const tx2 = -this._position.x;\n const ty2 = alignCoorindate ? -this._position.y : this._position.y;\n\n const s = devicePixelRatio;\n const s2 = this._zoomLevel;\n const θ = alignCoorindate ? -this._rotation : this._rotation;\n\n const sin = Math.sin(θ);\n const cos = Math.cos(θ);\n\n const a = s2 * s * cos;\n const b = s2 * s * sin;\n const c = -s * s2 * sin;\n const d = s2 * s * cos;\n const e = s * s2 * cos * tx2 - s * s2 * sin * ty2 + tx;\n const f = s * s2 * sin * tx2 + s * s2 * cos * ty2 + ty;\n this.currentCachedTransform = {transform: {a, b, c, d, e, f}, position: this._position, rotation: this._rotation, zoomLevel: this._zoomLevel, alignCoorindate, devicePixelRatio, viewPortWidth: this._viewPortWidth, viewPortHeight: this._viewPortHeight};\n return {a, b, c, d, e, f, cached: false};\n }\n\n /**\n * Decomposes the transformation matrix into Translation, Rotation, and Scale components.\n *\n * @param devicePixelRatio - Device pixel ratio for high-DPI displays\n * @param alignCoorindate - If true, uses standard y-up coordinate system. If false, inverts y-axis\n * @returns Object containing separate scale, rotation, and translation values\n *\n * @remarks\n * This is useful when you need individual transformation components rather than\n * the combined matrix. Internally calls {@link getTransform} and decomposes the result.\n */\n getTRS(devicePixelRatio: number = 1, alignCoorindate: boolean = true){\n const transform = this.getTransform(devicePixelRatio, alignCoorindate);\n if(this.currentCachedTRS !== undefined && this.currentCachedTRS.transformMatrix.a === transform.a && this.currentCachedTRS.transformMatrix.b === transform.b && this.currentCachedTRS.transformMatrix.c === transform.c && this.currentCachedTRS.transformMatrix.d === transform.d && this.currentCachedTRS.transformMatrix.e === transform.e && this.currentCachedTRS.transformMatrix.f === transform.f){\n return {\n scale: this.currentCachedTRS.scale,\n rotation: this.currentCachedTRS.rotation,\n translation: this.currentCachedTRS.translation,\n cached: true\n }\n }\n const decompositionRes = decomposeTRS(transform);\n this.currentCachedTRS = {\n scale: decompositionRes.scale,\n rotation: decompositionRes.rotation,\n translation: decompositionRes.translation,\n transformMatrix: transform\n };\n return {\n scale: decompositionRes.scale,\n rotation: decompositionRes.rotation,\n translation: decompositionRes.translation,\n cached: false\n };\n }\n\n /**\n * Sets camera state by decomposing a transformation matrix.\n * Inverse operation of {@link getTransform}.\n *\n * @param transformationMatrix - 2D transformation matrix to decompose\n *\n * @remarks\n * The matrix is decomposed assuming the same transformation order as {@link getTransform}:\n * Scale(devicePixelRatio) → Translation(viewport center) → Rotation → Zoom → Translation(position)\n *\n * Extracted position, zoom, and rotation values are still validated against boundaries.\n *\n * @example\n * ```typescript\n * // Apply a transformation matrix from an external source\n * const matrix = { a: 2, b: 0, c: 0, d: 2, e: 100, f: 100 };\n * camera.setUsingTransformationMatrix(matrix);\n * ```\n */\n setUsingTransformationMatrix(transformationMatrix: TransformationMatrix, devicePixelRatio: number = 1){\n const decomposed = decomposeCameraMatrix(transformationMatrix, devicePixelRatio, this._viewPortWidth, this._viewPortHeight);\n\n // TODO clamp the attributes?\n this.setPosition(decomposed.position);\n this.setRotation(decomposed.rotation);\n this.setZoomLevel(decomposed.zoom);\n }\n\n /**\n * Sets the camera rotation with boundary validation and normalization.\n *\n * @param rotation - Target rotation in radians\n * @returns True if rotation was updated, false if outside boundaries or already at limit\n *\n * @remarks\n * Rotation is automatically normalized to 0-2π range. Returns false if:\n * - Rotation is outside configured boundaries\n * - Already at maximum boundary and trying to rotate beyond it\n * - Already at minimum boundary and trying to rotate below it\n *\n * @example\n * ```typescript\n * camera.setRotation(Math.PI / 4); // 45 degrees\n * camera.setRotation(Math.PI); // 180 degrees\n * ```\n */\n setRotation(rotation: number){\n if(!rotationWithinLimits(rotation, this._rotationBoundaries)){\n return false;\n }\n rotation = normalizeAngleZero2TwoPI(rotation);\n if(this._rotationBoundaries !== undefined && this._rotationBoundaries.end !== undefined && clampRotation(rotation, this._rotationBoundaries) == this._rotationBoundaries.end && this._rotation == this._rotationBoundaries.end){\n return false;\n }\n if(this._rotationBoundaries !== undefined && this._rotationBoundaries.start !== undefined && clampRotation(rotation, this._rotationBoundaries) == this._rotationBoundaries.start && this._rotation == this._rotationBoundaries.start){\n return false;\n }\n this._rotation = rotation;\n return true;\n }\n\n /**\n * Gets the camera origin in window coordinates.\n *\n * @deprecated This method is deprecated and will be removed in a future version.\n * Currently just returns the input unchanged.\n *\n * @param centerInWindow - Center point in window coordinates\n * @returns The same point (camera origin equals window center)\n */\n getCameraOriginInWindow(centerInWindow: Point): Point{\n return centerInWindow;\n }\n\n /**\n * Converts a point from viewport coordinates to world coordinates.\n *\n * @param point - Point in viewport space (relative to viewport center, in CSS pixels)\n * @returns Corresponding point in world coordinates\n *\n * @remarks\n * This accounts for camera position, zoom, and rotation. Useful for converting\n * mouse/touch input to world space.\n *\n * @example\n * ```typescript\n * // Convert mouse click to world position\n * const rect = canvas.getBoundingClientRect();\n * const viewportPoint = {\n * x: event.clientX - rect.left - rect.width / 2,\n * y: event.clientY - rect.top - rect.height / 2\n * };\n * const worldPoint = camera.convertFromViewPort2WorldSpace(viewportPoint);\n * ```\n */\n convertFromViewPort2WorldSpace(point: Point): Point{\n return convert2WorldSpaceAnchorAtCenter(point, this._position, this._zoomLevel, this._rotation);\n }\n\n /**\n * Converts a point from world coordinates to viewport coordinates.\n *\n * @param point - Point in world coordinates\n * @returns Corresponding point in viewport space (relative to viewport center, in CSS pixels)\n *\n * @remarks\n * This accounts for camera position, zoom, and rotation. Useful for positioning\n * UI elements at world object locations.\n *\n * @example\n * ```typescript\n * // Position a DOM element at a world object's location\n * const viewportPos = camera.convertFromWorld2ViewPort(objectWorldPos);\n * element.style.left = `${viewportPos.x + canvas.width / 2}px`;\n * element.style.top = `${viewportPos.y + canvas.height / 2}px`;\n * ```\n */\n convertFromWorld2ViewPort(point: Point): Point{\n return convert2ViewPortSpaceAnchorAtCenter(point, this._position, this._zoomLevel, this._rotation);\n }\n\n /**\n * Converts a point from world coordinates to viewport coordinates.\n * Alternative implementation of {@link convertFromWorld2ViewPort}.\n *\n * @param point - Point in world coordinates\n * @returns Corresponding point in viewport space (relative to viewport center, in CSS pixels)\n *\n * @remarks\n * This method provides an alternative calculation approach. In most cases,\n * prefer using {@link convertFromWorld2ViewPort} for consistency.\n */\n invertFromWorldSpace2ViewPort(point: Point): Point{\n let cameraFrameCenter = {x: this.viewPortWidth / 2, y: this._viewPortHeight / 2};\n let delta2Point = PointCal.subVector(point, this._position);\n delta2Point = PointCal.rotatePoint(delta2Point, -this._rotation);\n delta2Point = PointCal.multiplyVectorByScalar(delta2Point, this._zoomLevel);\n return PointCal.addVector(cameraFrameCenter, delta2Point);\n }\n\n /**\n * Sets horizontal (x-axis) position boundaries for camera movement.\n *\n * @param min - Minimum x coordinate in world space\n * @param max - Maximum x coordinate in world space\n *\n * @remarks\n * If min > max, the values are automatically swapped. The current camera position\n * is not automatically clamped when boundaries are set.\n *\n * @example\n * ```typescript\n * camera.setHorizontalBoundaries(-1000, 1000);\n * // Camera can now only move between x: -1000 and x: 1000\n * ```\n */\n setHorizontalBoundaries(min: number, max: number){\n if (min > max){\n let temp = max;\n max = min;\n min = temp;\n }\n if(this._boundaries == undefined){\n this._boundaries = {min: {x: undefined, y: undefined}, max: {x: undefined, y: undefined}};\n }\n if(this._boundaries.min == undefined){\n this._boundaries.min = {x: undefined, y: undefined};\n }\n if(this._boundaries.max == undefined){\n this._boundaries.max = {x: undefined, y: undefined};\n }\n this._boundaries.min.x = min;\n this._boundaries.max.x = max;\n //NOTE leave for future optimization when setting the boundaries if the camera lies outside the boundaries clamp the position of the camera\n // if(!this.withinBoundaries(this.position)){\n // this.position = this.clampPoint(this.position);\n // }\n }\n\n /**\n * Sets vertical (y-axis) position boundaries for camera movement.\n *\n * @param min - Minimum y coordinate in world space\n * @param max - Maximum y coordinate in world space\n *\n * @remarks\n * If min > max, the values are automatically swapped. The current camera position\n * is not automatically clamped when boundaries are set.\n *\n * @example\n * ```typescript\n * camera.setVerticalBoundaries(-500, 500);\n * // Camera can now only move between y: -500 and y: 500\n * ```\n */\n setVerticalBoundaries(min: number, max: number){\n if (min > max){\n let temp = max;\n max = min;\n min = temp;\n }\n if(this._boundaries == undefined){\n this._boundaries = {min: {x: undefined, y: undefined}, max: {x: undefined, y: undefined}};\n }\n if(this._boundaries.min == undefined){\n this._boundaries.min = {x: undefined, y: undefined};\n }\n if(this._boundaries.max == undefined){\n this._boundaries.max = {x: undefined, y: undefined};\n }\n this._boundaries.min.y = min;\n this._boundaries.max.y = max;\n }\n\n /**\n * Calculates the four corners of the viewport in world space, accounting for rotation.\n *\n * @param alignCoordinate - If true, uses standard y-up coordinate system. If false, inverts y-axis (default: true)\n * @returns Object containing the four corner points organized as top/bottom and left/right\n *\n * @remarks\n * Returns the actual rotated viewport corners. This is more precise than {@link viewPortAABB}\n * which returns the axis-aligned bounding box. Use this when you need the exact viewport bounds.\n *\n * @example\n * ```typescript\n * const corners = camera.viewPortInWorldSpace();\n * console.log(corners.top.left); // Top-left corner in world coords\n * console.log(corners.top.right); // Top-right corner\n * console.log(corners.bottom.left); // Bottom-left corner\n * console.log(corners.bottom.right);// Bottom-right corner\n * ```\n */\n viewPortInWorldSpace(alignCoordinate: boolean = true): {top: {left: Point, right: Point}, bottom: {left: Point, right: Point}}{\n const topLeftCorner = convert2WorldSpaceAnchorAtCenter({x: -this._viewPortWidth / 2, y: alignCoordinate ? -this._viewPortHeight / 2 : this._viewPortHeight / 2}, this._position, this._zoomLevel, this._rotation);\n const topRightCorner = convert2WorldSpaceAnchorAtCenter({x: this._viewPortWidth / 2, y: alignCoordinate ? -this._viewPortHeight / 2 : this._viewPortHeight / 2}, this._position, this._zoomLevel, this._rotation);\n const bottomLeftCorner = convert2WorldSpaceAnchorAtCenter({x: -this._viewPortWidth / 2, y: alignCoordinate ? this._viewPortHeight / 2 : -this._viewPortHeight / 2}, this._position, this._zoomLevel, this._rotation);\n const bottomRightCorner = convert2WorldSpaceAnchorAtCenter({x: this._viewPortWidth / 2, y: alignCoordinate ? this._viewPortHeight / 2 : -this._viewPortHeight / 2}, this._position, this._zoomLevel, this._rotation);\n\n return {\n top: {left: topLeftCorner, right: topRightCorner},\n bottom: {left: bottomLeftCorner, right: bottomRightCorner},\n }\n }\n\n /**\n * Calculates the axis-aligned bounding box (AABB) of the viewport in world space.\n *\n * @param alignCoordinate - If true, uses standard y-up coordinate system. If false, inverts y-axis\n * @returns Object with min and max points defining the AABB\n *\n * @remarks\n * This returns the smallest axis-aligned rectangle that contains the entire viewport.\n * When the camera is rotated, this AABB will be larger than the actual viewport.\n * For exact viewport bounds, use {@link viewPortInWorldSpace}.\n *\n * Useful for:\n * - Frustum culling (checking if objects are visible)\n * - Broad-phase collision detection\n * - Determining which tiles/chunks to load\n *\n * @example\n * ```typescript\n * const aabb = camera.viewPortAABB();\n * const isVisible = (\n * object.x >= aabb.min.x && object.x <= aabb.max.x &&\n * object.y >= aabb.min.y && object.y <= aabb.max.y\n * );\n * ```\n */\n viewPortAABB(alignCoordinate?: boolean): {min: Point, max: Point}{\n const {top: {left: topLeft, right: topRight}, bottom: {left: bottomLeft, right: bottomRight}} = this.viewPortInWorldSpace(alignCoordinate);\n\n return {\n min: {x: Math.min(topLeft.x, bottomLeft.x, topRight.x, bottomRight.x), y: Math.min(topLeft.y, bottomLeft.y, topRight.y, bottomRight.y)},\n max: {x: Math.max(topLeft.x, bottomLeft.x, topRight.x, bottomRight.x), y: Math.max(topLeft.y, bottomLeft.y, topRight.y, bottomRight.y)},\n };\n }\n}\n",
16
+ "import type { EventArgs } from \"@ue-too/being\";\nimport type { KmtInputEventMapping, KmtInputStateMachine } from \"../../input-interpretation/input-state-machine\";\nimport type { InputOrchestrator } from \"../input-orchestrator\";\n\n/**\n * Interface for KMT (Keyboard/Mouse/Trackpad) event parsers.\n *\n * @remarks\n * Event parsers bridge the gap between DOM events and the state machine.\n * They listen for raw DOM events, convert them to state machine events,\n * and coordinate with the orchestrator for output processing.\n *\n * @category Raw Input Parser\n */\nexport interface KMTEventParser {\n /** Whether the parser is currently disabled */\n disabled: boolean;\n /** Initializes event listeners */\n setUp(): void;\n /** Removes event listeners and cleans up */\n tearDown(): void;\n /** Attaches to a new canvas element */\n attach(canvas: HTMLCanvasElement): void;\n /** Disables the parser; the event listeners are still attached just not processing any events*/\n disable(): void;\n /** Enables the parser */\n enable(): void;\n}\n\n/**\n * Minimal pointer event interface for framework interoperability.\n *\n * @remarks\n * This subset of the DOM PointerEvent interface allows the parser to work with\n * both vanilla JavaScript PointerEvents and framework-wrapped events (e.g., PixiJS).\n *\n * @category Raw Input Parser\n */\nexport type MinimumPointerEvent = {\n /** Mouse button number (0=left, 1=middle, 2=right) */\n button: number;\n /** Pointer type (\"mouse\", \"pen\", \"touch\") */\n pointerType: string;\n /** X coordinate in window space */\n clientX: number;\n /** Y coordinate in window space */\n clientY: number;\n /** Bitmask of currently pressed buttons */\n buttons: number;\n}\n\n/**\n * Minimal wheel event interface for framework interoperability.\n *\n * @remarks\n * This subset of the DOM WheelEvent interface allows the parser to work with\n * both vanilla JavaScript WheelEvents and framework-wrapped events.\n *\n * @category Raw Input Parser\n */\nexport type MinimumWheelEvent = {\n /** Prevents default scroll behavior */\n preventDefault: () => void;\n /** Horizontal scroll delta */\n deltaX: number;\n /** Vertical scroll delta */\n deltaY: number;\n /** Whether Ctrl key is pressed (for zoom) */\n ctrlKey: boolean;\n /** X coordinate in window space */\n clientX: number;\n /** Y coordinate in window space */\n clientY: number;\n}\n\n/**\n * Minimal keyboard event interface for framework interoperability.\n *\n * @remarks\n * This subset of the DOM KeyboardEvent interface allows the parser to work with\n * both vanilla JavaScript KeyboardEvents and framework-wrapped events.\n *\n * @category Raw Input Parser\n */\nexport type MinimumKeyboardEvent = {\n /** Prevents default keyboard behavior */\n preventDefault: () => void;\n /** The key that was pressed */\n key: string;\n};\n\n/**\n * Minimal event target interface for framework interoperability.\n *\n * @remarks\n * This interface allows the parser to attach event listeners to different\n * types of event targets (HTMLElement, Canvas, PixiJS Container, etc.).\n *\n * @category Raw Input Parser\n */\nexport type EventTargetWithPointerEvents = {\n addEventListener: (type: string, listener: (event: any) => void, options?: {passive: boolean}) => void;\n removeEventListener: (type: string, listener: (event: any) => void) => void;\n};\n\n\n/**\n * DOM event parser for Keyboard/Mouse/Trackpad input.\n *\n * @remarks\n * This parser converts raw DOM events into state machine events and coordinates with\n * the orchestrator to process outputs. It serves as the entry point for all KMT input\n * in the input interpretation pipeline.\n *\n * **Event Flow**:\n * ```\n * DOM Events → Parser → State Machine → Parser → Orchestrator → Camera/Observers\n * ```\n *\n * **Responsibilities**:\n * 1. Listen for DOM pointer, wheel, and keyboard events\n * 2. Convert DOM events to state machine event format\n * 3. Send events to the state machine\n * 4. Forward state machine outputs to the orchestrator\n *\n * **Handled DOM Events**:\n * - pointerdown/up/move (canvas-scoped)\n * - wheel (canvas-scoped)\n * - keydown/up (window-scoped for spacebar)\n *\n * **Keyboard Handling**:\n * Keyboard events are only processed when `document.body` is the target,\n * preventing interference with text inputs and other UI elements.\n *\n * The parser can be disabled to temporarily stop input processing (e.g., during\n * modal dialogs or animations).\n *\n * @category Raw Input Parser\n *\n * @example\n * ```typescript\n * const canvasElement = document.getElementById(\"canvas\");\n * const stateMachine = createKmtInputStateMachine(context);\n * const orchestrator = new InputOrchestrator(cameraMux, cameraRig, publisher);\n * const parser = new VanillaKMTEventParser(stateMachine, orchestrator, canvasElement);\n *\n * parser.setUp(); // Starts listening for events\n *\n * // Later, to disable input temporarily\n * parser.disabled = true;\n *\n * // Cleanup when done\n * parser.tearDown();\n * ```\n */\nexport class VanillaKMTEventParser implements KMTEventParser {\n\n private _disabled: boolean = false;\n private _stateMachine: KmtInputStateMachine;\n private _orchestrator: InputOrchestrator;\n private _keyfirstPressed: Map<string, boolean>;\n private _abortController: AbortController;\n private _canvas?: HTMLCanvasElement | SVGSVGElement;\n\n\n constructor(kmtInputStateMachine: KmtInputStateMachine, orchestrator: InputOrchestrator, canvas?: HTMLCanvasElement | SVGSVGElement){\n this._canvas = canvas;\n this.bindFunctions();\n this._abortController = new AbortController();\n this._stateMachine = kmtInputStateMachine;\n this._orchestrator = orchestrator;\n this._keyfirstPressed = new Map();\n }\n\n get disabled(): boolean {\n return this._disabled;\n }\n\n disable(): void {\n this._disabled = true;\n }\n\n enable(): void {\n this._disabled = false;\n }\n\n addEventListeners(signal: AbortSignal){\n if(this._canvas == undefined){\n return;\n }\n this._canvas.addEventListener('pointerdown', this.pointerDownHandler as EventListener, {signal});\n this._canvas.addEventListener('pointerup', this.pointerUpHandler as EventListener, {signal});\n this._canvas.addEventListener('pointermove', this.pointerMoveHandler as EventListener, {signal});\n this._canvas.addEventListener('wheel', this.scrollHandler as EventListener, {signal});\n window.addEventListener('keydown', this.keypressHandler, {signal});\n window.addEventListener('keyup', this.keyupHandler, {signal});\n }\n \n setUp(): void {\n if(this._abortController.signal.aborted){\n this._abortController = new AbortController();\n }\n this.addEventListeners(this._abortController.signal);\n }\n\n tearDown(): void {\n this._abortController.abort();\n this._abortController = new AbortController();\n this._canvas = undefined;\n }\n\n bindFunctions(): void {\n this.pointerDownHandler = this.pointerDownHandler.bind(this);\n this.pointerUpHandler = this.pointerUpHandler.bind(this);\n this.pointerMoveHandler = this.pointerMoveHandler.bind(this);\n this.scrollHandler = this.scrollHandler.bind(this);\n this.keypressHandler = this.keypressHandler.bind(this);\n this.keyupHandler = this.keyupHandler.bind(this);\n }\n\n private processEvent<K extends keyof KmtInputEventMapping>(\n ...args: EventArgs<KmtInputEventMapping, K>\n ): void {\n const result = this._stateMachine.happens(...args);\n if (result.handled && \"output\" in result) {\n this._orchestrator.processInputEventOutput(result.output);\n }\n }\n\n pointerDownHandler(e: PointerEvent){\n if(this._disabled){\n return;\n }\n if(e.button === 0 && e.pointerType === \"mouse\"){\n this.processEvent(\"leftPointerDown\", {x: e.clientX, y: e.clientY});\n return;\n }\n if(e.button === 1 && e.pointerType === \"mouse\"){\n this.processEvent(\"middlePointerDown\", {x: e.clientX, y: e.clientY});\n return;\n }\n }\n\n pointerUpHandler(e: PointerEvent){\n if(this._disabled){\n return;\n }\n if(e.button === 0 && e.pointerType === \"mouse\"){\n this.processEvent(\"leftPointerUp\", {x: e.clientX, y: e.clientY});\n return;\n }\n if(e.button === 1 && e.pointerType === \"mouse\"){\n this.processEvent(\"middlePointerUp\", {x: e.clientX, y: e.clientY});\n return;\n }\n }\n\n pointerMoveHandler(e: PointerEvent){\n if(this._disabled){\n return;\n }\n if((e.buttons === 1) && e.pointerType === \"mouse\"){\n this.processEvent(\"leftPointerMove\", {x: e.clientX, y: e.clientY});\n return;\n }\n if((e.buttons === 4) && e.pointerType === \"mouse\"){\n this.processEvent(\"middlePointerMove\", {x: e.clientX, y: e.clientY});\n return;\n }\n this.processEvent(\"pointerMove\", {x: e.clientX, y: e.clientY});\n }\n\n scrollHandler(e: WheelEvent){\n if(this._disabled) return;\n e.preventDefault();\n if(e.ctrlKey){\n this.processEvent(\"scrollWithCtrl\", {x: e.clientX, y: e.clientY, deltaX: e.deltaX, deltaY: e.deltaY});\n } else {\n this.processEvent(\"scroll\", {x: e.clientX, y: e.clientY, deltaX: e.deltaX, deltaY: e.deltaY});\n }\n }\n\n keypressHandler(e: KeyboardEvent){\n if(e.target !== document.body){\n return;\n }\n if(this._keyfirstPressed.has(e.key)){\n return;\n }\n this._keyfirstPressed.set(e.key, true);\n if(e.key === \" \"){\n this.processEvent(\"spacebarDown\");\n }\n }\n\n keyupHandler(e: KeyboardEvent){\n if(this._keyfirstPressed.has(e.key)){\n this._keyfirstPressed.delete(e.key);\n }\n if(e.key === \" \"){\n this.processEvent(\"spacebarUp\");\n }\n }\n\n attach(canvas: HTMLCanvasElement){\n this.tearDown();\n this._canvas = canvas;\n this.setUp();\n }\n}\n",
17
+ "import { TouchEventMapping, TouchInputStateMachine } from \"../../input-interpretation/input-state-machine/touch-input-state-machine\";\nimport { TouchPoints } from \"../../input-interpretation/input-state-machine/touch-input-context\";\nimport type { InputOrchestrator } from \"../input-orchestrator\";\nimport { EventArgs } from \"@ue-too/being\";\n\n/**\n * Interface for touch event parsers.\n *\n * @remarks\n * Touch event parsers bridge DOM TouchEvents and the touch state machine.\n * They provide granular control over which gesture types are enabled.\n *\n * @category Raw Input Parser\n */\nexport interface TouchEventParser {\n /** Whether all touch input is disabled */\n disabled: boolean;\n /** Initializes event listeners */\n setUp(): void;\n /** Removes event listeners and cleans up */\n tearDown(): void;\n /** Attaches to a new canvas element */\n attach(canvas: HTMLCanvasElement): void;\n /** Disables the parser; the event listeners are still attached just not processing any events*/\n disable(): void;\n /** Enables the parser */\n enable(): void;\n}\n\n/**\n * DOM event parser for touch input.\n *\n * @remarks\n * This parser converts raw DOM TouchEvents into state machine events and coordinates\n * with the orchestrator to process outputs. It serves as the entry point for all touch\n * input in the input interpretation pipeline.\n *\n * **Event Flow**:\n * ```\n * DOM TouchEvents → Parser → State Machine → Parser → Orchestrator → Camera/Observers\n * ```\n *\n * **Responsibilities**:\n * 1. Listen for DOM touch events (touchstart/move/end/cancel)\n * 2. Extract touch point data (identifier, x, y)\n * 3. Convert to state machine event format\n * 4. Send events to the state machine\n * 5. Forward state machine outputs to the orchestrator\n *\n * **Touch Point Extraction**:\n * - touchstart/touchend: Uses `changedTouches` (only new/removed touches)\n * - touchmove: Uses `targetTouches` (all touches on the canvas)\n *\n * **Gesture Control**:\n * Individual gesture types (pan, zoom, rotate) can be disabled independently,\n * though currently the state machine outputs are filtered by the orchestrator\n * rather than the parser.\n *\n * The parser prevents default touch behavior to avoid browser scroll/zoom\n * interfering with canvas gestures.\n *\n * @category Raw Input Parser\n *\n * @example\n * ```typescript\n * const canvasElement = document.getElementById(\"canvas\");\n * const stateMachine = createTouchInputStateMachine(context);\n * const orchestrator = new InputOrchestrator(cameraMux, cameraRig, publisher);\n * const parser = new VanillaTouchEventParser(stateMachine, orchestrator, canvasElement);\n *\n * parser.setUp(); // Starts listening for touch events\n *\n * // Disable zoom gestures temporarily\n * parser.zoomDisabled = true;\n *\n * // Cleanup when done\n * parser.tearDown();\n * ```\n */\nexport class VanillaTouchEventParser implements TouchEventParser {\n\n private _canvas?: HTMLCanvasElement | SVGSVGElement;\n private _disabled: boolean;\n private _panDisabled: boolean = false;\n private _zoomDisabled: boolean = false;\n private _rotateDisabled: boolean = false;\n\n private _stateMachine: TouchInputStateMachine;\n private _orchestrator: InputOrchestrator;\n\n private _abortController: AbortController;\n\n constructor(touchInputStateMachine: TouchInputStateMachine, orchestrator: InputOrchestrator, canvas?: HTMLCanvasElement | SVGSVGElement){\n this._canvas = canvas;\n this._disabled = false;\n this._stateMachine = touchInputStateMachine;\n this._orchestrator = orchestrator;\n this._abortController = new AbortController();\n\n this.bindListeners();\n }\n\n get orchestrator(): InputOrchestrator {\n return this._orchestrator;\n }\n\n bindListeners(): void{\n this.touchstartHandler = this.touchstartHandler.bind(this);\n this.touchendHandler = this.touchendHandler.bind(this);\n this.touchcancelHandler = this.touchcancelHandler.bind(this);\n this.touchmoveHandler = this.touchmoveHandler.bind(this);\n }\n\n enableStrategy(): void {\n this._disabled = false;\n }\n\n disableStrategy(): void {\n this._disabled = true;\n }\n\n setUp(): void {\n if(this._canvas == undefined){\n return;\n }\n if(this._abortController.signal.aborted){\n this._abortController = new AbortController();\n }\n this._canvas.addEventListener('touchstart', this.touchstartHandler as EventListener, {signal: this._abortController.signal});\n this._canvas.addEventListener('touchend', this.touchendHandler as EventListener, {signal: this._abortController.signal});\n this._canvas.addEventListener('touchcancel', this.touchcancelHandler as EventListener, {signal: this._abortController.signal});\n this._canvas.addEventListener('touchmove', this.touchmoveHandler as EventListener, {signal: this._abortController.signal});\n }\n\n tearDown(): void {\n this._abortController.abort();\n this._abortController = new AbortController();\n this._canvas = undefined;\n }\n\n get disabled(): boolean {\n return this._disabled;\n }\n\n disable(): void {\n this._disabled = true;\n }\n\n enable(): void {\n this._disabled = false;\n }\n\n private processEvent<K extends keyof TouchEventMapping>(\n ...args: EventArgs<TouchEventMapping, K>\n ): void {\n const result = this._stateMachine.happens(...args);\n if (result.handled && \"output\" in result) {\n this._orchestrator.processInputEventOutput(result.output);\n }\n }\n\n touchstartHandler(e: TouchEvent){\n if(this._disabled) {\n return;\n }\n\n const pointsAdded: TouchPoints[] = [];\n for (let i = 0; i < e.changedTouches.length; i++) {\n pointsAdded.push({ident: e.changedTouches[i].identifier, x: e.changedTouches[i].clientX, y: e.changedTouches[i].clientY});\n }\n this.processEvent(\"touchstart\", {points: pointsAdded});\n e.preventDefault();\n }\n\n touchcancelHandler(e: TouchEvent){\n if(this._disabled) {\n return;\n }\n const pointsRemoved: TouchPoints[] = [];\n for (let i = 0; i < e.changedTouches.length; i++) {\n pointsRemoved.push({ident: e.changedTouches[i].identifier, x: e.changedTouches[i].clientX, y: e.changedTouches[i].clientY});\n }\n this.processEvent(\"touchend\", {points: pointsRemoved});\n }\n\n touchendHandler(e: TouchEvent){\n if(this._disabled) {\n return;\n }\n const pointsRemoved: TouchPoints[] = [];\n for (let i = 0; i < e.changedTouches.length; i++) {\n pointsRemoved.push({ident: e.changedTouches[i].identifier, x: e.changedTouches[i].clientX, y: e.changedTouches[i].clientY});\n }\n this.processEvent(\"touchend\", {points: pointsRemoved});\n }\n\n touchmoveHandler(e: TouchEvent){\n if(this._disabled) {\n return;\n }\n e.preventDefault();\n const pointsMoved: TouchPoints[] = [];\n for (let i = 0; i < e.targetTouches.length; i++) {\n pointsMoved.push({ident: e.targetTouches[i].identifier, x: e.targetTouches[i].clientX, y: e.targetTouches[i].clientY});\n }\n this.processEvent(\"touchmove\", {points: pointsMoved});\n }\n\n attach(canvas: HTMLCanvasElement){\n this.tearDown();\n this._canvas = canvas;\n this.setUp();\n }\n}\n",
18
18
  "import { Point, PointCal } from \"@ue-too/math\";\nimport { Canvas } from \"../../input-interpretation/input-state-machine/kmt-input-context\";\n\n/**\n * Converts a point from browser window coordinates to canvas coordinates.\n *\n * @param pointInWindow - The point in window coordinates (relative to browser viewport)\n * @param canvas - The canvas object containing position information\n * @returns The point in canvas coordinates (relative to canvas element)\n *\n * @remarks\n * Window coordinates are relative to the browser's viewport (top-left = 0,0).\n * Canvas coordinates are relative to the canvas element's top-left corner.\n *\n * This conversion is essential for processing mouse and touch events, which\n * provide coordinates relative to the window, not the canvas element.\n *\n * The conversion simply subtracts the canvas position from the window position:\n * ```\n * canvasPoint = windowPoint - canvasPosition\n * ```\n *\n * Note: This function expects the canvas object to have a `position` property\n * containing the canvas element's position in window coordinates (typically\n * from getBoundingClientRect()).\n *\n * @example\n * ```typescript\n * // Mouse click at window position (500, 300)\n * const clickPos = { x: 500, y: 300 };\n *\n * // Canvas positioned at (100, 50) in window\n * const canvas = {\n * position: { x: 100, y: 50 },\n * width: 800,\n * height: 600\n * };\n *\n * const canvasPos = convertFromWindow2Canvas(clickPos, canvas);\n * // Result: { x: 400, y: 250 }\n * // (500 - 100 = 400, 300 - 50 = 250)\n * ```\n *\n * @category Coordinate Conversion\n * @see {@link convertFromCanvas2Window} for inverse conversion\n */\nexport function convertFromWindow2Canvas(pointInWindow: Point, canvas: Canvas): Point {\n return PointCal.subVector(pointInWindow, canvas.position);\n}\n\n/**\n * Converts a point from canvas coordinates to browser window coordinates.\n *\n * @param pointInCanvas - The point in canvas coordinates (relative to canvas element)\n * @param canvas - The canvas object containing position information\n * @returns The point in window coordinates (relative to browser viewport)\n *\n * @remarks\n * This is the inverse of {@link convertFromWindow2Canvas}. It translates canvas-relative\n * coordinates to window-relative coordinates.\n *\n * The conversion adds the canvas position to the canvas-relative point:\n * ```\n * windowPoint = canvasPoint + canvasPosition\n * ```\n *\n * This is useful for positioning DOM elements (like tooltips or menus) at specific\n * canvas coordinates, as DOM elements use window coordinates.\n *\n * @example\n * ```typescript\n * // Point on canvas at (400, 250)\n * const canvasPos = { x: 400, y: 250 };\n *\n * // Canvas positioned at (100, 50) in window\n * const canvas = {\n * position: { x: 100, y: 50 },\n * width: 800,\n * height: 600\n * };\n *\n * const windowPos = convertFromCanvas2Window(canvasPos, canvas);\n * // Result: { x: 500, y: 300 }\n * // (400 + 100 = 500, 250 + 50 = 300)\n *\n * // Use for positioning a tooltip\n * tooltip.style.left = `${windowPos.x}px`;\n * tooltip.style.top = `${windowPos.y}px`;\n * ```\n *\n * @category Coordinate Conversion\n * @see {@link convertFromWindow2Canvas} for inverse conversion\n */\nexport function convertFromCanvas2Window(pointInCanvas: Point, canvas: Canvas): Point {\n return PointCal.addVector(pointInCanvas, canvas.position);\n}\n\n",
19
19
  "import { Point } from \"@ue-too/math\";\nimport { Canvas } from \"../input-interpretation/input-state-machine/kmt-input-context\";\nimport { convertFromWindow2Canvas } from \"./coordinate-conversions/window-canvas\";\nimport { convertFromCanvas2ViewPort } from \"./coordinate-conversions/canvas-viewport\";\n\n/**\n * Converts an isometric 3D point to a flat 2D world point.\n *\n * @param point - The 3D point in isometric space (with optional z coordinate)\n * @returns The 2D point in flat world coordinates\n *\n * @remarks\n * This function performs an isometric projection transformation, converting 3D\n * coordinates to 2D using standard isometric angles (30 degrees).\n *\n * The transformation uses:\n * - cos(30°) ≈ 0.866 for x-axis projection\n * - cos(60°) = 0.5 for y-axis projection\n * - Z-coordinate is added directly to the y-axis (height)\n *\n * Mathematical formulas:\n * ```\n * x_2d = (x_3d * cos30) - (y_3d * cos30)\n * y_2d = (x_3d * cos60) + (y_3d * cos60) + z_3d\n * ```\n *\n * This creates the classic isometric diamond grid appearance where:\n * - Moving along +X goes down-right\n * - Moving along +Y goes down-left\n * - Moving along +Z goes straight up\n *\n * @example\n * ```typescript\n * // Convert a 3D cube corner to 2D isometric projection\n * const point3D = { x: 10, y: 10, z: 5 };\n * const point2D = pointConversion(point3D);\n * // Result: { x: 0, y: 15 }\n * // x: (10 * 0.866) - (10 * 0.866) = 0\n * // y: (10 * 0.5) + (10 * 0.5) + 5 = 15\n *\n * // 2D point without z-coordinate\n * const flatPoint = { x: 20, y: 0 };\n * const projected = pointConversion(flatPoint);\n * // Result: { x: 17.32, y: 10 }\n * ```\n *\n * @category Coordinate Conversion\n */\nexport function pointConversion(point: Point) {\n const cos30 = Math.cos(Math.PI / 6);\n const cos60 = Math.cos(Math.PI / 3);\n\n return {\n x: point.x * cos30 - point.y * cos30,\n y: point.x * cos60 + point.y * cos60 + (point.z ?? 0)\n }\n}\n\n/**\n * Converts a point from window coordinates to viewport coordinates in one step.\n *\n * @param point - The point in window coordinates (browser viewport)\n * @param canvas - The canvas object with position and dimensions\n * @param viewportOriginInCanvasSpace - Viewport origin in canvas space (default: canvas center)\n * @param viewportHasFlippedYAxis - Whether viewport uses mathematical y-axis (default: false)\n * @returns The point in viewport coordinates\n *\n * @remarks\n * This is a convenience function that combines two conversions:\n * 1. Window to Canvas: {@link convertFromWindow2Canvas}\n * 2. Canvas to Viewport: {@link convertFromCanvas2ViewPort}\n *\n * It's particularly useful for processing input events (mouse clicks, touches)\n * that need to be converted directly to viewport space for interaction handling.\n *\n * The default viewport origin is the canvas center, which is common for\n * mathematical/engineering applications where (0,0) should be in the middle.\n *\n * @example\n * ```typescript\n * // Mouse click event\n * const clickPos = { x: event.clientX, y: event.clientY };\n *\n * const canvas = {\n * position: { x: 100, y: 50 },\n * width: 800,\n * height: 600\n * };\n *\n * // Convert to centered viewport with y-up\n * const viewportPos = convertFromWindow2ViewPortWithCanvasOperator(\n * clickPos,\n * canvas,\n * { x: 400, y: 300 }, // center of canvas\n * true // mathematical coordinates\n * );\n *\n * // viewportPos is now relative to viewport center with y-up\n * ```\n *\n * @category Coordinate Conversion\n * @see {@link convertFromWindow2Canvas} for window to canvas conversion\n * @see {@link convertFromCanvas2ViewPort} for canvas to viewport conversion\n */\nexport function convertFromWindow2ViewPortWithCanvasOperator(point: Point, canvas: Canvas, viewportOriginInCanvasSpace: Point = {x: canvas.width / 2, y: canvas.height / 2}, viewportHasFlippedYAxis: boolean = false): Point {\n const pointInCanvas = convertFromWindow2Canvas(point, canvas);\n return convertFromCanvas2ViewPort(pointInCanvas, viewportOriginInCanvasSpace, viewportHasFlippedYAxis);\n}",
20
20
  "/**\n * Calculates the order of magnitude (power of 10) of a number.\n *\n * @param value - The number to analyze\n * @returns The power of 10 that best represents this value's magnitude\n *\n * @remarks\n * The order of magnitude helps determine appropriate tick spacing for rulers\n * and grids. For example:\n * - Value 1234 has order 3 (10^3 = 1000)\n * - Value 0.056 has order -2 (10^-2 = 0.01)\n *\n * The calculation finds the largest power of 10 that is less than or equal\n * to the absolute value.\n *\n * Edge cases:\n * - Returns 0 for values <= 0\n * - For values >= 1: Counts powers of 10 until reaching the value\n * - For values < 1: Counts negative powers of 10 until reaching the value\n *\n * This is used by drawing utilities to automatically scale tick marks and\n * grid lines appropriately for different zoom levels.\n *\n * @example\n * ```typescript\n * calculateOrderOfMagnitude(1500); // 3 (between 10^3=1000 and 10^4=10000)\n * calculateOrderOfMagnitude(85); // 1 (between 10^1=10 and 10^2=100)\n * calculateOrderOfMagnitude(7); // 0 (between 10^0=1 and 10^1=10)\n * calculateOrderOfMagnitude(0.05); // -2 (between 10^-2=0.01 and 10^-1=0.1)\n * calculateOrderOfMagnitude(0); // 0 (edge case)\n * calculateOrderOfMagnitude(-100); // 0 (negative edge case)\n * ```\n *\n * @category Drawing Utilities\n * @see {@link drawRuler} for usage in ruler drawing\n */\nexport function calculateOrderOfMagnitude(value: number): number{\n if (value <= 0) return 0;\n let count = 0;\n if (value < 1) {\n let divisor = 1;\n while (divisor > value){\n divisor /= 10;\n count--;\n }\n } else {\n let divisor = 1;\n while (divisor * 10 <= value){\n divisor *= 10;\n count++;\n }\n }\n return count;\n}\n",
21
21
  "/**\n * @description Type definition for a handler function that takes a generic value and additional arguments\n * The handler must return the same type as its first argument\n * This is a utility type to be used in the handler pipeline. (Probably don't need to use this directly)\n * Using the {@link createHandlerChain} function to create a handler chain would have typescript infer the correct type for the handler chain.\n * \n * @category Utils\n */\nexport type Handler<T, Args extends any[]> = (value: T, ...args: Args) => T;\n\n/**\n * @description Creates a handler chain from an array of handlers.\n * \n * Use it like this:\n * ```typescript\n * const handlerChain = createHandlerChain(handler1, handler2, handler3);\n * ```\n * or like this:\n * ```typescript\n * const handlers = [handler1, handler2, handler3];\n * const handlerChain = createHandlerChain(handlers);\n * ```\n * \n * The function signature of all the handlers must be the same. (if they're not, you need to explicitly specify the type for the handler chain)\n * \n * @param handlers Array of handler functions to be chained\n * @returns A single handler function that executes all handlers in sequence\n * \n * @category Utils\n */\nexport function createHandlerChain<T, Args extends any[]>(\n ...handlers: Handler<T, Args>[] | [Handler<T, Args>[]]\n): Handler<T, Args> {\n const normalizedHandlers = Array.isArray(handlers[0]) ? handlers[0] : handlers as Handler<T, Args>[];\n return (value: T, ...args: Args): T => {\n return normalizedHandlers.reduce(\n (acc, handler) => handler(acc, ...args),\n value\n );\n };\n}\n",
22
- "import { Observer, SubscriptionOptions, SynchronousObservable } from \"../utils/observable\";\n\n/**\n * Monitors and publishes position and dimension changes for SVG elements.\n *\n * @remarks\n * This class tracks SVG element position and dimensions using multiple browser APIs\n * to ensure comprehensive detection of all changes:\n * - ResizeObserver: Detects size changes\n * - IntersectionObserver: Detects visibility and position changes\n * - MutationObserver: Detects attribute changes (width, height, style)\n * - Window scroll/resize events: Detects changes from page layout\n *\n * The reported DOMRect excludes padding and borders to provide the actual\n * content dimensions using {@link getTrueRect}.\n *\n * Position and dimension changes are published synchronously to all subscribers,\n * ensuring immediate updates for coordinate transformations and rendering logic.\n *\n * @example\n * ```typescript\n * const svg = document.querySelector('svg');\n * const publisher = new SvgPositionDimensionPublisher(svg);\n *\n * // Subscribe to position/dimension updates\n * publisher.onPositionUpdate((rect) => {\n * console.log(`SVG at (${rect.x}, ${rect.y}) with size ${rect.width}x${rect.height}`);\n * });\n *\n * // Clean up when done\n * publisher.dispose();\n * ```\n *\n * @category Canvas Position\n */\nexport class SvgPositionDimensionPublisher {\n\n private lastRect?: DOMRect;\n private resizeObserver: ResizeObserver;\n private intersectionObserver: IntersectionObserver;\n private mutationObserver: MutationObserver;\n private scrollHandler?: (() => void);\n private resizeHandler?: (() => void);\n private _observers: SynchronousObservable<[DOMRect]>;\n\n /**\n * Creates a new SVG position/dimension publisher.\n *\n * @param canvas - Optional SVG element to immediately attach to\n *\n * @remarks\n * If a canvas is provided, observers are immediately attached and monitoring begins.\n * Otherwise, call {@link attach} later to begin monitoring.\n */\n constructor(canvas?: SVGSVGElement) {\n this._observers = new SynchronousObservable<[DOMRect]>();\n\n this.resizeObserver = new ResizeObserver(((entries: ResizeObserverEntry[]) => {\n for (const entry of entries) {\n const newRect = entry.target.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(entry.target));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }\n }).bind(this));\n\n this.intersectionObserver = new IntersectionObserver(((entries: IntersectionObserverEntry[]) => {\n if(this.lastRect === undefined){\n return;\n }\n for (const entry of entries) {\n if (entry.isIntersecting) {\n const newRect = entry.boundingClientRect;\n const trueRect = getTrueRect(newRect, window.getComputedStyle(entry.target));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }\n }\n }).bind(this));\n\n this.attributeCallBack = this.attributeCallBack.bind(this);\n this.mutationObserver = new MutationObserver(this.attributeCallBack);\n\n if(canvas){\n this.attach(canvas);\n }\n }\n \n /**\n * Cleans up all observers and event listeners.\n *\n * @remarks\n * Disconnects all observers (ResizeObserver, IntersectionObserver, MutationObserver)\n * and removes window event listeners (scroll, resize). Always call this method\n * when the publisher is no longer needed to prevent memory leaks.\n */\n public dispose(): void {\n this.resizeObserver.disconnect();\n this.intersectionObserver.disconnect();\n this.mutationObserver.disconnect();\n if(this.scrollHandler){\n window.removeEventListener('scroll', this.scrollHandler);\n }\n if(this.resizeHandler){\n window.removeEventListener('resize', this.resizeHandler);\n }\n }\n\n /**\n * Attaches observers to an SVG element and begins monitoring.\n *\n * @param canvas - The SVG element to monitor\n *\n * @remarks\n * Automatically calls {@link dispose} first to clean up any previous attachments.\n * Sets up all observers and records the initial position/dimensions.\n *\n * The initial rect is calculated immediately and stored, but no notification\n * is sent to observers for this initial state.\n */\n attach(canvas: SVGSVGElement) {\n this.dispose();\n this.resizeObserver.observe(canvas);\n this.intersectionObserver.observe(canvas);\n this.mutationObserver.observe(canvas, {\n attributes: true,\n attributeFilter: [\"width\", \"height\", \"style\"]\n });\n const boundingRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(boundingRect, window.getComputedStyle(canvas));\n this.lastRect = trueRect;\n\n this.scrollHandler = (() => {\n if(this.lastRect === undefined){\n return;\n }\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }).bind(this);\n this.resizeHandler = (() => {\n if(this.lastRect === undefined){\n return;\n }\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }).bind(this);\n window.addEventListener(\"scroll\", this.scrollHandler, { passive: true });\n window.addEventListener(\"resize\", this.resizeHandler, { passive: true });\n }\n\n private publishPositionUpdate(rect: DOMRect) {\n this._observers.notify(rect);\n }\n\n /**\n * Subscribes to position and dimension updates.\n *\n * @param observer - Callback function that receives the updated DOMRect\n * @param options - Optional subscription options (e.g., AbortSignal for cleanup)\n * @returns Unsubscribe function to remove this observer\n *\n * @remarks\n * The observer is called synchronously whenever the SVG's position or dimensions change.\n * The DOMRect parameter represents the actual content area (excluding padding and borders).\n *\n * @example\n * ```typescript\n * const unsubscribe = publisher.onPositionUpdate((rect) => {\n * console.log(`Position: ${rect.x}, ${rect.y}`);\n * console.log(`Size: ${rect.width}x${rect.height}`);\n * });\n *\n * // Later, when done:\n * unsubscribe();\n * ```\n */\n onPositionUpdate(observer: Observer<[DOMRect]>, options?: SubscriptionOptions) {\n return this._observers.subscribe(observer, options);\n }\n\n private attributeCallBack(mutationsList: MutationRecord[], observer: MutationObserver){\n for(let mutation of mutationsList){\n if(mutation.type === \"attributes\"){\n if(mutation.attributeName === \"width\"){\n const canvas = mutation.target as SVGSVGElement;\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n } else if(mutation.attributeName === \"height\"){\n const canvas = mutation.target as SVGSVGElement;\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n } else if (mutation.attributeName === \"style\"){\n const canvas = mutation.target as SVGSVGElement;\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }\n }\n }\n }\n}\n\n/**\n * Monitors and publishes position and dimension changes for HTML Canvas elements.\n *\n * @remarks\n * Similar to {@link SvgPositionDimensionPublisher} but specifically for HTMLCanvasElement.\n * Automatically handles device pixel ratio adjustments to maintain crisp rendering\n * at different screen densities.\n *\n * Key differences from SVG version:\n * - Automatically adjusts canvas.width/height attributes based on devicePixelRatio\n * - Synchronizes CSS dimensions (style.width/height) with canvas buffer size\n * - Ensures canvas maintains proper resolution on high-DPI displays\n *\n * The class uses multiple browser APIs for comprehensive change detection:\n * - ResizeObserver: Detects size changes\n * - IntersectionObserver: Detects visibility and position changes\n * - MutationObserver: Detects attribute changes and synchronizes dimensions\n * - Window scroll/resize events: Detects changes from page layout\n *\n * @example\n * ```typescript\n * const canvas = document.querySelector('canvas');\n * const publisher = new CanvasPositionDimensionPublisher(canvas);\n *\n * // Subscribe to updates\n * publisher.onPositionUpdate((rect) => {\n * // Canvas dimensions automatically adjusted for devicePixelRatio\n * console.log(`Canvas at (${rect.x}, ${rect.y})`);\n * console.log(`Display size: ${rect.width}x${rect.height}`);\n * });\n *\n * publisher.dispose();\n * ```\n *\n * @category Canvas Position\n * @see {@link SvgPositionDimensionPublisher} for SVG elements\n */\nexport class CanvasPositionDimensionPublisher {\n\n private lastRect?: DOMRect;\n private resizeObserver: ResizeObserver;\n private intersectionObserver: IntersectionObserver;\n private mutationObserver: MutationObserver;\n private scrollHandler?: (() => void);\n private resizeHandler?: (() => void);\n private _observers: SynchronousObservable<[DOMRect]>;\n\n /**\n * Creates a new Canvas position/dimension publisher.\n *\n * @param canvas - Optional canvas element to immediately attach to\n *\n * @remarks\n * If a canvas is provided, observers are immediately attached and monitoring begins.\n * The canvas dimensions are automatically adjusted for devicePixelRatio.\n */\n constructor(canvas?: HTMLCanvasElement) {\n this._observers = new SynchronousObservable<[DOMRect]>();\n\n this.resizeObserver = new ResizeObserver(((entries: ResizeObserverEntry[]) => {\n for (const entry of entries) {\n const newRect = entry.target.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(entry.target));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }\n }).bind(this));\n\n this.intersectionObserver = new IntersectionObserver(((entries: IntersectionObserverEntry[]) => {\n if(this.lastRect === undefined){\n return;\n }\n for (const entry of entries) {\n if (entry.isIntersecting) {\n const newRect = entry.boundingClientRect;\n const trueRect = getTrueRect(newRect, window.getComputedStyle(entry.target));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }\n }\n }).bind(this));\n\n this.attributeCallBack = this.attributeCallBack.bind(this);\n this.mutationObserver = new MutationObserver(this.attributeCallBack);\n\n if(canvas){\n this.attach(canvas);\n }\n }\n \n /**\n * Cleans up all observers and event listeners.\n *\n * @remarks\n * Disconnects all observers and removes window event listeners.\n * Always call this method when the publisher is no longer needed to prevent memory leaks.\n */\n public dispose(): void {\n this.resizeObserver.disconnect();\n this.intersectionObserver.disconnect();\n this.mutationObserver.disconnect();\n if(this.scrollHandler){\n window.removeEventListener('scroll', this.scrollHandler);\n }\n if(this.resizeHandler){\n window.removeEventListener('resize', this.resizeHandler);\n }\n }\n\n /**\n * Attaches observers to a canvas element and begins monitoring.\n *\n * @param canvas - The canvas element to monitor\n *\n * @remarks\n * Automatically calls {@link dispose} first to clean up any previous attachments.\n * Sets up all observers, adjusts canvas dimensions for devicePixelRatio,\n * and records the initial position/dimensions.\n */\n attach(canvas: HTMLCanvasElement) {\n this.dispose();\n this.resizeObserver.observe(canvas);\n this.intersectionObserver.observe(canvas);\n this.mutationObserver.observe(canvas, {\n attributes: true,\n attributeFilter: [\"width\", \"height\", \"style\"]\n });\n const boundingRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(boundingRect, window.getComputedStyle(canvas));\n this.lastRect = trueRect;\n\n this.scrollHandler = (() => {\n if(this.lastRect === undefined){\n return;\n }\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }).bind(this);\n this.resizeHandler = (() => {\n if(this.lastRect === undefined){\n return;\n }\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }).bind(this);\n window.addEventListener(\"scroll\", this.scrollHandler, { passive: true });\n window.addEventListener(\"resize\", this.resizeHandler, { passive: true });\n }\n\n private publishPositionUpdate(rect: DOMRect) {\n this._observers.notify(rect);\n }\n\n /**\n * Subscribes to position and dimension updates.\n *\n * @param observer - Callback function that receives the updated DOMRect\n * @param options - Optional subscription options (e.g., AbortSignal for cleanup)\n * @returns Unsubscribe function to remove this observer\n *\n * @remarks\n * The observer is called synchronously whenever the canvas position or dimensions change.\n * The DOMRect represents the actual content area (excluding padding and borders).\n * Canvas buffer dimensions are automatically adjusted for devicePixelRatio.\n */\n onPositionUpdate(observer: Observer<[DOMRect]>, options?: SubscriptionOptions) {\n return this._observers.subscribe(observer, options);\n }\n\n /**\n * Handles attribute mutations on the canvas element.\n *\n * @param mutationsList - List of mutations detected\n * @param observer - The MutationObserver instance\n *\n * @remarks\n * This callback synchronizes canvas buffer size with CSS dimensions:\n * - When width/height attributes change: Updates CSS dimensions based on devicePixelRatio\n * - When style changes: Updates buffer size to match CSS dimensions\n *\n * This ensures the canvas maintains proper resolution on all displays.\n */\n private attributeCallBack(mutationsList: MutationRecord[], observer: MutationObserver){\n for(let mutation of mutationsList){\n if(mutation.type === \"attributes\"){\n if(mutation.attributeName === \"width\"){\n const canvas = mutation.target as HTMLCanvasElement;\n canvas.style.width = canvas.width / window.devicePixelRatio + \"px\";\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n } else if(mutation.attributeName === \"height\"){\n const canvas = mutation.target as HTMLCanvasElement;\n canvas.style.height = canvas.height / window.devicePixelRatio + \"px\";\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n } else if (mutation.attributeName === \"style\"){\n const canvas = mutation.target as HTMLCanvasElement;\n const styleWidth = parseFloat(canvas.style.width);\n const styleHeight = parseFloat(canvas.style.height);\n const newWidth = styleWidth * window.devicePixelRatio;\n const newHeight = styleHeight * window.devicePixelRatio;\n if(newWidth != canvas.width){\n canvas.width = newWidth;\n }\n if(newHeight != canvas.height){\n canvas.height = newHeight;\n }\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }\n }\n }\n }\n}\n\n/**\n * Calculates the actual content rectangle excluding padding and borders.\n *\n * @param rect - The element's bounding client rectangle\n * @param computedStyle - The computed CSS styles for the element\n * @returns DOMRect representing the content area only\n *\n * @remarks\n * Browser's getBoundingClientRect() includes padding and borders, but for\n * coordinate transformations we need the actual drawable content area.\n *\n * This function subtracts padding and border from all four sides to get\n * the \"true\" content rectangle. This is essential for accurate coordinate\n * conversions between window and canvas space.\n *\n * @example\n * ```typescript\n * const canvas = document.querySelector('canvas');\n * const rect = canvas.getBoundingClientRect();\n * const style = window.getComputedStyle(canvas);\n * const contentRect = getTrueRect(rect, style);\n *\n * // contentRect.width is less than rect.width if padding/borders exist\n * console.log(`Full size: ${rect.width}x${rect.height}`);\n * console.log(`Content size: ${contentRect.width}x${contentRect.height}`);\n * ```\n *\n * @category Canvas Position\n */\nexport function getTrueRect(rect: DOMRect, computedStyle: CSSStyleDeclaration) {\n const paddingLeft = parseFloat(computedStyle.paddingLeft);\n const paddingTop = parseFloat(computedStyle.paddingTop);\n const paddingRight = parseFloat(computedStyle.paddingRight);\n const paddingBottom = parseFloat(computedStyle.paddingBottom);\n\n const borderLeft = parseFloat(computedStyle.borderLeftWidth);\n const borderTop = parseFloat(computedStyle.borderTopWidth);\n const borderRight = parseFloat(computedStyle.borderRightWidth);\n const borderBottom = parseFloat(computedStyle.borderBottomWidth);\n\n const trueLeft = rect.left + paddingLeft + borderLeft;\n const trueTop = rect.top + paddingTop + borderTop;\n const trueWidth = rect.width - paddingLeft - paddingRight - borderLeft - borderRight;\n const trueHeight = rect.height - paddingTop - paddingBottom - borderTop - borderBottom;\n return new DOMRect(trueLeft, trueTop, trueWidth, trueHeight);\n}\n\n/**\n * Checks if two rectangles differ in position or dimensions.\n *\n * @param r1 - First rectangle (or undefined for initial state)\n * @param r2 - Second rectangle to compare\n * @returns True if rectangles differ or r1 is undefined\n *\n * @remarks\n * Used internally to avoid redundant notifications when position/dimensions\n * haven't actually changed. Compares top, left, width, and height.\n *\n * Returns true if r1 is undefined (initial state always counts as \"changed\").\n */\nfunction rectChanged(r1: DOMRect | undefined, r2: DOMRect) {\n if(r1 === undefined){\n return true;\n }\n return r1.top !== r2.top || r1.left !== r2.left ||\n r1.width !== r2.width || r1.height !== r2.height;\n}\n\n/**\n * Maps canvas context methods to the indices of their y-coordinate parameters.\n *\n * @remarks\n * Used by {@link reverseYAxis} to identify which method parameters need y-flipping\n * when converting from standard canvas coordinates (top-left origin, y-down)\n * to mathematical coordinates (center origin, y-up).\n *\n * Array values indicate the parameter indices that contain y-coordinates.\n * For example, fillRect(x, y, width, height) has y at index 1 and height at index 3.\n *\n * @internal\n * @category Canvas Position\n */\nconst methodsToFlip: Record<string, number[]> = {\n fillRect: [1, 3], // [yIndex] - indices of y-coordinates to flip\n strokeRect: [1, 3],\n fillText: [2],\n strokeText: [1],\n lineTo: [1],\n moveTo: [1],\n quadraticCurveTo: [1, 3],\n bezierCurveTo: [1, 3, 5],\n arc: [1],\n drawImage: [2], // Base case for first two signatures\n rect: [1, 3],\n roundRect: [1, 3],\n};\n\n/**\n * Creates a proxy that automatically flips y-coordinates for canvas context methods.\n *\n * @param context - The canvas 2D rendering context to wrap\n * @returns Proxied context that handles y-axis reversal automatically\n *\n * @remarks\n * Standard HTML canvas uses a top-left origin with y-axis pointing down.\n * This proxy inverts the y-axis to create a mathematical coordinate system\n * with y-axis pointing up.\n *\n * The proxy intercepts drawing methods (fillRect, strokeRect, moveTo, lineTo, etc.)\n * and automatically negates y-coordinates and height values. This allows you to\n * work in mathematical coordinates while still rendering correctly.\n *\n * Special handling for complex methods:\n * - drawImage with 9 args: Properly inverts source and destination rectangles\n * - drawImage with 5 args: Adjusts for image height\n * - All methods in {@link methodsToFlip}: Y-coordinates negated automatically\n *\n * @example\n * ```typescript\n * const canvas = document.querySelector('canvas');\n * const ctx = canvas.getContext('2d');\n * const flippedCtx = reverseYAxis(ctx);\n *\n * // Draw with mathematical coordinates (y-up)\n * flippedCtx.fillRect(0, 0, 100, 100); // Square in first quadrant\n * flippedCtx.moveTo(0, 0);\n * flippedCtx.lineTo(50, 100); // Line going upward\n * ```\n *\n * @category Canvas Position\n * @see {@link methodsToFlip} for list of intercepted methods\n * @see {@link invertYAxisForDrawImageWith9Args} for drawImage special handling\n */\nexport function reverseYAxis(context: CanvasRenderingContext2D): CanvasRenderingContext2D {\n return new Proxy(context, {\n get(target: CanvasRenderingContext2D, prop: string | symbol, receiver: any): any {\n const value = Reflect.get(target, prop, target);\n \n // Check if this is a method that needs y-coordinate flipping\n if (typeof prop === 'string' && prop in methodsToFlip && typeof value === 'function') {\n return function(...args: any[]) {\n // Create a copy of the arguments\n const newArgs = [...args];\n \n // Special handling for drawImage with 9 arguments (third signature of drawImage)\n if (prop === 'drawImage' && args.length === 9) {\n const convertedArgs = invertYAxisForDrawImageWith9Args(args);\n return value.apply(target, convertedArgs);\n } else {\n // Flip the y-coordinates based on methodsToFlip configuration\n const yIndices = methodsToFlip[prop];\n for (const index of yIndices) {\n if (index < newArgs.length) {\n newArgs[index] = -newArgs[index];\n }\n }\n // Special handling for drawImage with 5 arguments (first signature of drawImage)\n if(prop === \"drawImage\" && args.length === 5){\n newArgs[2] -= newArgs[4];\n }\n }\n \n // Call the original method with the modified arguments\n return value.apply(target, newArgs);\n };\n }\n \n // Return the original value for properties and methods that don't need modification\n if (typeof value === 'function') {\n return function(...args: any[]) {\n return value.apply(target, args);\n };\n }\n \n return value;\n },\n set(target, prop, value): boolean {\n return Reflect.set(target, prop, value);\n }\n });\n}\n\n/**\n * Inverts y-coordinates for the 9-argument variant of drawImage.\n *\n * @param args - The arguments array for drawImage\n * @returns Modified arguments with inverted y-coordinates\n *\n * @remarks\n * The 9-argument drawImage signature is:\n * drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)\n *\n * When inverting y-axis, we need to adjust:\n * - sy (source y): Flip relative to image height\n * - sHeight: Negate (height becomes negative in flipped space)\n * - dy (destination y): Negate\n * - dy offset: Subtract destination height\n *\n * This ensures images render correctly when the canvas y-axis is flipped.\n *\n * @example\n * ```typescript\n * // Original call (top-left origin):\n * ctx.drawImage(img, 0, 0, 100, 100, 50, 50, 200, 200);\n *\n * // With flipped y-axis, this becomes:\n * // sy = imageHeight - 0, sHeight = -100, dy = -50 - 200, dHeight = -200\n * ```\n *\n * @category Canvas Position\n * @see {@link reverseYAxis} for the main y-axis flipping proxy\n */\nexport function invertYAxisForDrawImageWith9Args(args: any[]): typeof args {\n if(args.length !== 9){\n return args;\n }\n const newArgs = [...args];\n const imageHeight = args[0].height;\n if(imageHeight !== undefined){\n newArgs[2] = imageHeight - newArgs[2];\n newArgs[6] = -newArgs[6];\n newArgs[6] -= newArgs[8];\n newArgs[4] = -newArgs[4];\n }\n return newArgs;\n}\n",
22
+ "import { Observer, SubscriptionOptions, SynchronousObservable } from \"../utils/observable\";\n\n/**\n * Monitors and publishes position and dimension changes for SVG elements.\n *\n * @remarks\n * This class tracks SVG element position and dimensions using multiple browser APIs\n * to ensure comprehensive detection of all changes:\n * - ResizeObserver: Detects size changes\n * - IntersectionObserver: Detects visibility and position changes\n * - MutationObserver: Detects attribute changes (width, height, style)\n * - Window scroll/resize events: Detects changes from page layout\n *\n * The reported DOMRect excludes padding and borders to provide the actual\n * content dimensions using {@link getTrueRect}.\n *\n * Position and dimension changes are published synchronously to all subscribers,\n * ensuring immediate updates for coordinate transformations and rendering logic.\n *\n * @example\n * ```typescript\n * const svg = document.querySelector('svg');\n * const publisher = new SvgPositionDimensionPublisher(svg);\n *\n * // Subscribe to position/dimension updates\n * publisher.onPositionUpdate((rect) => {\n * console.log(`SVG at (${rect.x}, ${rect.y}) with size ${rect.width}x${rect.height}`);\n * });\n *\n * // Clean up when done\n * publisher.dispose();\n * ```\n *\n * @category Canvas Position\n */\nexport class SvgPositionDimensionPublisher {\n\n private lastRect?: DOMRect;\n private resizeObserver: ResizeObserver;\n private intersectionObserver: IntersectionObserver;\n private mutationObserver: MutationObserver;\n private scrollHandler?: (() => void);\n private resizeHandler?: (() => void);\n private _observers: SynchronousObservable<[DOMRect]>;\n\n /**\n * Creates a new SVG position/dimension publisher.\n *\n * @param canvas - Optional SVG element to immediately attach to\n *\n * @remarks\n * If a canvas is provided, observers are immediately attached and monitoring begins.\n * Otherwise, call {@link attach} later to begin monitoring.\n */\n constructor(canvas?: SVGSVGElement) {\n this._observers = new SynchronousObservable<[DOMRect]>();\n\n this.resizeObserver = new ResizeObserver(((entries: ResizeObserverEntry[]) => {\n for (const entry of entries) {\n const newRect = entry.target.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(entry.target));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }\n }).bind(this));\n\n this.intersectionObserver = new IntersectionObserver(((entries: IntersectionObserverEntry[]) => {\n if(this.lastRect === undefined){\n return;\n }\n for (const entry of entries) {\n if (entry.isIntersecting) {\n const newRect = entry.boundingClientRect;\n const trueRect = getTrueRect(newRect, window.getComputedStyle(entry.target));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }\n }\n }).bind(this));\n\n this.attributeCallBack = this.attributeCallBack.bind(this);\n this.mutationObserver = new MutationObserver(this.attributeCallBack);\n\n if(canvas){\n this.attach(canvas);\n }\n }\n \n /**\n * Cleans up all observers and event listeners.\n *\n * @remarks\n * Disconnects all observers (ResizeObserver, IntersectionObserver, MutationObserver)\n * and removes window event listeners (scroll, resize). Always call this method\n * when the publisher is no longer needed to prevent memory leaks.\n */\n public dispose(): void {\n this.resizeObserver.disconnect();\n this.intersectionObserver.disconnect();\n this.mutationObserver.disconnect();\n if(this.scrollHandler){\n window.removeEventListener('scroll', this.scrollHandler);\n }\n if(this.resizeHandler){\n window.removeEventListener('resize', this.resizeHandler);\n }\n }\n\n /**\n * Attaches observers to an SVG element and begins monitoring.\n *\n * @param canvas - The SVG element to monitor\n *\n * @remarks\n * Automatically calls {@link dispose} first to clean up any previous attachments.\n * Sets up all observers and records the initial position/dimensions.\n *\n * The initial rect is calculated immediately and stored, but no notification\n * is sent to observers for this initial state.\n */\n attach(canvas: SVGSVGElement) {\n this.dispose();\n this.resizeObserver.observe(canvas);\n this.intersectionObserver.observe(canvas);\n this.mutationObserver.observe(canvas, {\n attributes: true,\n attributeFilter: [\"width\", \"height\", \"style\"]\n });\n const boundingRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(boundingRect, window.getComputedStyle(canvas));\n this.lastRect = trueRect;\n\n this.scrollHandler = (() => {\n if(this.lastRect === undefined){\n return;\n }\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }).bind(this);\n this.resizeHandler = (() => {\n if(this.lastRect === undefined){\n return;\n }\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }).bind(this);\n window.addEventListener(\"scroll\", this.scrollHandler, { passive: true });\n window.addEventListener(\"resize\", this.resizeHandler, { passive: true });\n }\n\n private publishPositionUpdate(rect: DOMRect) {\n this._observers.notify(rect);\n }\n\n /**\n * Subscribes to position and dimension updates.\n *\n * @param observer - Callback function that receives the updated DOMRect\n * @param options - Optional subscription options (e.g., AbortSignal for cleanup)\n * @returns Unsubscribe function to remove this observer\n *\n * @remarks\n * The observer is called synchronously whenever the SVG's position or dimensions change.\n * The DOMRect parameter represents the actual content area (excluding padding and borders).\n *\n * @example\n * ```typescript\n * const unsubscribe = publisher.onPositionUpdate((rect) => {\n * console.log(`Position: ${rect.x}, ${rect.y}`);\n * console.log(`Size: ${rect.width}x${rect.height}`);\n * });\n *\n * // Later, when done:\n * unsubscribe();\n * ```\n */\n onPositionUpdate(observer: Observer<[DOMRect]>, options?: SubscriptionOptions) {\n return this._observers.subscribe(observer, options);\n }\n\n private attributeCallBack(mutationsList: MutationRecord[], observer: MutationObserver){\n for(let mutation of mutationsList){\n if(mutation.type === \"attributes\"){\n if(mutation.attributeName === \"width\"){\n const canvas = mutation.target as SVGSVGElement;\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n } else if(mutation.attributeName === \"height\"){\n const canvas = mutation.target as SVGSVGElement;\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n } else if (mutation.attributeName === \"style\"){\n const canvas = mutation.target as SVGSVGElement;\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }\n }\n }\n }\n}\n\n/**\n * Monitors and publishes position and dimension changes for HTML Canvas elements.\n *\n * @remarks\n * Similar to {@link SvgPositionDimensionPublisher} but specifically for HTMLCanvasElement.\n * Automatically handles device pixel ratio adjustments to maintain crisp rendering\n * at different screen densities.\n *\n * Key differences from SVG version:\n * - Automatically adjusts canvas.width/height attributes based on devicePixelRatio\n * - Synchronizes CSS dimensions (style.width/height) with canvas buffer size\n * - Ensures canvas maintains proper resolution on high-DPI displays\n *\n * The class uses multiple browser APIs for comprehensive change detection:\n * - ResizeObserver: Detects size changes\n * - IntersectionObserver: Detects visibility and position changes\n * - MutationObserver: Detects attribute changes and synchronizes dimensions\n * - Window scroll/resize events: Detects changes from page layout\n *\n * @example\n * ```typescript\n * const canvas = document.querySelector('canvas');\n * const publisher = new CanvasPositionDimensionPublisher(canvas);\n *\n * // Subscribe to updates\n * publisher.onPositionUpdate((rect) => {\n * // Canvas dimensions automatically adjusted for devicePixelRatio\n * console.log(`Canvas at (${rect.x}, ${rect.y})`);\n * console.log(`Display size: ${rect.width}x${rect.height}`);\n * });\n *\n * publisher.dispose();\n * ```\n *\n * @category Canvas Position\n * @see {@link SvgPositionDimensionPublisher} for SVG elements\n */\nexport class CanvasPositionDimensionPublisher {\n\n private lastRect?: DOMRect;\n private resizeObserver: ResizeObserver;\n private intersectionObserver: IntersectionObserver;\n private mutationObserver: MutationObserver;\n private scrollHandler?: (() => void);\n private resizeHandler?: (() => void);\n private _observers: SynchronousObservable<[DOMRect]>;\n\n private _abortController: AbortController;\n private _pixelRatioAbortController: AbortController;\n /**\n * Creates a new Canvas position/dimension publisher.\n *\n * @param canvas - Optional canvas element to immediately attach to\n *\n * @remarks\n * If a canvas is provided, observers are immediately attached and monitoring begins.\n * The canvas dimensions are automatically adjusted for devicePixelRatio.\n */\n constructor(canvas?: HTMLCanvasElement) {\n this._abortController = new AbortController();\n this._pixelRatioAbortController = new AbortController();\n this._observers = new SynchronousObservable<[DOMRect]>();\n\n this.resizeObserver = new ResizeObserver(((entries: ResizeObserverEntry[]) => {\n for (const entry of entries) {\n const newRect = entry.target.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(entry.target));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }\n }).bind(this));\n\n this.intersectionObserver = new IntersectionObserver(((entries: IntersectionObserverEntry[]) => {\n if(this.lastRect === undefined){\n return;\n }\n for (const entry of entries) {\n if (entry.isIntersecting) {\n const newRect = entry.boundingClientRect;\n const trueRect = getTrueRect(newRect, window.getComputedStyle(entry.target));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }\n }\n }).bind(this));\n\n this.attributeCallBack = this.attributeCallBack.bind(this);\n this.mutationObserver = new MutationObserver(this.attributeCallBack);\n\n if(canvas){\n this.attach(canvas);\n }\n }\n \n /**\n * Cleans up all observers and event listeners.\n *\n * @remarks\n * Disconnects all observers and removes window event listeners.\n * Always call this method when the publisher is no longer needed to prevent memory leaks.\n */\n public dispose(): void {\n this.resizeObserver.disconnect();\n this.intersectionObserver.disconnect();\n this.mutationObserver.disconnect();\n this._abortController.abort();\n this._pixelRatioAbortController.abort();\n }\n\n /**\n * Attaches observers to a canvas element and begins monitoring.\n *\n * @param canvas - The canvas element to monitor\n *\n * @remarks\n * Automatically calls {@link dispose} first to clean up any previous attachments.\n * Sets up all observers, adjusts canvas dimensions for devicePixelRatio,\n * and records the initial position/dimensions.\n */\n attach(canvas: HTMLCanvasElement) {\n this.dispose();\n this._abortController.abort();\n this._abortController = new AbortController();\n this._pixelRatioAbortController.abort();\n this._pixelRatioAbortController = new AbortController();\n this.resizeObserver.observe(canvas);\n this.intersectionObserver.observe(canvas);\n this.mutationObserver.observe(canvas, {\n attributes: true,\n attributeFilter: [\"width\", \"height\", \"style\"]\n });\n const boundingRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(boundingRect, window.getComputedStyle(canvas));\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n this.scrollHandler = (() => {\n if(this.lastRect === undefined){\n return;\n }\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }).bind(this);\n this.resizeHandler = (() => {\n if(this.lastRect === undefined){\n return;\n }\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }).bind(this);\n window.addEventListener(\"scroll\", this.scrollHandler, { passive: true, signal: this._abortController.signal });\n window.addEventListener(\"resize\", this.resizeHandler, { passive: true, signal: this._abortController.signal });\n \n const updatePixelRatio = (() => {\n this._pixelRatioAbortController.abort();\n this._pixelRatioAbortController = new AbortController();\n console.log('updatePixelRatio', window.devicePixelRatio);\n\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n this.publishPositionUpdate(trueRect);\n\n const mqString = `(resolution: ${window.devicePixelRatio}dppx)`;\n const media = matchMedia(mqString);\n media.addEventListener(\"change\", updatePixelRatio, { signal: this._pixelRatioAbortController.signal });\n }).bind(this);\n updatePixelRatio();\n }\n\n private publishPositionUpdate(rect: DOMRect) {\n this._observers.notify(rect);\n }\n\n /**\n * Subscribes to position and dimension updates.\n *\n * @param observer - Callback function that receives the updated DOMRect\n * @param options - Optional subscription options (e.g., AbortSignal for cleanup)\n * @returns Unsubscribe function to remove this observer\n *\n * @remarks\n * The observer is called synchronously whenever the canvas position or dimensions change.\n * The DOMRect represents the actual content area (excluding padding and borders).\n * Canvas buffer dimensions are automatically adjusted for devicePixelRatio.\n */\n onPositionUpdate(observer: Observer<[DOMRect]>, options?: SubscriptionOptions) {\n return this._observers.subscribe(observer, options);\n }\n\n /**\n * Handles attribute mutations on the canvas element.\n *\n * @param mutationsList - List of mutations detected\n * @param observer - The MutationObserver instance\n *\n * @remarks\n * This callback synchronizes canvas buffer size with CSS dimensions:\n * - When width/height attributes change: Updates CSS dimensions based on devicePixelRatio\n * - When style changes: Updates buffer size to match CSS dimensions\n *\n * This ensures the canvas maintains proper resolution on all displays.\n */\n private attributeCallBack(mutationsList: MutationRecord[], observer: MutationObserver){\n for(let mutation of mutationsList){\n if(mutation.type === \"attributes\"){\n if(mutation.attributeName === \"width\"){\n const canvas = mutation.target as HTMLCanvasElement;\n // canvas.style.width = canvas.width / window.devicePixelRatio + \"px\";\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n } else if(mutation.attributeName === \"height\"){\n const canvas = mutation.target as HTMLCanvasElement;\n // canvas.style.height = canvas.height / window.devicePixelRatio + \"px\";\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n } else if (mutation.attributeName === \"style\"){\n const canvas = mutation.target as HTMLCanvasElement;\n // const styleWidth = parseFloat(canvas.style.width);\n // const styleHeight = parseFloat(canvas.style.height);\n // const newWidth = styleWidth * window.devicePixelRatio;\n // const newHeight = styleHeight * window.devicePixelRatio;\n // if(newWidth != canvas.width){\n // canvas.width = newWidth;\n // }\n // if(newHeight != canvas.height){\n // canvas.height = newHeight;\n // }\n const newRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(newRect, window.getComputedStyle(canvas));\n if (rectChanged(this.lastRect, trueRect)) {\n this.publishPositionUpdate(trueRect);\n this.lastRect = trueRect;\n }\n }\n }\n }\n }\n}\n\n/**\n * Calculates the actual content rectangle excluding padding and borders.\n *\n * @param rect - The element's bounding client rectangle\n * @param computedStyle - The computed CSS styles for the element\n * @returns DOMRect representing the content area only\n *\n * @remarks\n * Browser's getBoundingClientRect() includes padding and borders, but for\n * coordinate transformations we need the actual drawable content area.\n *\n * This function subtracts padding and border from all four sides to get\n * the \"true\" content rectangle. This is essential for accurate coordinate\n * conversions between window and canvas space.\n *\n * @example\n * ```typescript\n * const canvas = document.querySelector('canvas');\n * const rect = canvas.getBoundingClientRect();\n * const style = window.getComputedStyle(canvas);\n * const contentRect = getTrueRect(rect, style);\n *\n * // contentRect.width is less than rect.width if padding/borders exist\n * console.log(`Full size: ${rect.width}x${rect.height}`);\n * console.log(`Content size: ${contentRect.width}x${contentRect.height}`);\n * ```\n *\n * @category Canvas Position\n */\nexport function getTrueRect(rect: DOMRect, computedStyle: CSSStyleDeclaration) {\n const paddingLeft = parseFloat(computedStyle.paddingLeft);\n const paddingTop = parseFloat(computedStyle.paddingTop);\n const paddingRight = parseFloat(computedStyle.paddingRight);\n const paddingBottom = parseFloat(computedStyle.paddingBottom);\n\n const borderLeft = parseFloat(computedStyle.borderLeftWidth);\n const borderTop = parseFloat(computedStyle.borderTopWidth);\n const borderRight = parseFloat(computedStyle.borderRightWidth);\n const borderBottom = parseFloat(computedStyle.borderBottomWidth);\n\n const trueLeft = rect.left + paddingLeft + borderLeft;\n const trueTop = rect.top + paddingTop + borderTop;\n const trueWidth = rect.width - paddingLeft - paddingRight - borderLeft - borderRight;\n const trueHeight = rect.height - paddingTop - paddingBottom - borderTop - borderBottom;\n return new DOMRect(trueLeft, trueTop, trueWidth, trueHeight);\n}\n\n/**\n * Checks if two rectangles differ in position or dimensions.\n *\n * @param r1 - First rectangle (or undefined for initial state)\n * @param r2 - Second rectangle to compare\n * @returns True if rectangles differ or r1 is undefined\n *\n * @remarks\n * Used internally to avoid redundant notifications when position/dimensions\n * haven't actually changed. Compares top, left, width, and height.\n *\n * Returns true if r1 is undefined (initial state always counts as \"changed\").\n */\nfunction rectChanged(r1: DOMRect | undefined, r2: DOMRect) {\n if(r1 === undefined){\n return true;\n }\n return r1.top !== r2.top || r1.left !== r2.left ||\n r1.width !== r2.width || r1.height !== r2.height;\n}\n\n/**\n * Maps canvas context methods to the indices of their y-coordinate parameters.\n *\n * @remarks\n * Used by {@link reverseYAxis} to identify which method parameters need y-flipping\n * when converting from standard canvas coordinates (top-left origin, y-down)\n * to mathematical coordinates (center origin, y-up).\n *\n * Array values indicate the parameter indices that contain y-coordinates.\n * For example, fillRect(x, y, width, height) has y at index 1 and height at index 3.\n *\n * @internal\n * @category Canvas Position\n */\nconst methodsToFlip: Record<string, number[]> = {\n fillRect: [1, 3], // [yIndex] - indices of y-coordinates to flip\n strokeRect: [1, 3],\n fillText: [2],\n strokeText: [1],\n lineTo: [1],\n moveTo: [1],\n quadraticCurveTo: [1, 3],\n bezierCurveTo: [1, 3, 5],\n arc: [1],\n drawImage: [2], // Base case for first two signatures\n rect: [1, 3],\n roundRect: [1, 3],\n};\n\n/**\n * Creates a proxy that automatically flips y-coordinates for canvas context methods.\n *\n * @param context - The canvas 2D rendering context to wrap\n * @returns Proxied context that handles y-axis reversal automatically\n *\n * @remarks\n * Standard HTML canvas uses a top-left origin with y-axis pointing down.\n * This proxy inverts the y-axis to create a mathematical coordinate system\n * with y-axis pointing up.\n *\n * The proxy intercepts drawing methods (fillRect, strokeRect, moveTo, lineTo, etc.)\n * and automatically negates y-coordinates and height values. This allows you to\n * work in mathematical coordinates while still rendering correctly.\n *\n * Special handling for complex methods:\n * - drawImage with 9 args: Properly inverts source and destination rectangles\n * - drawImage with 5 args: Adjusts for image height\n * - All methods in {@link methodsToFlip}: Y-coordinates negated automatically\n *\n * @example\n * ```typescript\n * const canvas = document.querySelector('canvas');\n * const ctx = canvas.getContext('2d');\n * const flippedCtx = reverseYAxis(ctx);\n *\n * // Draw with mathematical coordinates (y-up)\n * flippedCtx.fillRect(0, 0, 100, 100); // Square in first quadrant\n * flippedCtx.moveTo(0, 0);\n * flippedCtx.lineTo(50, 100); // Line going upward\n * ```\n *\n * @category Canvas Position\n * @see {@link methodsToFlip} for list of intercepted methods\n * @see {@link invertYAxisForDrawImageWith9Args} for drawImage special handling\n */\nexport function reverseYAxis(context: CanvasRenderingContext2D): CanvasRenderingContext2D {\n return new Proxy(context, {\n get(target: CanvasRenderingContext2D, prop: string | symbol, receiver: any): any {\n const value = Reflect.get(target, prop, target);\n \n // Check if this is a method that needs y-coordinate flipping\n if (typeof prop === 'string' && prop in methodsToFlip && typeof value === 'function') {\n return function(...args: any[]) {\n // Create a copy of the arguments\n const newArgs = [...args];\n \n // Special handling for drawImage with 9 arguments (third signature of drawImage)\n if (prop === 'drawImage' && args.length === 9) {\n const convertedArgs = invertYAxisForDrawImageWith9Args(args);\n return value.apply(target, convertedArgs);\n } else {\n // Flip the y-coordinates based on methodsToFlip configuration\n const yIndices = methodsToFlip[prop];\n for (const index of yIndices) {\n if (index < newArgs.length) {\n newArgs[index] = -newArgs[index];\n }\n }\n // Special handling for drawImage with 5 arguments (first signature of drawImage)\n if(prop === \"drawImage\" && args.length === 5){\n newArgs[2] -= newArgs[4];\n }\n }\n \n // Call the original method with the modified arguments\n return value.apply(target, newArgs);\n };\n }\n \n // Return the original value for properties and methods that don't need modification\n if (typeof value === 'function') {\n return function(...args: any[]) {\n return value.apply(target, args);\n };\n }\n \n return value;\n },\n set(target, prop, value): boolean {\n return Reflect.set(target, prop, value);\n }\n });\n}\n\n/**\n * Inverts y-coordinates for the 9-argument variant of drawImage.\n *\n * @param args - The arguments array for drawImage\n * @returns Modified arguments with inverted y-coordinates\n *\n * @remarks\n * The 9-argument drawImage signature is:\n * drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)\n *\n * When inverting y-axis, we need to adjust:\n * - sy (source y): Flip relative to image height\n * - sHeight: Negate (height becomes negative in flipped space)\n * - dy (destination y): Negate\n * - dy offset: Subtract destination height\n *\n * This ensures images render correctly when the canvas y-axis is flipped.\n *\n * @example\n * ```typescript\n * // Original call (top-left origin):\n * ctx.drawImage(img, 0, 0, 100, 100, 50, 50, 200, 200);\n *\n * // With flipped y-axis, this becomes:\n * // sy = imageHeight - 0, sHeight = -100, dy = -50 - 200, dHeight = -200\n * ```\n *\n * @category Canvas Position\n * @see {@link reverseYAxis} for the main y-axis flipping proxy\n */\nexport function invertYAxisForDrawImageWith9Args(args: any[]): typeof args {\n if(args.length !== 9){\n return args;\n }\n const newArgs = [...args];\n const imageHeight = args[0].height;\n if(imageHeight !== undefined){\n newArgs[2] = imageHeight - newArgs[2];\n newArgs[6] = -newArgs[6];\n newArgs[6] -= newArgs[8];\n newArgs[4] = -newArgs[4];\n }\n return newArgs;\n}\n",
23
23
  "import type { Point } from \"@ue-too/math\";\nimport { PointCal } from \"@ue-too/math\";\nimport { calculateOrderOfMagnitude } from \"./ruler\";\nimport { boundariesFullyDefined, translationHeightOf, translationWidthOf, Boundaries } from \"../camera/utils/position\";\n\n/**\n * @description Draws a crosshair on the canvas.\n * @deprecated\n * \n * @category Board\n */\nexport function drawCrossHair(context: CanvasRenderingContext2D, pos: Point, cameraZoomLevel: number, alignCoordinateSystem: boolean, size: number, color: string = \"red\"): void{\n // size is the pixel shown in the viewport\n let halfSize = size / 2;\n halfSize = halfSize / cameraZoomLevel;\n context.beginPath();\n context.strokeStyle = color;\n context.lineWidth = 2 / cameraZoomLevel;\n if(alignCoordinateSystem){\n context.moveTo(pos.x - halfSize, pos.y);\n context.lineTo(pos.x + halfSize, pos.y);\n context.moveTo(pos.x, pos.y - halfSize);\n context.lineTo(pos.x, pos.y + halfSize);\n } else {\n context.moveTo(pos.x - halfSize, -pos.y);\n context.lineTo(pos.x + halfSize, -pos.y);\n context.moveTo(pos.x, -pos.y - halfSize);\n context.lineTo(pos.x, -pos.y + halfSize);\n }\n context.stroke();\n context.lineWidth = 3;\n}\n\n/**\n * @description Draws a bounding box on the canvas.\n * @deprecated\n * \n * @category Board\n */\nexport function drawBoundingBox(context: CanvasRenderingContext2D, boundaries: Boundaries, alignCoordinateSystem: boolean): void{\n if(!boundariesFullyDefined(boundaries)){\n return;\n }\n const width = translationWidthOf(boundaries);\n const height = translationHeightOf(boundaries);\n const curMin = boundaries == undefined ? undefined: boundaries.min;\n const curMinX = curMin == undefined ? undefined: curMin.x;\n const curMinY = curMin == undefined ? undefined: curMin.y;\n if(curMinX == undefined || curMinY == undefined || width == undefined || height == undefined){\n return;\n }\n context.beginPath();\n context.strokeStyle = \"blue\";\n context.lineWidth = 100;\n if(alignCoordinateSystem){\n context.roundRect(curMinX, curMinY, width, height, 5);\n } else {\n context.roundRect(curMinX, -curMinY, width, -height, 5);\n }\n context.stroke();\n context.lineWidth = 3;\n context.strokeStyle = \"black\";\n}\n\n/**\n * @description Draws the axis of the board.\n * @deprecated\n * \n * @category Board\n */\nexport function drawAxis(context: CanvasRenderingContext2D, boundaries: Boundaries, zoomLevel: number, alignCoordinateSystem: boolean): void{\n if(!boundariesFullyDefined(boundaries)){\n // one of the direction is not defined\n return;\n }\n const width = translationWidthOf(boundaries);\n const height = translationHeightOf(boundaries);\n const curMin = boundaries == undefined ? undefined: boundaries.min;\n const curMinX = curMin == undefined ? undefined: curMin.x;\n const curMinY = curMin == undefined ? undefined: curMin.y;\n if(curMinX == undefined || curMinY == undefined || width == undefined || height == undefined){\n return;\n }\n context.lineWidth = 1 / zoomLevel;\n // y axis\n context.beginPath();\n context.strokeStyle = `rgba(87, 173, 72, 0.8)`;\n context.moveTo(0, 0);\n if(alignCoordinateSystem){\n context.lineTo(0, curMinY + (height));\n } else {\n context.lineTo(0, -curMinY - (height));\n }\n context.stroke();\n \n // x axis\n context.beginPath();\n context.strokeStyle = `rgba(220, 59, 59, 0.8)`;\n context.moveTo(0, 0);\n context.lineTo(curMinX + width, 0);\n context.stroke();\n context.strokeStyle = \"black\";\n}\n\n/**\n * @description Draws the grid of the board.\n * argument points are in world space\n * @deprecated\n * \n * @category Board\n */\nexport function drawGrid(context: CanvasRenderingContext2D, topLeftCorner: Point, topRightCorner: Point, bottomLeftCorner: Point, bottomRightCorner: Point, alignCoordinateSystem: boolean, cameraZoomLevel: number): void{\n let leftRightDirection = PointCal.unitVectorFromA2B(topLeftCorner, topRightCorner);\n let topDownDirection = PointCal.unitVectorFromA2B(bottomLeftCorner, topLeftCorner);\n let width = PointCal.distanceBetweenPoints(topLeftCorner, topRightCorner);\n let height = PointCal.distanceBetweenPoints(topLeftCorner, bottomLeftCorner);\n let orderOfMagnitude = calculateOrderOfMagnitude(width);\n let divisor = Math.pow(10, orderOfMagnitude);\n let subDivisor = divisor / 10;\n let minHorizontalSmallTick = Math.ceil(topLeftCorner.x / subDivisor) * subDivisor;\n let maxHorizontalSmallTick = Math.floor(topRightCorner.x / subDivisor) * subDivisor;\n let minVerticalSmallTick = alignCoordinateSystem ? Math.floor(topLeftCorner.y / subDivisor) * subDivisor : Math.ceil(bottomLeftCorner.y / subDivisor) * subDivisor;\n let maxVerticalSmallTick = alignCoordinateSystem ? Math.ceil(bottomLeftCorner.y / subDivisor) * subDivisor : Math.floor(topLeftCorner.y / subDivisor) * subDivisor;;\n\n // vertical lines\n for(let i = minHorizontalSmallTick; i <= maxHorizontalSmallTick; i += subDivisor){\n const startPoint = PointCal.addVector({x: i, y: topLeftCorner.y}, PointCal.multiplyVectorByScalar(leftRightDirection, subDivisor));\n const endPoint = PointCal.addVector({x: i, y: bottomLeftCorner.y}, PointCal.multiplyVectorByScalar(leftRightDirection, subDivisor));\n context.beginPath();\n context.strokeStyle = \"black\";\n context.fillStyle = \"black\";\n context.lineWidth = 0.5 / cameraZoomLevel;\n context.moveTo(startPoint.x, startPoint.y);\n context.lineTo(endPoint.x, endPoint.y);\n context.stroke();\n }\n for(let i = minVerticalSmallTick; i <= maxVerticalSmallTick; i += subDivisor){\n const startPoint = PointCal.addVector({x: topLeftCorner.x, y: i}, PointCal.multiplyVectorByScalar(topDownDirection, subDivisor));\n const endPoint = PointCal.addVector({x: topRightCorner.x, y: i}, PointCal.multiplyVectorByScalar(topDownDirection, subDivisor));\n context.beginPath();\n context.strokeStyle = \"black\";\n context.fillStyle = \"black\";\n context.lineWidth = 0.5 / cameraZoomLevel;\n context.moveTo(startPoint.x, startPoint.y);\n context.lineTo(endPoint.x, endPoint.y);\n context.stroke();\n }\n}\n\n/**\n * @description Draws a ruler on the canvas.\n * argument points are in world space\n * @deprecated\n * \n * @category Board\n */\nexport function drawRulerLegacy(context: CanvasRenderingContext2D, topLeftCorner: Point, topRightCorner: Point, bottomLeftCorner: Point, bottomRightCorner: Point, alignCoordinateSystem: boolean, cameraZoomLevel: number): void{\n let leftRightDirection = PointCal.unitVectorFromA2B(topLeftCorner, topRightCorner);\n let topDownDirection = PointCal.unitVectorFromA2B(bottomLeftCorner, topLeftCorner);\n let width = PointCal.distanceBetweenPoints(topLeftCorner, topRightCorner);\n let orderOfMagnitude = calculateOrderOfMagnitude(width);\n let divisor = Math.pow(10, orderOfMagnitude);\n // console.log(\"divisor\", divisor);\n let halfDivisor = divisor / 2;\n let subDivisor = divisor / 10;\n let scaling = 1;\n if (orderOfMagnitude <= 0){\n scaling = Math.pow(10, -orderOfMagnitude + 1);\n }\n let minHorizontalLargeTick = Math.ceil(topLeftCorner.x / divisor) * divisor;\n let maxHorizontalLargeTick = Math.floor(topRightCorner.x / divisor) * divisor;\n let minVerticalLargeTick = alignCoordinateSystem ? Math.ceil(topLeftCorner.y / divisor) * divisor : Math.floor(bottomLeftCorner.y / divisor) * divisor;\n let maxVerticalLargeTick = alignCoordinateSystem ? Math.floor(bottomLeftCorner.y / divisor) * divisor : Math.ceil(topLeftCorner.y / divisor) * divisor;\n let minHorizontalMediumTick = Math.ceil(topLeftCorner.x / halfDivisor) * halfDivisor;\n let maxHorizontalMediumTick = Math.floor(topRightCorner.x / halfDivisor) * halfDivisor;\n let minVerticalMediumTick = alignCoordinateSystem ? Math.ceil(topLeftCorner.y / halfDivisor) * halfDivisor : Math.floor(bottomLeftCorner.y / halfDivisor) * halfDivisor;\n let maxVerticalMediumTick = alignCoordinateSystem ? Math.floor(bottomLeftCorner.y / halfDivisor) * halfDivisor : Math.ceil(topLeftCorner.y / halfDivisor) * halfDivisor;\n let minHorizontalSmallTick = Math.ceil(topLeftCorner.x / subDivisor) * subDivisor;\n let maxHorizontalSmallTick = Math.floor(topRightCorner.x / subDivisor) * subDivisor;\n let minVerticalSmallTick = alignCoordinateSystem ? Math.ceil(topLeftCorner.y / subDivisor) * subDivisor : Math.floor(bottomLeftCorner.y / subDivisor) * subDivisor;\n let maxVerticalSmallTick = alignCoordinateSystem ? Math.floor(bottomLeftCorner.y / subDivisor) * subDivisor : Math.ceil(topLeftCorner.y / subDivisor) * subDivisor;\n \n let divisorInActualPixel = divisor * cameraZoomLevel;\n let halfDivisorInActualPixel = halfDivisor * cameraZoomLevel;\n let subDivisorInActualPixel = subDivisor * cameraZoomLevel;\n\n \n context.font = `bold ${20 / cameraZoomLevel}px Helvetica`;\n const midBaseLineTextDimensions = context.measureText(`${-(halfDivisor + minHorizontalMediumTick)}`);\n const midBaseLineHeight = midBaseLineTextDimensions.fontBoundingBoxAscent + midBaseLineTextDimensions.fontBoundingBoxDescent;\n const subBaseLineTextDimensions = context.measureText(`${-(subDivisor + minHorizontalSmallTick)}`);\n const subBaseLineHeight = subBaseLineTextDimensions.fontBoundingBoxAscent + subBaseLineTextDimensions.fontBoundingBoxDescent;\n\n const largeHorizontalStep = Math.ceil((maxHorizontalLargeTick - minHorizontalLargeTick) / divisor);\n for(let index = 0; index <= largeHorizontalStep; index ++){\n const i = minHorizontalLargeTick + index * divisor;\n context.beginPath();\n context.strokeStyle = \"black\";\n context.fillStyle = \"black\";\n context.lineWidth = 5 / cameraZoomLevel;\n let resPoint = PointCal.addVector({x: i, y: topLeftCorner.y}, PointCal.multiplyVectorByScalar(topDownDirection, 50 / cameraZoomLevel));\n if(!alignCoordinateSystem){\n context.moveTo(resPoint.x, -resPoint.y);\n } else {\n context.moveTo(resPoint.x, resPoint.y);\n }\n resPoint = PointCal.addVector({x: i, y: topLeftCorner.y}, PointCal.multiplyVectorByScalar(topDownDirection, -50 / cameraZoomLevel));\n if(!alignCoordinateSystem){\n context.lineTo(resPoint.x, -resPoint.y);\n } else {\n context.lineTo(resPoint.x, resPoint.y);\n }\n context.textAlign = \"center\";\n context.textBaseline = \"middle\";\n context.font = `bold ${20 / cameraZoomLevel}px Helvetica`;\n const textDimensions = context.measureText(`${Math.abs(i) % 1 == 0 ? i.toFixed(0) : i.toFixed(2)}`);\n const height = textDimensions.fontBoundingBoxAscent + textDimensions.fontBoundingBoxDescent;\n if(!alignCoordinateSystem){\n resPoint = PointCal.addVector(resPoint, {x: 0, y: -height / 2 - height * 0.2})\n context.fillText(`${Math.abs(i) % 1 == 0 ? i.toFixed(0) : i.toFixed(2)}`, resPoint.x , -resPoint.y);\n } else {\n resPoint = PointCal.addVector(resPoint, {x: 0, y: height / 2 + height * 0.2})\n context.fillText(`${Math.abs(i) % 1 == 0 ? i.toFixed(0) : i.toFixed(2)}`, resPoint.x , resPoint.y);\n }\n context.stroke();\n }\n const largeVerticalStep = Math.ceil((maxHorizontalLargeTick - minHorizontalLargeTick) / divisor);\n for(let index = 0; index <= largeVerticalStep; index++){\n const i = minVerticalLargeTick + index * divisor;\n context.beginPath();\n context.strokeStyle = \"black\";\n context.fillStyle = \"black\";\n context.lineWidth = 5 / cameraZoomLevel;\n let resPoint = PointCal.addVector({x: topLeftCorner.x, y: i}, PointCal.multiplyVectorByScalar(leftRightDirection, -50 / cameraZoomLevel));\n if(!alignCoordinateSystem){\n context.moveTo(resPoint.x, -resPoint.y);\n } else {\n context.moveTo(resPoint.x, resPoint.y);\n }\n resPoint = PointCal.addVector({x: topLeftCorner.x, y: i}, PointCal.multiplyVectorByScalar(leftRightDirection, 50 / cameraZoomLevel));\n if(!alignCoordinateSystem){\n context.lineTo(resPoint.x, -resPoint.y);\n } else {\n context.lineTo(resPoint.x, resPoint.y);\n }\n context.textAlign = \"center\";\n context.textBaseline = \"middle\";\n context.font = `bold ${20 / cameraZoomLevel}px Helvetica`;\n \n const textDimensions = context.measureText(`${Math.abs(i) % 1 == 0 ? i.toFixed(0) : i.toFixed(2)}`);\n resPoint = PointCal.addVector(resPoint, {x: textDimensions.width / 2 + textDimensions.width * 0.3, y: 0});\n if(!alignCoordinateSystem){\n context.fillText(`${Math.abs(i) % 1 == 0 ? i.toFixed(0) : i.toFixed(2)}`, resPoint.x, -resPoint.y);\n } else {\n context.fillText(`${Math.abs(i) % 1 == 0 ? i.toFixed(0) : i.toFixed(2)}`, resPoint.x, resPoint.y);\n }\n context.stroke();\n }\n\n const mediumHorizontalStep = Math.ceil((maxHorizontalMediumTick - minHorizontalMediumTick) / halfDivisor);\n for(let index = 0; index <= mediumHorizontalStep; index++ ){\n const i = minHorizontalMediumTick + index * halfDivisor;\n if(Math.floor(i * scaling) % Math.floor(divisor * scaling )== 0) continue;\n context.beginPath();\n context.strokeStyle = \"black\";\n context.fillStyle = \"black\";\n context.lineWidth = 3 / cameraZoomLevel;\n let resPoint = PointCal.addVector({x: i, y: topLeftCorner.y}, PointCal.multiplyVectorByScalar(topDownDirection, 25 / cameraZoomLevel));\n if(!alignCoordinateSystem){\n context.moveTo(resPoint.x, -resPoint.y);\n } else {\n context.moveTo(resPoint.x, resPoint.y);\n }\n resPoint = PointCal.addVector({x: i, y: topLeftCorner.y}, PointCal.multiplyVectorByScalar(topDownDirection, -25 / cameraZoomLevel));\n if(!alignCoordinateSystem){\n context.lineTo(resPoint.x, -resPoint.y);\n } else {\n context.lineTo(resPoint.x, resPoint.y);\n }\n context.font = `${15 / cameraZoomLevel}px Helvetica`;\n const textDimensions = context.measureText(`${Math.abs(i) % 1 == 0 ? i.toFixed(0) : i.toFixed(2)}`);\n if(halfDivisorInActualPixel > midBaseLineTextDimensions.width * 2) {\n context.textAlign = \"center\";\n context.textBaseline = \"middle\";\n const height = textDimensions.fontBoundingBoxAscent + textDimensions.fontBoundingBoxDescent;\n if(!alignCoordinateSystem){\n resPoint = PointCal.addVector(resPoint, {x: 0, y: -height / 2 - height * 0.2});\n resPoint = PointCal.flipYAxis(resPoint);\n } else {\n resPoint = PointCal.addVector(resPoint, {x: 0, y: height / 2 + height * 0.2});\n }\n context.fillText(`${Math.abs(i) % 1 == 0 ? i.toFixed(0) : i.toFixed(2)}`, resPoint.x , resPoint.y);\n }\n context.stroke();\n }\n const mediumVerticalStep = Math.ceil((maxVerticalMediumTick - minVerticalMediumTick) / halfDivisor);\n for(let index = 0; index <= mediumVerticalStep; index++){\n const i = minVerticalMediumTick + index * halfDivisor;\n if(Math.floor(i * scaling) % Math.floor(divisor * scaling)== 0) continue;\n context.beginPath();\n context.strokeStyle = \"black\";\n context.fillStyle = \"black\";\n context.lineWidth = 3 / cameraZoomLevel;\n let resPoint = PointCal.addVector({x: topLeftCorner.x, y: i}, PointCal.multiplyVectorByScalar(leftRightDirection, -25 / cameraZoomLevel));\n if(!alignCoordinateSystem){\n context.moveTo(resPoint.x, -resPoint.y);\n } else {\n context.moveTo(resPoint.x, resPoint.y);\n }\n resPoint = PointCal.addVector({x: topLeftCorner.x, y: i}, PointCal.multiplyVectorByScalar(leftRightDirection, 25 / cameraZoomLevel));\n if(!alignCoordinateSystem){\n context.lineTo(resPoint.x, -resPoint.y);\n } else {\n context.lineTo(resPoint.x, resPoint.y);\n }\n context.font = `${18 / cameraZoomLevel}px Helvetica`;\n const textDimensions = context.measureText(`${Math.abs(i) % 1 == 0 ? i.toFixed(0) : i.toFixed(2)}`);\n const height = textDimensions.fontBoundingBoxAscent + textDimensions.fontBoundingBoxDescent;\n if(halfDivisorInActualPixel > midBaseLineHeight * 2) {\n context.textAlign = \"center\";\n context.textBaseline = \"middle\";\n resPoint = PointCal.addVector(resPoint, {x: textDimensions.width / 2 + textDimensions.width * 0.3, y: 0});\n if(!alignCoordinateSystem){\n resPoint = PointCal.flipYAxis(resPoint);\n }\n context.fillText(`${Math.abs(i) % 1 == 0 ? i.toFixed(0) : i.toFixed(2)}`, resPoint.x, resPoint.y );\n }\n context.stroke();\n }\n const smallHorizontalStep = Math.ceil((maxHorizontalSmallTick - minHorizontalSmallTick) / subDivisor);\n for(let index = 0; index <= smallHorizontalStep; index++){\n const i = minHorizontalSmallTick + index * subDivisor;\n if(Math.floor(i * scaling) % Math.floor(divisor * scaling) == 0 || Math.floor(i * scaling) % Math.floor(halfDivisor * scaling) == 0) continue;\n context.beginPath();\n context.strokeStyle = \"black\";\n context.fillStyle = \"black\";\n context.lineWidth = 1 / cameraZoomLevel;\n let resPoint = PointCal.addVector({x: i, y: topLeftCorner.y}, PointCal.multiplyVectorByScalar(topDownDirection, 12.5 / cameraZoomLevel));\n if(!alignCoordinateSystem){\n context.moveTo(resPoint.x, -resPoint.y);\n } else {\n context.moveTo(resPoint.x, resPoint.y);\n }\n resPoint = PointCal.addVector({x: i, y: topLeftCorner.y}, PointCal.multiplyVectorByScalar(topDownDirection, -12.5 / cameraZoomLevel));\n if(!alignCoordinateSystem){\n context.lineTo(resPoint.x, -resPoint.y);\n } else {\n context.lineTo(resPoint.x, resPoint.y);\n }\n context.font = `${10 / cameraZoomLevel}px Helvetica`;\n const textDimensions = context.measureText(`${Math.abs(i) % 1 == 0 ? i.toFixed(0) : i.toFixed(2)}`);\n if(subDivisorInActualPixel > subBaseLineTextDimensions.width * 2) {\n context.textAlign = \"center\";\n context.textBaseline = \"middle\";\n const height = textDimensions.fontBoundingBoxAscent + textDimensions.fontBoundingBoxDescent;\n if(!alignCoordinateSystem){\n resPoint = PointCal.addVector(resPoint, {x: 0, y: -height / 2 - height * 0.2});\n resPoint = PointCal.flipYAxis(resPoint);\n } else {\n resPoint = PointCal.addVector(resPoint, {x: 0, y: height / 2 + height * 0.2});\n }\n context.fillText(`${Math.abs(i) % 1 == 0 ? i.toFixed(0) : i.toFixed(2)}`, resPoint.x , resPoint.y);\n }\n context.stroke();\n }\n const smallVerticalStep = Math.ceil((maxVerticalSmallTick - minVerticalSmallTick) / subDivisor);\n for(let index = 0; index <= smallVerticalStep; index++){\n const i = minVerticalSmallTick + index * subDivisor;\n if(Math.floor(i * scaling) % Math.floor(divisor * scaling) == 0 || Math.floor(i * scaling) % Math.floor(halfDivisor * scaling) == 0) continue;\n context.beginPath();\n context.strokeStyle = \"black\";\n context.fillStyle = \"black\";\n context.lineWidth = 1 / cameraZoomLevel;\n let resPoint = PointCal.addVector({x: topLeftCorner.x, y: i}, PointCal.multiplyVectorByScalar(leftRightDirection, -12.5 / cameraZoomLevel));\n if(!alignCoordinateSystem){\n context.moveTo(resPoint.x, -resPoint.y);\n } else {\n context.moveTo(resPoint.x, resPoint.y);\n }\n resPoint = PointCal.addVector({x: topLeftCorner.x, y: i}, PointCal.multiplyVectorByScalar(leftRightDirection, 12.5 / cameraZoomLevel));\n if(!alignCoordinateSystem){\n context.lineTo(resPoint.x, -resPoint.y);\n } else {\n context.lineTo(resPoint.x, resPoint.y);\n }\n context.font = `${12 / cameraZoomLevel}px Helvetica`;\n const textDimensions = context.measureText(`${Math.abs(i) % 1 == 0 ? i.toFixed(0) : i.toFixed(2)}`);\n const height = textDimensions.fontBoundingBoxAscent + textDimensions.fontBoundingBoxDescent;\n if(subDivisorInActualPixel > subBaseLineHeight * 2) {\n context.textAlign = \"center\";\n context.textBaseline = \"middle\";\n resPoint = PointCal.addVector(resPoint, {x: textDimensions.width / 2 + textDimensions.width * 0.3, y: 0});\n if(!alignCoordinateSystem){\n resPoint = PointCal.flipYAxis(resPoint);\n }\n context.fillText(`${Math.abs(i) % 1 == 0 ? i.toFixed(0) : i.toFixed(2)}`, resPoint.x, resPoint.y );\n }\n context.stroke();\n }\n}\n\n/**\n * @description Draws the position text on the canvas.\n * argument points are in world space\n * @deprecated\n * \n * @category Board\n */\nexport function drawPositionText(context: CanvasRenderingContext2D, pos: Point, cameraZoomLevel: number, alignCoordinateSystem: boolean, offset: number = 20, color: string=\"red\"): void{\n offset = offset / cameraZoomLevel; \n context.font = `${20 / cameraZoomLevel}px Arial`;\n context.fillStyle = color;\n if(alignCoordinateSystem){\n context.fillText(`x: ${pos.x.toFixed(2)}, y: ${pos.y.toFixed(2)}`, pos.x + offset, pos.y + offset);\n } else {\n context.fillText(`x: ${pos.x.toFixed(2)}, y: ${pos.y.toFixed(2)}`, pos.x + offset, -pos.y - offset);\n }\n context.fillStyle = \"black\";\n}\n\n/**\n * @description Draws a reference circle on the canvas.\n * argument points are in world space\n * @deprecated\n * \n * @category Board\n */\nexport function drawReferenceCircle(context: CanvasRenderingContext2D, pos: Point, alignCoordinateSystem: boolean): void {\n context.beginPath();\n context.strokeStyle = `rgba(87, 173, 72, 0.8)`;\n // context.moveTo(pos.x, -pos.y);\n if(alignCoordinateSystem){\n context.arc(pos.x, pos.y, 5, 0, 2 * Math.PI);\n } else {\n context.arc(pos.x, -pos.y, 5, 0, 2 * Math.PI);\n }\n context.stroke();\n context.strokeStyle = \"black\";\n}\n",
24
24
  "import { Point, PointCal } from \"@ue-too/math\";\nimport { calculateOrderOfMagnitude } from \"./ruler\";\n\n/**\n * Draws an arrow from start to end point with an arrowhead.\n *\n * @param context - The canvas 2D rendering context\n * @param cameraZoomLevel - Current camera zoom level for scale-independent sizing\n * @param startPoint - Arrow tail position in world coordinates\n * @param endPoint - Arrow head position in world coordinates\n * @param width - Line width in world units (default: 1)\n * @param arrowRatio - Ratio of arrowhead size to total length (default: 0.3, unused in implementation)\n *\n * @remarks\n * The arrow consists of a line segment and a triangular arrowhead. The arrowhead\n * size is adaptive:\n * - Maximum 10 pixels in viewport space\n * - Minimum half the arrow length\n *\n * This ensures arrows look good at all zoom levels and lengths.\n *\n * The arrowhead is constructed perpendicular to the arrow direction, creating\n * a filled triangle at the end point.\n *\n * @example\n * ```typescript\n * const ctx = canvas.getContext('2d');\n * const zoom = 1.5;\n *\n * // Draw a simple arrow\n * ctx.fillStyle = 'blue';\n * ctx.strokeStyle = 'blue';\n * drawArrow(ctx, zoom, { x: 0, y: 0 }, { x: 100, y: 50 });\n *\n * // Draw a thicker arrow\n * ctx.fillStyle = 'red';\n * ctx.strokeStyle = 'red';\n * drawArrow(ctx, zoom, { x: 0, y: 0 }, { x: 100, y: -50 }, 3);\n * ```\n *\n * @category Drawing Utilities\n */\nexport function drawArrow(context: CanvasRenderingContext2D, cameraZoomLevel: number, startPoint: Point, endPoint: Point, width: number = 1, arrowRatio: number = 0.3) {\n const length = PointCal.distanceBetweenPoints(startPoint, endPoint);\n const arrowHeight = 10 < length * cameraZoomLevel * 0.5 ? 10 / cameraZoomLevel : length * 0.5;\n const offsetLength = length - arrowHeight;\n const offsetPoint = PointCal.linearInterpolation(startPoint, endPoint, offsetLength / length);\n context.beginPath();\n context.lineWidth = width / cameraZoomLevel;\n context.moveTo(startPoint.x, startPoint.y);\n context.lineTo(offsetPoint.x, offsetPoint.y);\n context.stroke();\n const unitVector = PointCal.rotatePoint(PointCal.unitVectorFromA2B(endPoint, startPoint), Math.PI / 2);\n const arrowPoint1 = PointCal.addVector(offsetPoint, PointCal.multiplyVectorByScalar(unitVector, arrowHeight * 0.5));\n const arrowPoint2 = PointCal.subVector(offsetPoint, PointCal.multiplyVectorByScalar(unitVector, arrowHeight * 0.5));\n context.beginPath();\n context.moveTo(endPoint.x, endPoint.y);\n context.lineTo(arrowPoint1.x, arrowPoint1.y);\n context.lineTo(arrowPoint2.x, arrowPoint2.y);\n context.closePath();\n context.fill();\n}\n\n/**\n * Length of major tick marks in pixels (viewport space).\n * @category Drawing Utilities\n */\nexport const MAJOR_TICK_LENGTH = 30;\n\n/**\n * Length of minor tick marks in pixels (viewport space).\n * @category Drawing Utilities\n */\nexport const MINOR_TICK_LENGTH = MAJOR_TICK_LENGTH * 0.3;\n\n/**\n * Length of half-step tick marks in pixels (viewport space).\n * @category Drawing Utilities\n */\nexport const HALF_TICK_LENGTH = MAJOR_TICK_LENGTH * 0.5;\n\n/**\n * Offset for major tick labels in pixels (viewport space).\n * @category Drawing Utilities\n */\nexport const TEXT_MAJOR_TICK_OFFSET = 10;\n\n/**\n * Offset for half-step tick labels in pixels (viewport space).\n * @category Drawing Utilities\n */\nexport const TEXT_HALF_TICK_OFFSET = 2.5;\n\n/**\n * Font size for major tick labels in pixels (viewport space).\n * @category Drawing Utilities\n */\nexport const TEXT_MAJOR_TICK_FONT_SIZE = 20;\n\n/**\n * Font size for half-step tick labels in pixels (viewport space).\n * @category Drawing Utilities\n */\nexport const TEXT_HALF_TICK_FONT_SIZE = 10;\n\n/**\n * Draws calibrated rulers along the edges of the viewport.\n *\n * @param context - The canvas 2D rendering context\n * @param topLeftCorner - Top-left corner of viewport in world coordinates\n * @param topRightCorner - Top-right corner of viewport in world coordinates\n * @param bottomLeftCorner - Bottom-left corner of viewport in world coordinates\n * @param alignCoordinateSystem - Whether coordinates align with canvas (y-down) or are mathematical (y-up)\n * @param cameraZoomLevel - Current camera zoom level\n *\n * @remarks\n * This function draws rulers with three levels of tick marks:\n * - Major ticks: At powers of 10 (1, 10, 100, etc.) with large labels\n * - Half ticks: At half-steps (5, 50, 500, etc.) with small labels\n * - Minor ticks: At 1/10 steps with no labels\n *\n * The ruler automatically adapts to the zoom level by calculating appropriate\n * tick spacing using {@link calculateOrderOfMagnitude} and {@link calculateTickValues}.\n *\n * Rulers are drawn along:\n * - Top edge (horizontal ruler, red)\n * - Left edge (vertical ruler, green)\n *\n * Tick positions are calibrated to align with round numbers in world space,\n * making it easy to read coordinates at any zoom level.\n *\n * @example\n * ```typescript\n * const ctx = canvas.getContext('2d');\n * const zoom = 2.0;\n *\n * // Viewport corners in world space\n * const topLeft = { x: -100, y: 100 };\n * const topRight = { x: 100, y: 100 };\n * const bottomLeft = { x: -100, y: -100 };\n *\n * drawRuler(ctx, topLeft, topRight, bottomLeft, false, zoom);\n * // Draws rulers with ticks at -100, -50, 0, 50, 100\n * ```\n *\n * @category Drawing Utilities\n * @see {@link calculateTickValues} for tick calculation logic\n * @see {@link calculateOrderOfMagnitude} for order of magnitude calculation\n */\nexport function drawRuler(\n context: CanvasRenderingContext2D,\n topLeftCorner: Point,\n topRightCorner: Point,\n bottomLeftCorner: Point,\n alignCoordinateSystem: boolean,\n cameraZoomLevel: number,\n): void{\n\n // NOTE horizontal ruler\n const {\n minMajorTickValue, \n maxMajorTickValue, \n majorTickStep, \n minMinTickValue, \n maxMaxTickValue, \n minTickStep, \n minHalfTickValue, \n maxHalfTickValue, \n halfTickStep, \n calibrationMultiplier, \n normalizedOrderOfMagnitude,\n } = calculateTickValues(topLeftCorner.x, topRightCorner.x);\n\n context.save();\n context.strokeStyle = 'red';\n for(let i = minMajorTickValue; i <= maxMajorTickValue; i += majorTickStep){\n const majorTickPoint = {x: i * calibrationMultiplier, y: topLeftCorner.y};\n const majorTickLength = alignCoordinateSystem ? MAJOR_TICK_LENGTH / cameraZoomLevel : -MAJOR_TICK_LENGTH / cameraZoomLevel;\n const textOffset = alignCoordinateSystem ? TEXT_MAJOR_TICK_OFFSET / cameraZoomLevel : -TEXT_MAJOR_TICK_OFFSET / cameraZoomLevel;\n drawXAxisTick(context, cameraZoomLevel, majorTickPoint, majorTickLength, i * calibrationMultiplier, {textOffset, fontSize: TEXT_MAJOR_TICK_FONT_SIZE});\n }\n\n for(let i = minMinTickValue; i <= maxMaxTickValue; i += minTickStep){\n if(i % majorTickStep === 0){\n continue;\n }\n if(i % halfTickStep === 0){\n continue;\n }\n const minTickPoint = {x: i * calibrationMultiplier, y: topLeftCorner.y};\n const minTickLength = alignCoordinateSystem ? MINOR_TICK_LENGTH / cameraZoomLevel : -MINOR_TICK_LENGTH / cameraZoomLevel;\n drawXAxisTick(context, cameraZoomLevel, minTickPoint, minTickLength, i);\n }\n\n for(let i = minHalfTickValue; i <= maxHalfTickValue; i += halfTickStep){\n if(i % majorTickStep === 0){\n continue;\n }\n const halfTickPoint = {x: i * calibrationMultiplier, y: topLeftCorner.y};\n const halfTickLength = alignCoordinateSystem ? HALF_TICK_LENGTH / cameraZoomLevel : -HALF_TICK_LENGTH / cameraZoomLevel;\n const textOffset = alignCoordinateSystem ? TEXT_HALF_TICK_OFFSET / cameraZoomLevel : -TEXT_HALF_TICK_OFFSET / cameraZoomLevel;\n drawXAxisTick(context, cameraZoomLevel, halfTickPoint, halfTickLength, i * calibrationMultiplier, {textOffset, fontSize: TEXT_HALF_TICK_FONT_SIZE, color: 'red'});\n }\n\n context.restore();\n\n // NOTE vertical ruler\n const {\n minMajorTickValue: vMinMajorTickValue, maxMajorTickValue: vMaxMajorTickValue, majorTickStep: vMajorTickStep, \n minMinTickValue: vMinMinTickValue, maxMaxTickValue: vMaxMaxTickValue, \n minTickStep: vMinTickStep, \n minHalfTickValue: vMinHalfTickValue, maxHalfTickValue: vMaxHalfTickValue, \n halfTickStep: vHalfTickStep, \n calibrationMultiplier: vCalibrationMultiplier,\n } = calculateTickValues(topLeftCorner.y, bottomLeftCorner.y, normalizedOrderOfMagnitude);\n\n context.save();\n context.strokeStyle = 'green';\n for(let i = vMinMajorTickValue; i <= vMaxMajorTickValue; i += vMajorTickStep){\n const majorTickPoint = {x: topLeftCorner.x, y: i * vCalibrationMultiplier};\n const majorTickLength = MAJOR_TICK_LENGTH / cameraZoomLevel;\n const textOffset = TEXT_MAJOR_TICK_OFFSET / cameraZoomLevel;\n drawYAxisTick(context, cameraZoomLevel, majorTickPoint, majorTickLength, i, {textOffset, fontSize: TEXT_MAJOR_TICK_FONT_SIZE});\n }\n\n for(let i = vMinHalfTickValue; i <= vMaxHalfTickValue; i += vHalfTickStep){\n if(i % vMajorTickStep === 0){\n continue;\n }\n const halfTickPoint = {x: topLeftCorner.x, y: i * vCalibrationMultiplier};\n const halfTickLength = HALF_TICK_LENGTH / cameraZoomLevel;\n const textOffset = TEXT_HALF_TICK_OFFSET / cameraZoomLevel;\n drawYAxisTick(context, cameraZoomLevel, halfTickPoint, halfTickLength, i, {textOffset, fontSize: TEXT_HALF_TICK_FONT_SIZE});\n }\n\n for(let i = vMinMinTickValue; i <= vMaxMaxTickValue; i += vMinTickStep){\n if(i % vMajorTickStep === 0){\n continue;\n }\n const minTickPoint = {x: topLeftCorner.x, y: i * vCalibrationMultiplier};\n const minTickLength = MINOR_TICK_LENGTH / cameraZoomLevel;\n drawYAxisTick(context, cameraZoomLevel, minTickPoint, minTickLength, i);\n }\n context.restore();\n}\n\nfunction drawYAxisTick(\n context: CanvasRenderingContext2D, \n cameraZoomLevel: number, \n majorTickPoint: { x: number; y: number; }, \n majorTickLength: number, \n tickValue: number, \n textOption?: {\n textOffset: number,\n fontSize: number,\n color?: string,\n }\n) {\n const drawText = textOption !== undefined;\n context.save();\n context.lineWidth = 1 / cameraZoomLevel;\n context.beginPath();\n context.moveTo(majorTickPoint.x, majorTickPoint.y);\n context.lineTo(majorTickPoint.x + majorTickLength, majorTickPoint.y);\n context.stroke();\n context.restore();\n if(!drawText){\n return;\n }\n const color = textOption.color ?? 'green';\n context.save();\n context.textAlign = \"left\";\n context.textBaseline = \"middle\";\n context.fillStyle = color;\n context.font = `${textOption.fontSize / cameraZoomLevel}px Arial`;\n const tickValueText = tickValue % 1 == 0 ? tickValue : tickValue.toFixed(2);\n context.fillText(`${tickValueText}`, majorTickPoint.x + majorTickLength + textOption.textOffset, majorTickPoint.y);\n context.restore();\n}\n\nfunction drawXAxisTick(\n context: CanvasRenderingContext2D, \n cameraZoomLevel: number, \n majorTickPoint: { x: number; y: number; }, \n majorTickLength: number, \n tickValue: number, \n textOption?: {\n textOffset: number,\n fontSize: number,\n color?: string,\n }\n) {\n const drawText = textOption !== undefined;\n context.save();\n context.lineWidth = 1 / cameraZoomLevel;\n context.beginPath();\n context.moveTo(majorTickPoint.x, majorTickPoint.y);\n context.lineTo(majorTickPoint.x, majorTickPoint.y + majorTickLength);\n context.stroke();\n context.restore();\n if(!drawText){\n return;\n }\n const color = textOption.color ?? 'red';\n context.save();\n context.textAlign = \"center\";\n context.textBaseline = \"top\";\n context.fillStyle = color;\n context.font = `${textOption.fontSize / cameraZoomLevel}px Arial`;\n const tickValueText = tickValue % 1 == 0 ? tickValue : tickValue.toFixed(2);\n context.fillText(`${tickValueText}`, majorTickPoint.x, majorTickPoint.y + majorTickLength + textOption.textOffset);\n context.restore();\n}\n\n/**\n * Calculates tick mark positions and spacing for a ruler.\n *\n * @param minValue - Minimum value on the ruler axis\n * @param maxValue - Maximum value on the ruler axis\n * @param orderOfMagnitude - Optional pre-calculated order of magnitude (for consistency across axes)\n * @returns Object containing tick positions and spacing for major, half, and minor ticks\n *\n * @remarks\n * This function determines where to place tick marks on a ruler to show round\n * numbers at appropriate intervals. It calculates three levels of ticks:\n *\n * 1. Major ticks: At powers of 10 (step = 10^n)\n * 2. Half ticks: At half the major step (step = 5×10^(n-1))\n * 3. Minor ticks: At 1/10 the major step (step = 10^(n-1))\n *\n * The calibration multiplier handles cases where the order of magnitude is very\n * small (< 1), ensuring tick positions are calculated correctly for zoomed-in views.\n *\n * For consistency between x and y axes, you can provide a pre-calculated\n * orderOfMagnitude. Otherwise, it's calculated from the range width.\n *\n * @example\n * ```typescript\n * // Ruler showing -100 to 100\n * const ticks = calculateTickValues(-100, 100);\n * // Result:\n * // majorTickStep: 100\n * // minMajorTickValue: -100, maxMajorTickValue: 100\n * // halfTickStep: 50\n * // minorTickStep: 10\n * // calibrationMultiplier: 1\n *\n * // Zoomed in view: 0.001 to 0.01\n * const zoomedTicks = calculateTickValues(0.001, 0.01);\n * // Result:\n * // majorTickStep: 10 (calibrated)\n * // calibrationMultiplier: 0.001 (multiply tick values by this)\n * ```\n *\n * @category Drawing Utilities\n * @see {@link calculateOrderOfMagnitude} for order calculation\n * @see {@link drawRuler} for usage in ruler drawing\n */\nexport function calculateTickValues(minValue: number, maxValue: number, orderOfMagnitude?: number){\n const trueMinValue = Math.min(minValue, maxValue);\n const trueMaxValue = Math.max(minValue, maxValue);\n\n const width = trueMaxValue - trueMinValue;\n const trueOrderOfMagnitude = orderOfMagnitude ? orderOfMagnitude : calculateOrderOfMagnitude(width);\n\n const normalizedOrderOfMagnitude = Math.max(1, trueOrderOfMagnitude);\n const calibrationMultiplier = Math.pow(10, normalizedOrderOfMagnitude - trueOrderOfMagnitude); // this is the multiplier to calibrate the ruler to the correct length\n\n const minMajorTickMultiplier = \n minValue > 0 ? \n Math.floor(trueMinValue * calibrationMultiplier / Math.pow(10, normalizedOrderOfMagnitude)) : \n Math.ceil(trueMinValue * calibrationMultiplier / Math.pow(10, normalizedOrderOfMagnitude));\n const minMajorTickValue = minMajorTickMultiplier * Math.pow(10, normalizedOrderOfMagnitude);\n const maxMajorTickMultiplier = \n maxValue > 0 ? \n Math.floor(trueMaxValue * calibrationMultiplier / Math.pow(10, normalizedOrderOfMagnitude)) : \n Math.ceil(trueMaxValue * calibrationMultiplier / Math.pow(10, normalizedOrderOfMagnitude));\n const maxMajorTickValue = maxMajorTickMultiplier * Math.pow(10, normalizedOrderOfMagnitude);\n const majorTickStep = Math.pow(10, normalizedOrderOfMagnitude);\n\n // minor tick\n const minTickOrderOfMagnitude = normalizedOrderOfMagnitude - 1;\n const minMinTickMultiplier = \n minValue > 0 ? \n Math.floor(trueMinValue * calibrationMultiplier / Math.pow(10, minTickOrderOfMagnitude)) : \n Math.ceil(trueMinValue * calibrationMultiplier / Math.pow(10, minTickOrderOfMagnitude));\n const minMinTickValue = minMinTickMultiplier * Math.pow(10, minTickOrderOfMagnitude);\n const maxMaxTickMultiplier = \n maxValue > 0 ? \n Math.floor(trueMaxValue * calibrationMultiplier / Math.pow(10, minTickOrderOfMagnitude)) :\n Math.ceil(trueMaxValue * calibrationMultiplier / Math.pow(10, minTickOrderOfMagnitude));\n const maxMaxTickValue = maxMaxTickMultiplier * Math.pow(10, minTickOrderOfMagnitude);\n const minTickStep = Math.pow(10, minTickOrderOfMagnitude);\n\n const halfTickStep = majorTickStep / 2;\n const minHalfTickMultiplier = \n minValue > 0 ? \n Math.floor(trueMinValue * calibrationMultiplier / halfTickStep) : \n Math.ceil(trueMinValue * calibrationMultiplier / halfTickStep);\n const minHalfTickValue = minHalfTickMultiplier * halfTickStep;\n const maxHalfTickMultiplier = \n maxValue > 0 ? \n Math.floor(trueMaxValue * calibrationMultiplier / halfTickStep) : \n Math.ceil(trueMaxValue * calibrationMultiplier / halfTickStep);\n const maxHalfTickValue = maxHalfTickMultiplier * halfTickStep;\n\n return {\n minMajorTickValue,\n maxMajorTickValue,\n majorTickStep,\n minMinTickValue,\n maxMaxTickValue,\n minTickStep,\n minHalfTickValue,\n maxHalfTickValue,\n halfTickStep,\n calibrationMultiplier: 1 / calibrationMultiplier,\n normalizedOrderOfMagnitude,\n }\n}\n",
25
25
  "import { Boundaries, translationHeightOf, translationWidthOf } from \"../camera/utils/position\";\nimport { ZoomLevelLimits } from \"../camera/utils/zoom\";\n\n/**\n * Calculates minimum zoom level to fit boundaries within canvas at any rotation.\n *\n * @param boundaries - The world-space boundaries to fit\n * @param canvasWidth - Canvas width in pixels\n * @param canvasHeight - Canvas height in pixels\n * @param cameraRotation - Camera rotation angle in radians\n * @returns Minimum zoom level, or undefined if boundaries are incomplete\n *\n * @remarks\n * This function ensures the entire boundary region remains visible regardless\n * of camera rotation. It considers both width and height projections of the\n * rotated boundaries.\n *\n * When boundaries are rotated, they occupy a larger axis-aligned bounding box.\n * This function calculates the minimum zoom needed to fit that box:\n *\n * For each dimension (width/height):\n * 1. Project boundary width onto canvas width axis: `width * cos(rotation)`\n * 2. Project boundary height onto canvas width axis: `height * cos(rotation)`\n * 3. Calculate zoom needed for each projection\n * 4. Take the maximum of all zoom levels\n *\n * Returns undefined if boundaries don't have both width and height defined.\n *\n * Used when canvas is resized to automatically adjust zoom to keep content visible.\n *\n * @example\n * ```typescript\n * const boundaries = {\n * min: { x: 0, y: 0 },\n * max: { x: 1000, y: 500 }\n * };\n *\n * // No rotation, 800x600 canvas\n * const zoom1 = minZoomLevelBaseOnDimensions(boundaries, 800, 600, 0);\n * // Result: 1.2 (600/500, height is limiting)\n *\n * // 45 degree rotation\n * const zoom2 = minZoomLevelBaseOnDimensions(\n * boundaries, 800, 600, Math.PI / 4\n * );\n * // Result: higher zoom (rotated bounds need more space)\n * ```\n *\n * @category Camera\n * @see {@link minZoomLevelBaseOnWidth} for width-only calculation\n * @see {@link minZoomLevelBaseOnHeight} for height-only calculation\n */\nexport function minZoomLevelBaseOnDimensions(boundaries: Boundaries | undefined, canvasWidth: number, canvasHeight: number, cameraRotation: number): number | undefined{\n const width = translationWidthOf(boundaries);\n const height = translationHeightOf(boundaries);\n if(width == undefined || height == undefined){\n return undefined;\n }\n // console.log(canvasHeight, canvasWidth);\n const widthWidthProjection = Math.abs(width * Math.cos(cameraRotation));\n const heightWidthProjection = Math.abs(height * Math.cos(cameraRotation));\n const widthHeightProjection = Math.abs(width * Math.sin(cameraRotation));\n const heightHeightProjection = Math.abs(height * Math.sin(cameraRotation));\n let minZoomLevelWidthWidth = canvasWidth / widthWidthProjection;\n let minZoomLevelHeightWidth = canvasWidth / heightWidthProjection;\n let minZoomLevelWidthHeight = canvasHeight / widthHeightProjection;\n let minZoomLevelHeightHeight = canvasHeight / heightHeightProjection;\n if(minZoomLevelWidthWidth == Infinity){\n minZoomLevelWidthWidth = 0;\n }\n if(minZoomLevelHeightWidth == Infinity){\n minZoomLevelHeightWidth = 0;\n }\n if(minZoomLevelWidthHeight == Infinity){\n minZoomLevelWidthHeight = 0;\n }\n if(minZoomLevelHeightHeight == Infinity){\n minZoomLevelHeightHeight = 0;\n }\n\n // console.log(minZoomLevelWidthWidth, minZoomLevelHeightWidth, minZoomLevelWidthHeight, minZoomLevelHeightHeight);\n\n const minZoomLevelHeight = canvasHeight / height;\n const minZoomLevelWidth = canvasWidth / width;\n const minZoomLevel = Math.max(minZoomLevelHeight, minZoomLevelWidth, minZoomLevelWidthWidth, minZoomLevelHeightWidth, minZoomLevelWidthHeight, minZoomLevelHeightHeight);\n return minZoomLevel;\n}\n\n/**\n * Determines if zoom level boundaries should be updated.\n *\n * @param zoomLevelBoundaries - Current zoom level limits\n * @param targetMinZoomLevel - Proposed new minimum zoom level\n * @returns True if boundaries should be updated (type guard for targetMinZoomLevel)\n *\n * @remarks\n * Zoom level boundary updates only tighten (increase minimum zoom), never relax.\n * This prevents the camera from zooming out too far when boundaries shrink.\n *\n * Returns true (update needed) when:\n * - No current boundaries exist (first-time setup)\n * - Target minimum is higher than current minimum (tightening)\n *\n * Returns false (no update) when:\n * - Target is undefined (invalid/incomplete)\n * - Target is Infinity (invalid state)\n * - Target is lower than current minimum (would relax, not allowed)\n *\n * This function is a type guard: when it returns true, TypeScript knows\n * targetMinZoomLevel is a number (not undefined).\n *\n * @example\n * ```typescript\n * const currentLimits = { min: 0.5, max: 10 };\n * const newMin = 0.8;\n *\n * if (zoomLevelBoundariesShouldUpdate(currentLimits, newMin)) {\n * // Safe to use newMin as number here\n * currentLimits.min = newMin; // Tighten the limit\n * }\n *\n * // No update for lower values\n * zoomLevelBoundariesShouldUpdate(currentLimits, 0.3); // false\n * ```\n *\n * @category Camera\n */\nexport function zoomLevelBoundariesShouldUpdate(zoomLevelBoundaries: ZoomLevelLimits | undefined, targetMinZoomLevel: number | undefined): targetMinZoomLevel is number{\n if(targetMinZoomLevel == undefined){\n return false;\n }\n if(zoomLevelBoundaries == undefined){\n return true;\n }\n if(targetMinZoomLevel == Infinity){\n return false;\n }\n if(zoomLevelBoundaries !== undefined && (zoomLevelBoundaries.min == undefined || targetMinZoomLevel > zoomLevelBoundaries.min)){\n return true;\n }\n return false;\n}\n\n/**\n * Calculates minimum zoom level based only on boundary width.\n *\n * @param boundaries - The world-space boundaries\n * @param canvasWidth - Canvas width in pixels\n * @param canvasHeight - Canvas height in pixels\n * @param cameraRotation - Camera rotation angle in radians\n * @returns Minimum zoom level, or undefined if width is not defined\n *\n * @remarks\n * Similar to {@link minZoomLevelBaseOnDimensions} but only considers the\n * width constraint. Useful when height is unbounded or not relevant.\n *\n * Calculates zoom needed to fit the boundary width within the canvas,\n * accounting for rotation:\n * - Width projection on canvas X-axis: `width * cos(rotation)`\n * - Width projection on canvas Y-axis: `width * sin(rotation)`\n *\n * Takes the maximum of these to ensure the width fits regardless of\n * how rotation distributes it across canvas axes.\n *\n * @example\n * ```typescript\n * const boundaries = {\n * min: { x: 0 },\n * max: { x: 1000 }\n * };\n *\n * const zoom = minZoomLevelBaseOnWidth(boundaries, 800, 600, 0);\n * // Result: 0.8 (800/1000)\n * ```\n *\n * @category Camera\n * @see {@link minZoomLevelBaseOnDimensions} for full calculation\n */\nexport function minZoomLevelBaseOnWidth(boundaries: Boundaries | undefined, canvasWidth: number, canvasHeight: number, cameraRotation: number): number | undefined{\n const width = translationWidthOf(boundaries);\n if(width == undefined){\n return undefined;\n }\n const widthWidthProjection = Math.abs(width * Math.cos(cameraRotation));\n const widthHeightProjection = Math.abs(width * Math.sin(cameraRotation));\n const minZoomLevelWidthWidth = canvasWidth / widthWidthProjection;\n const minZoomLevelWidthHeight = canvasHeight / widthHeightProjection;\n if(minZoomLevelWidthWidth == Infinity){\n return minZoomLevelWidthHeight;\n }\n const minZoomLevel = Math.max(canvasWidth / widthWidthProjection, canvasHeight / widthHeightProjection);\n return minZoomLevel;\n}\n\n/**\n * Calculates minimum zoom level based only on boundary height.\n *\n * @param boundaries - The world-space boundaries\n * @param canvasWidth - Canvas width in pixels\n * @param canvasHeight - Canvas height in pixels\n * @param cameraRotation - Camera rotation angle in radians\n * @returns Minimum zoom level, or undefined if height is not defined\n *\n * @remarks\n * Similar to {@link minZoomLevelBaseOnDimensions} but only considers the\n * height constraint. Useful when width is unbounded or not relevant.\n *\n * Calculates zoom needed to fit the boundary height within the canvas,\n * accounting for rotation:\n * - Height projection on canvas X-axis: `height * cos(rotation)`\n * - Height projection on canvas Y-axis: `height * sin(rotation)`\n *\n * Takes the maximum of these to ensure the height fits regardless of\n * how rotation distributes it across canvas axes.\n *\n * @example\n * ```typescript\n * const boundaries = {\n * min: { y: 0 },\n * max: { y: 500 }\n * };\n *\n * const zoom = minZoomLevelBaseOnHeight(boundaries, 800, 600, 0);\n * // Result: 1.2 (600/500)\n * ```\n *\n * @category Camera\n * @see {@link minZoomLevelBaseOnDimensions} for full calculation\n */\nexport function minZoomLevelBaseOnHeight(boundaries: Boundaries | undefined, canvasWidth: number, canvasHeight: number, cameraRotation: number): number | undefined{\n const height = translationHeightOf(boundaries);\n if(height == undefined){\n return undefined;\n }\n const heightWidthProjection = Math.abs(height * Math.cos(cameraRotation));\n const heightHeightProjection = Math.abs(height * Math.sin(cameraRotation));\n const minZoomLevelHeightWidth = canvasWidth / heightWidthProjection;\n const minZoomLevelHeightHeight = canvasHeight / heightHeightProjection;\n if(minZoomLevelHeightHeight == Infinity){\n return minZoomLevelHeightWidth;\n }\n const minZoomLevel = Math.max(minZoomLevelHeightWidth, minZoomLevelHeightHeight);\n return minZoomLevel;\n}\n",
26
- "import { ObservableBoardCamera } from '../camera/interface';\nimport DefaultBoardCamera from '../camera/default-camera';\nimport { halfTranslationHeightOf, halfTranslationWidthOf } from '../camera/utils/position';\nimport { KMTEventParser, VanillaKMTEventParser } from '../input-interpretation/raw-input-parser';\nimport { TouchEventParser, VanillaTouchEventParser } from '../input-interpretation/raw-input-parser';\nimport { Point } from '@ue-too/math';\nimport { reverseYAxis } from '../utils';\nimport { PointCal } from '@ue-too/math';\n\nimport { CameraEventMap, CameraState, UnSubscribe } from '../camera/update-publisher';\nimport { minZoomLevelBaseOnDimensions, minZoomLevelBaseOnWidth, zoomLevelBoundariesShouldUpdate } from '../utils';\nimport { UnsubscribeToUserRawInput, RawUserInputEventMap, RawUserInputPublisher } from '../input-interpretation/raw-input-publisher';\n\nimport { CameraMux, createCameraMuxWithAnimationAndLockWithCameraRig } from '../camera/camera-mux';\nimport { CameraRig, DefaultCameraRig } from '../camera/camera-rig';\nimport { CanvasDimensions, CanvasProxy, createKmtInputStateMachine, createTouchInputStateMachine, ObservableInputTracker, TouchInputTracker } from '../input-interpretation/input-state-machine';\nimport { InputOrchestrator } from '../input-interpretation/input-orchestrator';\n\n/**\n * Main user-facing API class that provides an infinite canvas with pan, zoom, and rotate capabilities.\n *\n * The Board class is the primary entry point for using the board package. It integrates all subsystems\n * including camera management, input handling, and state machines into a simple, unified API for\n * creating interactive 2D canvases with advanced camera controls.\n *\n * @remarks\n * ## Architecture Overview\n *\n * The Board class orchestrates several subsystems:\n *\n * - **Camera System**: Manages viewport transformations (pan/zoom/rotate) through {@link ObservableBoardCamera}.\n * The camera can be configured with boundaries, zoom limits, and various movement constraints.\n *\n * - **Input System**: Processes user input through state machines for both mouse/keyboard/trackpad (KMT)\n * and touch events. Input is parsed, interpreted, and translated into camera movements.\n *\n * - **Camera Rig**: Enforces constraints and restrictions on camera movement (boundaries, zoom limits,\n * clamping behavior). See {@link CameraRig} for details.\n *\n * - **Camera Multiplexer**: Coordinates between different camera control sources (user input, animations,\n * programmatic control) to ensure smooth transitions. See {@link CameraMux} for details.\n *\n * ## Coordinate Systems\n *\n * The Board supports three coordinate systems:\n *\n * 1. **World Coordinates**: The infinite canvas space where your content lives. When the camera is at\n * position (0, 0) with no zoom or rotation, world coordinates map directly to viewport coordinates.\n *\n * 2. **Viewport Coordinates**: The visible area of the canvas relative to the camera center. The camera\n * center is at (0, 0) in viewport space, with coordinates extending in both directions based on the\n * canvas size.\n *\n * 3. **Window/Canvas Coordinates**: The browser's coordinate system, with (0, 0) at the top-left corner\n * of the canvas element. Use {@link convertWindowPoint2WorldCoord} to convert from window to world space.\n *\n * By default, {@link alignCoordinateSystem} is `true`, which means the Y-axis points down (standard HTML\n * canvas orientation). Set it to `false` to use a mathematical coordinate system where Y points up.\n *\n * ## Main Features\n *\n * - **Camera Control**: Pan, zoom, and rotate the viewport through user input or programmatic API\n * - **Boundaries**: Define world-space boundaries to constrain camera movement\n * - **Zoom Limits**: Set minimum and maximum zoom levels\n * - **Input Modes**: Support for mouse/keyboard/trackpad and touch input with customizable parsers\n * - **Event System**: Subscribe to camera events (pan, zoom, rotate) and input events\n * - **Coordinate Conversion**: Convert between window and world coordinates\n * - **Flexible Configuration**: Extensive options for restricting/clamping camera movement\n *\n * @example\n * Basic setup with drawing\n * ```typescript\n * const canvasElement = document.querySelector(\"canvas\") as HTMLCanvasElement;\n * const board = new Board(canvasElement);\n *\n * function draw(timestamp: number) {\n * board.step(timestamp);\n *\n * // Because board can be initialized without a canvas element,\n * // the context can be undefined until the canvas is attached\n * if(board.context == undefined) {\n * return;\n * }\n *\n * // Draw after the board has stepped\n * // The coordinate system has (0, 0) at the center of the canvas when camera position is at (0, 0)\n * board.context.beginPath();\n * board.context.rect(0, 0, 100, 100);\n * board.context.fill();\n *\n * requestAnimationFrame(draw);\n * }\n *\n * requestAnimationFrame(draw);\n * ```\n *\n * @example\n * Handling camera and input events\n * ```typescript\n * const board = new Board(canvasElement);\n *\n * // Listen to camera pan events\n * board.on('pan', (event, cameraState) => {\n * console.log('Camera panned to:', cameraState.position);\n * });\n *\n * // Listen to camera zoom events\n * board.on('zoom', (event, cameraState) => {\n * console.log('Camera zoom level:', cameraState.zoomLevel);\n * });\n *\n * // Listen to raw input events (before camera movement)\n * board.onInput('pan', (event) => {\n * console.log('User is panning');\n * });\n * ```\n *\n * @example\n * Configuring boundaries and zoom limits\n * ```typescript\n * const board = new Board(canvasElement);\n *\n * // Set world boundaries\n * board.camera.boundaries = {\n * min: { x: -1000, y: -1000 },\n * max: { x: 1000, y: 1000 }\n * };\n *\n * // Set zoom limits\n * board.camera.setMinZoomLevel(0.1);\n * board.camera.setMaxZoomLevel(5.0);\n *\n * // Ensure entire viewport stays within boundaries\n * board.limitEntireViewPort = true;\n *\n * // Clamp camera position to boundaries\n * board.clampTranslation = true;\n * board.clampZoom = true;\n * ```\n *\n * @example\n * Converting window coordinates to world coordinates\n * ```typescript\n * const board = new Board(canvasElement);\n *\n * canvasElement.addEventListener('click', (event) => {\n * const windowPoint = { x: event.clientX, y: event.clientY };\n * const worldPoint = board.convertWindowPoint2WorldCoord(windowPoint);\n * console.log('Clicked at world position:', worldPoint);\n * });\n * ```\n *\n * @example\n * Using fullscreen mode\n * ```typescript\n * const board = new Board();\n * board.fullScreen = true; // Canvas will resize with window\n *\n * // Attach canvas later\n * const canvasElement = document.createElement('canvas');\n * document.body.appendChild(canvasElement);\n * board.attach(canvasElement);\n * ```\n *\n * @category Board\n * @see {@link ObservableBoardCamera} for camera API details\n * @see {@link CameraRig} for camera constraint configuration\n * @see {@link CameraMux} for camera control coordination\n */\nexport default class Board {\n \n private _context?: CanvasRenderingContext2D;\n private _reversedContext?: CanvasRenderingContext2D;\n private _canvasProxy: CanvasProxy;\n\n private _kmtParser: KMTEventParser;\n private _touchParser: TouchEventParser;\n\n private _alignCoordinateSystem: boolean = true;\n private _fullScreen: boolean = false;\n \n private cameraRig: CameraRig;\n private _cameraMux: CameraMux;\n private boardInputPublisher: RawUserInputPublisher;\n private _observableInputTracker: ObservableInputTracker;\n private _touchInputTracker: TouchInputTracker;\n private _inputOrchestrator: InputOrchestrator;\n\n private lastUpdateTime: number = 0;\n\n /**\n * Creates a new Board instance with an optional canvas element.\n *\n * The constructor initializes all subsystems including the camera, input parsers, state machines,\n * and event publishers. The board can be created with or without a canvas element - if no canvas\n * is provided, you can attach one later using {@link attach}.\n *\n * @param canvas - Optional HTMLCanvasElement to attach to the board. If provided, the board will\n * immediately initialize with this canvas. If omitted, you must call {@link attach} before the\n * board can be used.\n * @param debug - Optional debug flag that enables `willReadFrequently` hint on the canvas context,\n * which optimizes the canvas for frequent readback operations. Default is `false`. Only use this\n * if you need to frequently read pixel data from the canvas.\n *\n * @remarks\n * ## Initialization Sequence\n *\n * When the constructor is called, it performs the following initialization:\n *\n * 1. **Camera Setup**: Creates a {@link DefaultBoardCamera} with default boundaries of ±50,000 units\n * in both X and Y directions. This provides a large working area for most use cases.\n *\n * 2. **Canvas Proxy**: Initializes a {@link CanvasProxy} that observes canvas dimension changes and\n * automatically updates the camera's viewport dimensions.\n *\n * 3. **Camera Rig**: Creates a {@link CameraRig} with default configuration:\n * - `limitEntireViewPort: true` - Entire viewport is constrained within boundaries\n * - `clampTranslation: true` - Camera position is clamped to boundaries\n * - `clampZoom: true` - Zoom level is clamped to min/max limits\n * - All translation restrictions are disabled by default\n *\n * 4. **Input System**: Initializes both keyboard/mouse/trackpad (KMT) and touch input parsers,\n * state machines, and the input orchestrator that coordinates camera control.\n *\n * 5. **Canvas Attachment** (if canvas provided): If a canvas element is provided, it's immediately\n * attached and the viewport dimensions are synchronized with the canvas size.\n *\n * ## Default Configuration\n *\n * The board is created with sensible defaults:\n * - World boundaries: (-50000, -50000) to (50000, 50000)\n * - Coordinate system: Aligned with HTML canvas (Y-axis points down)\n * - Camera position: (0, 0)\n * - Zoom level: 1.0\n * - Rotation: 0 radians\n * - Full screen: disabled\n *\n * You can customize these defaults after construction by setting properties on the board or camera.\n *\n * @example\n * Create board with canvas element\n * ```typescript\n * const canvas = document.querySelector('canvas') as HTMLCanvasElement;\n * const board = new Board(canvas);\n * // Board is ready to use immediately\n * ```\n *\n * @example\n * Create board without canvas, attach later\n * ```typescript\n * const board = new Board();\n * // ... later, when canvas is ready\n * const canvas = document.createElement('canvas');\n * document.body.appendChild(canvas);\n * board.attach(canvas);\n * ```\n *\n * @example\n * Enable debug mode for pixel readback\n * ```typescript\n * const board = new Board(canvas, true);\n * // Now getImageData() and similar operations will be optimized\n * ```\n *\n * @group LifeCycle\n * @see {@link attach} for attaching a canvas after construction\n * @see {@link tearDown} for cleanup when done with the board\n */\n constructor(canvas?: HTMLCanvasElement, debug: boolean = false){\n const camera = new DefaultBoardCamera();\n const bound = 50000;\n camera.boundaries = {min: {x: -bound, y: -bound}, max: {x: bound, y: bound}};\n\n this.bindFunctions();\n\n this._canvasProxy = new CanvasProxy(canvas);\n\n this._canvasProxy.subscribe((canvasDimensions)=>{\n this.syncViewPortDimensions(canvasDimensions);\n this.syncCameraZoomLevel(canvasDimensions);\n });\n\n this.cameraRig = new DefaultCameraRig({\n limitEntireViewPort: true,\n restrictRelativeXTranslation: false,\n restrictRelativeYTranslation: false,\n restrictXTranslation: false,\n restrictYTranslation: false,\n restrictZoom: false,\n clampTranslation: true,\n clampZoom: true,\n }, camera);\n\n this._cameraMux = createCameraMuxWithAnimationAndLockWithCameraRig(this.cameraRig);\n this.boardInputPublisher = new RawUserInputPublisher();\n\n // this._edgeAutoCameraInput = new EdgeAutoCameraInput(this._cameraMux);\n this._observableInputTracker = new ObservableInputTracker(this._canvasProxy);\n this._touchInputTracker = new TouchInputTracker(this._canvasProxy);\n\n const kmtInputStateMachine = createKmtInputStateMachine(this._observableInputTracker);\n const touchInputStateMachine = createTouchInputStateMachine(this._touchInputTracker);\n\n // Create single orchestrator as the point of camera control for both KMT and touch inputs\n // Since both state machines output the same event types (pan, zoom), one orchestrator handles both\n // Orchestrator receives CameraRig to execute camera operations when CameraMux allows passthrough\n this._inputOrchestrator = new InputOrchestrator(this._cameraMux, this.cameraRig, this.boardInputPublisher);\n\n // Parsers have direct dependency on state machines, shared orchestrator processes outputs and controls camera\n this._kmtParser = new VanillaKMTEventParser(kmtInputStateMachine, this._inputOrchestrator, canvas);\n this._touchParser = new VanillaTouchEventParser(touchInputStateMachine, this._inputOrchestrator, canvas);\n\n if(canvas != undefined){\n console.log('canvas exists on creation of board');\n this.attach(canvas, debug);\n this.syncViewPortDimensions({width: canvas.width, height: canvas.height});\n }\n }\n\n private syncViewPortDimensions(canvasDimensions: {width: number, height: number}){\n this.camera.viewPortHeight = canvasDimensions.height;\n this.camera.viewPortWidth = canvasDimensions.width;\n }\n\n /**\n * Attaches a canvas element to the board, enabling rendering and input handling.\n *\n * This method connects a canvas element to the board's rendering and input systems. It must be\n * called before the board can be used if no canvas was provided to the constructor. If a canvas\n * was already attached, this method will replace it with the new canvas.\n *\n * @param canvas - The HTMLCanvasElement to attach to the board. This canvas will be used for\n * rendering and will receive all input events.\n * @param debug - Optional debug flag that enables `willReadFrequently` hint on the canvas context.\n * Default is `false`. Set to `true` if you need to frequently read pixel data from the canvas,\n * which will optimize the context for readback operations.\n *\n * @remarks\n * When a canvas is attached, the following happens:\n *\n * 1. **Context Creation**: A 2D rendering context is obtained from the canvas with the specified\n * debug settings.\n *\n * 2. **Input Parser Attachment**: Both KMT (keyboard/mouse/trackpad) and touch input parsers are\n * attached to the canvas to begin receiving input events.\n *\n * 3. **Canvas Proxy Attachment**: The canvas proxy begins observing the canvas for dimension changes,\n * automatically updating the camera's viewport dimensions when the canvas is resized.\n *\n * 4. **Zoom Level Synchronization**: If {@link limitEntireViewPort} is enabled, the minimum zoom\n * level is calculated and set to ensure the entire viewport can fit within the camera boundaries.\n *\n * 5. **Coordinate System Setup**: Both standard and Y-reversed rendering contexts are created to\n * support both coordinate system modes (see {@link alignCoordinateSystem}).\n *\n * @example\n * Attach canvas during construction\n * ```typescript\n * const canvas = document.querySelector('canvas') as HTMLCanvasElement;\n * const board = new Board(canvas);\n * // No need to call attach() - already attached\n * ```\n *\n * @example\n * Attach canvas after construction\n * ```typescript\n * const board = new Board();\n *\n * // Later, when canvas is ready...\n * const canvas = document.createElement('canvas');\n * canvas.width = 800;\n * canvas.height = 600;\n * document.body.appendChild(canvas);\n *\n * board.attach(canvas);\n * // Board is now ready to use\n * ```\n *\n * @example\n * Switch to a different canvas\n * ```typescript\n * const board = new Board(canvas1);\n *\n * // Later, switch to a different canvas\n * const canvas2 = document.querySelector('#other-canvas') as HTMLCanvasElement;\n * board.attach(canvas2);\n * // Board is now rendering to canvas2\n * ```\n *\n * @group LifeCycle\n * @see {@link tearDown} for detaching and cleaning up\n * @see {@link context} for accessing the rendering context\n */\n attach(canvas: HTMLCanvasElement, debug: boolean = false){\n const newContext = canvas.getContext('2d', {willReadFrequently: debug});\n if(newContext == null){\n console.error(\"new canvas context is null\");\n return;\n }\n this._kmtParser.attach(canvas);\n this._touchParser.attach(canvas);\n this._canvasProxy.attach(canvas);\n\n if(this.limitEntireViewPort) {\n this.syncCameraZoomLevel(this._canvasProxy.dimensions);\n }\n\n this._context = newContext;\n this._reversedContext = reverseYAxis(this._context);\n }\n\n /**\n * @group LifeCycle\n * @description This function is used to clean up the board. It removes all the event listeners and disconnects the resize observer and the attribute observer. \n */\n tearDown(){\n this._kmtParser.tearDown();\n this._touchParser.tearDown();\n this._canvasProxy.tearDown();\n }\n\n private bindFunctions(){\n this.step = this.step.bind(this);\n }\n\n get width(): number {\n return this._canvasProxy.width;\n }\n\n get height(): number {\n return this._canvasProxy.height;\n }\n\n /**\n * @description This is an attribute that determines if the coordinate system should be aligned with the one of the HTML canvas element. The default is true.\n * If you set this to true, the coordinate system will be aligned with the one of the HTML canvas element.\n * If you change this value during runtime, you should update the context to be aligned with the new coordinate system. (just call board.context again)\n */\n set alignCoordinateSystem(align: boolean){\n this._alignCoordinateSystem = align;\n this._observableInputTracker.alignCoordinateSystem = align;\n this._touchInputTracker.alignCoordinateSystem = align;\n }\n\n get alignCoordinateSystem(): boolean{\n return this._alignCoordinateSystem;\n }\n\n /**\n * @description Determines if the board should be full screen. If this is set to true, the width and height of the board will be set to the window's inner width and inner height respectively, \n * and the width and height of the board will resize with the window.\n */\n get fullScreen(): boolean {\n return this._fullScreen;\n }\n\n set fullScreen(value: boolean) {\n this._fullScreen = value;\n if(this._fullScreen){\n this._canvasProxy.setWidth(window.innerWidth);\n this._canvasProxy.setHeight(window.innerHeight);\n }\n }\n\n /**\n * @description The context used to draw on the canvas.\n * If alignCoordinateSystem is false, this returns a proxy that automatically negates y-coordinates for relevant drawing methods.\n */\n get context(): CanvasRenderingContext2D | undefined {\n if (!this._alignCoordinateSystem) {\n return this._reversedContext;\n }\n return this._context;\n }\n\n /**\n * @description Determines the behavior of the camera when the camera is at the edge of the boundaries. If set to true, the entire view port would not move beyond the boundaries.\n * If set to false, only the center of the camera is bounded by the boundaries.\n */\n set limitEntireViewPort(value: boolean){\n this.cameraRig.config.limitEntireViewPort = value;\n if(this._canvasProxy.detached){\n return;\n }\n if(value){\n this.syncCameraZoomLevel(this._canvasProxy.dimensions);\n }\n }\n\n get limitEntireViewPort(): boolean{\n return this.cameraRig.config.limitEntireViewPort;\n }\n\n /**\n * @description The strategy used to handle the keyboard, mouse events. The default strategy is the DefaultBoardKMTStrategy. \n * You can implement your own strategy by implementing the BoardKMTStrategy interface.\n */\n set kmtParser(parser: KMTEventParser){\n this._kmtParser.tearDown();\n parser.setUp();\n this._kmtParser = parser;\n }\n\n get kmtParser(): KMTEventParser{\n return this._kmtParser;\n }\n\n /**\n * @description The parser used to handle touch events. The default parser is the DefaultTouchParser.\n * You can have your own parser by implementing the BoardTouchParser interface.\n */\n set touchParser(parser: TouchEventParser){\n this._touchParser.tearDown();\n parser.setUp();\n this._touchParser = parser;\n }\n\n get touchParser(): TouchEventParser{\n return this._touchParser;\n }\n\n /**\n * @description The underlying camera of the board. The camera of the board can be switched.\n * The boundaries are based on camera meaning you can have cameras with different boundaries, and you can switch between them during runtime.\n */\n get camera(): ObservableBoardCamera{\n return this.cameraRig.camera;\n }\n\n set camera(camera: ObservableBoardCamera){\n if(!this._canvasProxy.detached){\n camera.viewPortHeight = this._canvasProxy.height / window.devicePixelRatio;\n camera.viewPortWidth = this._canvasProxy.width / window.devicePixelRatio;\n }\n this.cameraRig.camera = camera;\n }\n\n get cameraMux(): CameraMux{\n return this._cameraMux;\n }\n\n set cameraMux(cameraMux: CameraMux){\n this._cameraMux = cameraMux;\n // Update all components that depend on cameraMux\n // Note: TouchInputTracker and Orchestrator would need to be recreated or have setter methods\n\n // input orchestrator\n this._inputOrchestrator.cameraMux = cameraMux;\n }\n\n /**\n * @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.\n * @param timestamp \n */\n public step(timestamp: number){\n if(this._canvasProxy.detached || this._context == undefined){\n return;\n }\n\n this.cameraRig.update();\n let deltaTime = timestamp - this.lastUpdateTime;\n this.lastUpdateTime = timestamp;\n deltaTime = deltaTime / 1000;\n\n this._context.reset();\n this._context.clearRect(0, 0, this._canvasProxy.width * window.devicePixelRatio, this._canvasProxy.height * window.devicePixelRatio);\n\n if(this._fullScreen && (this._canvasProxy.width != window.innerWidth || this._canvasProxy.height != window.innerHeight)){\n this._canvasProxy.setWidth(window.innerWidth);\n this._canvasProxy.setHeight(window.innerHeight);\n }\n\n const transfromMatrix = this.camera.getTransform(window.devicePixelRatio, this._alignCoordinateSystem);\n this._context.setTransform(transfromMatrix.a, transfromMatrix.b, transfromMatrix.c, transfromMatrix.d, transfromMatrix.e, transfromMatrix.f);\n }\n\n /**\n * 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)\n * @description Converts a point from window coordinates to world coordinates.\n * @param clickPointInWindow The point in window coordinates to convert.\n * @returns The converted point in world coordinates.\n */\n convertWindowPoint2WorldCoord(clickPointInWindow: Point): Point {\n const boundingRect = this._canvasProxy.dimensions;\n const cameraCenterInWindow = {x: boundingRect.position.x + boundingRect.width / 2, y: boundingRect.position.y + boundingRect.height / 2};\n const pointInViewPort = PointCal.subVector(clickPointInWindow, cameraCenterInWindow);\n if(!this._alignCoordinateSystem){\n pointInViewPort.y = -pointInViewPort.y;\n }\n return this.camera.convertFromViewPort2WorldSpace(pointInViewPort);\n }\n\n /**\n * @description Add an camera movement event listener. The events are \"pan\", \"zoom\", and \"rotate\".\n * There's also an \"all\" event that will be triggered when any of the above events are triggered.\n * @param eventName The event name to listen for. The events are \"pan\", \"zoom\", and \"rotate\".\n * @param callback The callback function to call when the event is triggered. The event provided to the callback is different for the different events.\n * @returns The converted point in world coordinates.\n */\n on<K extends keyof CameraEventMap>(eventName: K, callback: (event: CameraEventMap[K], cameraState: CameraState)=>void): UnSubscribe {\n return this.camera.on(eventName, callback);\n }\n\n /**\n * @description Add an input event listener. The events are \"pan\", \"zoom\", and \"rotate\". This is different from the camera event listener as this is for input events. \n * There's also an \"all\" event that will be triggered when any of the above events are triggered.\n * Input event does not necesarily mean that the camera will move. The input events are the events triggered when the user interacts with the board.\n * @param eventName \n * @param callback \n * @returns \n */\n onInput<K extends keyof RawUserInputEventMap>(eventName: K, callback: (event: RawUserInputEventMap[K])=> void): UnsubscribeToUserRawInput {\n return this.boardInputPublisher.on(eventName, callback);\n }\n\n /**\n * @description The max translation height of the camera. This is the maximum distance the camera can move in the vertical direction.\n */\n get maxHalfTransHeight(): number | undefined{\n return halfTranslationHeightOf(this.camera.boundaries);\n }\n\n /**\n * @description The max translation width of the camera. This is the maximum distance the camera can move in the horizontal direction.\n */\n get maxHalfTransWidth(): number | undefined{\n return halfTranslationWidthOf(this.camera.boundaries);\n }\n\n private syncCameraZoomLevel(canvasDimensions: CanvasDimensions){\n if(this.limitEntireViewPort){\n const targetMinZoomLevel = minZoomLevelBaseOnDimensions(this.camera.boundaries, canvasDimensions.width, canvasDimensions.height, this.camera.rotation);\n if(targetMinZoomLevel != undefined && zoomLevelBoundariesShouldUpdate(this.camera.zoomBoundaries, targetMinZoomLevel)){\n this.camera.setMinZoomLevel(targetMinZoomLevel);\n }\n }\n\n }\n\n /**\n * @group Helper Methods\n * @description This function sets the max translation width of the camera while fixing the minimum x boundary.\n */\n setMaxTransWidthWithFixedMinBoundary(value: number){\n const curBoundaries = this.camera.boundaries;\n const curMin = curBoundaries == undefined ? undefined: curBoundaries.min;\n const curHorizontalMin = curMin == undefined ? undefined: curMin.x;\n if(curHorizontalMin == undefined){\n this.camera.setHorizontalBoundaries(-value, value);\n } else {\n this.camera.setHorizontalBoundaries(curHorizontalMin, curHorizontalMin + value * 2);\n }\n if(this.limitEntireViewPort){\n const targetMinZoomLevel = minZoomLevelBaseOnWidth(this.camera.boundaries, this.camera.viewPortWidth, this.camera.viewPortHeight, this.camera.rotation);\n if(zoomLevelBoundariesShouldUpdate(this.camera.zoomBoundaries, targetMinZoomLevel)){\n this.camera.setMinZoomLevel(targetMinZoomLevel);\n }\n }\n }\n\n /**\n * @group Helper Methods\n * @description This function sets the max translation width of the camera while fixing the minimum x boundary.\n */\n setMaxTransWidthWithFixedMaxBoundary(value: number){\n const curBoundaries = this.camera.boundaries;\n const curMax = curBoundaries == undefined ? undefined: curBoundaries.max;\n const curHorizontalMax = curMax == undefined ? undefined: curMax.x;\n if(curHorizontalMax == undefined){\n this.camera.setHorizontalBoundaries(-value, value);\n } else {\n this.camera.setHorizontalBoundaries(curHorizontalMax - value * 2, curHorizontalMax);\n }\n if(this.limitEntireViewPort){\n const targetMinZoomLevel = minZoomLevelBaseOnWidth(this.camera.boundaries, this.camera.viewPortWidth, this.camera.viewPortHeight, this.camera.rotation);\n if(zoomLevelBoundariesShouldUpdate(this.camera.zoomBoundaries, targetMinZoomLevel)){\n this.camera.setMinZoomLevel(targetMinZoomLevel);\n }\n }\n }\n\n get restrictRelativeXTranslation(): boolean{\n return this.cameraRig.config.restrictRelativeXTranslation;\n }\n\n get restrictRelativeYTranslation(): boolean{\n return this.cameraRig.config.restrictRelativeYTranslation;\n }\n\n get restrictXTranslation(): boolean{\n return this.cameraRig.config.restrictXTranslation;\n }\n\n get restrictYTranslation(): boolean{\n return this.cameraRig.config.restrictYTranslation;\n }\n \n set restrictRelativeXTranslation(value: boolean){\n this.cameraRig.config.restrictRelativeXTranslation = value;\n }\n\n set restrictRelativeYTranslation(value: boolean){\n this.cameraRig.configure({restrictRelativeYTranslation: value});\n }\n\n set restrictXTranslation(value: boolean){\n this.cameraRig.configure({restrictXTranslation: value});\n }\n \n set restrictYTranslation(value: boolean){\n this.cameraRig.configure({restrictYTranslation: value});\n }\n\n get restrictZoom(): boolean{\n return this.cameraRig.config.restrictZoom;\n }\n\n set restrictZoom(value: boolean){\n this.cameraRig.configure({restrictZoom: value});\n }\n\n get restrictRotation(): boolean{\n return this.cameraRig.config.restrictRotation;\n }\n\n set restrictRotation(value: boolean){\n this.cameraRig.configure({restrictRotation: value});\n }\n\n get clampTranslation(): boolean{\n return this.cameraRig.config.clampTranslation;\n }\n\n set clampTranslation(value: boolean){\n this.cameraRig.configure({clampTranslation: value});\n }\n \n get clampZoom(): boolean{\n return this.cameraRig.config.clampZoom;\n }\n\n set clampZoom(value: boolean){\n this.cameraRig.configure({clampZoom: value});\n }\n\n get clampRotation(): boolean{\n return this.cameraRig.config.clampRotation;\n }\n\n set clampRotation(value: boolean){\n this.cameraRig.configure({clampRotation: value});\n }\n\n getCameraRig(): CameraRig {\n return this.cameraRig;\n }\n\n setInputMode(mode: 'kmt' | 'trackpad'): void {\n this._observableInputTracker.setMode(mode);\n }\n}\n",
26
+ "import { ObservableBoardCamera } from '../camera/interface';\nimport DefaultBoardCamera from '../camera/default-camera';\nimport { halfTranslationHeightOf, halfTranslationWidthOf } from '../camera/utils/position';\nimport { KMTEventParser, VanillaKMTEventParser } from '../input-interpretation/raw-input-parser';\nimport { TouchEventParser, VanillaTouchEventParser } from '../input-interpretation/raw-input-parser';\nimport { Point } from '@ue-too/math';\nimport { reverseYAxis } from '../utils';\nimport { PointCal } from '@ue-too/math';\n\nimport { CameraEventMap, CameraState, UnSubscribe } from '../camera/update-publisher';\nimport { minZoomLevelBaseOnDimensions, minZoomLevelBaseOnWidth, zoomLevelBoundariesShouldUpdate } from '../utils';\nimport { UnsubscribeToUserRawInput, RawUserInputEventMap, RawUserInputPublisher } from '../input-interpretation/raw-input-publisher';\n\nimport { CameraMux, createCameraMuxWithAnimationAndLock } from '../camera/camera-mux';\nimport { CameraRig, DefaultCameraRig } from '../camera/camera-rig';\nimport { CanvasDimensions, CanvasProxy, createKmtInputStateMachine, createTouchInputStateMachine, ObservableInputTracker, TouchInputTracker } from '../input-interpretation/input-state-machine';\nimport { InputOrchestrator } from '../input-interpretation/input-orchestrator';\n\n/**\n * Main user-facing API class that provides an infinite canvas with pan, zoom, and rotate capabilities.\n *\n * The Board class is the primary entry point for using the board package. It integrates all subsystems\n * including camera management, input handling, and state machines into a simple, unified API for\n * creating interactive 2D canvases with advanced camera controls.\n *\n * @remarks\n * ## Architecture Overview\n *\n * The Board class orchestrates several subsystems:\n *\n * - **Camera System**: Manages viewport transformations (pan/zoom/rotate) through {@link ObservableBoardCamera}.\n * The camera can be configured with boundaries, zoom limits, and various movement constraints.\n *\n * - **Input System**: Processes user input through state machines for both mouse/keyboard/trackpad (KMT)\n * and touch events. Input is parsed, interpreted, and translated into camera movements.\n *\n * - **Camera Rig**: Enforces constraints and restrictions on camera movement (boundaries, zoom limits,\n * clamping behavior). See {@link CameraRig} for details.\n *\n * - **Camera Multiplexer**: Coordinates between different camera control sources (user input, animations,\n * programmatic control) to ensure smooth transitions. See {@link CameraMux} for details.\n *\n * ## Coordinate Systems\n *\n * The Board supports three coordinate systems:\n *\n * 1. **World Coordinates**: The infinite canvas space where your content lives. When the camera is at\n * position (0, 0) with no zoom or rotation, world coordinates map directly to viewport coordinates.\n *\n * 2. **Viewport Coordinates**: The visible area of the canvas relative to the camera center. The camera\n * center is at (0, 0) in viewport space, with coordinates extending in both directions based on the\n * canvas size.\n *\n * 3. **Window/Canvas Coordinates**: The browser's coordinate system, with (0, 0) at the top-left corner\n * of the canvas element. Use {@link convertWindowPoint2WorldCoord} to convert from window to world space.\n *\n * By default, {@link alignCoordinateSystem} is `true`, which means the Y-axis points down (standard HTML\n * canvas orientation). Set it to `false` to use a mathematical coordinate system where Y points up.\n *\n * ## Main Features\n *\n * - **Camera Control**: Pan, zoom, and rotate the viewport through user input or programmatic API\n * - **Boundaries**: Define world-space boundaries to constrain camera movement\n * - **Zoom Limits**: Set minimum and maximum zoom levels\n * - **Input Modes**: Support for mouse/keyboard/trackpad and touch input with customizable parsers\n * - **Event System**: Subscribe to camera events (pan, zoom, rotate) and input events\n * - **Coordinate Conversion**: Convert between window and world coordinates\n * - **Flexible Configuration**: Extensive options for restricting/clamping camera movement\n *\n * @example\n * Basic setup with drawing\n * ```typescript\n * const canvasElement = document.querySelector(\"canvas\") as HTMLCanvasElement;\n * const board = new Board(canvasElement);\n *\n * function draw(timestamp: number) {\n * board.step(timestamp);\n *\n * // Because board can be initialized without a canvas element,\n * // the context can be undefined until the canvas is attached\n * if(board.context == undefined) {\n * return;\n * }\n *\n * // Draw after the board has stepped\n * // The coordinate system has (0, 0) at the center of the canvas when camera position is at (0, 0)\n * board.context.beginPath();\n * board.context.rect(0, 0, 100, 100);\n * board.context.fill();\n *\n * requestAnimationFrame(draw);\n * }\n *\n * requestAnimationFrame(draw);\n * ```\n *\n * @example\n * Handling camera and input events\n * ```typescript\n * const board = new Board(canvasElement);\n *\n * // Listen to camera pan events\n * board.on('pan', (event, cameraState) => {\n * console.log('Camera panned to:', cameraState.position);\n * });\n *\n * // Listen to camera zoom events\n * board.on('zoom', (event, cameraState) => {\n * console.log('Camera zoom level:', cameraState.zoomLevel);\n * });\n *\n * // Listen to raw input events (before camera movement)\n * board.onInput('pan', (event) => {\n * console.log('User is panning');\n * });\n * ```\n *\n * @example\n * Configuring boundaries and zoom limits\n * ```typescript\n * const board = new Board(canvasElement);\n *\n * // Set world boundaries\n * board.camera.boundaries = {\n * min: { x: -1000, y: -1000 },\n * max: { x: 1000, y: 1000 }\n * };\n *\n * // Set zoom limits\n * board.camera.setMinZoomLevel(0.1);\n * board.camera.setMaxZoomLevel(5.0);\n *\n * // Ensure entire viewport stays within boundaries\n * board.limitEntireViewPort = true;\n *\n * // Clamp camera position to boundaries\n * board.clampTranslation = true;\n * board.clampZoom = true;\n * ```\n *\n * @example\n * Converting window coordinates to world coordinates\n * ```typescript\n * const board = new Board(canvasElement);\n *\n * canvasElement.addEventListener('click', (event) => {\n * const windowPoint = { x: event.clientX, y: event.clientY };\n * const worldPoint = board.convertWindowPoint2WorldCoord(windowPoint);\n * console.log('Clicked at world position:', worldPoint);\n * });\n * ```\n *\n * @example\n * Using fullscreen mode\n * ```typescript\n * const board = new Board();\n * board.fullScreen = true; // Canvas will resize with window\n *\n * // Attach canvas later\n * const canvasElement = document.createElement('canvas');\n * document.body.appendChild(canvasElement);\n * board.attach(canvasElement);\n * ```\n *\n * @category Board\n * @see {@link ObservableBoardCamera} for camera API details\n * @see {@link CameraRig} for camera constraint configuration\n * @see {@link CameraMux} for camera control coordination\n */\nexport default class Board {\n \n private _context?: CanvasRenderingContext2D;\n private _reversedContext?: CanvasRenderingContext2D;\n private _canvasProxy: CanvasProxy;\n\n private _kmtParser: KMTEventParser;\n private _touchParser: TouchEventParser;\n\n private _alignCoordinateSystem: boolean = true;\n private _fullScreen: boolean = false;\n \n private cameraRig: CameraRig;\n private _cameraMux: CameraMux;\n private boardInputPublisher: RawUserInputPublisher;\n private _observableInputTracker: ObservableInputTracker;\n private _touchInputTracker: TouchInputTracker;\n private _inputOrchestrator: InputOrchestrator;\n\n private _cachedCanvasWidth: number = 0;\n private _cachedCanvasHeight: number = 0;\n private _cachedCanvasPixelRatio: number = 1;\n\n private lastUpdateTime: number = 0;\n\n private _canvasElement: HTMLCanvasElement | undefined;\n\n /**\n * Creates a new Board instance with an optional canvas element.\n *\n * The constructor initializes all subsystems including the camera, input parsers, state machines,\n * and event publishers. The board can be created with or without a canvas element - if no canvas\n * is provided, you can attach one later using {@link attach}.\n *\n * @param canvas - Optional HTMLCanvasElement to attach to the board. If provided, the board will\n * immediately initialize with this canvas. If omitted, you must call {@link attach} before the\n * board can be used.\n * @param debug - Optional debug flag that enables `willReadFrequently` hint on the canvas context,\n * which optimizes the canvas for frequent readback operations. Default is `false`. Only use this\n * if you need to frequently read pixel data from the canvas.\n *\n * @remarks\n * ## Initialization Sequence\n *\n * When the constructor is called, it performs the following initialization:\n *\n * 1. **Camera Setup**: Creates a {@link DefaultBoardCamera} with default boundaries of ±50,000 units\n * in both X and Y directions. This provides a large working area for most use cases.\n *\n * 2. **Canvas Proxy**: Initializes a {@link CanvasProxy} that observes canvas dimension changes and\n * automatically updates the camera's viewport dimensions.\n *\n * 3. **Camera Rig**: Creates a {@link CameraRig} with default configuration:\n * - `limitEntireViewPort: true` - Entire viewport is constrained within boundaries\n * - `clampTranslation: true` - Camera position is clamped to boundaries\n * - `clampZoom: true` - Zoom level is clamped to min/max limits\n * - All translation restrictions are disabled by default\n *\n * 4. **Input System**: Initializes both keyboard/mouse/trackpad (KMT) and touch input parsers,\n * state machines, and the input orchestrator that coordinates camera control.\n *\n * 5. **Canvas Attachment** (if canvas provided): If a canvas element is provided, it's immediately\n * attached and the viewport dimensions are synchronized with the canvas size.\n *\n * ## Default Configuration\n *\n * The board is created with sensible defaults:\n * - World boundaries: (-50000, -50000) to (50000, 50000)\n * - Coordinate system: Aligned with HTML canvas (Y-axis points down)\n * - Camera position: (0, 0)\n * - Zoom level: 1.0\n * - Rotation: 0 radians\n * - Full screen: disabled\n *\n * You can customize these defaults after construction by setting properties on the board or camera.\n *\n * @example\n * Create board with canvas element\n * ```typescript\n * const canvas = document.querySelector('canvas') as HTMLCanvasElement;\n * const board = new Board(canvas);\n * // Board is ready to use immediately\n * ```\n *\n * @example\n * Create board without canvas, attach later\n * ```typescript\n * const board = new Board();\n * // ... later, when canvas is ready\n * const canvas = document.createElement('canvas');\n * document.body.appendChild(canvas);\n * board.attach(canvas);\n * ```\n *\n * @example\n * Enable debug mode for pixel readback\n * ```typescript\n * const board = new Board(canvas, true);\n * // Now getImageData() and similar operations will be optimized\n * ```\n *\n * @group LifeCycle\n * @see {@link attach} for attaching a canvas after construction\n * @see {@link tearDown} for cleanup when done with the board\n */\n constructor(canvas?: HTMLCanvasElement, debug: boolean = false){\n const camera = new DefaultBoardCamera();\n const bound = 50000;\n camera.boundaries = {min: {x: -bound, y: -bound}, max: {x: bound, y: bound}};\n\n this.bindFunctions();\n\n this._canvasProxy = new CanvasProxy(canvas);\n\n this._canvasProxy.subscribe((canvasDimensions)=>{\n this.syncViewPortDimensions(canvasDimensions);\n this.syncCameraZoomLevel(canvasDimensions);\n });\n\n this.cameraRig = new DefaultCameraRig({\n limitEntireViewPort: true,\n restrictRelativeXTranslation: false,\n restrictRelativeYTranslation: false,\n restrictXTranslation: false,\n restrictYTranslation: false,\n restrictZoom: false,\n clampTranslation: true,\n clampZoom: true,\n }, camera);\n\n this._cameraMux = createCameraMuxWithAnimationAndLock();\n this.boardInputPublisher = new RawUserInputPublisher();\n\n // this._edgeAutoCameraInput = new EdgeAutoCameraInput(this._cameraMux);\n this._observableInputTracker = new ObservableInputTracker(this._canvasProxy);\n this._touchInputTracker = new TouchInputTracker(this._canvasProxy);\n\n const kmtInputStateMachine = createKmtInputStateMachine(this._observableInputTracker);\n const touchInputStateMachine = createTouchInputStateMachine(this._touchInputTracker);\n\n // Create single orchestrator as the point of camera control for both KMT and touch inputs\n // Since both state machines output the same event types (pan, zoom), one orchestrator handles both\n // Orchestrator receives CameraRig to execute camera operations when CameraMux allows passthrough\n this._inputOrchestrator = new InputOrchestrator(this._cameraMux, this.cameraRig, this.boardInputPublisher);\n\n // Parsers have direct dependency on state machines, shared orchestrator processes outputs and controls camera\n this._kmtParser = new VanillaKMTEventParser(kmtInputStateMachine, this._inputOrchestrator, canvas);\n this._touchParser = new VanillaTouchEventParser(touchInputStateMachine, this._inputOrchestrator, canvas);\n\n if(canvas != undefined){\n console.log('canvas exists on creation of board');\n this.attach(canvas, debug);\n }\n }\n\n private syncViewPortDimensions(canvasDimensions: {width: number, height: number}){\n this.camera.viewPortHeight = canvasDimensions.height;\n this.camera.viewPortWidth = canvasDimensions.width;\n }\n\n /**\n * Attaches a canvas element to the board, enabling rendering and input handling.\n *\n * This method connects a canvas element to the board's rendering and input systems. It must be\n * called before the board can be used if no canvas was provided to the constructor. If a canvas\n * was already attached, this method will replace it with the new canvas.\n *\n * @param canvas - The HTMLCanvasElement to attach to the board. This canvas will be used for\n * rendering and will receive all input events.\n * @param debug - Optional debug flag that enables `willReadFrequently` hint on the canvas context.\n * Default is `false`. Set to `true` if you need to frequently read pixel data from the canvas,\n * which will optimize the context for readback operations.\n *\n * @remarks\n * When a canvas is attached, the following happens:\n *\n * 1. **Context Creation**: A 2D rendering context is obtained from the canvas with the specified\n * debug settings.\n *\n * 2. **Input Parser Attachment**: Both KMT (keyboard/mouse/trackpad) and touch input parsers are\n * attached to the canvas to begin receiving input events.\n *\n * 3. **Canvas Proxy Attachment**: The canvas proxy begins observing the canvas for dimension changes,\n * automatically updating the camera's viewport dimensions when the canvas is resized.\n *\n * 4. **Zoom Level Synchronization**: If {@link limitEntireViewPort} is enabled, the minimum zoom\n * level is calculated and set to ensure the entire viewport can fit within the camera boundaries.\n *\n * 5. **Coordinate System Setup**: Both standard and Y-reversed rendering contexts are created to\n * support both coordinate system modes (see {@link alignCoordinateSystem}).\n *\n * @example\n * Attach canvas during construction\n * ```typescript\n * const canvas = document.querySelector('canvas') as HTMLCanvasElement;\n * const board = new Board(canvas);\n * // No need to call attach() - already attached\n * ```\n *\n * @example\n * Attach canvas after construction\n * ```typescript\n * const board = new Board();\n *\n * // Later, when canvas is ready...\n * const canvas = document.createElement('canvas');\n * canvas.width = 800;\n * canvas.height = 600;\n * document.body.appendChild(canvas);\n *\n * board.attach(canvas);\n * // Board is now ready to use\n * ```\n *\n * @example\n * Switch to a different canvas\n * ```typescript\n * const board = new Board(canvas1);\n *\n * // Later, switch to a different canvas\n * const canvas2 = document.querySelector('#other-canvas') as HTMLCanvasElement;\n * board.attach(canvas2);\n * // Board is now rendering to canvas2\n * ```\n *\n * @group LifeCycle\n * @see {@link tearDown} for detaching and cleaning up\n * @see {@link context} for accessing the rendering context\n */\n attach(canvas: HTMLCanvasElement, debug: boolean = false){\n this._canvasElement = canvas;\n const newContext = canvas.getContext('2d', {willReadFrequently: debug});\n if(newContext == null){\n console.error(\"new canvas context is null\");\n return;\n }\n this._kmtParser.attach(canvas);\n this._touchParser.attach(canvas);\n this._canvasProxy.attach(canvas);\n\n if(this.limitEntireViewPort) {\n this.syncCameraZoomLevel(this._canvasProxy.dimensions);\n }\n\n this._context = newContext;\n this._reversedContext = reverseYAxis(this._context);\n }\n\n disableEventListeners(){\n this._kmtParser.tearDown();\n this._touchParser.tearDown();\n }\n\n enableEventListeners(){\n this._kmtParser.setUp();\n this._touchParser.setUp();\n }\n\n get inputOrchestrator(): InputOrchestrator{\n return this._inputOrchestrator;\n }\n\n /**\n * @group LifeCycle\n * @description This function is used to clean up the board. It removes all the event listeners and disconnects the resize observer and the attribute observer. \n */\n tearDown(){\n this._kmtParser.tearDown();\n this._touchParser.tearDown();\n this._canvasProxy.tearDown();\n }\n\n private bindFunctions(){\n this.step = this.step.bind(this);\n }\n\n get width(): number {\n return this._canvasProxy.width;\n }\n\n get height(): number {\n return this._canvasProxy.height;\n }\n\n /**\n * @description This is an attribute that determines if the coordinate system should be aligned with the one of the HTML canvas element. The default is true.\n * If you set this to true, the coordinate system will be aligned with the one of the HTML canvas element.\n * If you change this value during runtime, you should update the context to be aligned with the new coordinate system. (just call board.context again)\n */\n set alignCoordinateSystem(align: boolean){\n this._alignCoordinateSystem = align;\n this._observableInputTracker.alignCoordinateSystem = align;\n this._touchInputTracker.alignCoordinateSystem = align;\n }\n\n get alignCoordinateSystem(): boolean{\n return this._alignCoordinateSystem;\n }\n\n /**\n * @description Determines if the board should be full screen. If this is set to true, the width and height of the board will be set to the window's inner width and inner height respectively, \n * and the width and height of the board will resize with the window.\n */\n get fullScreen(): boolean {\n return this._fullScreen;\n }\n\n set fullScreen(value: boolean) {\n this._fullScreen = value;\n if(this._fullScreen){\n this._canvasProxy.setWidth(window.innerWidth);\n this._canvasProxy.setHeight(window.innerHeight);\n }\n }\n\n /**\n * @description The context used to draw on the canvas.\n * If alignCoordinateSystem is false, this returns a proxy that automatically negates y-coordinates for relevant drawing methods.\n */\n get context(): CanvasRenderingContext2D | undefined {\n if (!this._alignCoordinateSystem) {\n return this._reversedContext;\n }\n return this._context;\n }\n\n /**\n * @description Determines the behavior of the camera when the camera is at the edge of the boundaries. If set to true, the entire view port would not move beyond the boundaries.\n * If set to false, only the center of the camera is bounded by the boundaries.\n */\n set limitEntireViewPort(value: boolean){\n this.cameraRig.config.limitEntireViewPort = value;\n if(this._canvasProxy.detached){\n return;\n }\n if(value){\n this.syncCameraZoomLevel(this._canvasProxy.dimensions);\n }\n }\n\n get limitEntireViewPort(): boolean{\n return this.cameraRig.config.limitEntireViewPort;\n }\n\n /**\n * @description The strategy used to handle the keyboard, mouse events. The default strategy is the DefaultBoardKMTStrategy. \n * You can implement your own strategy by implementing the BoardKMTStrategy interface.\n */\n set kmtParser(parser: KMTEventParser){\n this._kmtParser.tearDown();\n parser.tearDown();\n if(this._canvasElement != undefined){\n parser.attach(this._canvasElement);\n }\n parser.setUp();\n this._kmtParser = parser;\n }\n\n get kmtParser(): KMTEventParser{\n return this._kmtParser;\n }\n\n /**\n * @description The parser used to handle touch events. The default parser is the DefaultTouchParser.\n * You can have your own parser by implementing the BoardTouchParser interface.\n */\n set touchParser(parser: TouchEventParser){\n this._touchParser.tearDown();\n parser.tearDown();\n if(this._canvasElement != undefined){\n parser.attach(this._canvasElement);\n }\n parser.setUp();\n this._touchParser = parser;\n }\n\n get touchParser(): TouchEventParser{\n return this._touchParser;\n }\n\n /**\n * @description The underlying camera of the board. The camera of the board can be switched.\n * The boundaries are based on camera meaning you can have cameras with different boundaries, and you can switch between them during runtime.\n */\n get camera(): ObservableBoardCamera{\n return this.cameraRig.camera;\n }\n\n set camera(camera: ObservableBoardCamera){\n if(!this._canvasProxy.detached){\n camera.viewPortHeight = this._canvasProxy.height / window.devicePixelRatio;\n camera.viewPortWidth = this._canvasProxy.width / window.devicePixelRatio;\n }\n this.cameraRig.camera = camera;\n }\n\n get cameraMux(): CameraMux{\n return this._cameraMux;\n }\n\n set cameraMux(cameraMux: CameraMux){\n this._cameraMux = cameraMux;\n // Update all components that depend on cameraMux\n // Note: TouchInputTracker and Orchestrator would need to be recreated or have setter methods\n\n // input orchestrator\n this._inputOrchestrator.cameraMux = cameraMux;\n }\n\n /**\n * @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.\n * @param timestamp \n */\n public step(timestamp: number){\n if(this._canvasProxy.detached || this._context == undefined){\n return;\n }\n\n this.cameraRig.update();\n let deltaTime = timestamp - this.lastUpdateTime;\n this.lastUpdateTime = timestamp;\n deltaTime = deltaTime / 1000;\n\n this._context.reset();\n this._context.clearRect(0, 0, this._canvasProxy.width * window.devicePixelRatio, this._canvasProxy.height * window.devicePixelRatio);\n\n if(this._cachedCanvasPixelRatio !== window.devicePixelRatio){\n this._cachedCanvasPixelRatio = window.devicePixelRatio;\n this._canvasProxy.setCanvasHeight(this._canvasProxy.height);\n this._canvasProxy.setCanvasWidth(this._canvasProxy.width);\n }\n\n if(this._cachedCanvasHeight !== this._canvasProxy.height){\n this._cachedCanvasHeight = this._canvasProxy.height;\n this._canvasProxy.setCanvasHeight(this._canvasProxy.height);\n }\n if(this._cachedCanvasWidth !== this._canvasProxy.width){\n this._cachedCanvasWidth = this._canvasProxy.width;\n this._canvasProxy.setCanvasWidth(this._canvasProxy.width);\n }\n\n\n const transfromMatrix = this.camera.getTransform(window.devicePixelRatio, this._alignCoordinateSystem);\n this._context.setTransform(transfromMatrix.a, transfromMatrix.b, transfromMatrix.c, transfromMatrix.d, transfromMatrix.e, transfromMatrix.f);\n }\n\n /**\n * 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)\n * @description Converts a point from window coordinates to world coordinates.\n * @param clickPointInWindow The point in window coordinates to convert.\n * @returns The converted point in world coordinates.\n */\n convertWindowPoint2WorldCoord(clickPointInWindow: Point): Point {\n const boundingRect = this._canvasProxy.dimensions;\n const cameraCenterInWindow = {x: boundingRect.position.x + boundingRect.width / 2, y: boundingRect.position.y + boundingRect.height / 2};\n const pointInViewPort = PointCal.subVector(clickPointInWindow, cameraCenterInWindow);\n if(!this._alignCoordinateSystem){\n pointInViewPort.y = -pointInViewPort.y;\n }\n return this.camera.convertFromViewPort2WorldSpace(pointInViewPort);\n }\n\n /**\n * @description Add an camera movement event listener. The events are \"pan\", \"zoom\", and \"rotate\".\n * There's also an \"all\" event that will be triggered when any of the above events are triggered.\n * @param eventName The event name to listen for. The events are \"pan\", \"zoom\", and \"rotate\".\n * @param callback The callback function to call when the event is triggered. The event provided to the callback is different for the different events.\n * @returns The converted point in world coordinates.\n */\n on<K extends keyof CameraEventMap>(eventName: K, callback: (event: CameraEventMap[K], cameraState: CameraState)=>void): UnSubscribe {\n return this.camera.on(eventName, callback);\n }\n\n /**\n * @description Add an input event listener. The events are \"pan\", \"zoom\", and \"rotate\". This is different from the camera event listener as this is for input events. \n * There's also an \"all\" event that will be triggered when any of the above events are triggered.\n * Input event does not necesarily mean that the camera will move. The input events are the events triggered when the user interacts with the board.\n * @param eventName \n * @param callback \n * @returns \n */\n onInput<K extends keyof RawUserInputEventMap>(eventName: K, callback: (event: RawUserInputEventMap[K])=> void): UnsubscribeToUserRawInput {\n return this.boardInputPublisher.on(eventName, callback);\n }\n\n /**\n * @description The max translation height of the camera. This is the maximum distance the camera can move in the vertical direction.\n */\n get maxHalfTransHeight(): number | undefined{\n return halfTranslationHeightOf(this.camera.boundaries);\n }\n\n /**\n * @description The max translation width of the camera. This is the maximum distance the camera can move in the horizontal direction.\n */\n get maxHalfTransWidth(): number | undefined{\n return halfTranslationWidthOf(this.camera.boundaries);\n }\n\n private syncCameraZoomLevel(canvasDimensions: CanvasDimensions){\n if(this.limitEntireViewPort){\n const targetMinZoomLevel = minZoomLevelBaseOnDimensions(this.camera.boundaries, canvasDimensions.width, canvasDimensions.height, this.camera.rotation);\n if(targetMinZoomLevel != undefined && zoomLevelBoundariesShouldUpdate(this.camera.zoomBoundaries, targetMinZoomLevel)){\n this.camera.setMinZoomLevel(targetMinZoomLevel);\n }\n }\n\n }\n\n /**\n * @group Helper Methods\n * @description This function sets the max translation width of the camera while fixing the minimum x boundary.\n */\n setMaxTransWidthWithFixedMinBoundary(value: number){\n const curBoundaries = this.camera.boundaries;\n const curMin = curBoundaries == undefined ? undefined: curBoundaries.min;\n const curHorizontalMin = curMin == undefined ? undefined: curMin.x;\n if(curHorizontalMin == undefined){\n this.camera.setHorizontalBoundaries(-value, value);\n } else {\n this.camera.setHorizontalBoundaries(curHorizontalMin, curHorizontalMin + value * 2);\n }\n if(this.limitEntireViewPort){\n const targetMinZoomLevel = minZoomLevelBaseOnWidth(this.camera.boundaries, this.camera.viewPortWidth, this.camera.viewPortHeight, this.camera.rotation);\n if(zoomLevelBoundariesShouldUpdate(this.camera.zoomBoundaries, targetMinZoomLevel)){\n this.camera.setMinZoomLevel(targetMinZoomLevel);\n }\n }\n }\n\n /**\n * @group Helper Methods\n * @description This function sets the max translation width of the camera while fixing the minimum x boundary.\n */\n setMaxTransWidthWithFixedMaxBoundary(value: number){\n const curBoundaries = this.camera.boundaries;\n const curMax = curBoundaries == undefined ? undefined: curBoundaries.max;\n const curHorizontalMax = curMax == undefined ? undefined: curMax.x;\n if(curHorizontalMax == undefined){\n this.camera.setHorizontalBoundaries(-value, value);\n } else {\n this.camera.setHorizontalBoundaries(curHorizontalMax - value * 2, curHorizontalMax);\n }\n if(this.limitEntireViewPort){\n const targetMinZoomLevel = minZoomLevelBaseOnWidth(this.camera.boundaries, this.camera.viewPortWidth, this.camera.viewPortHeight, this.camera.rotation);\n if(zoomLevelBoundariesShouldUpdate(this.camera.zoomBoundaries, targetMinZoomLevel)){\n this.camera.setMinZoomLevel(targetMinZoomLevel);\n }\n }\n }\n\n get restrictRelativeXTranslation(): boolean{\n return this.cameraRig.config.restrictRelativeXTranslation;\n }\n\n get restrictRelativeYTranslation(): boolean{\n return this.cameraRig.config.restrictRelativeYTranslation;\n }\n\n get restrictXTranslation(): boolean{\n return this.cameraRig.config.restrictXTranslation;\n }\n\n get restrictYTranslation(): boolean{\n return this.cameraRig.config.restrictYTranslation;\n }\n \n set restrictRelativeXTranslation(value: boolean){\n this.cameraRig.config.restrictRelativeXTranslation = value;\n }\n\n set restrictRelativeYTranslation(value: boolean){\n this.cameraRig.configure({restrictRelativeYTranslation: value});\n }\n\n set restrictXTranslation(value: boolean){\n this.cameraRig.configure({restrictXTranslation: value});\n }\n \n set restrictYTranslation(value: boolean){\n this.cameraRig.configure({restrictYTranslation: value});\n }\n\n get restrictZoom(): boolean{\n return this.cameraRig.config.restrictZoom;\n }\n\n set restrictZoom(value: boolean){\n this.cameraRig.configure({restrictZoom: value});\n }\n\n get restrictRotation(): boolean{\n return this.cameraRig.config.restrictRotation;\n }\n\n set restrictRotation(value: boolean){\n this.cameraRig.configure({restrictRotation: value});\n }\n\n get clampTranslation(): boolean{\n return this.cameraRig.config.clampTranslation;\n }\n\n set clampTranslation(value: boolean){\n this.cameraRig.configure({clampTranslation: value});\n }\n \n get clampZoom(): boolean{\n return this.cameraRig.config.clampZoom;\n }\n\n set clampZoom(value: boolean){\n this.cameraRig.configure({clampZoom: value});\n }\n\n get clampRotation(): boolean{\n return this.cameraRig.config.clampRotation;\n }\n\n set clampRotation(value: boolean){\n this.cameraRig.configure({clampRotation: value});\n }\n\n getCameraRig(): CameraRig {\n return this.cameraRig;\n }\n\n setInputMode(mode: 'kmt' | 'trackpad'): void {\n this._observableInputTracker.setMode(mode);\n }\n\n onCanvasDimensionChange(callback: (dimensions: CanvasDimensions) => void) {\n return this._canvasProxy.subscribe(callback);\n }\n\n get canvasDimensions(): CanvasDimensions {\n return this._canvasProxy.dimensions;\n }\n}\n",
27
27
  "import type { Point } from \"@ue-too/math\";\nimport { AsyncObservable, Observable, Observer } from \"../../utils/observable\";\n\n/**\n * Function to unsubscribe from raw user input events.\n *\n * @remarks\n * Calling this function removes the subscriber from the event stream.\n *\n * @category Raw Input Publisher\n */\nexport type UnsubscribeToUserRawInput = () => void;\n\n/**\n * Payload for pan input events.\n *\n * @property diff - The pan delta in viewport pixels\n *\n * @category Raw Input Publisher\n */\nexport type RawUserPanInputEventPayload = {\n diff: Point;\n}\n\n/**\n * Pan input event with discriminated type.\n *\n * @remarks\n * The `type` property allows TypeScript discriminated unions to distinguish\n * between different event types when subscribing to the \"all\" event stream.\n *\n * @category Raw Input Publisher\n */\nexport type RawUserPanInputEvent = {\n type: \"pan\",\n} & RawUserPanInputEventPayload;\n\n/**\n * Payload for zoom input events.\n *\n * @property deltaZoomAmount - The zoom delta (scale change)\n * @property anchorPoint - The zoom anchor point in viewport coordinates\n *\n * @category Raw Input Publisher\n */\nexport type RawUserZoomInputEventPayload = {\n deltaZoomAmount: number;\n anchorPoint: Point;\n}\n\n/**\n * Zoom input event with discriminated type.\n *\n * @remarks\n * The `type` property allows TypeScript discriminated unions to distinguish\n * between different event types when subscribing to the \"all\" event stream.\n *\n * @category Raw Input Publisher\n */\nexport type RawUserZoomInputEvent = {\n type: \"zoom\",\n} & RawUserZoomInputEventPayload;\n\n/**\n * Payload for rotate input events.\n *\n * @property deltaRotation - The rotation delta in radians\n *\n * @category Raw Input Publisher\n */\nexport type RawUserRotateInputEventPayload = {\n deltaRotation: number;\n}\n\n/**\n * Rotate input event with discriminated type.\n *\n * @remarks\n * The `type` property allows TypeScript discriminated unions to distinguish\n * between different event types when subscribing to the \"all\" event stream.\n *\n * @category Raw Input Publisher\n */\nexport type RawUserRotateInputEvent = {\n type: \"rotate\",\n} & RawUserRotateInputEventPayload;\n\n/**\n * Mapping of event names to their payload types.\n *\n * @remarks\n * This type enables type-safe event subscription:\n * - Subscribe to specific events (\"pan\", \"zoom\", \"rotate\") to receive only those payloads\n * - Subscribe to \"all\" to receive all events with discriminated type property\n *\n * @category Raw Input Publisher\n */\nexport type RawUserInputEventMap = {\n \"pan\": RawUserPanInputEventPayload,\n \"zoom\": RawUserZoomInputEventPayload,\n \"rotate\": RawUserRotateInputEventPayload,\n \"all\": RawUserInputEvent,\n}\n\n/**\n * Union type of all raw user input events.\n *\n * @remarks\n * Use the `type` discriminator property to determine which event variant you have.\n *\n * @category Raw Input Publisher\n */\nexport type RawUserInputEvent = RawUserPanInputEvent | RawUserZoomInputEvent | RawUserRotateInputEvent;\n\n/**\n * Callback function type for raw user input events.\n *\n * @typeParam K - The event name key from RawUserInputEventMap\n *\n * @category Raw Input Publisher\n */\nexport type RawUserInputCallback<K extends keyof RawUserInputEventMap> = (event: RawUserInputEventMap[K])=>void;\n\n/**\n * Interface for publishing raw user input events to observers.\n *\n * @remarks\n * This interface defines the contract for broadcasting user input events\n * to external subscribers. Implementations provide the observable pattern\n * for input event distribution.\n *\n * @category Raw Input Publisher\n */\nexport interface UserInputPublisher {\n /** Notifies subscribers of a pan gesture */\n notifyPan(diff: Point): void;\n /** Notifies subscribers of a zoom gesture */\n notifyZoom(deltaZoomAmount: number, anchorPoint: Point): void;\n /** Notifies subscribers of a rotate gesture */\n notifyRotate(deltaRotation: number): void;\n /** Subscribes to input events */\n on<K extends keyof RawUserInputEventMap>(eventName: K, callback: (event: RawUserInputEventMap[K])=>void): UnsubscribeToUserRawInput;\n}\n\n/**\n * Publisher for broadcasting raw user input events to observers.\n *\n * @remarks\n * This class implements the observable pattern to distribute user input events\n * to external subscribers. It operates in parallel to camera control - the\n * orchestrator both sends events to this publisher AND controls the camera.\n *\n * **Architecture**:\n * ```\n * Orchestrator → Publisher → Observers (UI, analytics, etc.)\n * ↓\n * CameraMux → CameraRig\n * ```\n *\n * **Event Streams**:\n * - **Specific streams**: Subscribe to \"pan\", \"zoom\", or \"rotate\" for typed events\n * - **Unified stream**: Subscribe to \"all\" for all events with type discriminator\n *\n * **Use Cases**:\n * - Update UI elements based on user interactions\n * - Log analytics about user gestures\n * - Synchronize secondary views or previews\n * - Implement custom gesture reactions independent of camera\n *\n * **Observable Implementation**:\n * Uses AsyncObservable for asynchronous event delivery, preventing observers\n * from blocking the input processing pipeline.\n *\n * @category Raw Input Publisher\n *\n * @example\n * ```typescript\n * const publisher = new RawUserInputPublisher();\n *\n * // Subscribe to pan events\n * const unsubscribe = publisher.on(\"pan\", (event) => {\n * console.log(\"User panned by:\", event.diff);\n * updateMinimap(event.diff);\n * });\n *\n * // Subscribe to all events\n * publisher.on(\"all\", (event) => {\n * switch (event.type) {\n * case \"pan\":\n * analytics.log(\"pan\", event.diff);\n * break;\n * case \"zoom\":\n * analytics.log(\"zoom\", event.deltaZoomAmount, event.anchorPoint);\n * break;\n * case \"rotate\":\n * analytics.log(\"rotate\", event.deltaRotation);\n * break;\n * }\n * });\n *\n * // Later, unsubscribe\n * unsubscribe();\n * ```\n */\nexport class RawUserInputPublisher implements UserInputPublisher {\n\n private pan: Observable<Parameters<RawUserInputCallback<\"pan\">>>;\n private zoom: Observable<Parameters<RawUserInputCallback<\"zoom\">>>;\n private rotate: Observable<Parameters<RawUserInputCallback<\"rotate\">>>;\n private all: Observable<Parameters<RawUserInputCallback<\"all\">>>;\n\n constructor(){\n this.pan = new AsyncObservable<Parameters<RawUserInputCallback<\"pan\">>>();\n this.zoom = new AsyncObservable<Parameters<RawUserInputCallback<\"zoom\">>>();\n this.rotate = new AsyncObservable<Parameters<RawUserInputCallback<\"rotate\">>>();\n this.all = new AsyncObservable<Parameters<RawUserInputCallback<\"all\">>>();\n }\n\n notifyPan(diff: Point): void {\n this.pan.notify({diff: diff});\n this.all.notify({type: \"pan\", diff: diff});\n }\n\n notifyZoom(deltaZoomAmount: number, anchorPoint: Point): void {\n this.zoom.notify({deltaZoomAmount: deltaZoomAmount, anchorPoint: anchorPoint});\n this.all.notify({type: \"zoom\", deltaZoomAmount: deltaZoomAmount, anchorPoint: anchorPoint});\n }\n\n notifyRotate(deltaRotation: number): void {\n this.rotate.notify({deltaRotation: deltaRotation});\n this.all.notify({type: \"rotate\", deltaRotation: deltaRotation});\n }\n\n on<K extends keyof RawUserInputEventMap>(eventName: K, callback: (event: RawUserInputEventMap[K])=>void): UnsubscribeToUserRawInput {\n switch (eventName){\n case \"pan\":\n return this.pan.subscribe(callback as Observer<Parameters<RawUserInputCallback<\"pan\">>>);\n case \"zoom\":\n return this.zoom.subscribe(callback as Observer<Parameters<RawUserInputCallback<\"zoom\">>>);\n case \"rotate\":\n return this.rotate.subscribe(callback as Observer<Parameters<RawUserInputCallback<\"rotate\">>>);\n case \"all\":\n return this.all.subscribe(callback as Observer<Parameters<RawUserInputCallback<\"all\">>>);\n default:\n throw new Error(\"Invalid raw user input event name\");\n }\n }\n}\n\n/**\n * Creates a default raw user input publisher.\n *\n * @returns A new RawUserInputPublisher instance\n *\n * @remarks\n * Factory function for creating a standard publisher. Useful for dependency injection\n * and testing scenarios where you want to swap implementations.\n *\n * @category Raw Input Publisher\n */\nexport function createDefaultRawUserInputPublisher(): RawUserInputPublisher {\n return new RawUserInputPublisher();\n}\n\nexport class RawUserInputPublisherWithWebWorkerRelay implements UserInputPublisher {\n\n private pan: Observable<Parameters<RawUserInputCallback<\"pan\">>>;\n private zoom: Observable<Parameters<RawUserInputCallback<\"zoom\">>>;\n private rotate: Observable<Parameters<RawUserInputCallback<\"rotate\">>>;\n private all: Observable<Parameters<RawUserInputCallback<\"all\">>>;\n private webWorker: Worker;\n\n constructor(webWorker: Worker){\n this.pan = new AsyncObservable<Parameters<RawUserInputCallback<\"pan\">>>();\n this.zoom = new AsyncObservable<Parameters<RawUserInputCallback<\"zoom\">>>();\n this.rotate = new AsyncObservable<Parameters<RawUserInputCallback<\"rotate\">>>();\n this.all = new AsyncObservable<Parameters<RawUserInputCallback<\"all\">>>();\n this.webWorker = webWorker;\n }\n\n notifyPan(diff: Point): void {\n this.webWorker.postMessage({type: \"notifyUserInput\", payload: {type: \"pan\", diff: diff}});\n this.pan.notify({diff: diff});\n this.all.notify({type: \"pan\", diff: diff});\n }\n\n notifyZoom(deltaZoomAmount: number, anchorPoint: Point): void {\n this.webWorker.postMessage({type: \"notifyUserInput\", payload: {type: \"zoom\", deltaZoomAmount: deltaZoomAmount, anchorPoint: anchorPoint}});\n this.zoom.notify({deltaZoomAmount: deltaZoomAmount, anchorPoint: anchorPoint});\n this.all.notify({type: \"zoom\", deltaZoomAmount: deltaZoomAmount, anchorPoint: anchorPoint});\n }\n\n notifyRotate(deltaRotation: number): void {\n this.webWorker.postMessage({type: \"notifyUserInput\", payload: {type: \"rotate\", deltaRotation: deltaRotation}});\n this.rotate.notify({deltaRotation: deltaRotation});\n this.all.notify({type: \"rotate\", deltaRotation: deltaRotation});\n }\n \n on<K extends keyof RawUserInputEventMap>(eventName: K, callback: (event: RawUserInputEventMap[K])=>void): UnsubscribeToUserRawInput {\n switch (eventName){\n case \"pan\":\n return this.pan.subscribe(callback as Observer<Parameters<RawUserInputCallback<\"pan\">>>);\n case \"zoom\":\n return this.zoom.subscribe(callback as Observer<Parameters<RawUserInputCallback<\"zoom\">>>);\n case \"rotate\":\n return this.rotate.subscribe(callback as Observer<Parameters<RawUserInputCallback<\"rotate\">>>);\n case \"all\":\n return this.all.subscribe(callback as Observer<Parameters<RawUserInputCallback<\"all\">>>);\n default:\n throw new Error(\"Invalid raw user input event name\");\n }\n }\n}\n",
28
28
  "import { CameraMux, CameraMuxPanOutput, CameraMuxZoomOutput, CameraMuxRotationOutput } from \"./interface\";\nimport { Point } from \"@ue-too/math\";\n\n/**\n * Stateless camera input multiplexer that always allows inputs to pass through.\n * This is the simplest {@link CameraMux} implementation with no filtering or state management.\n *\n * @remarks\n * The Relay class provides a \"transparent\" mux that:\n * - Never blocks inputs\n * - Passes all inputs unchanged\n * - Has no internal state\n * - Acts as a simple conduit between input sources and camera control\n *\n * Use this when you want:\n * - Direct, unfiltered camera control\n * - No animation system or input blocking\n * - Maximum simplicity with minimal overhead\n *\n * For more advanced use cases (animations, input blocking, state management),\n * implement a custom {@link CameraMux} or use a stateful implementation.\n *\n * @example\n * ```typescript\n * const relay = new Relay();\n *\n * // All inputs pass through unchanged\n * const panResult = relay.notifyPanInput({ x: 10, y: 5 });\n * // panResult = { allowPassThrough: true, delta: { x: 10, y: 5 } }\n *\n * const zoomResult = relay.notifyZoomInput(0.5, { x: 100, y: 200 });\n * // zoomResult = { allowPassThrough: true, delta: 0.5, anchorPoint: { x: 100, y: 200 } }\n *\n * const rotateResult = relay.notifyRotationInput(0.1);\n * // rotateResult = { allowPassThrough: true, delta: 0.1 }\n * ```\n *\n * @category Input Flow Control\n * @see {@link CameraMux} for the interface specification\n * @see {@link createDefaultCameraMux} for a factory function\n */\nexport class Relay implements CameraMux {\n\n /**\n * Creates a new stateless relay multiplexer.\n */\n constructor() {\n }\n\n /**\n * Processes pan input by always allowing it through unchanged.\n *\n * @param diff - Pan displacement in viewport space\n * @returns Output allowing passthrough with the original delta\n */\n notifyPanInput(diff: Point): CameraMuxPanOutput {\n return { allowPassThrough: true, delta: diff };\n }\n\n /**\n * Processes zoom input by always allowing it through unchanged.\n *\n * @param deltaZoomAmount - Change in zoom level\n * @param anchorPoint - Point to zoom towards in viewport coordinates\n * @returns Output allowing passthrough with the original parameters\n */\n notifyZoomInput(deltaZoomAmount: number, anchorPoint: Point): CameraMuxZoomOutput {\n return { allowPassThrough: true, delta: deltaZoomAmount, anchorPoint: anchorPoint };\n }\n\n /**\n * Processes rotation input by always allowing it through unchanged.\n *\n * @param deltaRotation - Change in rotation in radians\n * @returns Output allowing passthrough with the original delta\n */\n notifyRotationInput(deltaRotation: number): CameraMuxRotationOutput {\n return { allowPassThrough: true, delta: deltaRotation };\n }\n\n}\n\n/**\n * Factory function to create a default camera input multiplexer.\n * Returns a {@link Relay} instance that allows all inputs to pass through.\n *\n * @returns A new stateless relay multiplexer\n *\n * @remarks\n * This is a convenience function for creating the simplest possible camera mux.\n * The returned instance has no state and never blocks inputs.\n *\n * @example\n * ```typescript\n * const cameraMux = createDefaultCameraMux();\n *\n * // Use with camera rig or input handlers\n * const cameraRig = new CameraRig(camera, cameraMux);\n * ```\n *\n * @category Input Flow Control\n * @see {@link Relay} for implementation details\n */\nexport function createDefaultCameraMux(): CameraMux {\n return new Relay();\n}\n",
29
- "import { Point } from \"@ue-too/math\";\nimport type { EventReactions, State, BaseContext } from \"@ue-too/being\";\nimport { NO_OP, TemplateState, TemplateStateMachine } from \"@ue-too/being\";\n\n/**\n * State identifiers for the pan control state machine.\n *\n * @remarks\n * Three states manage pan input and animations:\n * - `ACCEPTING_USER_INPUT`: Normal state, accepts user pan input\n * - `TRANSITION`: Animation/transition state, may block user input\n * - `LOCKED_ON_OBJECT`: Camera locked to follow a specific object/position\n *\n * @category Input Flow Control\n */\nexport type PanControlStates = \"ACCEPTING_USER_INPUT\" | \"TRANSITION\" | \"LOCKED_ON_OBJECT\";\n\n/**\n * Payload for pan-by input events (relative panning).\n * @category Input Flow Control\n */\nexport type PanByInputEventPayload = {\n /** Pan displacement in viewport coordinates */\n diff: Point;\n};\n\n/**\n * Payload for pan-to input events (absolute panning).\n * @category Input Flow Control\n */\nexport type PanToInputEventPayload = {\n /** Target position to pan to */\n target: Point;\n};\n\n/** Empty payload for events that don't need data */\ntype EmptyPayload = {};\n\n/**\n * Event payload type mapping for the pan control state machine.\n *\n * @remarks\n * Maps event names to their payload types. Events include:\n * - User input events (`userPanByInput`, `userPanToInput`)\n * - Transition/animation events (`transitionPanByInput`, `transitionPanToInput`)\n * - Locked object events (`lockedOnObjectPanByInput`, `lockedOnObjectPanToInput`)\n * - Control events (`unlock`, `initateTransition`)\n *\n * @category Input Flow Control\n */\nexport type PanEventPayloadMapping = {\n \"userPanByInput\": PanByInputEventPayload,\n \"userPanToInput\": PanToInputEventPayload,\n \"transitionPanByInput\": PanByInputEventPayload,\n \"transitionPanToInput\": PanToInputEventPayload,\n \"lockedOnObjectPanByInput\": PanByInputEventPayload,\n \"lockedOnObjectPanToInput\": PanToInputEventPayload,\n \"unlock\": EmptyPayload,\n \"initateTransition\": EmptyPayload,\n};\n\n/**\n * Discriminated union of output events from pan control state machine.\n *\n * @remarks\n * Output events instruct the camera system what pan operation to perform:\n * - `panByViewPort`: Relative pan in viewport coordinates\n * - `panToWorld`: Absolute pan to world position\n * - `none`: No operation (input blocked)\n *\n * @category Input Flow Control\n */\nexport type PanControlOutputEvent =\n | { type: \"panByViewPort\", delta: Point }\n | { type: \"panToWorld\", target: Point }\n | { type: \"none\" };\n\n/**\n * Output event type mapping for pan control events.\n * Maps input event names to their corresponding output event types.\n *\n * @category Input Flow Control\n */\nexport type PanControlOutputMapping = {\n \"userPanByInput\": PanControlOutputEvent,\n \"userPanToInput\": PanControlOutputEvent,\n \"transitionPanByInput\": PanControlOutputEvent,\n \"transitionPanToInput\": PanControlOutputEvent,\n \"lockedOnObjectPanByInput\": PanControlOutputEvent,\n \"lockedOnObjectPanToInput\": PanControlOutputEvent,\n};\n\n\n/**\n * State machine controlling pan input flow and animations.\n *\n * @remarks\n * This state machine manages the lifecycle of pan operations:\n * - **User input handling**: Accepts or blocks user pan gestures based on state\n * - **Animation control**: Manages smooth pan-to animations\n * - **Object tracking**: Supports locking camera to follow objects\n *\n * **State transitions:**\n * - `ACCEPTING_USER_INPUT` → `TRANSITION`: Start animation (`initateTransition`)\n * - `ACCEPTING_USER_INPUT` → `LOCKED_ON_OBJECT`: Lock to object (`lockedOnObjectPan...`)\n * - `TRANSITION` → `ACCEPTING_USER_INPUT`: User input interrupts animation\n * - `LOCKED_ON_OBJECT` → `ACCEPTING_USER_INPUT`: Unlock (`unlock` event)\n *\n * Helper methods simplify event dispatching without memorizing event names.\n *\n * @example\n * ```typescript\n * const stateMachine = createDefaultPanControlStateMachine(cameraRig);\n *\n * // User pans - accepted in ACCEPTING_USER_INPUT state\n * const result = stateMachine.notifyPanInput({ x: 50, y: 30 });\n *\n * // Start animation - transitions to TRANSITION state\n * stateMachine.notifyPanToAnimationInput({ x: 1000, y: 500 });\n *\n * // User input now blocked while animating\n * ```\n *\n * @category Input Flow Control\n * @see {@link createDefaultPanControlStateMachine} for factory function\n */\nexport class PanControlStateMachine extends TemplateStateMachine<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping> {\n\n constructor(states: Record<PanControlStates, State<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping>>, initialState: PanControlStates, context: BaseContext){\n super(states, initialState, context);\n }\n\n /**\n * Notifies the state machine of user pan input.\n *\n * @param diff - Pan displacement in viewport coordinates\n * @returns Event handling result with output event\n *\n * @remarks\n * Dispatches `userPanByInput` event. Accepted in `ACCEPTING_USER_INPUT` and `TRANSITION` states,\n * where it may transition back to `ACCEPTING_USER_INPUT` (user interrupting animation).\n */\n notifyPanInput(diff: Point) {\n return this.happens(\"userPanByInput\", {diff: diff});\n }\n\n /**\n * Initiates a pan animation to a target position.\n *\n * @param target - Target position in world coordinates\n * @returns Event handling result\n *\n * @remarks\n * Dispatches `transitionPanToInput` event, starting a pan animation.\n * Transitions to `TRANSITION` state where animation updates occur.\n */\n notifyPanToAnimationInput(target: Point) {\n return this.happens(\"transitionPanToInput\", {target: target});\n }\n\n /**\n * Initiates transition to `TRANSITION` state.\n *\n * @remarks\n * Forces state change to begin animation or transition sequence.\n * Called when starting programmatic camera movements.\n */\n initateTransition(): void{\n this.happens(\"initateTransition\");\n }\n}\n\n/**\n * State implementation for accepting user pan input (idle/normal state).\n * Accepts user pan input and can transition to animation or locked states.\n * @category Input Flow Control\n */\nexport class AcceptingUserInputState extends TemplateState<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping> {\n\n constructor(){\n super();\n }\n\n eventReactions: EventReactions<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping> = {\n userPanByInput: {action: this.userPanByInputHandler, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n userPanToInput: {action: this.userPanToInputHandler, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n lockedOnObjectPanByInput: {action: this.lockedOnObjectPanByInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n lockedOnObjectPanToInput: {action: this.lockedOnObjectPanToInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n initateTransition: {action: NO_OP, defaultTargetState: \"TRANSITION\"},\n }\n\n userPanByInputHandler(context: BaseContext, payload: PanByInputEventPayload): PanControlOutputEvent {\n return { type: \"panByViewPort\", delta: payload.diff };\n }\n\n userPanToInputHandler(context: BaseContext, payload: PanToInputEventPayload): PanControlOutputEvent {\n return { type: \"panToWorld\", target: payload.target };\n }\n\n lockedOnObjectPanByInputHandler(context: BaseContext, payload: PanByInputEventPayload): PanControlOutputEvent {\n return { type: \"panByViewPort\", delta: payload.diff };\n }\n\n lockedOnObjectPanToInputHandler(context: BaseContext, payload: PanToInputEventPayload): PanControlOutputEvent {\n return { type: \"panToWorld\", target: payload.target };\n }\n\n}\n\n/**\n * State implementation for pan animations and transitions.\n * Processes animation updates and allows user input to interrupt.\n * @category Input Flow Control\n */\nexport class TransitionState extends TemplateState<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping> {\n\n constructor(){\n super();\n }\n\n eventReactions: EventReactions<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping> = {\n userPanByInput: {action: this.userPanByInputHandler, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n userPanToInput: {action: this.userPanToInputHandler, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n transitionPanByInput: {action: this.transitionPanByInputHandler, defaultTargetState: \"TRANSITION\"},\n transitionPanToInput: {action: this.transitionPanToInputHandler, defaultTargetState: \"TRANSITION\"},\n lockedOnObjectPanByInput: {action: this.lockedOnObjectPanByInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n lockedOnObjectPanToInput: {action: this.lockedOnObjectPanToInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n }\n\n userPanByInputHandler(context: BaseContext, payload: PanByInputEventPayload): PanControlOutputEvent {\n return { type: \"panByViewPort\", delta: payload.diff };\n }\n\n userPanToInputHandler(context: BaseContext, payload: PanToInputEventPayload): PanControlOutputEvent {\n return { type: \"panToWorld\", target: payload.target };\n }\n\n transitionPanByInputHandler(context: BaseContext, payload: PanByInputEventPayload): PanControlOutputEvent {\n return { type: \"panByViewPort\", delta: payload.diff };\n }\n\n transitionPanToInputHandler(context: BaseContext, payload: PanToInputEventPayload): PanControlOutputEvent {\n return { type: \"panToWorld\", target: payload.target };\n }\n\n lockedOnObjectPanByInputHandler(context: BaseContext, payload: PanByInputEventPayload): PanControlOutputEvent {\n return { type: \"panByViewPort\", delta: payload.diff };\n }\n\n lockedOnObjectPanToInputHandler(context: BaseContext, payload: PanToInputEventPayload): PanControlOutputEvent {\n return { type: \"panToWorld\", target: payload.target };\n }\n\n}\n\n/**\n * State implementation for camera locked to follow an object.\n * Only accepts locked object pan events until unlocked.\n * @category Input Flow Control\n */\nexport class LockedOnObjectState extends TemplateState<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping> {\n\n constructor(){\n super();\n }\n\n eventReactions: EventReactions<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping> = {\n unlock: {action: NO_OP, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n lockedOnObjectPanByInput: {action: this.lockedOnObjectPanByInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n lockedOnObjectPanToInput: {action: this.lockedOnObjectPanToInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n }\n\n lockedOnObjectPanByInputHandler(context: BaseContext, payload: PanByInputEventPayload): PanControlOutputEvent {\n return { type: \"panByViewPort\", delta: payload.diff };\n }\n\n lockedOnObjectPanToInputHandler(context: BaseContext, payload: PanToInputEventPayload): PanControlOutputEvent {\n return { type: \"panToWorld\", target: payload.target };\n }\n\n}\n\n/**\n * Creates the default set of pan control states.\n * @returns State instances for all pan control states\n * @category Input Flow Control\n */\nexport function createDefaultPanControlStates(): Record<PanControlStates, State<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping>> {\n return {\n ACCEPTING_USER_INPUT: new AcceptingUserInputState(),\n TRANSITION: new TransitionState(),\n LOCKED_ON_OBJECT: new LockedOnObjectState(),\n }\n}\n\n/**\n * Creates a pan control state machine with default configuration.\n *\n * @param context - Camera rig or context for pan operations\n * @returns Configured pan control state machine starting in `ACCEPTING_USER_INPUT` state\n *\n * @remarks\n * Factory function for creating a pan state machine with sensible defaults.\n * The machine starts in `ACCEPTING_USER_INPUT` state, ready to accept user pan gestures.\n *\n * @example\n * ```typescript\n * const cameraRig = createDefaultCameraRig(camera);\n * const panSM = createDefaultPanControlStateMachine(cameraRig);\n * ```\n *\n * @category Input Flow Control\n */\nexport function createDefaultPanControlStateMachine(context: BaseContext): PanControlStateMachine {\n return new PanControlStateMachine(createDefaultPanControlStates(), \"ACCEPTING_USER_INPUT\", context);\n}\n",
30
- "import type { State, EventReactions, BaseContext } from \"@ue-too/being\";\nimport { NO_OP, TemplateState, TemplateStateMachine } from \"@ue-too/being\";\nimport { Point } from \"@ue-too/math\";\n\n/**\n * State identifiers for the zoom control state machine.\n *\n * @remarks\n * Three states manage zoom input and animations:\n * - `ACCEPTING_USER_INPUT`: Normal state, accepts user zoom input\n * - `TRANSITION`: Animation/transition state, may block user input\n * - `LOCKED_ON_OBJECT`: Camera locked to follow a specific object with zoom\n *\n * @category Input Flow Control\n */\nexport type ZoomControlStates = \"ACCEPTING_USER_INPUT\" | \"TRANSITION\" | \"LOCKED_ON_OBJECT\";\n\n/**\n * Payload for zoom-by-at input events (relative zoom around a point).\n * @category Input Flow Control\n */\nexport type ZoomByAtInputPayload = {\n /** Zoom delta amount (multiplier) */\n deltaZoom: number;\n /** Anchor point for zoom operation */\n anchorPoint: Point;\n}\n\n/**\n * Payload for zoom-to-at input events (absolute zoom to target around a point).\n * @category Input Flow Control\n */\nexport type ZoomToAtInputPayload = {\n /** Target zoom level */\n targetZoom: number;\n /** Anchor point for zoom operation */\n anchorPoint: Point;\n}\n\n/**\n * Payload for zoom-by input events (relative zoom without anchor).\n * @category Input Flow Control\n */\nexport type ZoomByPayload = {\n /** Zoom delta amount (multiplier) */\n deltaZoom: number;\n}\n\n/**\n * Payload for zoom-to input events (absolute zoom to target level).\n * @category Input Flow Control\n */\nexport type ZoomToPayload = {\n /** Target zoom level */\n targetZoom: number;\n}\n\n/**\n * Event payload type mapping for the zoom control state machine.\n *\n * @remarks\n * Maps event names to their payload types. Events include:\n * - User input events (`userZoomByAtInput`, `userZoomToAtInput`)\n * - Transition/animation events (`transitionZoomByAtInput`, `transitionZoomToAtInput`, etc.)\n * - Locked object events (`lockedOnObjectZoomByAtInput`, `lockedOnObjectZoomToAtInput`)\n * - Control events (`unlock`, `initiateTransition`)\n *\n * @category Input Flow Control\n */\nexport type ZoomEventPayloadMapping = {\n \"userZoomByAtInput\": ZoomByAtInputPayload,\n \"userZoomToAtInput\": ZoomToAtInputPayload,\n \"transitionZoomByAtInput\": ZoomByAtInputPayload,\n \"transitionZoomToAtInput\": ZoomToAtInputPayload,\n \"transitionZoomByAtCenterInput\": ZoomByPayload,\n \"transitionZoomToAtCenterInput\": ZoomToAtInputPayload,\n \"transitionZoomToAtWorldInput\": ZoomToAtInputPayload,\n \"lockedOnObjectZoomByAtInput\": ZoomByAtInputPayload,\n \"lockedOnObjectZoomToAtInput\": ZoomToAtInputPayload,\n \"unlock\": {},\n \"initiateTransition\": {},\n};\n\n/**\n * Discriminated union of output events from zoom control state machine.\n *\n * @remarks\n * Output events instruct the camera system what zoom operation to perform:\n * - `zoomByAt`: Relative zoom around anchor point\n * - `zoomToAt`: Absolute zoom to target level around anchor point\n * - `zoomBy`: Relative zoom without anchor\n * - `zoomTo`: Absolute zoom to target level without anchor\n * - `zoomByAtWorld`: Relative zoom around world anchor point\n * - `zoomToAtWorld`: Absolute zoom to target level around world anchor point\n * - `none`: No operation (input blocked)\n *\n * @category Input Flow Control\n */\nexport type ZoomControlOutputEvent =\n | { type: \"zoomByAt\", deltaZoom: number, anchorPoint: Point }\n | { type: \"zoomToAt\", targetZoom: number, anchorPoint: Point }\n | { type: \"zoomBy\", deltaZoom: number }\n | { type: \"zoomTo\", targetZoom: number }\n | { type: \"zoomByAtWorld\", deltaZoom: number, anchorPoint: Point }\n | { type: \"zoomToAtWorld\", targetZoom: number, anchorPoint: Point }\n | { type: \"none\" };\n\n/**\n * Output event type mapping for zoom control events.\n * Maps input event names to their corresponding output event types.\n *\n * @category Input Flow Control\n */\nexport type ZoomControlOutputMapping = {\n \"userZoomByAtInput\": ZoomControlOutputEvent,\n \"userZoomToAtInput\": ZoomControlOutputEvent,\n \"transitionZoomByAtInput\": ZoomControlOutputEvent,\n \"transitionZoomToAtInput\": ZoomControlOutputEvent,\n \"transitionZoomByAtCenterInput\": ZoomControlOutputEvent,\n \"transitionZoomToAtCenterInput\": ZoomControlOutputEvent,\n \"transitionZoomToAtWorldInput\": ZoomControlOutputEvent,\n \"lockedOnObjectZoomByAtInput\": ZoomControlOutputEvent,\n \"lockedOnObjectZoomToAtInput\": ZoomControlOutputEvent,\n};\n\n/**\n * State implementation for accepting user zoom input (idle/normal state).\n * Accepts user zoom input and can transition to animation or locked states.\n * @category Input Flow Control\n */\nexport class ZoomAcceptingUserInputState extends TemplateState<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> {\n\n private _eventReactions: EventReactions<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> = {\n userZoomByAtInput: {action: this.userZoomByAtInput, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n userZoomToAtInput: {action: this.userZoomToAtInput, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n initiateTransition: {action: NO_OP, defaultTargetState: \"TRANSITION\"},\n };\n\n get eventReactions(): EventReactions<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> {\n return this._eventReactions;\n }\n\n userZoomByAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"userZoomByAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomByAt\", deltaZoom: payload.deltaZoom, anchorPoint: payload.anchorPoint };\n }\n\n userZoomToAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"userZoomToAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomToAt\", targetZoom: payload.targetZoom, anchorPoint: payload.anchorPoint };\n }\n}\n\n/**\n * State implementation for zoom animations and transitions.\n * Processes animation updates and allows user input to interrupt.\n * @category Input Flow Control\n */\nexport class ZoomTransitionState extends TemplateState<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> {\n\n constructor(){\n super();\n }\n\n private _eventReactions: EventReactions<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> = {\n lockedOnObjectZoomByAtInput: {action: this.lockedOnObjectZoomByAtInput, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n lockedOnObjectZoomToAtInput: {action: this.lockedOnObjectZoomToAtInput, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n transitionZoomByAtInput: {action: this.transitionZoomByAtInput, defaultTargetState: \"TRANSITION\"},\n transitionZoomToAtInput: {action: this.transitionZoomToAtInput, defaultTargetState: \"TRANSITION\"},\n transitionZoomToAtCenterInput: {action: this.transitionZoomToAtCenterInput, defaultTargetState: \"TRANSITION\"},\n transitionZoomToAtWorldInput: {action: this.transitionZoomToAtWorldInput, defaultTargetState: \"TRANSITION\"},\n userZoomByAtInput: {action: this.userZoomByAtInput, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n userZoomToAtInput: {action: this.userZoomToAtInput, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n }\n\n get eventReactions(): EventReactions<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> {\n return this._eventReactions;\n }\n\n lockedOnObjectZoomByAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"lockedOnObjectZoomByAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomBy\", deltaZoom: payload.deltaZoom };\n }\n\n lockedOnObjectZoomToAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"lockedOnObjectZoomToAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomTo\", targetZoom: payload.targetZoom };\n }\n\n userZoomByAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"userZoomByAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomByAt\", deltaZoom: payload.deltaZoom, anchorPoint: payload.anchorPoint };\n }\n\n userZoomToAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"userZoomToAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomToAt\", targetZoom: payload.targetZoom, anchorPoint: payload.anchorPoint };\n }\n\n transitionZoomByAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"transitionZoomByAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomByAt\", deltaZoom: payload.deltaZoom, anchorPoint: payload.anchorPoint };\n }\n\n transitionZoomByAtCenterInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"transitionZoomByAtCenterInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomBy\", deltaZoom: payload.deltaZoom };\n }\n\n transitionZoomToAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"transitionZoomToAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomToAt\", targetZoom: payload.targetZoom, anchorPoint: payload.anchorPoint };\n }\n\n transitionZoomToAtCenterInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"transitionZoomToAtCenterInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomTo\", targetZoom: payload.targetZoom };\n }\n\n transitionZoomToAtWorldInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"transitionZoomToAtWorldInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomToAtWorld\", targetZoom: payload.targetZoom, anchorPoint: payload.anchorPoint };\n }\n}\n\n/**\n * State implementation for camera locked to follow an object with zoom.\n * Accepts locked object zoom events and user input to unlock.\n * @category Input Flow Control\n */\nexport class ZoomLockedOnObjectState extends TemplateState<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> {\n\n constructor(){\n super();\n }\n\n private _eventReactions: EventReactions<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> = {\n lockedOnObjectZoomByAtInput: {action: this.lockedOnObjectZoomByAtInput, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n lockedOnObjectZoomToAtInput: {action: this.lockedOnObjectZoomToAtInput, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n userZoomByAtInput: {action: this.userZoomByAtInput, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n userZoomToAtInput: {action: this.userZoomToAtInput, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n }\n\n get eventReactions(): EventReactions<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> {\n return this._eventReactions;\n }\n\n lockedOnObjectZoomByAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"lockedOnObjectZoomByAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomByAt\", deltaZoom: payload.deltaZoom, anchorPoint: payload.anchorPoint };\n }\n\n lockedOnObjectZoomToAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"lockedOnObjectZoomToAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomToAt\", targetZoom: payload.targetZoom, anchorPoint: payload.anchorPoint };\n }\n\n userZoomByAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"userZoomByAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomByAt\", deltaZoom: payload.deltaZoom, anchorPoint: payload.anchorPoint };\n }\n\n userZoomToAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"userZoomToAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomToAt\", targetZoom: payload.targetZoom, anchorPoint: payload.anchorPoint };\n }\n}\n\n/**\n * State machine controlling zoom input flow and animations.\n *\n * @remarks\n * This state machine manages the lifecycle of zoom operations:\n * - **User input handling**: Accepts or blocks user zoom gestures based on state\n * - **Animation control**: Manages smooth zoom-to animations\n * - **Object tracking**: Supports locking camera to follow objects with zoom\n *\n * **State transitions:**\n * - `ACCEPTING_USER_INPUT` → `TRANSITION`: Start animation (`initiateTransition`)\n * - `ACCEPTING_USER_INPUT` → `LOCKED_ON_OBJECT`: Lock to object (`lockedOnObjectZoom...`)\n * - `TRANSITION` → `ACCEPTING_USER_INPUT`: User input interrupts animation\n * - `LOCKED_ON_OBJECT` → `ACCEPTING_USER_INPUT`: User input unlocks\n *\n * Helper methods simplify event dispatching without memorizing event names.\n *\n * @example\n * ```typescript\n * const stateMachine = createDefaultZoomControlStateMachine(cameraRig);\n *\n * // User zooms - accepted in ACCEPTING_USER_INPUT state\n * const result = stateMachine.notifyZoomByAtInput(1.2, { x: 400, y: 300 });\n *\n * // Start animation - transitions to TRANSITION state\n * stateMachine.notifyZoomToAtWorldInput(2.0, { x: 1000, y: 500 });\n *\n * // User input now may interrupt animation\n * ```\n *\n * @category Input Flow Control\n * @see {@link createDefaultZoomControlStateMachine} for factory function\n */\nexport class ZoomControlStateMachine extends TemplateStateMachine<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> {\n\n constructor(states: Record<ZoomControlStates, State<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping>>, initialState: ZoomControlStates, context: BaseContext){\n super(states, initialState, context);\n }\n\n /**\n * Notifies the state machine of user zoom input around an anchor point.\n *\n * @param delta - Zoom delta (multiplier)\n * @param at - Anchor point for zoom\n * @returns Event handling result with output event\n *\n * @remarks\n * Dispatches `userZoomByAtInput` event. Accepted in `ACCEPTING_USER_INPUT` and `TRANSITION` states.\n */\n notifyZoomByAtInput(delta: number, at: Point) {\n return this.happens(\"userZoomByAtInput\", {deltaZoom: delta, anchorPoint: at});\n }\n\n /**\n * Initiates a zoom animation around an anchor point.\n *\n * @param delta - Zoom delta (multiplier)\n * @param at - Anchor point for zoom\n * @returns Event handling result\n *\n * @remarks\n * Dispatches `transitionZoomByAtInput` event, starting a zoom animation.\n */\n notifyZoomByAtInputAnimation(delta: number, at: Point) {\n return this.happens(\"transitionZoomByAtInput\", {deltaZoom: delta, anchorPoint: at});\n }\n\n /**\n * Initiates a zoom animation to target level around center anchor.\n *\n * @param targetZoom - Target zoom level\n * @param at - Anchor point for zoom\n * @returns Event handling result\n *\n * @remarks\n * Dispatches `transitionZoomToAtCenterInput` event for center-anchored zoom animation.\n */\n notifyZoomToAtCenterInput(targetZoom: number, at: Point) {\n return this.happens(\"transitionZoomToAtCenterInput\", {targetZoom: targetZoom, anchorPoint: at});\n }\n\n /**\n * Initiates a zoom animation to target level around world anchor.\n *\n * @param targetZoom - Target zoom level\n * @param at - World anchor point for zoom\n * @returns Event handling result\n *\n * @remarks\n * Dispatches `transitionZoomToAtWorldInput` event for world-anchored zoom animation.\n */\n notifyZoomToAtWorldInput(targetZoom: number, at: Point) {\n return this.happens(\"transitionZoomToAtWorldInput\", {targetZoom: targetZoom, anchorPoint: at});\n }\n\n /**\n * Initiates transition to `TRANSITION` state.\n *\n * @remarks\n * Forces state change to begin animation or transition sequence.\n * Called when starting programmatic camera movements.\n */\n initateTransition() {\n return this.happens(\"initiateTransition\");\n }\n}\n\n/**\n * Creates the default set of zoom control states.\n * @returns State instances for all zoom control states\n * @category Input Flow Control\n */\nexport function createDefaultZoomControlStates(): Record<ZoomControlStates, State<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping>> {\n return {\n ACCEPTING_USER_INPUT: new ZoomAcceptingUserInputState(),\n TRANSITION: new ZoomTransitionState(),\n LOCKED_ON_OBJECT: new ZoomLockedOnObjectState(),\n }\n}\n\n/**\n * Creates a zoom control state machine with default configuration.\n *\n * @param context - Camera rig or context for zoom operations\n * @returns Configured zoom control state machine starting in `ACCEPTING_USER_INPUT` state\n *\n * @remarks\n * Factory function for creating a zoom state machine with sensible defaults.\n * The machine starts in `ACCEPTING_USER_INPUT` state, ready to accept user zoom gestures.\n *\n * @example\n * ```typescript\n * const cameraRig = createDefaultCameraRig(camera);\n * const zoomSM = createDefaultZoomControlStateMachine(cameraRig);\n * ```\n *\n * @category Input Flow Control\n */\nexport function createDefaultZoomControlStateMachine(context: BaseContext): ZoomControlStateMachine {\n return new ZoomControlStateMachine(createDefaultZoomControlStates(), \"ACCEPTING_USER_INPUT\", context);\n}\n",
31
- "import type { EventReactions, State, BaseContext } from \"@ue-too/being\";\nimport { NO_OP, TemplateState, TemplateStateMachine } from \"@ue-too/being\";\n\n/**\n * State identifiers for the rotation control state machine.\n *\n * @remarks\n * Three states manage rotation input and animations:\n * - `ACCEPTING_USER_INPUT`: Normal state, accepts user rotation input\n * - `TRANSITION`: Animation/transition state, may block user input\n * - `LOCKED_ON_OBJECT`: Camera locked to follow a specific object rotation\n *\n * @category Input Flow Control\n */\nexport type RotateControlStates = \"ACCEPTING_USER_INPUT\" | \"TRANSITION\" | \"LOCKED_ON_OBJECT\";\n\n/**\n * Payload for rotate-by input events (relative rotation).\n * @category Input Flow Control\n */\nexport type RotateByInputEventPayload = {\n /** Rotation angle delta in radians */\n diff: number;\n};\n\n/**\n * Payload for rotate-to input events (absolute rotation).\n * @category Input Flow Control\n */\nexport type RotateToInputEventPayload = {\n /** Target rotation angle in radians */\n target: number;\n};\n\n/** Empty payload for events that don't need data */\ntype EmptyPayload = {};\n\n/**\n * Event payload type mapping for the rotation control state machine.\n *\n * @remarks\n * Maps event names to their payload types. Events include:\n * - User input events (`userRotateByInput`, `userRotateToInput`)\n * - Transition/animation events (`transitionRotateByInput`, `transitionRotateToInput`)\n * - Locked object events (`lockedOnObjectRotateByInput`, `lockedOnObjectRotateToInput`)\n * - Control events (`unlock`, `initateTransition`)\n *\n * @category Input Flow Control\n */\nexport type RotateEventPayloadMapping = {\n \"userRotateByInput\": RotateByInputEventPayload,\n \"userRotateToInput\": RotateToInputEventPayload,\n \"transitionRotateByInput\": RotateByInputEventPayload,\n \"transitionRotateToInput\": RotateToInputEventPayload,\n \"lockedOnObjectRotateByInput\": RotateByInputEventPayload,\n \"lockedOnObjectRotateToInput\": RotateToInputEventPayload,\n \"unlock\": EmptyPayload,\n \"initateTransition\": EmptyPayload,\n};\n\n/**\n * Discriminated union of output events from rotation control state machine.\n *\n * @remarks\n * Output events instruct the camera system what rotation operation to perform:\n * - `rotateBy`: Relative rotation by delta angle\n * - `rotateTo`: Absolute rotation to target angle\n * - `none`: No operation (input blocked)\n *\n * @category Input Flow Control\n */\nexport type RotateControlOutputEvent =\n | { type: \"rotateBy\", delta: number }\n | { type: \"rotateTo\", target: number }\n | { type: \"none\" };\n\n/**\n * Output event type mapping for rotation control events.\n * Maps input event names to their corresponding output event types.\n *\n * @category Input Flow Control\n */\nexport type RotateControlOutputMapping = {\n \"userRotateByInput\": RotateControlOutputEvent,\n \"userRotateToInput\": RotateControlOutputEvent,\n \"transitionRotateByInput\": RotateControlOutputEvent,\n \"transitionRotateToInput\": RotateControlOutputEvent,\n \"lockedOnObjectRotateByInput\": RotateControlOutputEvent,\n \"lockedOnObjectRotateToInput\": RotateControlOutputEvent,\n};\n\n/**\n * State machine controlling rotation input flow and animations.\n *\n * @remarks\n * This state machine manages the lifecycle of rotation operations:\n * - **User input handling**: Accepts or blocks user rotation gestures based on state\n * - **Animation control**: Manages smooth rotate-to animations\n * - **Object tracking**: Supports locking camera to follow objects with rotation\n *\n * **State transitions:**\n * - `ACCEPTING_USER_INPUT` → `TRANSITION`: Start animation (`initateTransition`)\n * - `ACCEPTING_USER_INPUT` → `LOCKED_ON_OBJECT`: Lock to object (`lockedOnObjectRotate...`)\n * - `TRANSITION` → `ACCEPTING_USER_INPUT`: User input interrupts animation\n * - `LOCKED_ON_OBJECT` → `ACCEPTING_USER_INPUT`: Unlock (`unlock` event)\n *\n * Helper methods simplify event dispatching without memorizing event names.\n *\n * @example\n * ```typescript\n * const stateMachine = createDefaultRotateControlStateMachine(cameraRig);\n *\n * // User rotates - accepted in ACCEPTING_USER_INPUT state\n * const result = stateMachine.notifyRotateByInput(Math.PI / 4);\n *\n * // Start animation - transitions to TRANSITION state\n * stateMachine.notifyRotateToAnimationInput(Math.PI);\n *\n * // User input now blocked while animating\n * ```\n *\n * @category Input Flow Control\n * @see {@link createDefaultRotateControlStateMachine} for factory function\n */\nexport class RotateControlStateMachine extends TemplateStateMachine<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping> {\n\n constructor(states: Record<RotateControlStates, State<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping>>, initialState: RotateControlStates, context: BaseContext){\n super(states, initialState, context);\n }\n\n /**\n * Notifies the state machine of user rotation input.\n *\n * @param diff - Rotation angle delta in radians\n * @returns Event handling result with output event\n *\n * @remarks\n * Dispatches `userRotateByInput` event. Accepted in `ACCEPTING_USER_INPUT` and `TRANSITION` states,\n * where it may transition back to `ACCEPTING_USER_INPUT` (user interrupting animation).\n */\n notifyRotateByInput(diff: number) {\n return this.happens(\"userRotateByInput\", {diff: diff});\n }\n\n /**\n * Initiates a rotation animation to a target angle.\n *\n * @param target - Target rotation angle in radians\n * @returns Event handling result\n *\n * @remarks\n * Dispatches `transitionRotateToInput` event, starting a rotation animation.\n * Transitions to `TRANSITION` state where animation updates occur.\n */\n notifyRotateToAnimationInput(target: number) {\n return this.happens(\"transitionRotateToInput\", {target: target});\n }\n\n /**\n * Initiates transition to `TRANSITION` state.\n *\n * @remarks\n * Forces state change to begin animation or transition sequence.\n * Called when starting programmatic camera movements.\n */\n initateTransition(): void{\n this.happens(\"initateTransition\");\n }\n\n}\n\n/**\n * State implementation for accepting user rotation input (idle/normal state).\n * Accepts user rotation input and can transition to animation or locked states.\n * @category Input Flow Control\n */\nexport class RotationAcceptingUserInputState extends TemplateState<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping> {\n\n constructor(){\n super();\n }\n\n eventReactions: EventReactions<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping> = {\n userRotateByInput: {action: this.userRotateByInputHandler, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n userRotateToInput: {action: this.userRotateToInputHandler, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n lockedOnObjectRotateByInput: {action: this.lockedOnObjectRotateByInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n lockedOnObjectRotateToInput: {action: this.lockedOnObjectRotateToInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n initateTransition: {action: NO_OP, defaultTargetState: \"TRANSITION\"},\n }\n\n userRotateByInputHandler(context: BaseContext, payload: RotateByInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateBy\", delta: payload.diff };\n }\n\n userRotateToInputHandler(context: BaseContext, payload: RotateToInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateTo\", target: payload.target };\n }\n\n lockedOnObjectRotateByInputHandler(context: BaseContext, payload: RotateByInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateBy\", delta: payload.diff };\n }\n\n lockedOnObjectRotateToInputHandler(context: BaseContext, payload: RotateToInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateTo\", target: payload.target };\n }\n\n}\n\n/**\n * State implementation for rotation animations and transitions.\n * Processes animation updates and allows user input to interrupt.\n * @category Input Flow Control\n */\nexport class RotationTransitionState extends TemplateState<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping> {\n\n constructor(){\n super();\n }\n\n eventReactions: EventReactions<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping> = {\n userRotateByInput: {action: this.userRotateByInputHandler, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n userRotateToInput: {action: this.userRotateToInputHandler, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n transitionRotateByInput: {action: this.transitionRotateByInputHandler, defaultTargetState: \"TRANSITION\"},\n transitionRotateToInput: {action: this.transitionRotateToInputHandler, defaultTargetState: \"TRANSITION\"},\n lockedOnObjectRotateByInput: {action: this.lockedOnObjectRotateByInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n lockedOnObjectRotateToInput: {action: this.lockedOnObjectRotateToInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n }\n\n userRotateByInputHandler(context: BaseContext, payload: RotateByInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateBy\", delta: payload.diff };\n }\n\n userRotateToInputHandler(context: BaseContext, payload: RotateToInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateTo\", target: payload.target };\n }\n\n transitionRotateByInputHandler(context: BaseContext, payload: RotateByInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateBy\", delta: payload.diff };\n }\n\n transitionRotateToInputHandler(context: BaseContext, payload: RotateToInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateTo\", target: payload.target };\n }\n\n lockedOnObjectRotateByInputHandler(context: BaseContext, payload: RotateByInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateBy\", delta: payload.diff };\n }\n\n lockedOnObjectRotateToInputHandler(context: BaseContext, payload: RotateToInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateTo\", target: payload.target };\n }\n\n}\n\n/**\n * State implementation for camera locked to follow an object rotation.\n * Only accepts locked object rotation events until unlocked.\n * @category Input Flow Control\n */\nexport class RotationLockedOnObjectState extends TemplateState<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping> {\n\n constructor(){\n super();\n }\n\n eventReactions: EventReactions<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping> = {\n unlock: {action: NO_OP, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n lockedOnObjectRotateByInput: {action: this.lockedOnObjectRotateByInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n lockedOnObjectRotateToInput: {action: this.lockedOnObjectRotateToInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n }\n\n lockedOnObjectRotateByInputHandler(context: BaseContext, payload: RotateByInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateBy\", delta: payload.diff };\n }\n\n lockedOnObjectRotateToInputHandler(context: BaseContext, payload: RotateToInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateTo\", target: payload.target };\n }\n\n}\n\n/**\n * Creates the default set of rotation control states.\n * @returns State instances for all rotation control states\n * @category Input Flow Control\n */\nexport function createDefaultRotateControlStates(): Record<RotateControlStates, State<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping>> {\n return {\n ACCEPTING_USER_INPUT: new RotationAcceptingUserInputState(),\n TRANSITION: new RotationTransitionState(),\n LOCKED_ON_OBJECT: new RotationLockedOnObjectState(),\n }\n}\n\n/**\n * Creates a rotation control state machine with default configuration.\n *\n * @param context - Camera rig or context for rotation operations\n * @returns Configured rotation control state machine starting in `ACCEPTING_USER_INPUT` state\n *\n * @remarks\n * Factory function for creating a rotation state machine with sensible defaults.\n * The machine starts in `ACCEPTING_USER_INPUT` state, ready to accept user rotation gestures.\n *\n * @example\n * ```typescript\n * const cameraRig = createDefaultCameraRig(camera);\n * const rotateSM = createDefaultRotateControlStateMachine(cameraRig);\n * ```\n *\n * @category Input Flow Control\n */\nexport function createDefaultRotateControlStateMachine(context: BaseContext): RotateControlStateMachine {\n return new RotateControlStateMachine(createDefaultRotateControlStates(), \"ACCEPTING_USER_INPUT\", context);\n}\n",
29
+ "import { Point } from \"@ue-too/math\";\nimport type { EventReactions, State, BaseContext } from \"@ue-too/being\";\nimport { NO_OP, TemplateState, TemplateStateMachine } from \"@ue-too/being\";\n\n/**\n * State identifiers for the pan control state machine.\n *\n * @remarks\n * Three states manage pan input and animations:\n * - `ACCEPTING_USER_INPUT`: Normal state, accepts user pan input\n * - `TRANSITION`: Animation/transition state, may block user input\n * - `LOCKED_ON_OBJECT`: Camera locked to follow a specific object/position\n *\n * @category Input Flow Control\n */\nexport type PanControlStates = \"ACCEPTING_USER_INPUT\" | \"TRANSITION\" | \"LOCKED_ON_OBJECT\";\n\n/**\n * Payload for pan-by input events (relative panning).\n * @category Input Flow Control\n */\nexport type PanByInputEventPayload = {\n /** Pan displacement in viewport coordinates */\n diff: Point;\n};\n\n/**\n * Payload for pan-to input events (absolute panning).\n * @category Input Flow Control\n */\nexport type PanToInputEventPayload = {\n /** Target position to pan to */\n target: Point;\n};\n\n/** Empty payload for events that don't need data */\ntype EmptyPayload = {};\n\n/**\n * Event payload type mapping for the pan control state machine.\n *\n * @remarks\n * Maps event names to their payload types. Events include:\n * - User input events (`userPanByInput`, `userPanToInput`)\n * - Transition/animation events (`transitionPanByInput`, `transitionPanToInput`)\n * - Locked object events (`lockedOnObjectPanByInput`, `lockedOnObjectPanToInput`)\n * - Control events (`unlock`, `initateTransition`)\n *\n * @category Input Flow Control\n */\nexport type PanEventPayloadMapping = {\n \"userPanByInput\": PanByInputEventPayload,\n \"userPanToInput\": PanToInputEventPayload,\n \"transitionPanByInput\": PanByInputEventPayload,\n \"transitionPanToInput\": PanToInputEventPayload,\n \"lockedOnObjectPanByInput\": PanByInputEventPayload,\n \"lockedOnObjectPanToInput\": PanToInputEventPayload,\n \"unlock\": EmptyPayload,\n \"initateTransition\": EmptyPayload,\n};\n\n/**\n * Discriminated union of output events from pan control state machine.\n *\n * @remarks\n * Output events instruct the camera system what pan operation to perform:\n * - `panByViewPort`: Relative pan in viewport coordinates\n * - `panToWorld`: Absolute pan to world position\n * - `none`: No operation (input blocked)\n *\n * @category Input Flow Control\n */\nexport type PanControlOutputEvent =\n | { type: \"panByViewPort\", delta: Point }\n | { type: \"panToWorld\", target: Point }\n | { type: \"none\" };\n\n/**\n * Output event type mapping for pan control events.\n * Maps input event names to their corresponding output event types.\n *\n * @category Input Flow Control\n */\nexport type PanControlOutputMapping = {\n \"userPanByInput\": PanControlOutputEvent,\n \"userPanToInput\": PanControlOutputEvent,\n \"transitionPanByInput\": PanControlOutputEvent,\n \"transitionPanToInput\": PanControlOutputEvent,\n \"lockedOnObjectPanByInput\": PanControlOutputEvent,\n \"lockedOnObjectPanToInput\": PanControlOutputEvent,\n};\n\n\n/**\n * State machine controlling pan input flow and animations.\n *\n * @remarks\n * This state machine manages the lifecycle of pan operations:\n * - **User input handling**: Accepts or blocks user pan gestures based on state\n * - **Animation control**: Manages smooth pan-to animations\n * - **Object tracking**: Supports locking camera to follow objects\n *\n * **State transitions:**\n * - `ACCEPTING_USER_INPUT` → `TRANSITION`: Start animation (`initateTransition`)\n * - `ACCEPTING_USER_INPUT` → `LOCKED_ON_OBJECT`: Lock to object (`lockedOnObjectPan...`)\n * - `TRANSITION` → `ACCEPTING_USER_INPUT`: User input interrupts animation\n * - `LOCKED_ON_OBJECT` → `ACCEPTING_USER_INPUT`: Unlock (`unlock` event)\n *\n * Helper methods simplify event dispatching without memorizing event names.\n *\n * @example\n * ```typescript\n * const stateMachine = createDefaultPanControlStateMachine(cameraRig);\n *\n * // User pans - accepted in ACCEPTING_USER_INPUT state\n * const result = stateMachine.notifyPanInput({ x: 50, y: 30 });\n *\n * // Start animation - transitions to TRANSITION state\n * stateMachine.notifyPanToAnimationInput({ x: 1000, y: 500 });\n *\n * // User input now blocked while animating\n * ```\n *\n * @category Input Flow Control\n * @see {@link createDefaultPanControlStateMachine} for factory function\n */\nexport class PanControlStateMachine extends TemplateStateMachine<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping> {\n\n constructor(states: Record<PanControlStates, State<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping>>, initialState: PanControlStates, context: BaseContext){\n super(states, initialState, context);\n }\n\n /**\n * Notifies the state machine of user pan input.\n *\n * @param diff - Pan displacement in viewport coordinates\n * @returns Event handling result with output event\n *\n * @remarks\n * Dispatches `userPanByInput` event. Accepted in `ACCEPTING_USER_INPUT` and `TRANSITION` states,\n * where it may transition back to `ACCEPTING_USER_INPUT` (user interrupting animation).\n */\n notifyPanInput(diff: Point) {\n return this.happens(\"userPanByInput\", {diff: diff});\n }\n\n /**\n * Initiates a pan animation to a target position.\n *\n * @param target - Target position in world coordinates\n * @returns Event handling result\n *\n * @remarks\n * Dispatches `transitionPanToInput` event, starting a pan animation.\n * Transitions to `TRANSITION` state where animation updates occur.\n */\n notifyPanToAnimationInput(target: Point) {\n return this.happens(\"transitionPanToInput\", {target: target});\n }\n\n /**\n * Initiates transition to `TRANSITION` state.\n *\n * @remarks\n * Forces state change to begin animation or transition sequence.\n * Called when starting programmatic camera movements.\n */\n initateTransition() {\n return this.happens(\"initateTransition\");\n }\n}\n\n/**\n * State implementation for accepting user pan input (idle/normal state).\n * Accepts user pan input and can transition to animation or locked states.\n * @category Input Flow Control\n */\nexport class AcceptingUserInputState extends TemplateState<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping> {\n\n constructor(){\n super();\n }\n\n protected _eventReactions: EventReactions<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping> = {\n userPanByInput: {action: this.userPanByInputHandler, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n userPanToInput: {action: this.userPanToInputHandler, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n lockedOnObjectPanByInput: {action: this.lockedOnObjectPanByInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n lockedOnObjectPanToInput: {action: this.lockedOnObjectPanToInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n initateTransition: {action: NO_OP, defaultTargetState: \"TRANSITION\"},\n }\n\n userPanByInputHandler(context: BaseContext, payload: PanByInputEventPayload): PanControlOutputEvent {\n return { type: \"panByViewPort\", delta: payload.diff };\n }\n\n userPanToInputHandler(context: BaseContext, payload: PanToInputEventPayload): PanControlOutputEvent {\n return { type: \"panToWorld\", target: payload.target };\n }\n\n lockedOnObjectPanByInputHandler(context: BaseContext, payload: PanByInputEventPayload): PanControlOutputEvent {\n return { type: \"panByViewPort\", delta: payload.diff };\n }\n\n lockedOnObjectPanToInputHandler(context: BaseContext, payload: PanToInputEventPayload): PanControlOutputEvent {\n return { type: \"panToWorld\", target: payload.target };\n }\n\n}\n\n/**\n * State implementation for pan animations and transitions.\n * Processes animation updates and allows user input to interrupt.\n * @category Input Flow Control\n */\nexport class TransitionState extends TemplateState<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping> {\n\n constructor(){\n super();\n }\n\n protected _eventReactions: EventReactions<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping> = {\n userPanByInput: {action: this.userPanByInputHandler, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n userPanToInput: {action: this.userPanToInputHandler, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n transitionPanByInput: {action: this.transitionPanByInputHandler, defaultTargetState: \"TRANSITION\"},\n transitionPanToInput: {action: this.transitionPanToInputHandler, defaultTargetState: \"TRANSITION\"},\n lockedOnObjectPanByInput: {action: this.lockedOnObjectPanByInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n lockedOnObjectPanToInput: {action: this.lockedOnObjectPanToInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n }\n\n userPanByInputHandler(context: BaseContext, payload: PanByInputEventPayload): PanControlOutputEvent {\n return { type: \"panByViewPort\", delta: payload.diff };\n }\n\n userPanToInputHandler(context: BaseContext, payload: PanToInputEventPayload): PanControlOutputEvent {\n return { type: \"panToWorld\", target: payload.target };\n }\n\n transitionPanByInputHandler(context: BaseContext, payload: PanByInputEventPayload): PanControlOutputEvent {\n return { type: \"panByViewPort\", delta: payload.diff };\n }\n\n transitionPanToInputHandler(context: BaseContext, payload: PanToInputEventPayload): PanControlOutputEvent {\n return { type: \"panToWorld\", target: payload.target };\n }\n\n lockedOnObjectPanByInputHandler(context: BaseContext, payload: PanByInputEventPayload): PanControlOutputEvent {\n return { type: \"panByViewPort\", delta: payload.diff };\n }\n\n lockedOnObjectPanToInputHandler(context: BaseContext, payload: PanToInputEventPayload): PanControlOutputEvent {\n return { type: \"panToWorld\", target: payload.target };\n }\n\n}\n\n/**\n * State implementation for camera locked to follow an object.\n * Only accepts locked object pan events until unlocked.\n * @category Input Flow Control\n */\nexport class LockedOnObjectState extends TemplateState<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping> {\n\n constructor(){\n super();\n }\n\n protected _eventReactions: EventReactions<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping> = {\n unlock: {action: NO_OP, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n lockedOnObjectPanByInput: {action: this.lockedOnObjectPanByInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n lockedOnObjectPanToInput: {action: this.lockedOnObjectPanToInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n }\n\n lockedOnObjectPanByInputHandler(context: BaseContext, payload: PanByInputEventPayload): PanControlOutputEvent {\n return { type: \"panByViewPort\", delta: payload.diff };\n }\n\n lockedOnObjectPanToInputHandler(context: BaseContext, payload: PanToInputEventPayload): PanControlOutputEvent {\n return { type: \"panToWorld\", target: payload.target };\n }\n\n}\n\n/**\n * Creates the default set of pan control states.\n * @returns State instances for all pan control states\n * @category Input Flow Control\n */\nexport function createDefaultPanControlStates(): Record<PanControlStates, State<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping>> {\n return {\n ACCEPTING_USER_INPUT: new AcceptingUserInputState(),\n TRANSITION: new TransitionState(),\n LOCKED_ON_OBJECT: new LockedOnObjectState(),\n }\n}\n\n/**\n * Creates a pan control state machine with default configuration.\n *\n * @param context - Camera rig or context for pan operations\n * @returns Configured pan control state machine starting in `ACCEPTING_USER_INPUT` state\n *\n * @remarks\n * Factory function for creating a pan state machine with sensible defaults.\n * The machine starts in `ACCEPTING_USER_INPUT` state, ready to accept user pan gestures.\n *\n * @example\n * ```typescript\n * const cameraRig = createDefaultCameraRig(camera);\n * const panSM = createDefaultPanControlStateMachine(cameraRig);\n * ```\n *\n * @category Input Flow Control\n */\nexport function createDefaultPanControlStateMachine(context: BaseContext = {setup: NO_OP, cleanup: NO_OP}): PanControlStateMachine {\n return new PanControlStateMachine(createDefaultPanControlStates(), \"ACCEPTING_USER_INPUT\", context);\n}\n",
30
+ "import type { State, EventReactions, BaseContext } from \"@ue-too/being\";\nimport { NO_OP, TemplateState, TemplateStateMachine } from \"@ue-too/being\";\nimport { Point } from \"@ue-too/math\";\n\n/**\n * State identifiers for the zoom control state machine.\n *\n * @remarks\n * Three states manage zoom input and animations:\n * - `ACCEPTING_USER_INPUT`: Normal state, accepts user zoom input\n * - `TRANSITION`: Animation/transition state, may block user input\n * - `LOCKED_ON_OBJECT`: Camera locked to follow a specific object with zoom\n *\n * @category Input Flow Control\n */\nexport type ZoomControlStates = \"ACCEPTING_USER_INPUT\" | \"TRANSITION\" | \"LOCKED_ON_OBJECT\";\n\n/**\n * Payload for zoom-by-at input events (relative zoom around a point).\n * @category Input Flow Control\n */\nexport type ZoomByAtInputPayload = {\n /** Zoom delta amount (multiplier) */\n deltaZoom: number;\n /** Anchor point for zoom operation */\n anchorPoint: Point;\n}\n\n/**\n * Payload for zoom-to-at input events (absolute zoom to target around a point).\n * @category Input Flow Control\n */\nexport type ZoomToAtInputPayload = {\n /** Target zoom level */\n targetZoom: number;\n /** Anchor point for zoom operation */\n anchorPoint: Point;\n}\n\n/**\n * Payload for zoom-by input events (relative zoom without anchor).\n * @category Input Flow Control\n */\nexport type ZoomByPayload = {\n /** Zoom delta amount (multiplier) */\n deltaZoom: number;\n}\n\n/**\n * Payload for zoom-to input events (absolute zoom to target level).\n * @category Input Flow Control\n */\nexport type ZoomToPayload = {\n /** Target zoom level */\n targetZoom: number;\n}\n\n/**\n * Event payload type mapping for the zoom control state machine.\n *\n * @remarks\n * Maps event names to their payload types. Events include:\n * - User input events (`userZoomByAtInput`, `userZoomToAtInput`)\n * - Transition/animation events (`transitionZoomByAtInput`, `transitionZoomToAtInput`, etc.)\n * - Locked object events (`lockedOnObjectZoomByAtInput`, `lockedOnObjectZoomToAtInput`)\n * - Control events (`unlock`, `initiateTransition`)\n *\n * @category Input Flow Control\n */\nexport type ZoomEventPayloadMapping = {\n \"userZoomByAtInput\": ZoomByAtInputPayload,\n \"userZoomToAtInput\": ZoomToAtInputPayload,\n \"transitionZoomByAtInput\": ZoomByAtInputPayload,\n \"transitionZoomToAtInput\": ZoomToAtInputPayload,\n \"transitionZoomByAtCenterInput\": ZoomByPayload,\n \"transitionZoomToAtCenterInput\": ZoomToAtInputPayload,\n \"transitionZoomToAtWorldInput\": ZoomToAtInputPayload,\n \"lockedOnObjectZoomByAtInput\": ZoomByAtInputPayload,\n \"lockedOnObjectZoomToAtInput\": ZoomToAtInputPayload,\n \"unlock\": {},\n \"initiateTransition\": {},\n};\n\n/**\n * Discriminated union of output events from zoom control state machine.\n *\n * @remarks\n * Output events instruct the camera system what zoom operation to perform:\n * - `zoomByAt`: Relative zoom around anchor point\n * - `zoomToAt`: Absolute zoom to target level around anchor point\n * - `zoomBy`: Relative zoom without anchor\n * - `zoomTo`: Absolute zoom to target level without anchor\n * - `zoomByAtWorld`: Relative zoom around world anchor point\n * - `zoomToAtWorld`: Absolute zoom to target level around world anchor point\n * - `none`: No operation (input blocked)\n *\n * @category Input Flow Control\n */\nexport type ZoomControlOutputEvent =\n | { type: \"zoomByAt\", deltaZoom: number, anchorPoint: Point }\n | { type: \"zoomToAt\", targetZoom: number, anchorPoint: Point }\n | { type: \"zoomBy\", deltaZoom: number }\n | { type: \"zoomTo\", targetZoom: number }\n | { type: \"zoomByAtWorld\", deltaZoom: number, anchorPoint: Point }\n | { type: \"zoomToAtWorld\", targetZoom: number, anchorPoint: Point }\n | { type: \"none\" };\n\n/**\n * Output event type mapping for zoom control events.\n * Maps input event names to their corresponding output event types.\n *\n * @category Input Flow Control\n */\nexport type ZoomControlOutputMapping = {\n \"userZoomByAtInput\": ZoomControlOutputEvent,\n \"userZoomToAtInput\": ZoomControlOutputEvent,\n \"transitionZoomByAtInput\": ZoomControlOutputEvent,\n \"transitionZoomToAtInput\": ZoomControlOutputEvent,\n \"transitionZoomByAtCenterInput\": ZoomControlOutputEvent,\n \"transitionZoomToAtCenterInput\": ZoomControlOutputEvent,\n \"transitionZoomToAtWorldInput\": ZoomControlOutputEvent,\n \"lockedOnObjectZoomByAtInput\": ZoomControlOutputEvent,\n \"lockedOnObjectZoomToAtInput\": ZoomControlOutputEvent,\n};\n\n/**\n * State implementation for accepting user zoom input (idle/normal state).\n * Accepts user zoom input and can transition to animation or locked states.\n * @category Input Flow Control\n */\nexport class ZoomAcceptingUserInputState extends TemplateState<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> {\n\n protected _eventReactions: EventReactions<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> = {\n userZoomByAtInput: {action: this.userZoomByAtInput, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n userZoomToAtInput: {action: this.userZoomToAtInput, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n initiateTransition: {action: NO_OP, defaultTargetState: \"TRANSITION\"},\n };\n\n userZoomByAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"userZoomByAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomByAt\", deltaZoom: payload.deltaZoom, anchorPoint: payload.anchorPoint };\n }\n\n userZoomToAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"userZoomToAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomToAt\", targetZoom: payload.targetZoom, anchorPoint: payload.anchorPoint };\n }\n}\n\n/**\n * State implementation for zoom animations and transitions.\n * Processes animation updates and allows user input to interrupt.\n * @category Input Flow Control\n */\nexport class ZoomTransitionState extends TemplateState<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> {\n\n constructor(){\n super();\n }\n\n protected _eventReactions: EventReactions<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> = {\n lockedOnObjectZoomByAtInput: {action: this.lockedOnObjectZoomByAtInput, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n lockedOnObjectZoomToAtInput: {action: this.lockedOnObjectZoomToAtInput, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n transitionZoomByAtInput: {action: this.transitionZoomByAtInput, defaultTargetState: \"TRANSITION\"},\n transitionZoomToAtInput: {action: this.transitionZoomToAtInput, defaultTargetState: \"TRANSITION\"},\n transitionZoomToAtCenterInput: {action: this.transitionZoomToAtCenterInput, defaultTargetState: \"TRANSITION\"},\n transitionZoomToAtWorldInput: {action: this.transitionZoomToAtWorldInput, defaultTargetState: \"TRANSITION\"},\n userZoomByAtInput: {action: this.userZoomByAtInput, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n userZoomToAtInput: {action: this.userZoomToAtInput, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n }\n\n lockedOnObjectZoomByAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"lockedOnObjectZoomByAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomBy\", deltaZoom: payload.deltaZoom };\n }\n\n lockedOnObjectZoomToAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"lockedOnObjectZoomToAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomTo\", targetZoom: payload.targetZoom };\n }\n\n userZoomByAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"userZoomByAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomByAt\", deltaZoom: payload.deltaZoom, anchorPoint: payload.anchorPoint };\n }\n\n userZoomToAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"userZoomToAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomToAt\", targetZoom: payload.targetZoom, anchorPoint: payload.anchorPoint };\n }\n\n transitionZoomByAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"transitionZoomByAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomByAt\", deltaZoom: payload.deltaZoom, anchorPoint: payload.anchorPoint };\n }\n\n transitionZoomByAtCenterInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"transitionZoomByAtCenterInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomBy\", deltaZoom: payload.deltaZoom };\n }\n\n transitionZoomToAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"transitionZoomToAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomToAt\", targetZoom: payload.targetZoom, anchorPoint: payload.anchorPoint };\n }\n\n transitionZoomToAtCenterInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"transitionZoomToAtCenterInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomTo\", targetZoom: payload.targetZoom };\n }\n\n transitionZoomToAtWorldInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"transitionZoomToAtWorldInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomToAtWorld\", targetZoom: payload.targetZoom, anchorPoint: payload.anchorPoint };\n }\n}\n\n/**\n * State implementation for camera locked to follow an object with zoom.\n * Accepts locked object zoom events and user input to unlock.\n * @category Input Flow Control\n */\nexport class ZoomLockedOnObjectState extends TemplateState<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> {\n\n constructor(){\n super();\n }\n\n protected _eventReactions: EventReactions<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> = {\n lockedOnObjectZoomByAtInput: {action: this.lockedOnObjectZoomByAtInput, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n lockedOnObjectZoomToAtInput: {action: this.lockedOnObjectZoomToAtInput, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n userZoomByAtInput: {action: this.userZoomByAtInput, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n userZoomToAtInput: {action: this.userZoomToAtInput, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n }\n\n lockedOnObjectZoomByAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"lockedOnObjectZoomByAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomByAt\", deltaZoom: payload.deltaZoom, anchorPoint: payload.anchorPoint };\n }\n\n lockedOnObjectZoomToAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"lockedOnObjectZoomToAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomToAt\", targetZoom: payload.targetZoom, anchorPoint: payload.anchorPoint };\n }\n\n userZoomByAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"userZoomByAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomByAt\", deltaZoom: payload.deltaZoom, anchorPoint: payload.anchorPoint };\n }\n\n userZoomToAtInput(context: BaseContext, payload: ZoomEventPayloadMapping[\"userZoomToAtInput\"]): ZoomControlOutputEvent {\n return { type: \"zoomToAt\", targetZoom: payload.targetZoom, anchorPoint: payload.anchorPoint };\n }\n}\n\n/**\n * State machine controlling zoom input flow and animations.\n *\n * @remarks\n * This state machine manages the lifecycle of zoom operations:\n * - **User input handling**: Accepts or blocks user zoom gestures based on state\n * - **Animation control**: Manages smooth zoom-to animations\n * - **Object tracking**: Supports locking camera to follow objects with zoom\n *\n * **State transitions:**\n * - `ACCEPTING_USER_INPUT` → `TRANSITION`: Start animation (`initiateTransition`)\n * - `ACCEPTING_USER_INPUT` → `LOCKED_ON_OBJECT`: Lock to object (`lockedOnObjectZoom...`)\n * - `TRANSITION` → `ACCEPTING_USER_INPUT`: User input interrupts animation\n * - `LOCKED_ON_OBJECT` → `ACCEPTING_USER_INPUT`: User input unlocks\n *\n * Helper methods simplify event dispatching without memorizing event names.\n *\n * @example\n * ```typescript\n * const stateMachine = createDefaultZoomControlStateMachine(cameraRig);\n *\n * // User zooms - accepted in ACCEPTING_USER_INPUT state\n * const result = stateMachine.notifyZoomByAtInput(1.2, { x: 400, y: 300 });\n *\n * // Start animation - transitions to TRANSITION state\n * stateMachine.notifyZoomToAtWorldInput(2.0, { x: 1000, y: 500 });\n *\n * // User input now may interrupt animation\n * ```\n *\n * @category Input Flow Control\n * @see {@link createDefaultZoomControlStateMachine} for factory function\n */\nexport class ZoomControlStateMachine extends TemplateStateMachine<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> {\n\n constructor(states: Record<ZoomControlStates, State<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping>>, initialState: ZoomControlStates, context: BaseContext){\n super(states, initialState, context);\n }\n\n /**\n * Notifies the state machine of user zoom input around an anchor point.\n *\n * @param delta - Zoom delta (multiplier)\n * @param at - Anchor point for zoom\n * @returns Event handling result with output event\n *\n * @remarks\n * Dispatches `userZoomByAtInput` event. Accepted in `ACCEPTING_USER_INPUT` and `TRANSITION` states.\n */\n notifyZoomByAtInput(delta: number, at: Point) {\n return this.happens(\"userZoomByAtInput\", {deltaZoom: delta, anchorPoint: at});\n }\n\n /**\n * Initiates a zoom animation around an anchor point.\n *\n * @param delta - Zoom delta (multiplier)\n * @param at - Anchor point for zoom\n * @returns Event handling result\n *\n * @remarks\n * Dispatches `transitionZoomByAtInput` event, starting a zoom animation.\n */\n notifyZoomByAtInputAnimation(delta: number, at: Point) {\n return this.happens(\"transitionZoomByAtInput\", {deltaZoom: delta, anchorPoint: at});\n }\n\n /**\n * Initiates a zoom animation to target level around center anchor.\n *\n * @param targetZoom - Target zoom level\n * @param at - Anchor point for zoom\n * @returns Event handling result\n *\n * @remarks\n * Dispatches `transitionZoomToAtCenterInput` event for center-anchored zoom animation.\n */\n notifyZoomToAtCenterInput(targetZoom: number, at: Point) {\n return this.happens(\"transitionZoomToAtCenterInput\", {targetZoom: targetZoom, anchorPoint: at});\n }\n\n /**\n * Initiates a zoom animation to target level around world anchor.\n *\n * @param targetZoom - Target zoom level\n * @param at - World anchor point for zoom\n * @returns Event handling result\n *\n * @remarks\n * Dispatches `transitionZoomToAtWorldInput` event for world-anchored zoom animation.\n */\n notifyZoomToAtWorldInput(targetZoom: number, at: Point) {\n return this.happens(\"transitionZoomToAtWorldInput\", {targetZoom: targetZoom, anchorPoint: at});\n }\n\n /**\n * Initiates transition to `TRANSITION` state.\n *\n * @remarks\n * Forces state change to begin animation or transition sequence.\n * Called when starting programmatic camera movements.\n */\n initateTransition() {\n return this.happens(\"initiateTransition\");\n }\n}\n\n/**\n * Creates the default set of zoom control states.\n * @returns State instances for all zoom control states\n * @category Input Flow Control\n */\nexport function createDefaultZoomControlStates(): Record<ZoomControlStates, State<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping>> {\n return {\n ACCEPTING_USER_INPUT: new ZoomAcceptingUserInputState(),\n TRANSITION: new ZoomTransitionState(),\n LOCKED_ON_OBJECT: new ZoomLockedOnObjectState(),\n }\n}\n\n/**\n * Creates a zoom control state machine with default configuration.\n *\n * @param context - Camera rig or context for zoom operations\n * @returns Configured zoom control state machine starting in `ACCEPTING_USER_INPUT` state\n *\n * @remarks\n * Factory function for creating a zoom state machine with sensible defaults.\n * The machine starts in `ACCEPTING_USER_INPUT` state, ready to accept user zoom gestures.\n *\n * @example\n * ```typescript\n * const cameraRig = createDefaultCameraRig(camera);\n * const zoomSM = createDefaultZoomControlStateMachine(cameraRig);\n * ```\n *\n * @category Input Flow Control\n */\nexport function createDefaultZoomControlStateMachine(context: BaseContext = {setup: NO_OP, cleanup: NO_OP}): ZoomControlStateMachine {\n return new ZoomControlStateMachine(createDefaultZoomControlStates(), \"ACCEPTING_USER_INPUT\", context);\n}\n",
31
+ "import type { EventReactions, State, BaseContext } from \"@ue-too/being\";\nimport { NO_OP, TemplateState, TemplateStateMachine } from \"@ue-too/being\";\n\n/**\n * State identifiers for the rotation control state machine.\n *\n * @remarks\n * Three states manage rotation input and animations:\n * - `ACCEPTING_USER_INPUT`: Normal state, accepts user rotation input\n * - `TRANSITION`: Animation/transition state, may block user input\n * - `LOCKED_ON_OBJECT`: Camera locked to follow a specific object rotation\n *\n * @category Input Flow Control\n */\nexport type RotateControlStates = \"ACCEPTING_USER_INPUT\" | \"TRANSITION\" | \"LOCKED_ON_OBJECT\";\n\n/**\n * Payload for rotate-by input events (relative rotation).\n * @category Input Flow Control\n */\nexport type RotateByInputEventPayload = {\n /** Rotation angle delta in radians */\n diff: number;\n};\n\n/**\n * Payload for rotate-to input events (absolute rotation).\n * @category Input Flow Control\n */\nexport type RotateToInputEventPayload = {\n /** Target rotation angle in radians */\n target: number;\n};\n\n/** Empty payload for events that don't need data */\ntype EmptyPayload = {};\n\n/**\n * Event payload type mapping for the rotation control state machine.\n *\n * @remarks\n * Maps event names to their payload types. Events include:\n * - User input events (`userRotateByInput`, `userRotateToInput`)\n * - Transition/animation events (`transitionRotateByInput`, `transitionRotateToInput`)\n * - Locked object events (`lockedOnObjectRotateByInput`, `lockedOnObjectRotateToInput`)\n * - Control events (`unlock`, `initateTransition`)\n *\n * @category Input Flow Control\n */\nexport type RotateEventPayloadMapping = {\n \"userRotateByInput\": RotateByInputEventPayload,\n \"userRotateToInput\": RotateToInputEventPayload,\n \"transitionRotateByInput\": RotateByInputEventPayload,\n \"transitionRotateToInput\": RotateToInputEventPayload,\n \"lockedOnObjectRotateByInput\": RotateByInputEventPayload,\n \"lockedOnObjectRotateToInput\": RotateToInputEventPayload,\n \"unlock\": EmptyPayload,\n \"initateTransition\": EmptyPayload,\n};\n\n/**\n * Discriminated union of output events from rotation control state machine.\n *\n * @remarks\n * Output events instruct the camera system what rotation operation to perform:\n * - `rotateBy`: Relative rotation by delta angle\n * - `rotateTo`: Absolute rotation to target angle\n * - `none`: No operation (input blocked)\n *\n * @category Input Flow Control\n */\nexport type RotateControlOutputEvent =\n | { type: \"rotateBy\", delta: number }\n | { type: \"rotateTo\", target: number }\n | { type: \"none\" };\n\n/**\n * Output event type mapping for rotation control events.\n * Maps input event names to their corresponding output event types.\n *\n * @category Input Flow Control\n */\nexport type RotateControlOutputMapping = {\n \"userRotateByInput\": RotateControlOutputEvent,\n \"userRotateToInput\": RotateControlOutputEvent,\n \"transitionRotateByInput\": RotateControlOutputEvent,\n \"transitionRotateToInput\": RotateControlOutputEvent,\n \"lockedOnObjectRotateByInput\": RotateControlOutputEvent,\n \"lockedOnObjectRotateToInput\": RotateControlOutputEvent,\n};\n\n/**\n * State machine controlling rotation input flow and animations.\n *\n * @remarks\n * This state machine manages the lifecycle of rotation operations:\n * - **User input handling**: Accepts or blocks user rotation gestures based on state\n * - **Animation control**: Manages smooth rotate-to animations\n * - **Object tracking**: Supports locking camera to follow objects with rotation\n *\n * **State transitions:**\n * - `ACCEPTING_USER_INPUT` → `TRANSITION`: Start animation (`initateTransition`)\n * - `ACCEPTING_USER_INPUT` → `LOCKED_ON_OBJECT`: Lock to object (`lockedOnObjectRotate...`)\n * - `TRANSITION` → `ACCEPTING_USER_INPUT`: User input interrupts animation\n * - `LOCKED_ON_OBJECT` → `ACCEPTING_USER_INPUT`: Unlock (`unlock` event)\n *\n * Helper methods simplify event dispatching without memorizing event names.\n *\n * @example\n * ```typescript\n * const stateMachine = createDefaultRotateControlStateMachine(cameraRig);\n *\n * // User rotates - accepted in ACCEPTING_USER_INPUT state\n * const result = stateMachine.notifyRotateByInput(Math.PI / 4);\n *\n * // Start animation - transitions to TRANSITION state\n * stateMachine.notifyRotateToAnimationInput(Math.PI);\n *\n * // User input now blocked while animating\n * ```\n *\n * @category Input Flow Control\n * @see {@link createDefaultRotateControlStateMachine} for factory function\n */\nexport class RotateControlStateMachine extends TemplateStateMachine<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping> {\n\n constructor(states: Record<RotateControlStates, State<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping>>, initialState: RotateControlStates, context: BaseContext){\n super(states, initialState, context);\n }\n\n /**\n * Notifies the state machine of user rotation input.\n *\n * @param diff - Rotation angle delta in radians\n * @returns Event handling result with output event\n *\n * @remarks\n * Dispatches `userRotateByInput` event. Accepted in `ACCEPTING_USER_INPUT` and `TRANSITION` states,\n * where it may transition back to `ACCEPTING_USER_INPUT` (user interrupting animation).\n */\n notifyRotateByInput(diff: number) {\n return this.happens(\"userRotateByInput\", {diff: diff});\n }\n\n /**\n * Initiates a rotation animation to a target angle.\n *\n * @param target - Target rotation angle in radians\n * @returns Event handling result\n *\n * @remarks\n * Dispatches `transitionRotateToInput` event, starting a rotation animation.\n * Transitions to `TRANSITION` state where animation updates occur.\n */\n notifyRotateToAnimationInput(target: number) {\n return this.happens(\"transitionRotateToInput\", {target: target});\n }\n\n /**\n * Initiates transition to `TRANSITION` state.\n *\n * @remarks\n * Forces state change to begin animation or transition sequence.\n * Called when starting programmatic camera movements.\n */\n initateTransition(): void{\n this.happens(\"initateTransition\");\n }\n\n}\n\n/**\n * State implementation for accepting user rotation input (idle/normal state).\n * Accepts user rotation input and can transition to animation or locked states.\n * @category Input Flow Control\n */\nexport class RotationAcceptingUserInputState extends TemplateState<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping> {\n\n constructor(){\n super();\n }\n\n protected _eventReactions: EventReactions<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping> = {\n userRotateByInput: {action: this.userRotateByInputHandler, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n userRotateToInput: {action: this.userRotateToInputHandler, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n lockedOnObjectRotateByInput: {action: this.lockedOnObjectRotateByInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n lockedOnObjectRotateToInput: {action: this.lockedOnObjectRotateToInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n initateTransition: {action: NO_OP, defaultTargetState: \"TRANSITION\"},\n }\n\n userRotateByInputHandler(context: BaseContext, payload: RotateByInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateBy\", delta: payload.diff };\n }\n\n userRotateToInputHandler(context: BaseContext, payload: RotateToInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateTo\", target: payload.target };\n }\n\n lockedOnObjectRotateByInputHandler(context: BaseContext, payload: RotateByInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateBy\", delta: payload.diff };\n }\n\n lockedOnObjectRotateToInputHandler(context: BaseContext, payload: RotateToInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateTo\", target: payload.target };\n }\n\n}\n\n/**\n * State implementation for rotation animations and transitions.\n * Processes animation updates and allows user input to interrupt.\n * @category Input Flow Control\n */\nexport class RotationTransitionState extends TemplateState<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping> {\n\n constructor(){\n super();\n }\n\n protected _eventReactions: EventReactions<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping> = {\n userRotateByInput: {action: this.userRotateByInputHandler, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n userRotateToInput: {action: this.userRotateToInputHandler, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n transitionRotateByInput: {action: this.transitionRotateByInputHandler, defaultTargetState: \"TRANSITION\"},\n transitionRotateToInput: {action: this.transitionRotateToInputHandler, defaultTargetState: \"TRANSITION\"},\n lockedOnObjectRotateByInput: {action: this.lockedOnObjectRotateByInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n lockedOnObjectRotateToInput: {action: this.lockedOnObjectRotateToInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n }\n\n userRotateByInputHandler(context: BaseContext, payload: RotateByInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateBy\", delta: payload.diff };\n }\n\n userRotateToInputHandler(context: BaseContext, payload: RotateToInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateTo\", target: payload.target };\n }\n\n transitionRotateByInputHandler(context: BaseContext, payload: RotateByInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateBy\", delta: payload.diff };\n }\n\n transitionRotateToInputHandler(context: BaseContext, payload: RotateToInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateTo\", target: payload.target };\n }\n\n lockedOnObjectRotateByInputHandler(context: BaseContext, payload: RotateByInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateBy\", delta: payload.diff };\n }\n\n lockedOnObjectRotateToInputHandler(context: BaseContext, payload: RotateToInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateTo\", target: payload.target };\n }\n\n}\n\n/**\n * State implementation for camera locked to follow an object rotation.\n * Only accepts locked object rotation events until unlocked.\n * @category Input Flow Control\n */\nexport class RotationLockedOnObjectState extends TemplateState<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping> {\n\n constructor(){\n super();\n }\n\n protected _eventReactions: EventReactions<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping> = {\n unlock: {action: NO_OP, defaultTargetState: \"ACCEPTING_USER_INPUT\"},\n lockedOnObjectRotateByInput: {action: this.lockedOnObjectRotateByInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n lockedOnObjectRotateToInput: {action: this.lockedOnObjectRotateToInputHandler, defaultTargetState: \"LOCKED_ON_OBJECT\"},\n }\n\n lockedOnObjectRotateByInputHandler(context: BaseContext, payload: RotateByInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateBy\", delta: payload.diff };\n }\n\n lockedOnObjectRotateToInputHandler(context: BaseContext, payload: RotateToInputEventPayload): RotateControlOutputEvent {\n return { type: \"rotateTo\", target: payload.target };\n }\n\n}\n\n/**\n * Creates the default set of rotation control states.\n * @returns State instances for all rotation control states\n * @category Input Flow Control\n */\nexport function createDefaultRotateControlStates(): Record<RotateControlStates, State<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping>> {\n return {\n ACCEPTING_USER_INPUT: new RotationAcceptingUserInputState(),\n TRANSITION: new RotationTransitionState(),\n LOCKED_ON_OBJECT: new RotationLockedOnObjectState(),\n }\n}\n\n/**\n * Creates a rotation control state machine with default configuration.\n *\n * @param context - Camera rig or context for rotation operations\n * @returns Configured rotation control state machine starting in `ACCEPTING_USER_INPUT` state\n *\n * @remarks\n * Factory function for creating a rotation state machine with sensible defaults.\n * The machine starts in `ACCEPTING_USER_INPUT` state, ready to accept user rotation gestures.\n *\n * @example\n * ```typescript\n * const cameraRig = createDefaultCameraRig(camera);\n * const rotateSM = createDefaultRotateControlStateMachine(cameraRig);\n * ```\n *\n * @category Input Flow Control\n */\nexport function createDefaultRotateControlStateMachine(context: BaseContext = {setup: NO_OP, cleanup: NO_OP}): RotateControlStateMachine {\n return new RotateControlStateMachine(createDefaultRotateControlStates(), \"ACCEPTING_USER_INPUT\", context);\n}\n",
32
+ "\nimport { CameraMux, CameraMuxPanOutput, CameraMuxZoomOutput, CameraMuxRotationOutput } from \"../interface\";\nimport { Point } from \"@ue-too/math\";\nimport { ObservableBoardCamera } from \"../../interface\";\nimport { createDefaultPanControlStateMachine, PanControlStateMachine, PanControlOutputEvent } from \"./pan-control-state-machine\";\nimport { createDefaultZoomControlStateMachine, ZoomControlStateMachine, ZoomControlOutputEvent } from \"./zoom-control-state-machine\";\nimport { createDefaultRotateControlStateMachine, RotateControlStateMachine, RotateControlOutputEvent } from \"./rotation-control-state-machine\";\nimport { CameraRig } from \"../../camera-rig\";\nimport { createDefaultCameraRig } from \"../../camera-rig\";\n\n/**\n * Advanced camera input multiplexer with animation support and input locking via state machines.\n *\n * @remarks\n * This {@link CameraMux} implementation provides sophisticated input flow control using\n * separate state machines for pan, zoom, and rotation. Each state machine can:\n * - Block user input during camera animations\n * - Manage animation playback\n * - Arbitrate between user input and programmatic camera control\n * - Handle transitions between different camera control states\n *\n * **Key features:**\n * - **Animation system**: Support for smooth camera animations (pan-to, zoom-to, rotate-to)\n * - **Input locking**: Automatically block user input during animations\n * - **State-based control**: Each camera operation (pan/zoom/rotate) has its own state machine\n * - **Flexible transitions**: Initiate transitions to interrupt or chain animations\n *\n * **Architecture:**\n * - Three independent state machines: {@link PanControlStateMachine}, {@link ZoomControlStateMachine}, {@link RotateControlStateMachine}\n * - Each state machine decides whether to allow or block input based on current state\n * - State machines receive events and produce output events for camera operations\n *\n * **When to use:**\n * - Applications requiring smooth camera animations (e.g., \"focus on object\", \"zoom to region\")\n * - UI where user input should be blocked during programmatic camera movements\n * - Games or interactive experiences with scripted camera sequences\n *\n * **Alternatives:**\n * - Use {@link Relay} for simple passthrough without animation support\n * - Implement custom {@link CameraMux} for different state management approaches\n *\n * @example\n * ```typescript\n * const camera = new DefaultBoardCamera();\n * const mux = createCameraMuxWithAnimationAndLock(camera);\n *\n * // Start a pan animation - user input will be blocked\n * mux.notifyPanToAnimationInput({ x: 1000, y: 500 });\n *\n * // User tries to pan during animation - will be blocked\n * const result = mux.notifyPanInput({ x: 50, y: 30 });\n * // result.allowPassThrough = false (blocked during animation)\n *\n * // After animation completes, user input allowed again\n * ```\n *\n * @category Input Flow Control\n * @see {@link CameraMux} for the interface definition\n * @see {@link Relay} for simpler passthrough implementation\n * @see {@link createCameraMuxWithAnimationAndLock} for factory function\n */\nexport class CameraMuxWithAnimationAndLock implements CameraMux {\n\n private _panStateMachine: PanControlStateMachine;\n private _zoomStateMachine: ZoomControlStateMachine;\n private _rotateStateMachine: RotateControlStateMachine;\n\n /**\n * Creates a new camera mux with animation and locking capabilities.\n *\n * @param panStateMachine - State machine controlling pan operations and animations\n * @param zoomStateMachine - State machine controlling zoom operations and animations\n * @param rotateStateMachine - State machine controlling rotation operations and animations\n *\n * @remarks\n * Typically created via factory functions like {@link createCameraMuxWithAnimationAndLock}\n * rather than direct instantiation.\n */\n constructor(panStateMachine: PanControlStateMachine, zoomStateMachine: ZoomControlStateMachine, rotateStateMachine: RotateControlStateMachine){\n this._panStateMachine = panStateMachine;\n this._zoomStateMachine = zoomStateMachine;\n this._rotateStateMachine = rotateStateMachine;\n }\n\n /**\n * Initiates a pan animation to a target position.\n *\n * @param target - Target position in world coordinates\n * @returns Pan output indicating whether animation was initiated\n *\n * @remarks\n * This method starts a camera pan animation to the specified world position.\n * The state machine handles:\n * - Starting the animation\n * - Blocking user input during animation\n * - Producing incremental pan deltas each frame\n *\n * The animation continues until the camera reaches the target or is interrupted.\n *\n * @example\n * ```typescript\n * // Animate camera to world position\n * mux.notifyPanToAnimationInput({ x: 1000, y: 500 });\n * ```\n */\n notifyPanToAnimationInput(target: Point): CameraMuxPanOutput {\n const res = this._panStateMachine.notifyPanToAnimationInput(target);\n\n if(res.handled) {\n const output = res.output;\n if(output !== undefined){\n switch(output.type){\n case 'panByViewPort':\n return { allowPassThrough: true, delta: output.delta };\n case 'panToWorld':\n return { allowPassThrough: true, delta: output.target };\n default:\n return { allowPassThrough: false };\n }\n\n }\n }\n return { allowPassThrough: false };\n }\n\n /**\n * Processes user pan input (implements {@link CameraMux}).\n *\n * @param delta - Pan delta in viewport coordinates\n * @returns Output indicating whether pan is allowed\n *\n * @remarks\n * This method is called when the user attempts to pan the camera (e.g., mouse drag).\n * The pan state machine determines whether to allow the input based on current state:\n * - **Allowed**: When in idle state or user control state\n * - **Blocked**: When camera animation is playing\n *\n * @example\n * ```typescript\n * // User drags mouse\n * const result = mux.notifyPanInput({ x: 50, y: 30 });\n * if (result.allowPassThrough) {\n * // Apply pan to camera\n * cameraRig.panByViewPort(result.delta);\n * }\n * ```\n */\n notifyPanInput(delta: Point): CameraMuxPanOutput {\n const result = this._panStateMachine.happens(\"userPanByInput\", { diff: delta });\n if (result.handled && 'output' in result && result.output) {\n const output = result.output as PanControlOutputEvent;\n if (output.type !== \"none\") {\n return { allowPassThrough: true, delta: delta };\n }\n }\n return { allowPassThrough: false };\n }\n\n /**\n * Processes user zoom input (implements {@link CameraMux}).\n *\n * @param delta - Zoom delta (change in zoom level)\n * @param at - Anchor point in viewport coordinates\n * @returns Output indicating whether zoom is allowed\n *\n * @remarks\n * This method is called when the user attempts to zoom (e.g., mouse wheel).\n * The zoom state machine determines whether to allow the input based on current state:\n * - **Allowed**: When in idle state or user control state\n * - **Blocked**: When zoom animation is playing\n *\n * @example\n * ```typescript\n * // User scrolls mouse wheel\n * const result = mux.notifyZoomInput(0.1, mousePosition);\n * if (result.allowPassThrough) {\n * // Apply zoom to camera\n * cameraRig.zoomByAt(result.delta, result.anchorPoint);\n * }\n * ```\n */\n notifyZoomInput(delta: number, at: Point): CameraMuxZoomOutput {\n const result = this._zoomStateMachine.happens(\"userZoomByAtInput\", { deltaZoom: delta, anchorPoint: at });\n if (result.handled && 'output' in result && result.output) {\n const output = result.output as ZoomControlOutputEvent;\n if (output.type !== \"none\") {\n return { allowPassThrough: true, delta: delta, anchorPoint: at };\n }\n }\n return { allowPassThrough: false };\n }\n\n /**\n * Processes user rotation input (rotate-by variant).\n *\n * @param delta - Rotation delta in radians\n * @returns Output from rotation state machine\n *\n * @remarks\n * Delegates to the rotation state machine's rotate-by handler.\n * The state machine determines whether to allow rotation based on current state.\n */\n notifyRotateByInput(delta: number) {\n return this._rotateStateMachine.notifyRotateByInput(delta);\n }\n\n /**\n * Initiates a rotation animation to a target angle.\n *\n * @param target - Target rotation angle in radians\n * @returns Output from rotation state machine\n *\n * @remarks\n * Starts a camera rotation animation to the specified angle.\n * User input will be blocked during the animation.\n */\n notifyRotateToAnimationInput(target: number) {\n return this._rotateStateMachine.notifyRotateToAnimationInput(target);\n }\n\n /**\n * Initiates a zoom animation to a target level at a viewport position.\n *\n * @param targetZoom - Target zoom level\n * @param at - Anchor point in viewport coordinates (defaults to origin)\n *\n * @remarks\n * Starts a zoom animation that zooms to the specified level while keeping\n * the anchor point stationary (zoom-to-cursor behavior).\n * User input will be blocked during the animation.\n */\n notifyZoomInputAnimation(targetZoom: number, at: Point = {x: 0, y: 0}): void {\n this._zoomStateMachine.notifyZoomToAtCenterInput(targetZoom, at);\n }\n\n /**\n * Initiates a zoom animation to a target level at a world position.\n *\n * @param targetZoom - Target zoom level\n * @param at - Anchor point in world coordinates (defaults to origin)\n *\n * @remarks\n * Similar to {@link notifyZoomInputAnimation} but accepts world-space coordinates\n * for the anchor point instead of viewport coordinates.\n */\n notifyZoomInputAnimationWorld(targetZoom: number, at: Point = {x: 0, y: 0}): void {\n this._zoomStateMachine.notifyZoomToAtWorldInput(targetZoom, at);\n }\n\n /**\n * Processes user rotation input (implements {@link CameraMux}).\n *\n * @param delta - Rotation delta in radians\n * @returns Output indicating whether rotation is allowed\n *\n * @remarks\n * This method is called when the user attempts to rotate the camera.\n * The rotation state machine determines whether to allow the input based on current state:\n * - **Allowed**: When in idle state or user control state\n * - **Blocked**: When rotation animation is playing\n *\n * @example\n * ```typescript\n * // User rotates camera\n * const result = mux.notifyRotationInput(0.1);\n * if (result.allowPassThrough) {\n * cameraRig.rotateBy(result.delta);\n * }\n * ```\n */\n notifyRotationInput(delta: number): CameraMuxRotationOutput {\n const result = this._rotateStateMachine.happens(\"userRotateByInput\", { diff: delta });\n if (result.handled && 'output' in result && result.output) {\n const output = result.output as RotateControlOutputEvent;\n if (output.type !== \"none\") {\n return { allowPassThrough: true, delta: delta };\n }\n }\n return { allowPassThrough: false };\n }\n\n /**\n * Initiates a transition in the pan state machine.\n *\n * @remarks\n * This method forces the pan state machine to transition to its next state.\n * Can be used to interrupt animations or force state changes.\n */\n initatePanTransition(): void {\n this._panStateMachine.initateTransition();\n }\n\n /**\n * Initiates a transition in the zoom state machine.\n *\n * @remarks\n * This method forces the zoom state machine to transition to its next state.\n * Can be used to interrupt animations or force state changes.\n */\n initateZoomTransition(): void {\n this._zoomStateMachine.initateTransition();\n }\n\n /**\n * Initiates a transition in the rotation state machine.\n *\n * @remarks\n * This method forces the rotation state machine to transition to its next state.\n * Can be used to interrupt animations or force state changes.\n */\n initateRotateTransition(): void {\n this._rotateStateMachine.initateTransition();\n }\n\n /**\n * Gets the rotation state machine.\n *\n * @returns The rotation state machine instance\n *\n * @remarks\n * Provides direct access to the rotation state machine for advanced control\n * or state inspection.\n */\n get rotateStateMachine(): RotateControlStateMachine {\n return this._rotateStateMachine;\n }\n\n /**\n * Gets the pan state machine.\n *\n * @returns The pan state machine instance\n *\n * @remarks\n * Provides direct access to the pan state machine for advanced control\n * or state inspection.\n */\n get panStateMachine(): PanControlStateMachine {\n return this._panStateMachine;\n }\n\n /**\n * Gets the zoom state machine.\n *\n * @returns The zoom state machine instance\n *\n * @remarks\n * Provides direct access to the zoom state machine for advanced control\n * or state inspection.\n */\n get zoomStateMachine(): ZoomControlStateMachine {\n return this._zoomStateMachine;\n }\n}\n\n/**\n * Creates a camera mux with animation and locking capabilities from a camera instance.\n *\n * @param camera - Observable camera to control\n * @returns Configured camera mux with animation support\n *\n * @remarks\n * This factory function creates a complete camera input flow control system with:\n * 1. A default {@link CameraRig} wrapping the provided camera\n * 2. Three state machines (pan, zoom, rotation) for animation control\n * 3. A {@link CameraMuxWithAnimationAndLock} coordinating the state machines\n *\n * **What you get:**\n * - Smooth camera animations (pan-to, zoom-to, rotate-to)\n * - Automatic input blocking during animations\n * - State-based input arbitration\n * - All with sensible default configurations\n *\n * **Use this when:**\n * - You have a camera and want animation support out-of-the-box\n * - You don't need custom camera rig configuration\n * - You want the simplest setup for animated camera control\n *\n * @example\n * ```typescript\n * const camera = new DefaultBoardCamera(1920, 1080);\n * const mux = createCameraMuxWithAnimationAndLock(camera);\n *\n * // Start a pan animation\n * mux.notifyPanToAnimationInput({ x: 1000, y: 500 });\n *\n * // User input is blocked during animation\n * const result = mux.notifyPanInput({ x: 50, y: 30 });\n * console.log(result.allowPassThrough); // false during animation\n * ```\n *\n * @category Input Flow Control\n * @see {@link CameraMuxWithAnimationAndLock} for the implementation\n * @see {@link createCameraMuxWithAnimationAndLockWithCameraRig} for custom rig version\n */\nexport function createCameraMuxWithAnimationAndLock(): CameraMux {\n const panStateMachine = createDefaultPanControlStateMachine();\n const zoomStateMachine = createDefaultZoomControlStateMachine();\n const rotateStateMachine = createDefaultRotateControlStateMachine();\n return new CameraMuxWithAnimationAndLock(panStateMachine, zoomStateMachine, rotateStateMachine);\n}\n",
32
33
  "import { BoardCamera } from \"../interface\";\nimport { createHandlerChain } from \"../../utils/handler-pipeline\";\nimport { clampZoomLevel } from \"../utils/zoom\";\n\n/**\n * Combined configuration for zoom handler behavior, merging restriction and clamping settings.\n *\n * @remarks\n * This type combines {@link ZoomHandlerClampConfig} and {@link ZoomHandlerRestrictConfig}\n * to provide complete control over camera zoom behavior.\n *\n * Zoom handlers use this configuration to:\n * - Completely disable zoom operations (restriction)\n * - Clamp zoom level to stay within defined limits (min/max bounds)\n *\n * @category Camera Rig\n * @see {@link ZoomHandlerClampConfig} for boundary clamping options\n * @see {@link ZoomHandlerRestrictConfig} for zoom disabling options\n */\nexport type ZoomHandlerConfig = ZoomHandlerClampConfig & ZoomHandlerRestrictConfig;\n\n/**\n * Configuration for zoom level boundary clamping.\n *\n * @remarks\n * Controls whether zoom operations should be constrained to camera's zoom boundaries.\n *\n * When `clampZoom` is true, zoom handlers enforce {@link BoardCamera.zoomBoundaries}\n * limits (min/max zoom levels). When false, zoom can exceed configured boundaries.\n *\n * @example\n * ```typescript\n * const config: ZoomHandlerClampConfig = {\n * clampZoom: true // Enforce zoom boundaries\n * };\n *\n * camera.zoomBoundaries = { min: 0.5, max: 4.0 };\n * // Zoom will be clamped to [0.5, 4.0] range\n * ```\n *\n * @category Camera Rig\n */\nexport type ZoomHandlerClampConfig = {\n /**\n * Whether to enforce zoom level boundaries.\n */\n clampZoom: boolean;\n};\n\n/**\n * Configuration for completely disabling zoom operations.\n *\n * @remarks\n * Provides a global \"zoom lock\" to prevent any zoom changes.\n *\n * When `restrictZoom` is true:\n * - Zoom-to operations return current zoom level (no change)\n * - Zoom-by operations return zero delta (no change)\n *\n * This is useful for:\n * - Locking zoom during specific application states\n * - Fixed-zoom viewing modes\n * - Preventing user zoom in certain contexts\n *\n * @example\n * ```typescript\n * const config: ZoomHandlerRestrictConfig = {\n * restrictZoom: true // Disable all zoom operations\n * };\n *\n * // Any zoom attempt will be ignored\n * ```\n *\n * @category Camera Rig\n */\nexport type ZoomHandlerRestrictConfig = {\n /**\n * Whether to completely prevent zoom operations.\n */\n restrictZoom: boolean;\n};\n\n/**\n * Handler function type for absolute \"zoom to\" camera operations.\n *\n * @param destination - Target zoom level\n * @param camera - Current camera instance\n * @param config - Zoom behavior configuration\n * @returns Transformed zoom level (after applying restrictions and clamping)\n *\n * @remarks\n * Zoom-to handlers process absolute zoom level requests. They form a pipeline\n * that can apply restrictions, clamping, and other transformations.\n *\n * Handler pipeline pattern:\n * - Each handler receives the target zoom, camera state, and config\n * - Returns a potentially modified zoom level\n * - Handlers can be chained using {@link createHandlerChain}\n *\n * Common transformations:\n * - Boundary clamping (enforce min/max zoom limits)\n * - Zoom locking (prevent any zoom changes)\n * - Custom zoom constraints or snapping\n *\n * @example\n * ```typescript\n * const myZoomToHandler: ZoomToHandlerFunction = (target, camera, config) => {\n * // Custom logic: snap to integer zoom levels\n * return Math.round(target);\n * };\n * ```\n *\n * @category Camera Rig\n * @see {@link createHandlerChain} for composing handler pipelines\n * @see {@link createDefaultZoomToOnlyHandler} for the default implementation\n */\nexport type ZoomToHandlerFunction = (destination: number, camera: BoardCamera, config: ZoomHandlerConfig) => number;\n\n/**\n * Handler function type for relative \"zoom by\" camera operations.\n *\n * @param delta - Zoom level change (added to current zoom)\n * @param camera - Current camera instance\n * @param config - Zoom behavior configuration\n * @returns Transformed zoom delta (after applying restrictions and clamping)\n *\n * @remarks\n * Zoom-by handlers process relative zoom change requests. They form a pipeline\n * that can apply restrictions, clamping, and other transformations to the delta.\n *\n * Handler pipeline pattern:\n * - Each handler receives the zoom delta, camera state, and config\n * - Returns a potentially modified delta\n * - Handlers can be chained using {@link createHandlerChain}\n *\n * Common transformations:\n * - Boundary clamping (prevent exceeding min/max zoom)\n * - Zoom locking (return zero delta)\n * - Delta dampening or acceleration\n *\n * @example\n * ```typescript\n * const myZoomByHandler: ZoomByHandlerFunction = (delta, camera, config) => {\n * // Custom logic: dampen large zoom changes\n * if (Math.abs(delta) > 1.0) {\n * return delta * 0.5; // 50% dampening\n * }\n * return delta;\n * };\n * ```\n *\n * @category Camera Rig\n * @see {@link createHandlerChain} for composing handler pipelines\n * @see {@link createDefaultZoomByOnlyHandler} for the default implementation\n */\nexport type ZoomByHandlerFunction = (delta: number, camera: BoardCamera, config: ZoomHandlerConfig) => number;\n\n/**\n * Handler pipeline step that clamps \"zoom to\" targets to camera zoom boundaries.\n *\n * @param destination - Target zoom level\n * @param camera - Current camera instance (provides zoomBoundaries)\n * @param config - Clamping configuration\n * @returns Clamped zoom level\n *\n * @remarks\n * This handler enforces zoom level limits on absolute zoom requests.\n *\n * Behavior:\n * - If `clampZoom` is false: Returns destination unchanged\n * - If `clampZoom` is true: Clamps destination to {@link BoardCamera.zoomBoundaries} (min/max)\n *\n * The clamping is performed by {@link clampZoomLevel}, which handles:\n * - Missing boundaries (undefined min/max)\n * - One-sided constraints (only min or only max)\n * - Full range constraints\n *\n * Can be used standalone, but typically composed into a handler pipeline via\n * {@link createDefaultZoomToOnlyHandler} or {@link createHandlerChain}.\n *\n * @example\n * ```typescript\n * camera.zoomBoundaries = { min: 0.5, max: 3.0 };\n *\n * const config: ZoomHandlerClampConfig = {\n * clampZoom: true\n * };\n *\n * const target = 5.0; // Exceeds max\n * const clamped = clampZoomToHandler(target, camera, config);\n * // clamped = 3.0 (clamped to max boundary)\n * ```\n *\n * @category Camera Rig\n * @see {@link clampZoomLevel} for clamping implementation\n * @see {@link createDefaultZoomToOnlyHandler} for default pipeline usage\n */\nexport function clampZoomToHandler(destination: number, camera: BoardCamera, config: ZoomHandlerClampConfig): number {\n if(!config.clampZoom){\n return destination;\n }\n return clampZoomLevel(destination, camera.zoomBoundaries);\n}\n\n/**\n * Handler pipeline step that clamps \"zoom by\" deltas to prevent boundary violations.\n *\n * @param delta - Zoom level change\n * @param camera - Current camera instance (provides current zoom and boundaries)\n * @param config - Clamping configuration\n * @returns Adjusted delta that respects zoom boundaries\n *\n * @remarks\n * This handler ensures that applying the delta won't exceed zoom boundaries.\n *\n * Algorithm:\n * 1. Calculate potential new zoom level (current + delta)\n * 2. Clamp that level to boundaries\n * 3. Return the difference (clamped - current) as the new delta\n *\n * Behavior:\n * - If `clampZoom` is false: Returns delta unchanged\n * - If `clampZoom` is true: Adjusts delta to stay within boundaries\n *\n * The resulting delta may be zero if already at a boundary and trying to zoom further.\n *\n * Can be used standalone, but typically composed into a handler pipeline via\n * {@link createDefaultZoomByOnlyHandler} or {@link createHandlerChain}.\n *\n * @example\n * ```typescript\n * camera.zoomLevel = 2.8;\n * camera.zoomBoundaries = { max: 3.0 };\n *\n * const config: ZoomHandlerClampConfig = {\n * clampZoom: true\n * };\n *\n * const delta = 0.5; // Would exceed max\n * const clamped = clampZoomByHandler(delta, camera, config);\n * // clamped = 0.2 (only zoom to boundary, not beyond)\n * ```\n *\n * @category Camera Rig\n * @see {@link clampZoomLevel} for clamping implementation\n * @see {@link createDefaultZoomByOnlyHandler} for default pipeline usage\n */\nexport function clampZoomByHandler(delta: number, camera: BoardCamera, config: ZoomHandlerClampConfig): number {\n if(!config.clampZoom){\n return delta;\n }\n let targetZoom = camera.zoomLevel + delta;\n targetZoom = clampZoomLevel(targetZoom, camera.zoomBoundaries);\n delta = targetZoom - camera.zoomLevel;\n return delta;\n}\n\n/**\n * Handler pipeline step that prevents \"zoom to\" operations when zoom is locked.\n *\n * @param destination - Target zoom level\n * @param camera - Current camera instance\n * @param config - Restriction configuration\n * @returns Current zoom level (if locked) or destination (if unlocked)\n *\n * @remarks\n * This handler implements a global zoom lock for absolute zoom operations.\n *\n * Behavior:\n * - If `restrictZoom` is true: Returns current zoom level (prevents any change)\n * - If `restrictZoom` is false: Returns destination unchanged\n *\n * Use this for:\n * - Disabling zoom during specific application states\n * - Fixed-zoom viewing modes\n * - Read-only camera modes\n *\n * Can be used standalone, but typically composed into a handler pipeline via\n * {@link createDefaultZoomToOnlyHandler} or {@link createHandlerChain}.\n *\n * @example\n * ```typescript\n * camera.zoomLevel = 2.0;\n *\n * const config: ZoomHandlerRestrictConfig = {\n * restrictZoom: true // Lock zoom\n * };\n *\n * const target = 3.0;\n * const result = restrictZoomToHandler(target, camera, config);\n * // result = 2.0 (zoom locked, returns current level)\n * ```\n *\n * @category Camera Rig\n * @see {@link createDefaultZoomToOnlyHandler} for default pipeline usage\n */\nexport function restrictZoomToHandler(destination: number, camera: BoardCamera, config: ZoomHandlerRestrictConfig): number {\n if(config.restrictZoom){\n return camera.zoomLevel;\n }\n return destination;\n}\n\n/**\n * Handler pipeline step that prevents \"zoom by\" operations when zoom is locked.\n *\n * @param delta - Zoom level change\n * @param camera - Current camera instance\n * @param config - Restriction configuration\n * @returns Zero (if locked) or delta (if unlocked)\n *\n * @remarks\n * This handler implements a global zoom lock for relative zoom operations.\n *\n * Behavior:\n * - If `restrictZoom` is true: Returns 0 (prevents any change)\n * - If `restrictZoom` is false: Returns delta unchanged\n *\n * Use this for:\n * - Disabling zoom during specific application states\n * - Fixed-zoom viewing modes\n * - Read-only camera modes\n *\n * Can be used standalone, but typically composed into a handler pipeline via\n * {@link createDefaultZoomByOnlyHandler} or {@link createHandlerChain}.\n *\n * @example\n * ```typescript\n * const config: ZoomHandlerRestrictConfig = {\n * restrictZoom: true // Lock zoom\n * };\n *\n * const delta = 0.5;\n * const result = restrictZoomByHandler(delta, camera, config);\n * // result = 0 (zoom locked, no change allowed)\n * ```\n *\n * @category Camera Rig\n * @see {@link createDefaultZoomByOnlyHandler} for default pipeline usage\n */\nexport function restrictZoomByHandler(delta: number, camera: BoardCamera, config: ZoomHandlerRestrictConfig): number {\n if(config.restrictZoom){\n return 0;\n }\n return delta;\n}\n\n/**\n * Creates a default \"zoom to\" handler pipeline for absolute zoom operations.\n *\n * @returns Zoom-to handler function with clamping and restriction\n *\n * @remarks\n * The default handler pipeline applies transformations in this order:\n * 1. **Clamping** ({@link clampZoomToHandler}): Clamps zoom to configured boundaries\n * 2. **Restriction** ({@link restrictZoomToHandler}): Prevents zoom if locked\n *\n * This ensures that:\n * - Zoom level stays within configured min/max boundaries\n * - Zoom can be completely disabled via `restrictZoom` flag\n *\n * The pipeline is specifically for zoom operations without pan compensation.\n * For zoom-at-point operations, use {@link DefaultCameraRig.zoomToAt} which combines\n * zoom and pan handlers.\n *\n * @example\n * ```typescript\n * const zoomTo = createDefaultZoomToOnlyHandler();\n *\n * camera.zoomBoundaries = { min: 0.5, max: 4.0 };\n *\n * // Use in camera rig\n * const target = 5.0; // Exceeds max\n * const constrained = zoomTo(target, camera, {\n * clampZoom: true,\n * restrictZoom: false\n * });\n * // constrained = 4.0 (clamped to max boundary)\n * camera.setZoomLevel(constrained);\n * ```\n *\n * @example\n * ```typescript\n * // Create custom pipeline\n * const customZoomTo = createHandlerChain<number, [BoardCamera, ZoomHandlerConfig]>(\n * clampZoomToHandler, // From default\n * myCustomZoomHandler, // Your custom logic\n * restrictZoomToHandler // From default\n * );\n * ```\n *\n * @category Camera Rig\n * @see {@link createHandlerChain} for creating custom handler pipelines\n * @see {@link clampZoomToHandler} for the clamping step\n * @see {@link restrictZoomToHandler} for the restriction step\n */\nexport function createDefaultZoomToOnlyHandler(): ZoomToHandlerFunction {\n return createHandlerChain<number, [BoardCamera, ZoomHandlerConfig]>(\n clampZoomToHandler,\n restrictZoomToHandler,\n );\n}\n\n/**\n * Creates a default \"zoom by\" handler pipeline for relative zoom operations.\n *\n * @returns Zoom-by handler function with clamping and restriction\n *\n * @remarks\n * The default handler pipeline applies transformations in this order:\n * 1. **Clamping** ({@link clampZoomByHandler}): Adjusts delta to respect boundaries\n * 2. **Restriction** ({@link restrictZoomByHandler}): Returns zero delta if locked\n *\n * This ensures that:\n * - Resulting zoom level stays within configured min/max boundaries\n * - Zoom can be completely disabled via `restrictZoom` flag\n * - Delta is adjusted to prevent boundary violations\n *\n * The pipeline is specifically for zoom operations without pan compensation.\n * For zoom-at-point operations, use {@link DefaultCameraRig.zoomByAt} which combines\n * zoom and pan handlers.\n *\n * @example\n * ```typescript\n * const zoomBy = createDefaultZoomByOnlyHandler();\n *\n * camera.zoomLevel = 3.5;\n * camera.zoomBoundaries = { max: 4.0 };\n *\n * // Use in camera rig\n * const delta = 1.0; // Would exceed max\n * const constrained = zoomBy(delta, camera, {\n * clampZoom: true,\n * restrictZoom: false\n * });\n * // constrained = 0.5 (adjusted to reach boundary exactly)\n * camera.setZoomLevel(camera.zoomLevel + constrained);\n * ```\n *\n * @example\n * ```typescript\n * // Create custom pipeline with dampening\n * const dampenedZoomBy = createHandlerChain<number, [BoardCamera, ZoomHandlerConfig]>(\n * (delta) => delta * 0.7, // 30% dampening\n * clampZoomByHandler, // From default\n * restrictZoomByHandler // From default\n * );\n * ```\n *\n * @category Camera Rig\n * @see {@link createHandlerChain} for creating custom handler pipelines\n * @see {@link clampZoomByHandler} for the clamping step\n * @see {@link restrictZoomByHandler} for the restriction step\n */\nexport function createDefaultZoomByOnlyHandler(): ZoomByHandlerFunction {\n return createHandlerChain<number, [BoardCamera, ZoomHandlerConfig]>(\n clampZoomByHandler,\n restrictZoomByHandler,\n );\n}\n",
33
34
  "import { Point, PointCal } from \"@ue-too/math\";\nimport { BoardCamera } from \"../interface\";\nimport { createHandlerChain } from \"../../utils/handler-pipeline\";\nimport { clampPoint, clampPointEntireViewPort } from \"../utils/position\";\n\n/**\n * Combined configuration for pan handler behavior, merging restriction and clamping settings.\n *\n * @remarks\n * This type combines {@link PanHandlerRestrictionConfig} and {@link PanHandlerClampConfig}\n * to provide complete control over camera panning behavior.\n *\n * Pan handlers use this configuration to:\n * - Restrict movement along specific axes (world or viewport-relative)\n * - Clamp camera position to stay within boundaries\n * - Control whether entire viewport or just center must stay in bounds\n *\n * @category Camera Rig\n * @see {@link PanHandlerRestrictionConfig} for movement restriction options\n * @see {@link PanHandlerClampConfig} for boundary clamping options\n */\nexport type PanHandlerConfig = PanHandlerRestrictionConfig & PanHandlerClampConfig;\n\n/**\n * Configuration for boundary clamping behavior during camera panning.\n *\n * @remarks\n * Controls how camera position is constrained to stay within defined boundaries.\n *\n * @property limitEntireViewPort - When true, ensures the entire viewport rectangle stays within boundaries.\n * When false, only the camera center point (position) is constrained.\n * This affects how {@link BoardCamera.boundaries} are interpreted.\n *\n * @property clampTranslation - When true, enforces boundary constraints on pan operations.\n * When false, camera can pan freely outside boundaries.\n *\n * @example\n * ```typescript\n * const config: PanHandlerClampConfig = {\n * limitEntireViewPort: true, // Entire view must stay in bounds\n * clampTranslation: true // Enforce boundaries\n * };\n * ```\n *\n * @category Camera Rig\n */\nexport type PanHandlerClampConfig = {\n /**\n * Whether to constrain the entire viewport or just the camera center.\n */\n limitEntireViewPort: boolean;\n /**\n * Whether to enforce boundary constraints on panning.\n */\n clampTranslation: boolean;\n};\n\n/**\n * Configuration for restricting camera movement along specific axes.\n *\n * @remarks\n * Provides fine-grained control over which directions the camera can move.\n * Supports both world-space restrictions (absolute X/Y) and viewport-relative\n * restrictions (screen-space horizontal/vertical, accounting for rotation).\n *\n * **World-space restrictions:**\n * - `restrictXTranslation`: Prevents movement along world X axis\n * - `restrictYTranslation`: Prevents movement along world Y axis\n *\n * **Viewport-relative restrictions (rotation-aware):**\n * - `restrictRelativeXTranslation`: Prevents horizontal movement (screen-space)\n * - `restrictRelativeYTranslation`: Prevents vertical movement (screen-space)\n *\n * Use cases:\n * - Side-scrolling games: `restrictYTranslation = true`\n * - Locked vertical scrolling: `restrictRelativeYTranslation = true`\n * - Fixed-axis pan tools in editors\n *\n * @example\n * ```typescript\n * // Side-scroller: only allow horizontal movement in world space\n * const config: PanHandlerRestrictionConfig = {\n * restrictXTranslation: false,\n * restrictYTranslation: true,\n * restrictRelativeXTranslation: false,\n * restrictRelativeYTranslation: false\n * };\n *\n * // Lock to vertical screen movement only (with camera rotation)\n * const screenConfig: PanHandlerRestrictionConfig = {\n * restrictXTranslation: false,\n * restrictYTranslation: false,\n * restrictRelativeXTranslation: true,\n * restrictRelativeYTranslation: false\n * };\n * ```\n *\n * @category Camera Rig\n */\nexport type PanHandlerRestrictionConfig = {\n /**\n * Whether to prevent movement along the world X axis.\n */\n restrictXTranslation: boolean;\n /**\n * Whether to prevent movement along the world Y axis.\n */\n restrictYTranslation: boolean;\n /**\n * Whether to prevent horizontal movement in viewport/screen space.\n * Accounts for camera rotation - locks movement perpendicular to screen's vertical direction.\n */\n restrictRelativeXTranslation: boolean;\n /**\n * Whether to prevent vertical movement in viewport/screen space.\n * Accounts for camera rotation - locks movement perpendicular to screen's horizontal direction.\n */\n restrictRelativeYTranslation: boolean;\n};\n\n/**\n * Handler function type for absolute \"pan to\" camera operations.\n *\n * @param destination - Target camera position in world space\n * @param camera - Current camera instance\n * @param config - Pan behavior configuration\n * @returns Transformed destination position (after applying restrictions and clamping)\n *\n * @remarks\n * Pan-to handlers process absolute camera positioning requests. They form a pipeline\n * that can apply restrictions, clamping, and other transformations to the target position.\n *\n * Handler pipeline pattern:\n * - Each handler receives the current destination, camera state, and config\n * - Returns a potentially modified destination point\n * - Handlers can be chained using {@link createHandlerChain}\n *\n * Common transformations:\n * - Axis restrictions (prevent movement on specific axes)\n * - Boundary clamping (keep position within bounds)\n * - Viewport constraints (ensure entire viewport stays in bounds)\n *\n * @example\n * ```typescript\n * const myPanToHandler: PanToHandlerFunction = (dest, camera, config) => {\n * // Custom logic: snap to grid\n * return {\n * x: Math.round(dest.x / 100) * 100,\n * y: Math.round(dest.y / 100) * 100\n * };\n * };\n * ```\n *\n * @category Camera Rig\n * @see {@link createHandlerChain} for composing handler pipelines\n * @see {@link createDefaultPanToHandler} for the default implementation\n */\nexport type PanToHandlerFunction = (destination: Point, camera: BoardCamera, config: PanHandlerConfig) => Point;\n\n/**\n * Handler function type for relative \"pan by\" camera operations.\n *\n * @param delta - Movement delta in world space\n * @param camera - Current camera instance\n * @param config - Pan behavior configuration\n * @returns Transformed movement delta (after applying restrictions and clamping)\n *\n * @remarks\n * Pan-by handlers process relative camera movement requests. They form a pipeline\n * that can apply restrictions, clamping, and other transformations to the movement delta.\n *\n * Handler pipeline pattern:\n * - Each handler receives the current delta, camera state, and config\n * - Returns a potentially modified delta\n * - Handlers can be chained using {@link createHandlerChain}\n *\n * Common transformations:\n * - Axis restrictions (prevent movement on specific axes)\n * - Boundary clamping (prevent moving outside bounds)\n * - Delta dampening or acceleration\n *\n * @example\n * ```typescript\n * const myPanByHandler: PanByHandlerFunction = (delta, camera, config) => {\n * // Custom logic: dampen large movements\n * const magnitude = Math.sqrt(delta.x ** 2 + delta.y ** 2);\n * if (magnitude > 100) {\n * const scale = 100 / magnitude;\n * return { x: delta.x * scale, y: delta.y * scale };\n * }\n * return delta;\n * };\n * ```\n *\n * @category Camera Rig\n * @see {@link createHandlerChain} for composing handler pipelines\n * @see {@link createDefaultPanByHandler} for the default implementation\n */\nexport type PanByHandlerFunction = (delta: Point, camera: BoardCamera, config: PanHandlerConfig) => Point;\n\n/**\n * Creates a default \"pan to\" handler pipeline for absolute camera positioning.\n *\n * @returns Pan-to handler function with restriction and clamping\n *\n * @remarks\n * The default handler pipeline applies transformations in this order:\n * 1. **Restriction** ({@link restrictPanToHandler}): Applies axis restrictions based on config\n * 2. **Clamping** ({@link clampToHandler}): Clamps position to boundaries\n *\n * This ensures that:\n * - Camera respects axis lock settings (e.g., side-scroller constraints)\n * - Camera position stays within configured boundaries\n * - Entire viewport can be kept in bounds (if `limitEntireViewPort` is true)\n *\n * All operations work in world coordinate space.\n *\n * @example\n * ```typescript\n * const panTo = createDefaultPanToHandler();\n *\n * // Use in camera rig\n * const destination = { x: 1000, y: 500 };\n * const constrainedDest = panTo(destination, camera, {\n * restrictYTranslation: true, // Lock Y axis\n * clampTranslation: true,\n * limitEntireViewPort: true,\n * // ... other config\n * });\n * camera.setPosition(constrainedDest);\n * ```\n *\n * @example\n * ```typescript\n * // Create custom pipeline using default handlers\n * const customPanTo = createHandlerChain<Point, [BoardCamera, PanHandlerConfig]>(\n * restrictPanToHandler, // From default\n * myCustomHandler, // Your custom logic\n * clampToHandler // From default\n * );\n * ```\n *\n * @category Camera Rig\n * @see {@link createHandlerChain} for creating custom handler pipelines\n * @see {@link restrictPanToHandler} for the restriction step\n * @see {@link clampToHandler} for the clamping step\n */\nexport function createDefaultPanToHandler(): PanToHandlerFunction {\n return createHandlerChain<Point, [BoardCamera, PanHandlerConfig]>(\n restrictPanToHandler,\n clampToHandler,\n );\n}\n\n/**\n * Creates a default \"pan by\" handler pipeline for relative camera movement.\n *\n * @returns Pan-by handler function with restriction and clamping\n *\n * @remarks\n * The default handler pipeline applies transformations in this order:\n * 1. **Restriction** ({@link restrictPanByHandler}): Applies axis restrictions based on config\n * 2. **Clamping** ({@link clampByHandler}): Clamps resulting position to boundaries\n *\n * This ensures that:\n * - Camera movement respects axis lock settings\n * - Camera stays within configured boundaries after applying delta\n * - Delta is adjusted to prevent boundary violations\n *\n * The input delta is in world space. All operations work in world coordinates.\n *\n * @example\n * ```typescript\n * const panBy = createDefaultPanByHandler();\n *\n * // Use in camera rig\n * const delta = { x: 50, y: -30 };\n * const constrainedDelta = panBy(delta, camera, {\n * restrictRelativeYTranslation: true, // Lock screen-vertical movement\n * clampTranslation: true,\n * limitEntireViewPort: false,\n * // ... other config\n * });\n * camera.setPosition(PointCal.addVector(camera.position, constrainedDelta));\n * ```\n *\n * @example\n * ```typescript\n * // Create custom pipeline with dampening\n * const dampenedPanBy = createHandlerChain<Point, [BoardCamera, PanHandlerConfig]>(\n * restrictPanByHandler,\n * (delta) => ({ x: delta.x * 0.8, y: delta.y * 0.8 }), // 20% dampening\n * clampByHandler\n * );\n * ```\n *\n * @category Camera Rig\n * @see {@link createHandlerChain} for creating custom handler pipelines\n * @see {@link restrictPanByHandler} for the restriction step\n * @see {@link clampByHandler} for the clamping step\n */\nexport function createDefaultPanByHandler(): PanByHandlerFunction {\n return createHandlerChain<Point, [BoardCamera, PanHandlerConfig]>(\n restrictPanByHandler,\n clampByHandler,\n );\n}\n\n/**\n * Handler pipeline step that applies axis restrictions to \"pan to\" destinations.\n *\n * @param destination - Target camera position in world space\n * @param camera - Current camera instance\n * @param config - Restriction configuration\n * @returns Restricted destination position\n *\n * @remarks\n * This handler enforces axis-lock constraints on absolute camera positioning.\n * It converts the destination to a delta, applies restrictions, then converts back.\n *\n * Algorithm:\n * 1. Calculate delta from current position to destination\n * 2. Apply restrictions using {@link convertDeltaToComplyWithRestriction}\n * 3. If delta becomes zero, return original destination (already at target)\n * 4. Otherwise, return current position + restricted delta\n *\n * Can be used standalone, but typically composed into a handler pipeline via\n * {@link createDefaultPanToHandler} or {@link createHandlerChain}.\n *\n * @example\n * ```typescript\n * // Standalone usage\n * const config: PanHandlerRestrictionConfig = {\n * restrictYTranslation: true, // Lock Y axis\n * restrictXTranslation: false,\n * restrictRelativeXTranslation: false,\n * restrictRelativeYTranslation: false\n * };\n *\n * const destination = { x: 1000, y: 500 };\n * const restricted = restrictPanToHandler(destination, camera, config);\n * // If camera is at { x: 0, y: 200 }, result is { x: 1000, y: 200 }\n * ```\n *\n * @category Camera Rig\n * @see {@link convertDeltaToComplyWithRestriction} for restriction logic\n * @see {@link createDefaultPanToHandler} for default pipeline usage\n */\nexport function restrictPanToHandler(destination: Point, camera: BoardCamera, config: PanHandlerRestrictionConfig): Point {\n let delta = PointCal.subVector(destination, camera.position);\n delta = convertDeltaToComplyWithRestriction(delta, camera, config);\n if (delta.x === 0 && delta.y === 0) {\n return destination;\n }\n const dest = PointCal.addVector(camera.position, delta);\n return dest;\n}\n\n/**\n * Handler pipeline step that applies axis restrictions to \"pan by\" deltas.\n *\n * @param delta - Movement delta in world space\n * @param camera - Current camera instance\n * @param config - Restriction configuration\n * @returns Restricted movement delta\n *\n * @remarks\n * This handler enforces axis-lock constraints on relative camera movement.\n * It directly transforms the delta according to restriction rules.\n *\n * Restrictions applied by {@link convertDeltaToComplyWithRestriction}:\n * - World-space axis locks (X/Y)\n * - Viewport-relative axis locks (horizontal/vertical, accounting for rotation)\n *\n * Can be used standalone, but typically composed into a handler pipeline via\n * {@link createDefaultPanByHandler} or {@link createHandlerChain}.\n *\n * @example\n * ```typescript\n * // Standalone usage - lock to screen-horizontal movement\n * const config: PanHandlerRestrictionConfig = {\n * restrictXTranslation: false,\n * restrictYTranslation: false,\n * restrictRelativeXTranslation: false,\n * restrictRelativeYTranslation: true // Lock screen-vertical\n * };\n *\n * const delta = { x: 50, y: 30 };\n * const restricted = restrictPanByHandler(delta, camera, config);\n * // Result depends on camera rotation - only horizontal screen movement allowed\n * ```\n *\n * @category Camera Rig\n * @see {@link convertDeltaToComplyWithRestriction} for restriction logic\n * @see {@link createDefaultPanByHandler} for default pipeline usage\n */\nexport function restrictPanByHandler(delta: Point, camera: BoardCamera, config: PanHandlerRestrictionConfig): Point {\n delta = convertDeltaToComplyWithRestriction(delta, camera, config);\n return delta;\n}\n\n/**\n * Handler pipeline step that clamps \"pan to\" destinations to camera boundaries.\n *\n * @param destination - Target camera position in world space\n * @param camera - Current camera instance (provides boundaries and viewport dimensions)\n * @param config - Clamping configuration\n * @returns Clamped destination position\n *\n * @remarks\n * This handler enforces boundary constraints on absolute camera positioning.\n * Behavior depends on configuration:\n *\n * - If `clampTranslation` is false: Returns destination unchanged (no clamping)\n * - If `limitEntireViewPort` is false: Clamps camera center to boundaries\n * - If `limitEntireViewPort` is true: Ensures entire viewport rectangle stays in bounds\n *\n * The entire-viewport mode accounts for:\n * - Viewport dimensions (width/height)\n * - Current zoom level (affects viewport size in world space)\n * - Camera rotation (affects viewport orientation)\n *\n * Can be used standalone, but typically composed into a handler pipeline via\n * {@link createDefaultPanToHandler} or {@link createHandlerChain}.\n *\n * @example\n * ```typescript\n * // Standalone usage - ensure entire viewport stays in bounds\n * camera.boundaries = {\n * min: { x: 0, y: 0 },\n * max: { x: 2000, y: 1000 }\n * };\n *\n * const config: PanHandlerClampConfig = {\n * clampTranslation: true,\n * limitEntireViewPort: true\n * };\n *\n * const destination = { x: 2500, y: 500 }; // Outside bounds\n * const clamped = clampToHandler(destination, camera, config);\n * // Result keeps entire viewport within [0,0] to [2000,1000]\n * ```\n *\n * @category Camera Rig\n * @see {@link clampPoint} for center-point clamping\n * @see {@link clampPointEntireViewPort} for full-viewport clamping\n * @see {@link createDefaultPanToHandler} for default pipeline usage\n */\nexport function clampToHandler(destination: Point, camera: BoardCamera, config: PanHandlerClampConfig): Point {\n if(!config.clampTranslation){\n return destination;\n }\n let actualDest = clampPoint(destination, camera.boundaries);\n if(config.limitEntireViewPort){\n actualDest = clampPointEntireViewPort(destination, camera.viewPortWidth, camera.viewPortHeight, camera.boundaries, camera.zoomLevel, camera.rotation);\n }\n return actualDest;\n}\n\n/**\n * Handler pipeline step that clamps \"pan by\" deltas to prevent boundary violations.\n *\n * @param delta - Movement delta in world space\n * @param camera - Current camera instance (provides boundaries and viewport dimensions)\n * @param config - Clamping configuration\n * @returns Adjusted delta that respects boundaries\n *\n * @remarks\n * This handler ensures that applying the delta won't move the camera outside boundaries.\n * It works by:\n * 1. Calculating the potential new position (current + delta)\n * 2. Clamping that position to boundaries\n * 3. Returning the difference (clamped - current) as the new delta\n *\n * Behavior depends on configuration:\n * - If `clampTranslation` is false: Returns delta unchanged\n * - If `limitEntireViewPort` is false: Clamps based on camera center\n * - If `limitEntireViewPort` is true: Ensures entire viewport stays in bounds\n *\n * The resulting delta may be zero if the camera is already at a boundary\n * and trying to move further outside.\n *\n * Can be used standalone, but typically composed into a handler pipeline via\n * {@link createDefaultPanByHandler} or {@link createHandlerChain}.\n *\n * @example\n * ```typescript\n * // Standalone usage\n * camera.position = { x: 1950, y: 500 };\n * camera.boundaries = { max: { x: 2000 } };\n *\n * const config: PanHandlerClampConfig = {\n * clampTranslation: true,\n * limitEntireViewPort: false\n * };\n *\n * const delta = { x: 100, y: 0 }; // Try to move right\n * const clamped = clampByHandler(delta, camera, config);\n * // Result: { x: 50, y: 0 } - only move to boundary, not beyond\n * ```\n *\n * @category Camera Rig\n * @see {@link clampPoint} for center-point clamping\n * @see {@link clampPointEntireViewPort} for full-viewport clamping\n * @see {@link createDefaultPanByHandler} for default pipeline usage\n */\nexport function clampByHandler(delta: Point, camera: BoardCamera, config: PanHandlerClampConfig): Point {\n if(!config.clampTranslation){\n return delta;\n }\n let actualDelta = PointCal.subVector(clampPoint(PointCal.addVector(camera.position, delta), camera.boundaries), camera.position);\n if(config.limitEntireViewPort){\n actualDelta = PointCal.subVector(clampPointEntireViewPort(PointCal.addVector(camera.position, delta), camera.viewPortWidth, camera.viewPortHeight, camera.boundaries, camera.zoomLevel, camera.rotation), camera.position);\n }\n return actualDelta;\n}\n\n/**\n * Transforms a movement delta to comply with axis restriction configuration.\n *\n * @param delta - Original movement delta in world space\n * @param camera - Current camera instance (provides rotation for relative restrictions)\n * @param config - Restriction configuration\n * @returns Transformed delta that respects all enabled restrictions\n *\n * @remarks\n * This function applies axis-lock logic for both world-space and viewport-relative restrictions.\n * Restrictions are processed in priority order:\n *\n * 1. **Complete locks** (highest priority):\n * - Both world axes locked → return zero delta\n * - Both relative axes locked → return zero delta\n *\n * 2. **World-space axis locks**:\n * - `restrictXTranslation` → Zero out X component\n * - `restrictYTranslation` → Zero out Y component\n *\n * 3. **Viewport-relative axis locks** (rotation-aware):\n * - `restrictRelativeXTranslation` → Project delta onto screen-vertical direction\n * - `restrictRelativeYTranslation` → Project delta onto screen-horizontal direction\n *\n * For viewport-relative restrictions:\n * - \"Relative X\" = horizontal in viewport/screen space\n * - \"Relative Y\" = vertical in viewport/screen space\n * - These account for camera rotation by projecting onto rotated axes\n *\n * @example\n * ```typescript\n * // World-space restriction: lock Y axis\n * const config1 = {\n * restrictXTranslation: false,\n * restrictYTranslation: true,\n * restrictRelativeXTranslation: false,\n * restrictRelativeYTranslation: false\n * };\n *\n * const delta1 = { x: 50, y: 30 };\n * const result1 = convertDeltaToComplyWithRestriction(delta1, camera, config1);\n * // result1 = { x: 50, y: 0 } - Y component removed\n * ```\n *\n * @example\n * ```typescript\n * // Viewport-relative restriction: lock horizontal screen movement\n * const config2 = {\n * restrictXTranslation: false,\n * restrictYTranslation: false,\n * restrictRelativeXTranslation: true, // Lock screen-horizontal\n * restrictRelativeYTranslation: false\n * };\n *\n * // Camera rotated 45 degrees\n * const delta2 = { x: 100, y: 100 };\n * const result2 = convertDeltaToComplyWithRestriction(delta2, camera, config2);\n * // result2 projects delta onto screen-vertical direction\n * // (perpendicular to screen-horizontal)\n * ```\n *\n * @category Camera Rig\n * @see {@link restrictPanByHandler} for usage in pan-by pipeline\n * @see {@link restrictPanToHandler} for usage in pan-to pipeline\n */\nexport function convertDeltaToComplyWithRestriction(delta: Point, camera: BoardCamera, config: PanHandlerRestrictionConfig): Point {\n if(config.restrictXTranslation && config.restrictYTranslation){\n return {x: 0, y: 0};\n }\n if(config.restrictRelativeXTranslation && config.restrictRelativeYTranslation){\n return {x: 0, y: 0};\n }\n if(config.restrictXTranslation){\n delta.x = 0;\n }\n if(config.restrictYTranslation){\n delta.y = 0;\n }\n if(config.restrictRelativeXTranslation){\n const upDirection = PointCal.rotatePoint({x: 0, y: 1}, camera.rotation);\n const value = PointCal.dotProduct(upDirection, delta);\n delta = PointCal.multiplyVectorByScalar(upDirection, value);\n }\n if(config.restrictRelativeYTranslation){\n const rightDirection = PointCal.rotatePoint({x: 1, y: 0}, camera.rotation);\n const value = PointCal.dotProduct(rightDirection, delta);\n delta = PointCal.multiplyVectorByScalar(rightDirection, value);\n }\n return delta;\n}\n\n/**\n * Converts a user input delta (viewport space) to camera movement delta (world space).\n *\n * @param delta - Movement delta in viewport/screen coordinates (CSS pixels)\n * @param camera - Current camera instance (provides rotation and zoom)\n * @returns Equivalent delta in world space\n *\n * @remarks\n * This function performs the standard viewport-to-world delta conversion:\n * 1. Rotate delta by camera rotation (convert screen direction to world direction)\n * 2. Scale by inverse zoom (convert screen distance to world distance)\n *\n * Formula: `worldDelta = rotate(viewportDelta, cameraRotation) / zoomLevel`\n *\n * This is the core conversion used by {@link DefaultCameraRig.panByViewPort}.\n *\n * @example\n * ```typescript\n * // User drags mouse 100 pixels right, 50 pixels down\n * const viewportDelta = { x: 100, y: 50 };\n *\n * // Camera at 2x zoom, no rotation\n * camera.zoomLevel = 2.0;\n * camera.rotation = 0;\n *\n * const worldDelta = convertUserInputDeltaToCameraDelta(viewportDelta, camera);\n * // worldDelta = { x: 50, y: 25 } - half the viewport delta due to 2x zoom\n * ```\n *\n * @example\n * ```typescript\n * // With camera rotation\n * camera.zoomLevel = 1.0;\n * camera.rotation = Math.PI / 2; // 90 degrees\n *\n * const viewportDelta = { x: 100, y: 0 }; // Drag right\n * const worldDelta = convertUserInputDeltaToCameraDelta(viewportDelta, camera);\n * // worldDelta ≈ { x: 0, y: -100 } - rotated 90 degrees in world space\n * ```\n *\n * @category Camera Rig\n * @see {@link DefaultCameraRig.panByViewPort} for usage\n */\nexport function convertUserInputDeltaToCameraDelta(delta: Point, camera: BoardCamera): Point {\n return PointCal.multiplyVectorByScalar(PointCal.rotatePoint(delta, camera.rotation), 1 / camera.zoomLevel);\n}\n",
34
35
  "import { BoardCamera } from \"../interface\";\nimport { createHandlerChain } from \"../../utils/handler-pipeline\";\nimport { normalizeAngleZero2TwoPI, angleSpan, clampRotation } from \"../utils/rotation\";\n\n/**\n * Combined configuration for rotation handler behavior, merging restriction and clamping settings.\n *\n * @remarks\n * This type combines {@link RotationHandlerRestrictConfig} and {@link RotationHandlerClampConfig}\n * to provide complete control over camera rotation behavior.\n *\n * Rotation handlers use this configuration to:\n * - Completely disable rotation operations (restriction)\n * - Clamp rotation angle to stay within defined angular limits\n *\n * @category Camera Rig\n * @see {@link RotationHandlerRestrictConfig} for rotation locking options\n * @see {@link RotationHandlerClampConfig} for angular boundary options\n */\nexport type RotationHandlerConfig = RotationHandlerRestrictConfig & RotationHandlerClampConfig;\n\n/**\n * Configuration for completely disabling rotation operations.\n *\n * @remarks\n * Provides a global \"rotation lock\" to prevent any rotation changes.\n *\n * When `restrictRotation` is true:\n * - Rotate-to operations return current rotation (no change)\n * - Rotate-by operations return zero delta (no change)\n *\n * This is useful for:\n * - Locking rotation during specific application states\n * - Fixed-orientation viewing modes (north-up maps, etc.)\n * - Preventing user rotation in certain contexts\n *\n * @example\n * ```typescript\n * const config: RotationHandlerRestrictConfig = {\n * restrictRotation: true // Lock rotation\n * };\n *\n * // Any rotation attempt will be ignored\n * ```\n *\n * @category Camera Rig\n */\nexport type RotationHandlerRestrictConfig = {\n /**\n * Whether to completely prevent rotation operations.\n */\n restrictRotation: boolean;\n}\n\n/**\n * Configuration for rotation angle boundary clamping.\n *\n * @remarks\n * Controls whether rotation operations should be constrained to camera's rotation boundaries.\n *\n * When `clampRotation` is true, rotation handlers enforce {@link BoardCamera.rotationBoundaries}\n * limits (min/max angles in radians). When false, rotation can exceed configured boundaries.\n *\n * Rotation boundaries allow limiting camera rotation to a specific angular range,\n * useful for scenarios like:\n * - Restricting rotation to ±45 degrees from north\n * - Allowing only certain cardinal directions\n * - Preventing full 360-degree rotation\n *\n * @example\n * ```typescript\n * const config: RotationHandlerClampConfig = {\n * clampRotation: true // Enforce rotation boundaries\n * };\n *\n * camera.rotationBoundaries = { min: 0, max: Math.PI / 2 };\n * // Rotation clamped to [0, 90 degrees] range\n * ```\n *\n * @category Camera Rig\n */\nexport type RotationHandlerClampConfig = {\n /**\n * Whether to enforce rotation angle boundaries.\n */\n clampRotation: boolean;\n}\n\n/**\n * Handler function type for relative \"rotate by\" camera operations.\n *\n * @param delta - Rotation angle change in radians (positive = counter-clockwise)\n * @param camera - Current camera instance\n * @param config - Rotation behavior configuration\n * @returns Transformed rotation delta (after applying restrictions and clamping)\n *\n * @remarks\n * Rotate-by handlers process relative rotation change requests. They form a pipeline\n * that can apply restrictions, clamping, and other transformations to the delta.\n *\n * Handler pipeline pattern:\n * - Each handler receives the rotation delta, camera state, and config\n * - Returns a potentially modified delta\n * - Handlers can be chained using {@link createHandlerChain}\n *\n * Common transformations:\n * - Angular boundary clamping (prevent exceeding min/max angles)\n * - Rotation locking (return zero delta)\n * - Delta dampening or snapping\n *\n * Rotation angles are in radians where:\n * - 0 = North (no rotation)\n * - Positive values = Counter-clockwise rotation\n * - Negative values = Clockwise rotation\n *\n * @example\n * ```typescript\n * const myRotateByHandler: RotateByHandlerFunction = (delta, camera, config) => {\n * // Custom logic: snap to 45-degree increments\n * const totalRotation = camera.rotation + delta;\n * const snapped = Math.round(totalRotation / (Math.PI / 4)) * (Math.PI / 4);\n * return snapped - camera.rotation;\n * };\n * ```\n *\n * @category Camera Rig\n * @see {@link createHandlerChain} for composing handler pipelines\n * @see {@link createDefaultRotateByHandler} for the default implementation\n */\nexport type RotateByHandlerFunction = (delta: number, camera: BoardCamera, config: RotationHandlerConfig) => number;\n\n/**\n * Handler function type for absolute \"rotate to\" camera operations.\n *\n * @param targetRotation - Target rotation angle in radians\n * @param camera - Current camera instance\n * @param config - Rotation behavior configuration\n * @returns Transformed rotation angle (after applying restrictions and clamping)\n *\n * @remarks\n * Rotate-to handlers process absolute rotation angle requests. They form a pipeline\n * that can apply restrictions, clamping, and other transformations.\n *\n * Handler pipeline pattern:\n * - Each handler receives the target angle, camera state, and config\n * - Returns a potentially modified angle\n * - Handlers can be chained using {@link createHandlerChain}\n *\n * Common transformations:\n * - Angular boundary clamping (enforce min/max angles)\n * - Rotation locking (return current angle)\n * - Angle snapping or normalization\n *\n * Rotation angles are in radians where:\n * - 0 = North (no rotation)\n * - π/2 = West (90° counter-clockwise)\n * - π = South (180°)\n * - 3π/2 = East (270° counter-clockwise)\n *\n * @example\n * ```typescript\n * const myRotateToHandler: RotateToHandlerFunction = (target, camera, config) => {\n * // Custom logic: snap to cardinal directions\n * const cardinals = [0, Math.PI/2, Math.PI, 3*Math.PI/2];\n * return cardinals.reduce((prev, curr) =>\n * Math.abs(curr - target) < Math.abs(prev - target) ? curr : prev\n * );\n * };\n * ```\n *\n * @category Camera Rig\n * @see {@link createHandlerChain} for composing handler pipelines\n * @see {@link createDefaultRotateToHandler} for the default implementation\n */\nexport type RotateToHandlerFunction = (targetRotation: number, camera: BoardCamera, config: RotationHandlerConfig) => number;\n\n/**\n * Handler pipeline step that clamps \"rotate by\" deltas to prevent angular boundary violations.\n *\n * @param delta - Rotation angle change in radians\n * @param camera - Current camera instance (provides current rotation and boundaries)\n * @param config - Clamping configuration\n * @returns Adjusted delta that respects rotation boundaries\n *\n * @remarks\n * This handler ensures that applying the delta won't exceed rotation boundaries.\n *\n * Algorithm:\n * 1. Calculate potential new rotation (current + delta)\n * 2. Normalize angle to [0, 2π) range\n * 3. Clamp to rotation boundaries\n * 4. Calculate shortest angular distance from current to clamped angle\n * 5. Return that distance as the new delta\n *\n * Behavior:\n * - If `clampRotation` is false: Returns delta unchanged\n * - If `clampRotation` is true: Adjusts delta to stay within boundaries\n *\n * The resulting delta may be zero if already at a boundary and trying to rotate further.\n *\n * @example\n * ```typescript\n * camera.rotation = Math.PI * 0.4; // 72 degrees\n * camera.rotationBoundaries = { max: Math.PI / 2 }; // Max 90 degrees\n *\n * const config: RotationHandlerClampConfig = {\n * clampRotation: true\n * };\n *\n * const delta = Math.PI * 0.2; // Try to rotate 36 degrees (would exceed max)\n * const clamped = clampRotateByHandler(delta, camera, config);\n * // clamped ≈ 0.314 radians (18 degrees - only rotate to boundary)\n * ```\n *\n * @category Camera Rig\n * @see {@link normalizeAngleZero2TwoPI} for angle normalization\n * @see {@link clampRotation} for boundary clamping\n * @see {@link angleSpan} for calculating angular distance\n */\nexport function clampRotateByHandler(delta: number, camera: BoardCamera, config: RotationHandlerClampConfig): number {\n if(!config.clampRotation){\n return delta;\n }\n const targetRotation = normalizeAngleZero2TwoPI(camera.rotation + delta);\n const clampedRotation = clampRotation(targetRotation, camera.rotationBoundaries);\n const diff = angleSpan(camera.rotation, clampedRotation);\n return diff;\n}\n\n/**\n * Handler pipeline step that prevents \"rotate by\" operations when rotation is locked.\n *\n * @param delta - Rotation angle change in radians\n * @param camera - Current camera instance\n * @param config - Restriction configuration\n * @returns Zero (if locked) or delta (if unlocked)\n *\n * @remarks\n * This handler implements a global rotation lock for relative rotation operations.\n *\n * Behavior:\n * - If `restrictRotation` is true: Returns 0 (prevents any change)\n * - If `restrictRotation` is false: Returns delta unchanged\n *\n * @example\n * ```typescript\n * const config: RotationHandlerRestrictConfig = {\n * restrictRotation: true // Lock rotation\n * };\n *\n * const delta = Math.PI / 4; // Try to rotate 45 degrees\n * const result = restrictRotateByHandler(delta, camera, config);\n * // result = 0 (rotation locked, no change allowed)\n * ```\n *\n * @category Camera Rig\n * @see {@link createDefaultRotateByHandler} for default pipeline usage\n */\nexport function restrictRotateByHandler(delta: number, camera: BoardCamera, config: RotationHandlerRestrictConfig): number {\n if(config.restrictRotation){\n return 0;\n }\n return delta;\n}\n\n/**\n * Handler pipeline step that clamps \"rotate to\" targets to camera rotation boundaries.\n *\n * @param targetRotation - Target rotation angle in radians\n * @param camera - Current camera instance (provides rotationBoundaries)\n * @param config - Clamping configuration\n * @returns Clamped rotation angle\n *\n * @remarks\n * This handler enforces angular limits on absolute rotation requests.\n *\n * Behavior:\n * - If `clampRotation` is false: Returns target unchanged\n * - If `clampRotation` is true: Clamps target to {@link BoardCamera.rotationBoundaries}\n *\n * The clamping handles:\n * - Missing boundaries (undefined min/max)\n * - One-sided constraints (only min or only max)\n * - Full range constraints\n *\n * @example\n * ```typescript\n * camera.rotationBoundaries = { min: 0, max: Math.PI }; // [0°, 180°]\n *\n * const config: RotationHandlerClampConfig = {\n * clampRotation: true\n * };\n *\n * const target = Math.PI * 1.5; // 270 degrees (exceeds max)\n * const clamped = clampRotateToHandler(target, camera, config);\n * // clamped = π (180 degrees - clamped to max boundary)\n * ```\n *\n * @category Camera Rig\n * @see {@link clampRotation} for clamping implementation\n * @see {@link createDefaultRotateToHandler} for default pipeline usage\n */\nexport function clampRotateToHandler(targetRotation: number, camera: BoardCamera, config: RotationHandlerClampConfig): number {\n if(!config.clampRotation){\n return targetRotation;\n }\n const clampedRotation = clampRotation(targetRotation, camera.rotationBoundaries);\n return clampedRotation;\n}\n\n/**\n * Handler pipeline step that prevents \"rotate to\" operations when rotation is locked.\n *\n * @param targetRotation - Target rotation angle in radians\n * @param camera - Current camera instance\n * @param config - Restriction configuration\n * @returns Current rotation (if locked) or target (if unlocked)\n *\n * @remarks\n * This handler implements a global rotation lock for absolute rotation operations.\n *\n * Behavior:\n * - If `restrictRotation` is true: Returns current rotation (prevents any change)\n * - If `restrictRotation` is false: Returns target unchanged\n *\n * @example\n * ```typescript\n * camera.rotation = Math.PI / 2; // Currently at 90 degrees\n *\n * const config: RotationHandlerRestrictConfig = {\n * restrictRotation: true // Lock rotation\n * };\n *\n * const target = Math.PI; // Try to rotate to 180 degrees\n * const result = restrictRotateToHandler(target, camera, config);\n * // result = π/2 (rotation locked, returns current angle)\n * ```\n *\n * @category Camera Rig\n * @see {@link createDefaultRotateToHandler} for default pipeline usage\n */\nexport function restrictRotateToHandler(targetRotation: number, camera: BoardCamera, config: RotationHandlerRestrictConfig): number {\n if(config.restrictRotation){\n return camera.rotation;\n }\n return targetRotation;\n}\n\n/**\n * Creates a default \"rotate by\" handler pipeline for relative rotation operations.\n *\n * @returns Rotate-by handler function with restriction and clamping\n *\n * @remarks\n * The default handler pipeline applies transformations in this order:\n * 1. **Restriction** ({@link restrictRotateByHandler}): Returns zero delta if locked\n * 2. **Clamping** ({@link clampRotateByHandler}): Adjusts delta to respect boundaries\n *\n * This ensures that:\n * - Rotation can be completely disabled via `restrictRotation` flag\n * - Resulting rotation angle stays within configured angular boundaries\n * - Delta is adjusted to prevent boundary violations\n *\n * @example\n * ```typescript\n * const rotateBy = createDefaultRotateByHandler();\n *\n * camera.rotation = Math.PI * 0.4; // 72 degrees\n * camera.rotationBoundaries = { max: Math.PI / 2 }; // Max 90 degrees\n *\n * const delta = Math.PI * 0.3; // Try to rotate 54 degrees (would exceed max)\n * const constrained = rotateBy(delta, camera, {\n * clampRotation: true,\n * restrictRotation: false\n * });\n * // constrained adjusted to only rotate to boundary\n * camera.setRotation(camera.rotation + constrained);\n * ```\n *\n * @category Camera Rig\n * @see {@link createHandlerChain} for creating custom handler pipelines\n * @see {@link restrictRotateByHandler} for the restriction step\n * @see {@link clampRotateByHandler} for the clamping step\n */\nexport function createDefaultRotateByHandler(): RotateByHandlerFunction {\n return createHandlerChain<number, [BoardCamera, RotationHandlerConfig]>(\n restrictRotateByHandler,\n clampRotateByHandler,\n );\n}\n\n/**\n * Creates a default \"rotate to\" handler pipeline for absolute rotation operations.\n *\n * @returns Rotate-to handler function with restriction and clamping\n *\n * @remarks\n * The default handler pipeline applies transformations in this order:\n * 1. **Restriction** ({@link restrictRotateToHandler}): Returns current angle if locked\n * 2. **Clamping** ({@link clampRotateToHandler}): Clamps angle to configured boundaries\n *\n * This ensures that:\n * - Rotation can be completely disabled via `restrictRotation` flag\n * - Rotation angle stays within configured angular boundaries\n *\n * @example\n * ```typescript\n * const rotateTo = createDefaultRotateToHandler();\n *\n * camera.rotationBoundaries = { min: 0, max: Math.PI }; // [0°, 180°]\n *\n * const target = Math.PI * 1.5; // 270 degrees (exceeds max)\n * const constrained = rotateTo(target, camera, {\n * clampRotation: true,\n * restrictRotation: false\n * });\n * // constrained = π (clamped to max boundary of 180 degrees)\n * camera.setRotation(constrained);\n * ```\n *\n * @example\n * ```typescript\n * // Create custom pipeline with snapping\n * const cardinalRotateTo = createHandlerChain<number, [BoardCamera, RotationHandlerConfig]>(\n * restrictRotateToHandler,\n * (angle) => {\n * // Snap to cardinal directions (0°, 90°, 180°, 270°)\n * const cardinals = [0, Math.PI/2, Math.PI, 3*Math.PI/2];\n * return cardinals.reduce((prev, curr) =>\n * Math.abs(curr - angle) < Math.abs(prev - angle) ? curr : prev\n * );\n * },\n * clampRotateToHandler\n * );\n * ```\n *\n * @category Camera Rig\n * @see {@link createHandlerChain} for creating custom handler pipelines\n * @see {@link restrictRotateToHandler} for the restriction step\n * @see {@link clampRotateToHandler} for the clamping step\n */\nexport function createDefaultRotateToHandler(): RotateToHandlerFunction {\n return createHandlerChain<number, [BoardCamera, RotationHandlerConfig]>(\n restrictRotateToHandler,\n clampRotateToHandler,\n );\n}\n",
35
- "import { PointCal } from \"@ue-too/math\";\n\nimport { \n createDefaultPanByHandler, \n createDefaultPanToHandler, \n PanByHandlerFunction, \n PanHandlerConfig, \n PanToHandlerFunction } from \"./pan-handler\";\nimport { \n ZoomHandlerConfig, \n ZoomToHandlerFunction, \n createDefaultZoomToOnlyHandler, \n ZoomByHandlerFunction, \n createDefaultZoomByOnlyHandler, \n} from \"./zoom-handler\";\nimport DefaultBoardCamera from \"../default-camera\";\nimport { createDefaultRotateToHandler, createDefaultRotateByHandler } from \"./rotation-handler\";\nimport type { RotateToHandlerFunction, RotateByHandlerFunction, RotationHandlerConfig } from \"./rotation-handler\";\nimport { ObservableBoardCamera } from \"../interface\";\nimport { Point } from \"@ue-too/math\";\nimport { convertDeltaInViewPortToWorldSpace } from \"../utils\";\nimport type { BaseContext } from \"@ue-too/being\";\n\n/**\n * Configuration for camera rig behavior combining pan, zoom, and rotation settings.\n * Composed from individual handler configs.\n *\n * @remarks\n * This type merges configuration from:\n * - {@link PanHandlerConfig} - Pan clamping and boundaries\n * - {@link ZoomHandlerConfig} - Zoom limits and restrictions\n * - {@link RotationHandlerConfig} - Rotation constraints\n *\n * @category Camera Rig\n * @see {@link PanHandlerConfig}\n * @see {@link ZoomHandlerConfig}\n * @see {@link RotationHandlerConfig}\n */\nexport type CameraRigConfig = PanHandlerConfig & ZoomHandlerConfig & RotationHandlerConfig;\n\n/**\n * High-level camera control interface providing intuitive methods for pan, zoom, and rotation.\n * The camera rig acts as a facade over the camera, handling coordinate conversions and constraints.\n *\n * @remarks\n * CameraRig provides:\n * - **Coordinate-aware methods**: Separate methods for viewport and world coordinates\n * - **Anchor-point zooming**: Keep points stationary during zoom (zoom-to-cursor)\n * - **Configuration management**: Unified config for all camera operations\n * - **Handler composition**: Combines pan, zoom, rotation handlers with proper sequencing\n *\n * The rig ensures correct transformation order when combining operations\n * (e.g., zoom-at-point requires zoom followed by pan compensation).\n *\n * @category Camera Rig\n * @see {@link DefaultCameraRig} for the default implementation\n * @see {@link createDefaultCameraRig} for a factory function\n */\nexport interface CameraRig extends BaseContext {\n /** The underlying observable camera being controlled */\n camera: ObservableBoardCamera;\n\n /** Current configuration for all camera operations */\n config: CameraRigConfig;\n\n /**\n * Updates the camera rig configuration.\n * @param config - Partial configuration to merge with current config\n */\n configure(config: Partial<CameraRigConfig>): void;\n\n /**\n * Updates the camera rig state (called per frame if needed).\n */\n update(): void;\n\n /**\n * Pans the camera by a delta in viewport coordinates.\n * @param delta - Movement delta in viewport space (CSS pixels, origin at center)\n */\n panByViewPort: (delta: Point) => void;\n\n /**\n * Pans the camera to a target position in viewport coordinates.\n * @param target - Target position in viewport space\n */\n panToViewPort: (target: Point) => void;\n\n /**\n * Pans the camera by a delta in world coordinates.\n * @param delta - Movement delta in world space\n */\n panByWorld: (delta: Point) => void;\n\n /**\n * Pans the camera to a target position in world coordinates.\n * @param target - Target position in world space\n */\n panToWorld: (target: Point) => void;\n\n /**\n * Rotates the camera by a delta angle.\n * @param delta - Rotation delta in radians\n */\n rotateBy: (delta: number) => void;\n\n /**\n * Rotates the camera to a target angle.\n * @param target - Target rotation in radians\n */\n rotateTo: (target: number) => void;\n\n /**\n * Zooms to a target level, keeping a viewport point stationary.\n * @param targetZoom - Target zoom level\n * @param at - Anchor point in viewport coordinates\n */\n zoomToAt: (targetZoom: number, at: Point) => void;\n\n /**\n * Zooms by a delta, keeping a viewport point stationary.\n * @param delta - Zoom delta\n * @param at - Anchor point in viewport coordinates\n */\n zoomByAt: (delta: number, at: Point) => void;\n\n /**\n * Zooms to a target level at viewport center.\n * @param targetZoom - Target zoom level\n */\n zoomTo: (targetZoom: number) => void;\n\n /**\n * Zooms by a delta at viewport center.\n * @param delta - Zoom delta\n */\n zoomBy: (delta: number) => void;\n\n /**\n * Zooms to a target level, keeping a world point stationary.\n * @param targetZoom - Target zoom level\n * @param at - Anchor point in world coordinates\n */\n zoomToAtWorld: (targetZoom: number, at: Point) => void;\n\n /**\n * Zooms by a delta, keeping a world point stationary.\n * @param delta - Zoom delta\n * @param at - Anchor point in world coordinates\n */\n zoomByAtWorld: (delta: number, at: Point) => void;\n}\n\n/**\n * Default implementation of the camera rig providing comprehensive camera control.\n * Composes pan, zoom, and rotation handlers into a unified, easy-to-use API.\n *\n * @remarks\n * DefaultCameraRig serves as:\n * - **Context for state machines**: Passed to pan/zoom state machines as execution context\n * - **Handler composition**: Combines individual pan/zoom/rotation handlers\n * - **Coordinate conversion**: Manages conversions between viewport and world space\n * - **Configuration management**: Applies constraints and limits through handlers\n *\n * The rig ensures proper transformation sequencing:\n * 1. For anchor-point zoom: Apply zoom, then compensate camera position to keep anchor stationary\n * 2. For rotation: Transform coordinates based on current camera rotation\n * 3. For pan: Apply clamping and boundary constraints\n *\n * @example\n * ```typescript\n * const camera = new DefaultBoardCamera();\n * const rig = new DefaultCameraRig({\n * limitEntireViewPort: true,\n * clampTranslation: true,\n * clampZoom: true,\n * restrictZoom: false\n * }, camera);\n *\n * // Pan in viewport coordinates\n * rig.panByViewPort({ x: 50, y: -30 });\n *\n * // Zoom at cursor position\n * rig.zoomByAt(0.1, mousePosition);\n *\n * // Rotate camera\n * rig.rotateBy(Math.PI / 4);\n * ```\n *\n * @category Camera Rig\n * @see {@link CameraRig} for the interface definition\n * @see {@link createDefaultCameraRig} for a convenient factory function\n */\nexport class DefaultCameraRig implements CameraRig {\n\n private _panBy: PanByHandlerFunction;\n private _panTo: PanToHandlerFunction;\n private _zoomTo: ZoomToHandlerFunction;\n private _zoomBy: ZoomByHandlerFunction;\n private _rotateBy: RotateByHandlerFunction;\n private _rotateTo: RotateToHandlerFunction;\n private _config: CameraRigConfig;\n private _camera: ObservableBoardCamera;\n\n /**\n * Creates a new DefaultCameraRig with specified configuration and camera.\n *\n * @param config - Camera rig configuration for pan and zoom constraints\n * @param camera - Observable camera instance to control (defaults to new DefaultBoardCamera)\n *\n * @remarks\n * The constructor initializes:\n * - Default pan, zoom, and rotation handler functions\n * - Rotation config with `restrictRotation: false` and `clampRotation: true`\n * - Handler functions that will be used to process and constrain all camera operations\n *\n * @example\n * ```typescript\n * const rig = new DefaultCameraRig({\n * limitEntireViewPort: true,\n * clampTranslation: true,\n * clampZoom: true,\n * restrictZoom: false,\n * restrictXTranslation: false,\n * restrictYTranslation: false\n * });\n * ```\n */\n constructor(config: PanHandlerConfig & ZoomHandlerConfig, camera: ObservableBoardCamera = new DefaultBoardCamera()){\n this._panBy = createDefaultPanByHandler();\n this._panTo = createDefaultPanToHandler();\n this._zoomTo = createDefaultZoomToOnlyHandler();\n this._zoomBy = createDefaultZoomByOnlyHandler();\n this._rotateBy = createDefaultRotateByHandler();\n this._rotateTo = createDefaultRotateToHandler();\n this._config = {...config, restrictRotation: false, clampRotation: true};\n this._camera = camera;\n }\n\n /**\n * Zooms to a target level while keeping a viewport point stationary (zoom-to-cursor).\n *\n * @param targetZoom - Target zoom level to reach\n * @param at - Anchor point in viewport coordinates (center-anchored, CSS pixels)\n *\n * @remarks\n * This implements the \"zoom to cursor\" behavior commonly seen in map applications.\n * The algorithm:\n * 1. Converts anchor point from viewport to world space (before zoom)\n * 2. Applies zoom transformation (may be clamped by config)\n * 3. Converts anchor point from viewport to world space (after zoom)\n * 4. Calculates position difference and pans camera to compensate\n *\n * The anchor point remains stationary on screen, while the world zooms around it.\n *\n * @example\n * ```typescript\n * // Zoom to 2x at mouse cursor position\n * rig.zoomToAt(2.0, { x: mouseX, y: mouseY });\n *\n * // The world point under the cursor stays in place\n * ```\n */\n zoomToAt(targetZoom: number, at: Point): void {\n let originalAnchorInWorld = this._camera.convertFromViewPort2WorldSpace(at);\n const transformTarget = this._zoomTo(targetZoom, this._camera, this._config);\n this._camera.setZoomLevel(transformTarget);\n let anchorInWorldAfterZoom = this._camera.convertFromViewPort2WorldSpace(at);\n const cameraPositionDiff = PointCal.subVector(originalAnchorInWorld, anchorInWorldAfterZoom);\n const transformedCameraPositionDiff = this._panBy(cameraPositionDiff, this._camera, this._config);\n this._camera.setPosition(PointCal.addVector(this._camera.position, transformedCameraPositionDiff));\n }\n\n /**\n * Zooms by a relative delta while keeping a viewport point stationary.\n *\n * @param delta - Relative zoom delta (multiplied by current zoom level)\n * @param at - Anchor point in viewport coordinates (center-anchored, CSS pixels)\n *\n * @remarks\n * This method is ideal for mouse wheel zoom interactions where the delta\n * represents a relative change rather than an absolute target.\n *\n * The delta is scaled by current zoom level: `actualDelta = delta * currentZoom`\n * This provides consistent zoom \"speed\" regardless of current zoom level.\n *\n * Like {@link zoomToAt}, this keeps the anchor point stationary during zoom.\n *\n * @example\n * ```typescript\n * // Zoom in by 10% at cursor position (mouse wheel up)\n * rig.zoomByAt(0.1, cursorPosition);\n *\n * // Zoom out by 10% at cursor position (mouse wheel down)\n * rig.zoomByAt(-0.1, cursorPosition);\n * ```\n *\n * @see {@link zoomToAt} for zooming to an absolute level\n */\n zoomByAt(delta: number, at: Point): void {\n const convertedDelta = delta * this._camera.zoomLevel;\n let originalAnchorInWorld = this._camera.convertFromViewPort2WorldSpace(at);\n const transformedDelta = this._zoomBy(convertedDelta, this._camera, this._config);\n this._camera.setZoomLevel(this._camera.zoomLevel + transformedDelta);\n let anchorInWorldAfterZoom = this._camera.convertFromViewPort2WorldSpace(at);\n const diff = PointCal.subVector(originalAnchorInWorld, anchorInWorldAfterZoom);\n const transformedDiff = this._panBy(diff, this._camera, this._config);\n this._camera.setPosition(PointCal.addVector(this._camera.position, transformedDiff));\n }\n\n /**\n * Zooms to a target level with the viewport center as the anchor point.\n *\n * @param targetZoom - Target zoom level to reach\n *\n * @remarks\n * This is a simpler version of {@link zoomToAt} that always zooms relative to the\n * viewport center. The camera position remains unchanged, so the center point of\n * the viewport stays fixed in world space.\n *\n * Use this when you want straightforward zoom without anchor-point tracking,\n * such as zoom controls in a UI toolbar.\n *\n * @example\n * ```typescript\n * // Zoom to 2x, centered on current view\n * rig.zoomTo(2.0);\n *\n * // Zoom to fit (100%)\n * rig.zoomTo(1.0);\n * ```\n *\n * @see {@link zoomToAt} for zoom with custom anchor point\n */\n zoomTo(targetZoom: number): void {\n const transformedTarget = this._zoomTo(targetZoom, this._camera, this._config);\n this._camera.setZoomLevel(transformedTarget);\n }\n\n /**\n * Zooms by a relative delta with the viewport center as the anchor point.\n *\n * @param delta - Zoom delta (added to current zoom level)\n *\n * @remarks\n * Unlike {@link zoomByAt}, the delta is NOT scaled by current zoom level.\n * This provides absolute delta changes, useful for programmatic zoom adjustments.\n *\n * The camera position remains unchanged, keeping the viewport center fixed in world space.\n *\n * @example\n * ```typescript\n * // Increase zoom by 0.5\n * rig.zoomBy(0.5);\n *\n * // Decrease zoom by 0.2\n * rig.zoomBy(-0.2);\n * ```\n *\n * @see {@link zoomByAt} for zoom with custom anchor point and scaling\n */\n zoomBy(delta: number): void {\n const transformedDelta = this._zoomBy(delta, this._camera, this._config);\n this._camera.setZoomLevel(this._camera.zoomLevel + transformedDelta);\n }\n\n /**\n * Zooms to a target level while keeping a world-space point stationary.\n *\n * @param targetZoom - Target zoom level to reach\n * @param at - Anchor point in world coordinates\n *\n * @remarks\n * Similar to {@link zoomToAt}, but accepts world-space coordinates instead of viewport coordinates.\n * Useful when you want to zoom to keep a specific world object or location centered,\n * rather than a screen position.\n *\n * The algorithm:\n * 1. Converts world anchor to viewport space (before zoom)\n * 2. Applies zoom transformation\n * 3. Converts world anchor to viewport space (after zoom)\n * 4. Calculates viewport movement and converts to world space\n * 5. Pans camera to compensate\n *\n * @example\n * ```typescript\n * // Zoom to 3x while keeping a specific world object in place\n * const objectWorldPos = { x: 1000, y: 500 };\n * rig.zoomToAtWorld(3.0, objectWorldPos);\n * ```\n *\n * @see {@link zoomToAt} for viewport-space variant\n */\n zoomToAtWorld(targetZoom: number, at: Point): void {\n let originalAnchorInViewPort = this._camera.convertFromWorld2ViewPort(at);\n const transformedTarget = this._zoomTo(targetZoom, this._camera, this._config);\n this._camera.setZoomLevel(transformedTarget);\n let anchorInViewPortAfterZoom = this._camera.convertFromWorld2ViewPort(at);\n const cameraPositionDiffInViewPort = PointCal.subVector(anchorInViewPortAfterZoom, originalAnchorInViewPort);\n const cameraPositionDiffInWorld = convertDeltaInViewPortToWorldSpace(cameraPositionDiffInViewPort, this._camera.zoomLevel, this._camera.rotation);\n const transformedCameraPositionDiff = this._panBy(cameraPositionDiffInWorld, this._camera, this._config);\n this._camera.setPosition(PointCal.addVector(this._camera.position, transformedCameraPositionDiff));\n }\n\n /**\n * Zooms by a delta while keeping a world-space point stationary.\n *\n * @param delta - Zoom delta (added to current zoom level, not scaled)\n * @param at - Anchor point in world coordinates\n *\n * @remarks\n * World-space variant of {@link zoomByAt}. The delta is NOT scaled by current zoom level,\n * unlike the viewport-space version.\n *\n * Use this when programmatically zooming around specific world objects or coordinates.\n *\n * @example\n * ```typescript\n * // Zoom in by 0.5 while keeping a world landmark stationary\n * const landmarkPos = { x: 2000, y: 1500 };\n * rig.zoomByAtWorld(0.5, landmarkPos);\n * ```\n *\n * @see {@link zoomByAt} for viewport-space variant with scaled delta\n */\n zoomByAtWorld(delta: number, at: Point): void {\n let anchorInViewPortBeforeZoom = this._camera.convertFromWorld2ViewPort(at);\n const transformedDelta = this._zoomBy(delta, this._camera, this._config);\n this._camera.setZoomLevel(this._camera.zoomLevel + transformedDelta);\n let anchorInViewPortAfterZoom = this._camera.convertFromWorld2ViewPort(at);\n const diffInViewPort = PointCal.subVector(anchorInViewPortAfterZoom, anchorInViewPortBeforeZoom);\n const diffInWorld = convertDeltaInViewPortToWorldSpace(diffInViewPort, this._camera.zoomLevel, this._camera.rotation);\n const transformedDiff = this._panBy(diffInWorld, this._camera, this._config);\n this._camera.setPosition(PointCal.addVector(this._camera.position, transformedDiff));\n }\n\n /**\n * Pans the camera by a delta in viewport coordinates.\n *\n * @param delta - Movement delta in viewport space (center-anchored, CSS pixels)\n *\n * @remarks\n * This is the most common pan method for user input (mouse drag, touch pan).\n * The delta is in screen/viewport coordinates and gets converted to world space\n * accounting for current camera rotation and zoom.\n *\n * Conversion formula:\n * 1. Rotate delta by camera rotation\n * 2. Scale by inverse zoom (1 / zoomLevel)\n * 3. Apply as world-space pan\n *\n * @example\n * ```typescript\n * // Pan camera when user drags mouse\n * canvas.addEventListener('mousemove', (e) => {\n * if (isDragging) {\n * const delta = { x: e.movementX, y: e.movementY };\n * rig.panByViewPort(delta);\n * }\n * });\n * ```\n *\n * @see {@link panByWorld} for world-space panning\n */\n panByViewPort(delta: Point): void {\n const diffInWorld = PointCal.multiplyVectorByScalar(PointCal.rotatePoint(delta, this._camera.rotation), 1 / this._camera.zoomLevel);\n this.panByWorld(diffInWorld);\n }\n\n /**\n * Pans the camera by a delta in world coordinates.\n *\n * @param delta - Movement delta in world space\n *\n * @remarks\n * Use this for programmatic camera movement or when you already have world-space\n * coordinates (e.g., moving camera to follow a world object).\n *\n * The delta is passed through the pan handler which may apply:\n * - Boundary clamping\n * - Movement restrictions (restrictXTranslation, restrictYTranslation)\n * - Other constraints from {@link CameraRigConfig}\n *\n * @example\n * ```typescript\n * // Move camera 100 units right, 50 units up in world space\n * rig.panByWorld({ x: 100, y: -50 });\n *\n * // Follow a moving object\n * const objectMovement = { x: obj.dx, y: obj.dy };\n * rig.panByWorld(objectMovement);\n * ```\n *\n * @see {@link panByViewPort} for viewport-space panning\n */\n panByWorld(delta: Point): void {\n const transformedDelta = this._panBy(delta, this._camera, this._config);\n this._camera.setPosition(PointCal.addVector(this._camera.position, transformedDelta));\n }\n\n /**\n * Pans the camera to an absolute position in world coordinates.\n *\n * @param target - Target camera position in world space\n *\n * @remarks\n * Sets the camera position directly (subject to constraints).\n * Unlike pan-by methods, this is an absolute positioning operation.\n *\n * The target is passed through the pan handler which may apply:\n * - Boundary clamping\n * - Position restrictions\n *\n * Use this for:\n * - \"Go to location\" features\n * - Centering camera on specific world coordinates\n * - Resetting camera to a known position\n *\n * @example\n * ```typescript\n * // Center camera on world origin\n * rig.panToWorld({ x: 0, y: 0 });\n *\n * // Go to specific landmark\n * const landmark = { x: 1000, y: 500 };\n * rig.panToWorld(landmark);\n * ```\n *\n * @see {@link panToViewPort} for viewport-space variant\n */\n panToWorld(target: Point): void {\n const transformedTarget = this._panTo(target, this._camera, this._config);\n this._camera.setPosition(transformedTarget);\n }\n\n /**\n * Pans the camera to position a viewport point at a specific location.\n *\n * @param target - Target position in viewport coordinates (center-anchored, CSS pixels)\n *\n * @remarks\n * Moves the camera so that the specified viewport point ends up at the viewport center.\n * This is less commonly used than world-space pan-to operations.\n *\n * The method converts the viewport target to world space, then uses {@link panToWorld}.\n *\n * @example\n * ```typescript\n * // Center the camera on what's currently at the top-left of viewport\n * rig.panToViewPort({ x: -400, y: -300 });\n * ```\n *\n * @see {@link panToWorld} for world-space variant (more commonly used)\n */\n panToViewPort(target: Point): void {\n const targetInWorld = this._camera.convertFromViewPort2WorldSpace(target);\n this.panToWorld(targetInWorld);\n }\n\n /**\n * Rotates the camera by a delta angle.\n *\n * @param delta - Rotation delta in radians (positive = counter-clockwise)\n *\n * @remarks\n * Applies a relative rotation to the camera. The delta is passed through the\n * rotation handler which may apply clamping or restrictions based on {@link CameraRigConfig}.\n *\n * Camera rotation affects:\n * - How viewport coordinates map to world coordinates\n * - The orientation of pan operations\n * - Visual rendering of the world\n *\n * @example\n * ```typescript\n * // Rotate 45 degrees counter-clockwise\n * rig.rotateBy(Math.PI / 4);\n *\n * // Rotate 90 degrees clockwise\n * rig.rotateBy(-Math.PI / 2);\n * ```\n *\n * @see {@link rotateTo} for absolute rotation\n */\n rotateBy(delta: number): void {\n const transformedDelta = this._rotateBy(delta, this._camera, this._config);\n this._camera.setRotation(this._camera.rotation + transformedDelta);\n }\n\n /**\n * Rotates the camera to an absolute angle.\n *\n * @param target - Target rotation in radians (0 = no rotation, positive = counter-clockwise)\n *\n * @remarks\n * Sets the camera rotation to a specific angle (subject to constraints).\n * The target is passed through the rotation handler which may apply clamping.\n *\n * Use this for:\n * - Resetting camera to north-up orientation (0 radians)\n * - Snapping to cardinal directions\n * - Setting rotation from UI controls\n *\n * @example\n * ```typescript\n * // Reset to north-up\n * rig.rotateTo(0);\n *\n * // Rotate to 90 degrees\n * rig.rotateTo(Math.PI / 2);\n * ```\n *\n * @see {@link rotateBy} for relative rotation\n */\n rotateTo(target: number): void {\n const transformedTarget = this._rotateTo(target, this._camera, this._config);\n this._camera.setRotation(transformedTarget);\n }\n\n /**\n * Sets whether the entire viewport must remain within boundaries.\n *\n * @remarks\n * When true, pan boundaries ensure the entire viewport stays within configured limits.\n * When false, only the camera center point is constrained.\n *\n * This is a convenience setter for {@link CameraRigConfig.limitEntireViewPort}.\n */\n set limitEntireViewPort(limit: boolean){\n this._config.limitEntireViewPort = limit;\n }\n\n /**\n * Gets whether the entire viewport must remain within boundaries.\n *\n * @returns True if entire viewport is constrained, false if only center is constrained\n */\n get limitEntireViewPort(): boolean {\n return this._config.limitEntireViewPort;\n }\n\n /**\n * Gets the underlying observable camera instance.\n *\n * @returns The camera being controlled by this rig\n */\n get camera(): ObservableBoardCamera {\n return this._camera;\n }\n\n /**\n * Sets the underlying camera instance.\n *\n * @param camera - New camera to control\n *\n * @remarks\n * Use this to swap cameras at runtime, though this is uncommon.\n * Usually you create a new rig instead.\n */\n set camera(camera: ObservableBoardCamera){\n this._camera = camera;\n }\n\n /**\n * Gets the current camera rig configuration.\n *\n * @returns Current configuration object\n *\n * @remarks\n * Returns a reference to the internal config. Modifications will affect rig behavior.\n * For safer updates, use {@link configure} instead.\n */\n get config(): CameraRigConfig {\n return this._config;\n }\n\n /**\n * Sets the camera rig configuration.\n *\n * @param config - New configuration object\n *\n * @remarks\n * Creates a shallow copy of the provided config.\n * For partial updates, use {@link configure} instead.\n */\n set config(config: CameraRigConfig){\n this._config = {...config};\n }\n\n /**\n * Updates camera rig configuration with partial settings.\n *\n * @param config - Partial configuration to merge with current config\n *\n * @remarks\n * This is the recommended way to update configuration at runtime.\n * Only provided properties are updated; others remain unchanged.\n *\n * @example\n * ```typescript\n * // Enable zoom restrictions without changing other settings\n * rig.configure({\n * restrictZoom: true,\n * zoomLevelLimits: { min: 0.5, max: 5.0 }\n * });\n *\n * // Disable position clamping\n * rig.configure({ clampTranslation: false });\n * ```\n */\n configure(config: Partial<CameraRigConfig>){\n this._config = {...this._config, ...config};\n }\n\n /**\n * Cleans up resources used by the camera rig.\n *\n * @remarks\n * Currently a no-op as DefaultCameraRig has no resources to clean up.\n * Implements {@link BaseContext} interface for consistency with other systems.\n */\n cleanup(): void {\n }\n\n /**\n * Sets up the camera rig.\n *\n * @remarks\n * Currently a no-op as DefaultCameraRig requires no setup.\n * Implements {@link BaseContext} interface for consistency with other systems.\n */\n setup(): void {\n }\n\n /**\n * Updates the camera rig state.\n *\n * @remarks\n * Currently a no-op as DefaultCameraRig has no per-frame update logic.\n * Implements {@link BaseContext} interface for consistency with other systems.\n *\n * In stateful rig implementations, this might handle:\n * - Animation interpolation\n * - Momentum/inertia\n * - Smooth camera following\n */\n update(): void {\n }\n}\n\n/**\n * Creates a camera rig with sensible default configuration.\n *\n * @param camera - Observable camera instance to control\n * @returns Configured camera rig ready for use\n *\n * @remarks\n * This factory function creates a {@link DefaultCameraRig} with a balanced default configuration:\n *\n * **Enabled by default:**\n * - `limitEntireViewPort: true` - Entire viewport stays within boundaries\n * - `clampTranslation: true` - Position is clamped to boundaries\n * - `clampZoom: true` - Zoom is clamped to limits\n *\n * **Disabled by default:**\n * - All movement restrictions (`restrictXTranslation`, `restrictYTranslation`, etc.)\n * - Zoom restrictions (`restrictZoom`)\n * - Relative translation restrictions\n *\n * This configuration allows free camera movement with boundary enforcement,\n * suitable for most infinite canvas applications.\n *\n * @example\n * ```typescript\n * const camera = new DefaultBoardCamera(1920, 1080);\n * const rig = createDefaultCameraRig(camera);\n *\n * // Ready to use with sensible defaults\n * rig.configure({\n * boundaries: {\n * min: { x: -1000, y: -1000 },\n * max: { x: 1000, y: 1000 }\n * }\n * });\n *\n * rig.panByViewPort({ x: 100, y: 50 });\n * rig.zoomByAt(0.1, mousePosition);\n * ```\n *\n * @category Camera Rig\n * @see {@link DefaultCameraRig} for the implementation\n * @see {@link CameraRigConfig} for all available configuration options\n */\nexport function createDefaultCameraRig(camera: ObservableBoardCamera): CameraRig{\n return new DefaultCameraRig({\n limitEntireViewPort: true,\n restrictRelativeXTranslation: false,\n restrictRelativeYTranslation: false,\n restrictXTranslation: false,\n restrictYTranslation: false,\n restrictZoom: false,\n clampTranslation: true,\n clampZoom: true,\n }, camera);\n}\n",
36
- "\nimport { CameraMux, CameraMuxPanOutput, CameraMuxZoomOutput, CameraMuxRotationOutput } from \"../interface\";\nimport { Point } from \"@ue-too/math\";\nimport { ObservableBoardCamera } from \"../../interface\";\nimport { createDefaultPanControlStateMachine, PanControlStateMachine, PanControlOutputEvent } from \"./pan-control-state-machine\";\nimport { createDefaultZoomControlStateMachine, ZoomControlStateMachine, ZoomControlOutputEvent } from \"./zoom-control-state-machine\";\nimport { createDefaultRotateControlStateMachine, RotateControlStateMachine, RotateControlOutputEvent } from \"./rotation-control-state-machine\";\nimport { CameraRig } from \"../../camera-rig\";\nimport { createDefaultCameraRig } from \"../../camera-rig\";\n\n/**\n * Advanced camera input multiplexer with animation support and input locking via state machines.\n *\n * @remarks\n * This {@link CameraMux} implementation provides sophisticated input flow control using\n * separate state machines for pan, zoom, and rotation. Each state machine can:\n * - Block user input during camera animations\n * - Manage animation playback\n * - Arbitrate between user input and programmatic camera control\n * - Handle transitions between different camera control states\n *\n * **Key features:**\n * - **Animation system**: Support for smooth camera animations (pan-to, zoom-to, rotate-to)\n * - **Input locking**: Automatically block user input during animations\n * - **State-based control**: Each camera operation (pan/zoom/rotate) has its own state machine\n * - **Flexible transitions**: Initiate transitions to interrupt or chain animations\n *\n * **Architecture:**\n * - Three independent state machines: {@link PanControlStateMachine}, {@link ZoomControlStateMachine}, {@link RotateControlStateMachine}\n * - Each state machine decides whether to allow or block input based on current state\n * - State machines receive events and produce output events for camera operations\n *\n * **When to use:**\n * - Applications requiring smooth camera animations (e.g., \"focus on object\", \"zoom to region\")\n * - UI where user input should be blocked during programmatic camera movements\n * - Games or interactive experiences with scripted camera sequences\n *\n * **Alternatives:**\n * - Use {@link Relay} for simple passthrough without animation support\n * - Implement custom {@link CameraMux} for different state management approaches\n *\n * @example\n * ```typescript\n * const camera = new DefaultBoardCamera();\n * const mux = createCameraMuxWithAnimationAndLock(camera);\n *\n * // Start a pan animation - user input will be blocked\n * mux.notifyPanToAnimationInput({ x: 1000, y: 500 });\n *\n * // User tries to pan during animation - will be blocked\n * const result = mux.notifyPanInput({ x: 50, y: 30 });\n * // result.allowPassThrough = false (blocked during animation)\n *\n * // After animation completes, user input allowed again\n * ```\n *\n * @category Input Flow Control\n * @see {@link CameraMux} for the interface definition\n * @see {@link Relay} for simpler passthrough implementation\n * @see {@link createCameraMuxWithAnimationAndLock} for factory function\n */\nexport class CameraMuxWithAnimationAndLock implements CameraMux {\n\n private _panStateMachine: PanControlStateMachine;\n private _zoomStateMachine: ZoomControlStateMachine;\n private _rotateStateMachine: RotateControlStateMachine;\n\n /**\n * Creates a new camera mux with animation and locking capabilities.\n *\n * @param panStateMachine - State machine controlling pan operations and animations\n * @param zoomStateMachine - State machine controlling zoom operations and animations\n * @param rotateStateMachine - State machine controlling rotation operations and animations\n *\n * @remarks\n * Typically created via factory functions like {@link createCameraMuxWithAnimationAndLock}\n * rather than direct instantiation.\n */\n constructor(panStateMachine: PanControlStateMachine, zoomStateMachine: ZoomControlStateMachine, rotateStateMachine: RotateControlStateMachine){\n this._panStateMachine = panStateMachine;\n this._zoomStateMachine = zoomStateMachine;\n this._rotateStateMachine = rotateStateMachine;\n }\n\n /**\n * Initiates a pan animation to a target position.\n *\n * @param target - Target position in world coordinates\n * @returns Pan output indicating whether animation was initiated\n *\n * @remarks\n * This method starts a camera pan animation to the specified world position.\n * The state machine handles:\n * - Starting the animation\n * - Blocking user input during animation\n * - Producing incremental pan deltas each frame\n *\n * The animation continues until the camera reaches the target or is interrupted.\n *\n * @example\n * ```typescript\n * // Animate camera to world position\n * mux.notifyPanToAnimationInput({ x: 1000, y: 500 });\n * ```\n */\n notifyPanToAnimationInput(target: Point): CameraMuxPanOutput {\n const res = this._panStateMachine.notifyPanToAnimationInput(target);\n\n if(res.handled) {\n const output = res.output;\n if(output !== undefined){\n switch(output.type){\n case 'panByViewPort':\n return { allowPassThrough: true, delta: output.delta };\n case 'panToWorld':\n return { allowPassThrough: true, delta: output.target };\n default:\n return { allowPassThrough: false };\n }\n\n }\n }\n return { allowPassThrough: false };\n }\n\n /**\n * Processes user pan input (implements {@link CameraMux.notifyPanInput}).\n *\n * @param delta - Pan delta in viewport coordinates\n * @returns Output indicating whether pan is allowed\n *\n * @remarks\n * This method is called when the user attempts to pan the camera (e.g., mouse drag).\n * The pan state machine determines whether to allow the input based on current state:\n * - **Allowed**: When in idle state or user control state\n * - **Blocked**: When camera animation is playing\n *\n * @example\n * ```typescript\n * // User drags mouse\n * const result = mux.notifyPanInput({ x: 50, y: 30 });\n * if (result.allowPassThrough) {\n * // Apply pan to camera\n * cameraRig.panByViewPort(result.delta);\n * }\n * ```\n */\n notifyPanInput(delta: Point): CameraMuxPanOutput {\n const result = this._panStateMachine.happens(\"userPanByInput\", { diff: delta });\n if (result.handled && 'output' in result && result.output) {\n const output = result.output as PanControlOutputEvent;\n if (output.type !== \"none\") {\n return { allowPassThrough: true, delta: delta };\n }\n }\n return { allowPassThrough: false };\n }\n\n /**\n * Processes user zoom input (implements {@link CameraMux.notifyZoomInput}).\n *\n * @param delta - Zoom delta (change in zoom level)\n * @param at - Anchor point in viewport coordinates\n * @returns Output indicating whether zoom is allowed\n *\n * @remarks\n * This method is called when the user attempts to zoom (e.g., mouse wheel).\n * The zoom state machine determines whether to allow the input based on current state:\n * - **Allowed**: When in idle state or user control state\n * - **Blocked**: When zoom animation is playing\n *\n * @example\n * ```typescript\n * // User scrolls mouse wheel\n * const result = mux.notifyZoomInput(0.1, mousePosition);\n * if (result.allowPassThrough) {\n * // Apply zoom to camera\n * cameraRig.zoomByAt(result.delta, result.anchorPoint);\n * }\n * ```\n */\n notifyZoomInput(delta: number, at: Point): CameraMuxZoomOutput {\n const result = this._zoomStateMachine.happens(\"userZoomByAtInput\", { deltaZoom: delta, anchorPoint: at });\n if (result.handled && 'output' in result && result.output) {\n const output = result.output as ZoomControlOutputEvent;\n if (output.type !== \"none\") {\n return { allowPassThrough: true, delta: delta, anchorPoint: at };\n }\n }\n return { allowPassThrough: false };\n }\n\n /**\n * Processes user rotation input (rotate-by variant).\n *\n * @param delta - Rotation delta in radians\n * @returns Output from rotation state machine\n *\n * @remarks\n * Delegates to the rotation state machine's rotate-by handler.\n * The state machine determines whether to allow rotation based on current state.\n */\n notifyRotateByInput(delta: number) {\n return this._rotateStateMachine.notifyRotateByInput(delta);\n }\n\n /**\n * Initiates a rotation animation to a target angle.\n *\n * @param target - Target rotation angle in radians\n * @returns Output from rotation state machine\n *\n * @remarks\n * Starts a camera rotation animation to the specified angle.\n * User input will be blocked during the animation.\n */\n notifyRotateToAnimationInput(target: number) {\n return this._rotateStateMachine.notifyRotateToAnimationInput(target);\n }\n\n /**\n * Initiates a zoom animation to a target level at a viewport position.\n *\n * @param targetZoom - Target zoom level\n * @param at - Anchor point in viewport coordinates (defaults to origin)\n *\n * @remarks\n * Starts a zoom animation that zooms to the specified level while keeping\n * the anchor point stationary (zoom-to-cursor behavior).\n * User input will be blocked during the animation.\n */\n notifyZoomInputAnimation(targetZoom: number, at: Point = {x: 0, y: 0}): void {\n this._zoomStateMachine.notifyZoomToAtCenterInput(targetZoom, at);\n }\n\n /**\n * Initiates a zoom animation to a target level at a world position.\n *\n * @param targetZoom - Target zoom level\n * @param at - Anchor point in world coordinates (defaults to origin)\n *\n * @remarks\n * Similar to {@link notifyZoomInputAnimation} but accepts world-space coordinates\n * for the anchor point instead of viewport coordinates.\n */\n notifyZoomInputAnimationWorld(targetZoom: number, at: Point = {x: 0, y: 0}): void {\n this._zoomStateMachine.notifyZoomToAtWorldInput(targetZoom, at);\n }\n\n /**\n * Processes user rotation input (implements {@link CameraMux.notifyRotationInput}).\n *\n * @param delta - Rotation delta in radians\n * @returns Output indicating whether rotation is allowed\n *\n * @remarks\n * This method is called when the user attempts to rotate the camera.\n * The rotation state machine determines whether to allow the input based on current state:\n * - **Allowed**: When in idle state or user control state\n * - **Blocked**: When rotation animation is playing\n *\n * @example\n * ```typescript\n * // User rotates camera\n * const result = mux.notifyRotationInput(0.1);\n * if (result.allowPassThrough) {\n * cameraRig.rotateBy(result.delta);\n * }\n * ```\n */\n notifyRotationInput(delta: number): CameraMuxRotationOutput {\n const result = this._rotateStateMachine.happens(\"userRotateByInput\", { diff: delta });\n if (result.handled && 'output' in result && result.output) {\n const output = result.output as RotateControlOutputEvent;\n if (output.type !== \"none\") {\n return { allowPassThrough: true, delta: delta };\n }\n }\n return { allowPassThrough: false };\n }\n\n /**\n * Initiates a transition in the pan state machine.\n *\n * @remarks\n * This method forces the pan state machine to transition to its next state.\n * Can be used to interrupt animations or force state changes.\n */\n initatePanTransition(): void {\n this._panStateMachine.initateTransition();\n }\n\n /**\n * Initiates a transition in the zoom state machine.\n *\n * @remarks\n * This method forces the zoom state machine to transition to its next state.\n * Can be used to interrupt animations or force state changes.\n */\n initateZoomTransition(): void {\n this._zoomStateMachine.initateTransition();\n }\n\n /**\n * Initiates a transition in the rotation state machine.\n *\n * @remarks\n * This method forces the rotation state machine to transition to its next state.\n * Can be used to interrupt animations or force state changes.\n */\n initateRotateTransition(): void {\n this._rotateStateMachine.initateTransition();\n }\n\n /**\n * Gets the rotation state machine.\n *\n * @returns The rotation state machine instance\n *\n * @remarks\n * Provides direct access to the rotation state machine for advanced control\n * or state inspection.\n */\n get rotateStateMachine(): RotateControlStateMachine {\n return this._rotateStateMachine;\n }\n\n /**\n * Gets the pan state machine.\n *\n * @returns The pan state machine instance\n *\n * @remarks\n * Provides direct access to the pan state machine for advanced control\n * or state inspection.\n */\n get panStateMachine(): PanControlStateMachine {\n return this._panStateMachine;\n }\n\n /**\n * Gets the zoom state machine.\n *\n * @returns The zoom state machine instance\n *\n * @remarks\n * Provides direct access to the zoom state machine for advanced control\n * or state inspection.\n */\n get zoomStateMachine(): ZoomControlStateMachine {\n return this._zoomStateMachine;\n }\n}\n\n/**\n * Creates a camera mux with animation and locking capabilities from a camera instance.\n *\n * @param camera - Observable camera to control\n * @returns Configured camera mux with animation support\n *\n * @remarks\n * This factory function creates a complete camera input flow control system with:\n * 1. A default {@link CameraRig} wrapping the provided camera\n * 2. Three state machines (pan, zoom, rotation) for animation control\n * 3. A {@link CameraMuxWithAnimationAndLock} coordinating the state machines\n *\n * **What you get:**\n * - Smooth camera animations (pan-to, zoom-to, rotate-to)\n * - Automatic input blocking during animations\n * - State-based input arbitration\n * - All with sensible default configurations\n *\n * **Use this when:**\n * - You have a camera and want animation support out-of-the-box\n * - You don't need custom camera rig configuration\n * - You want the simplest setup for animated camera control\n *\n * @example\n * ```typescript\n * const camera = new DefaultBoardCamera(1920, 1080);\n * const mux = createCameraMuxWithAnimationAndLock(camera);\n *\n * // Start a pan animation\n * mux.notifyPanToAnimationInput({ x: 1000, y: 500 });\n *\n * // User input is blocked during animation\n * const result = mux.notifyPanInput({ x: 50, y: 30 });\n * console.log(result.allowPassThrough); // false during animation\n * ```\n *\n * @category Input Flow Control\n * @see {@link CameraMuxWithAnimationAndLock} for the implementation\n * @see {@link createCameraMuxWithAnimationAndLockWithCameraRig} for custom rig version\n */\nexport function createCameraMuxWithAnimationAndLock(camera: ObservableBoardCamera): CameraMux {\n const context = createDefaultCameraRig(camera);\n const panStateMachine = createDefaultPanControlStateMachine(context);\n const zoomStateMachine = createDefaultZoomControlStateMachine(context);\n const rotateStateMachine = createDefaultRotateControlStateMachine(context);\n return new CameraMuxWithAnimationAndLock(panStateMachine, zoomStateMachine, rotateStateMachine);\n}\n\n/**\n * Creates a camera mux with animation and locking capabilities from a camera rig.\n *\n * @param cameraRig - Pre-configured camera rig to use\n * @returns Configured camera mux with animation support\n *\n * @remarks\n * Similar to {@link createCameraMuxWithAnimationAndLock} but accepts an existing\n * camera rig instead of creating a default one. Use this when you need:\n * - Custom camera rig configuration\n * - Specific pan/zoom/rotation constraints\n * - Non-default handler pipelines\n * - To share a camera rig between multiple systems\n *\n * **Advantages over camera-only variant:**\n * - Full control over camera rig settings\n * - Ability to configure boundaries, restrictions, clamping\n * - Use custom handler functions\n * - Reuse existing rig instance\n *\n * @example\n * ```typescript\n * // Create custom camera rig with specific config\n * const camera = new DefaultBoardCamera(1920, 1080);\n * const rig = new DefaultCameraRig({\n * limitEntireViewPort: true,\n * clampTranslation: true,\n * boundaries: {\n * min: { x: 0, y: 0 },\n * max: { x: 2000, y: 1000 }\n * }\n * }, camera);\n *\n * // Create mux with custom rig\n * const mux = createCameraMuxWithAnimationAndLockWithCameraRig(rig);\n *\n * // Animations respect rig's boundaries and constraints\n * mux.notifyPanToAnimationInput({ x: 3000, y: 1500 });\n * // Camera will be clamped to boundaries during animation\n * ```\n *\n * @category Input Flow Control\n * @see {@link CameraMuxWithAnimationAndLock} for the implementation\n * @see {@link createCameraMuxWithAnimationAndLock} for simpler camera-only version\n */\nexport function createCameraMuxWithAnimationAndLockWithCameraRig(cameraRig: CameraRig): CameraMux {\n const panStateMachine = createDefaultPanControlStateMachine(cameraRig);\n const zoomStateMachine = createDefaultZoomControlStateMachine(cameraRig);\n const rotateStateMachine = createDefaultRotateControlStateMachine(cameraRig);\n return new CameraMuxWithAnimationAndLock(panStateMachine, zoomStateMachine, rotateStateMachine);\n}\n",
37
- "import { Point } from \"@ue-too/math\";\nimport { BaseContext, NO_OP } from \"@ue-too/being\";\nimport { CanvasPositionDimensionPublisher, getTrueRect, Observable, Observer, SubscriptionOptions, SvgPositionDimensionPublisher, SynchronousObservable } from \"../../utils\";\n\n/**\n * Cursor styles used to provide visual feedback for different input states.\n *\n * @remarks\n * These cursor styles indicate the current interaction mode to users:\n * - **GRAB**: Indicates the canvas is ready to be panned (spacebar pressed, no drag yet)\n * - **GRABBING**: Indicates active panning is in progress\n * - **DEFAULT**: Normal cursor state when no special interaction is active\n *\n * @category Input State Machine\n */\nexport enum CursorStyle {\n GRAB = \"grab\",\n DEFAULT = \"default\",\n GRABBING = \"grabbing\"\n}\n\n/**\n * Canvas dimension and position information.\n *\n * @property width - The canvas width in CSS pixels\n * @property height - The canvas height in CSS pixels\n * @property position - The top-left position of the canvas in window coordinates\n *\n * @category Input State Machine\n */\nexport type CanvasDimensions = {width: number, height: number, position: Point};\n\n/**\n * Abstraction interface for canvas element access and manipulation.\n *\n * @remarks\n * This interface provides a decoupled way to access canvas properties without direct DOM access.\n * Multiple implementations exist to support different use cases:\n * - **CanvasProxy**: Full implementation for HTML canvas elements with dimension tracking\n * - **SvgProxy**: Implementation for SVG elements\n * - **DummyCanvas**: No-op implementation for web worker contexts\n * - **WorkerRelayCanvas**: Relays canvas dimension updates to web workers\n * - **CanvasCacheInWebWorker**: Caches canvas dimensions within a web worker\n *\n * The abstraction enables:\n * - Coordinate system transformations (window → canvas → viewport)\n * - Canvas dimension tracking without repeated DOM queries\n * - Cursor style management\n * - Support for both canvas and SVG rendering contexts\n *\n * @category Input State Machine\n */\nexport interface Canvas {\n /** The canvas width in CSS pixels */\n width: number;\n /** The canvas height in CSS pixels */\n height: number;\n /** The top-left position of the canvas in window coordinates */\n position: Point;\n /** Sets the CSS cursor style for visual feedback */\n setCursor: (style: CursorStyle) => void;\n /** Combined dimensions and position information */\n dimensions: CanvasDimensions;\n /** Whether the canvas is currently detached from the DOM */\n detached: boolean;\n /** Cleanup method to dispose of resources and event listeners */\n tearDown: () => void;\n}\n\n/**\n * No-op implementation of Canvas interface for web worker relay contexts.\n *\n * @remarks\n * This class is used when an input state machine is configured to relay events to a web worker\n * rather than perform actual canvas operations. The state machine requires a Canvas in its context,\n * but in the relay scenario, no actual canvas operations are needed - events are simply forwarded\n * to the worker thread.\n *\n * All properties return default/empty values and all methods are no-ops.\n *\n * @category Input State Machine\n *\n * @see {@link DummyKmtInputContext}\n */\nexport class DummyCanvas implements Canvas {\n width: number = 0;\n height: number = 0;\n position: Point = {x: 0, y: 0};\n setCursor: (style: CursorStyle) => void = NO_OP;\n dimensions: {width: number, height: number, position: Point} = {width: 0, height: 0, position: {x: 0, y: 0}};\n detached: boolean = false;\n tearDown: () => void = NO_OP;\n}\n\nexport class CanvasCacheInWebWorker implements Canvas {\n\n private _width: number;\n private _height: number;\n private _position: Point;\n private _postMessageFunction: typeof postMessage;\n\n constructor(postMessageFunction: typeof postMessage){\n this._width = 0;\n this._height = 0;\n this._position = {x: 0, y: 0};\n this._postMessageFunction = postMessageFunction;\n }\n\n get dimensions(): {width: number, height: number, position: Point} {\n return {width: this._width, height: this._height, position: this._position};\n }\n\n tearDown(): void {\n }\n\n set width(width: number){\n this._width = width;\n }\n\n set height(height: number){\n this._height = height;\n }\n\n set position(position: Point){\n this._position = position;\n }\n\n get width(): number {\n return this._width;\n }\n\n get height(): number {\n return this._height;\n }\n\n get position(): Point {\n return this._position;\n }\n\n setCursor(style: \"grab\" | \"default\" | \"grabbing\"): void {\n this._postMessageFunction({type: \"setCursor\", style});\n }\n\n get detached(): boolean {\n return false;\n }\n}\n\nexport class CanvasProxy implements Canvas, Observable<[CanvasDimensions]> {\n\n private _width: number = 0;\n private _height: number = 0;\n private _position: Point = {x: 0, y: 0};\n private _canvasPositionDimensionPublisher: CanvasPositionDimensionPublisher;\n private _canvas: HTMLCanvasElement | undefined;\n private _internalSizeUpdateObservable: Observable<[CanvasDimensions]>;\n\n constructor(canvas?: HTMLCanvasElement) {\n this._internalSizeUpdateObservable = new SynchronousObservable<[CanvasDimensions]>();\n\n if(canvas){\n const boundingRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(boundingRect, window.getComputedStyle(canvas));\n this._width = trueRect.width;\n this._height = trueRect.height;\n this._position = {x: trueRect.left, y: trueRect.top};\n this._canvas = canvas;\n }\n\n this._canvasPositionDimensionPublisher = new CanvasPositionDimensionPublisher(canvas);\n this._canvasPositionDimensionPublisher.onPositionUpdate((rect)=>{\n // the rect is the canvas dimension in the DOM (the width and height attribute would need to multiply by the device pixel ratio)\n if(this._canvas == undefined){\n console.error('is not attached to any canvas should not have getting any updates');\n return;\n }\n\n this._width = rect.width;\n this._height = rect.height;\n this._position = {x: rect.left, y: rect.top};\n\n this._internalSizeUpdateObservable.notify({\n width: this._width,\n height: this._height,\n position: this._position\n });\n });\n }\n\n subscribe(observer: Observer<[CanvasDimensions]>, options?: SubscriptionOptions): () => void {\n return this._internalSizeUpdateObservable.subscribe(observer, options);\n }\n\n notify(...data: [CanvasDimensions]): void {\n this._internalSizeUpdateObservable.notify(...data);\n }\n\n get detached(): boolean {\n return this._canvas === undefined;\n }\n\n get dimensions(): {width: number, height: number, position: Point} {\n return {width: this._width, height: this._height, position: this._position};\n }\n\n get width(): number {\n return this._width;\n }\n\n /**\n * set the width of the canvas\n * the width is synonymous with the canvas style width not the canvas width\n */\n setWidth(width: number){\n if(this._canvas){\n this._canvas.width = width * window.devicePixelRatio;\n this._canvas.style.width = width + \"px\";\n }\n }\n\n /**\n * set the height of the canvas\n * the height is synonymous with the canvas style height not the canvas height\n */\n setHeight(height: number){\n if(this._canvas){\n this._canvas.height = height * window.devicePixelRatio;\n this._canvas.style.height = height + \"px\";\n }\n }\n\n get height(): number {\n return this._height;\n }\n\n get position(): Point {\n return this._position;\n }\n\n setCursor(style: \"grab\" | \"default\" | \"grabbing\"): void {\n if(this._canvas){\n this._canvas.style.cursor = style;\n }\n }\n\n tearDown(): void {\n this._canvasPositionDimensionPublisher.dispose();\n this._canvas = undefined;\n this._width = 0;\n this._height = 0;\n this._position = {x: 0, y: 0};\n }\n\n attach(canvas: HTMLCanvasElement){\n this._canvasPositionDimensionPublisher.attach(canvas);\n this._canvas = canvas;\n const boundingRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(boundingRect, window.getComputedStyle(canvas));\n this._canvas.width = trueRect.width * window.devicePixelRatio;\n this._canvas.height = trueRect.height * window.devicePixelRatio;\n this._canvas.style.width = trueRect.width + \"px\";\n this._canvas.style.height = trueRect.height + \"px\";\n this._width = trueRect.width;\n this._height = trueRect.height;\n this._position = {x: trueRect.left, y: trueRect.top};\n this._internalSizeUpdateObservable.notify({\n width: this._width,\n height: this._height,\n position: this._position\n });\n }\n\n logCanvasTrueSize(){\n if(this._canvas === undefined){\n return;\n }\n console.log('canvas true size');\n console.log('style width', this._canvas.style.width);\n console.log('style height', this._canvas.style.height);\n console.log('width', this._canvas.width);\n console.log('height', this._canvas.height);\n console.log('proxy width', this._width);\n console.log('proxy height', this._height);\n }\n\n}\n\nexport class SvgProxy implements Canvas, Observable<[CanvasDimensions]> {\n\n private _width: number = 0;\n private _height: number = 0;\n private _position: Point = {x: 0, y: 0};\n private _svgPositionDimensionPublisher: SvgPositionDimensionPublisher;\n private _svg: SVGSVGElement | undefined;\n private _internalSizeUpdateObservable: Observable<[CanvasDimensions]>;\n\n constructor(svg?: SVGSVGElement) {\n this._internalSizeUpdateObservable = new SynchronousObservable<[CanvasDimensions]>();\n\n if(svg){\n const boundingRect = svg.getBoundingClientRect();\n const trueRect = getTrueRect(boundingRect, window.getComputedStyle(svg));\n this._width = trueRect.width;\n this._height = trueRect.height;\n this._position = {x: trueRect.left, y: trueRect.top};\n this._svg = svg;\n }\n\n this._svgPositionDimensionPublisher = new SvgPositionDimensionPublisher(svg);\n this._svgPositionDimensionPublisher.onPositionUpdate((rect)=>{\n // the rect is the canvas dimension in the DOM (the width and height attribute would need to multiply by the device pixel ratio)\n if(this._svg == undefined){\n console.error('is not attached to any canvas should not have getting any updates');\n return;\n }\n\n this._width = rect.width;\n this._height = rect.height;\n this._position = {x: rect.left, y: rect.top};\n\n this._internalSizeUpdateObservable.notify({\n width: this._width,\n height: this._height,\n position: this._position\n });\n });\n }\n\n subscribe(observer: Observer<[CanvasDimensions]>, options?: SubscriptionOptions): () => void {\n return this._internalSizeUpdateObservable.subscribe(observer, options);\n }\n\n notify(...data: [CanvasDimensions]): void {\n this._internalSizeUpdateObservable.notify(...data);\n }\n\n get detached(): boolean {\n return this._svg === undefined;\n }\n\n get dimensions(): {width: number, height: number, position: Point} {\n return {width: this._width, height: this._height, position: this._position};\n }\n\n get width(): number {\n return this._width;\n }\n\n /**\n * set the width of the canvas\n * the width is synonymous with the canvas style width not the canvas width\n */\n setWidth(width: number){\n if(this._svg){\n this._svg.style.width = width + \"px\";\n }\n }\n\n /**\n * set the height of the canvas\n * the height is synonymous with the canvas style height not the canvas height\n */\n setHeight(height: number){\n if(this._svg){\n this._svg.style.height = height + \"px\";\n }\n }\n\n get height(): number {\n return this._height;\n }\n\n get position(): Point {\n return this._position;\n }\n\n setCursor(style: \"grab\" | \"default\" | \"grabbing\"): void {\n if(this._svg){\n this._svg.style.cursor = style;\n }\n }\n\n tearDown(): void {\n this._svgPositionDimensionPublisher.dispose();\n this._svg = undefined;\n this._width = 0;\n this._height = 0;\n this._position = {x: 0, y: 0};\n }\n\n attach(svg: SVGSVGElement){\n this._svgPositionDimensionPublisher.attach(svg);\n this._svg = svg;\n const boundingRect = svg.getBoundingClientRect();\n const trueRect = getTrueRect(boundingRect, window.getComputedStyle(svg));\n this._svg.style.width = trueRect.width + \"px\";\n this._svg.style.height = trueRect.height + \"px\";\n this._width = trueRect.width;\n this._height = trueRect.height;\n this._position = {x: trueRect.left, y: trueRect.top};\n this._internalSizeUpdateObservable.notify({\n width: this._width,\n height: this._height,\n position: this._position\n });\n }\n\n logCanvasTrueSize(){\n if(this._svg === undefined){\n return;\n }\n console.log('canvas true size');\n console.log('style width', this._svg.style.width);\n console.log('style height', this._svg.style.height);\n console.log('width', this._svg.width);\n console.log('height', this._svg.height);\n console.log('proxy width', this._width);\n console.log('proxy height', this._height);\n }\n\n}\n\n/**\n * @description A proxy for the canvas that is used to communicate with the web worker.\n * The primary purpose of this class is to cache the canvas dimensions and position in the DOM to reduce the calling of the getBoundingClientRect method.\n * This class only serves as a relay of the updated canvas dimensions and position to the web worker.\n * \n */\nexport class WorkerRelayCanvas implements Canvas {\n\n private _width: number;\n private _height: number;\n private _position: Point;\n private _webWorker: Worker;\n private _canvas: HTMLCanvasElement;\n private _canvasDiemsionPublisher: CanvasPositionDimensionPublisher;\n\n constructor(canvas: HTMLCanvasElement, webWorker: Worker, canvasDiemsionPublisher: CanvasPositionDimensionPublisher){\n const boundingRect = canvas.getBoundingClientRect();\n this._canvas = canvas;\n this._webWorker = webWorker;\n const trueRect = getTrueRect(boundingRect, window.getComputedStyle(canvas));\n this._width = trueRect.width;\n this._height = trueRect.height;\n this._position = {x: trueRect.left, y: trueRect.top};\n this._webWorker.postMessage({type: \"setCanvasDimensions\", width: boundingRect.width, height: boundingRect.height, position: {x: boundingRect.left, y: boundingRect.top}});\n canvasDiemsionPublisher.onPositionUpdate((rect)=>{\n this._width = rect.width;\n this._height = rect.height;\n this._position = {x: rect.left, y: rect.top};\n this._webWorker.postMessage({type: \"updateCanvasDimensions\", width: rect.width, height: rect.height, position: {x: rect.left, y: rect.top}});\n });\n this._canvasDiemsionPublisher = canvasDiemsionPublisher;\n }\n\n get width(): number {\n return this._width;\n }\n\n get height(): number {\n return this._height;\n }\n\n tearDown(): void {\n this._canvasDiemsionPublisher.dispose();\n }\n\n get position(): Point {\n return this._position;\n }\n\n get dimensions(): {width: number, height: number, position: Point} {\n return {width: this._width, height: this._height, position: this._position};\n }\n\n get detached(): boolean {\n return false;\n }\n\n setCursor(style: \"grab\" | \"default\" | \"grabbing\"): void {\n this._canvas.style.cursor = style;\n }\n}\n\n/**\n * Context interface for the Keyboard/Mouse/Trackpad (KMT) input state machine.\n *\n * @remarks\n * This context provides the state and behavior needed by the KMT state machine to:\n * 1. Track cursor positions for calculating pan deltas\n * 2. Distinguish between mouse and trackpad input modalities\n * 3. Access canvas dimensions for coordinate transformations\n * 4. Manage coordinate system alignment (inverted Y-axis handling)\n *\n * **Input Modality Detection**:\n * The context uses a scoring system (`kmtTrackpadTrackScore`) to differentiate between\n * mouse and trackpad input, which have different zoom behaviors:\n * - Mouse: Ctrl+Scroll = zoom, Scroll = pan\n * - Trackpad: Scroll = zoom (no Ctrl needed), Two-finger gesture = pan\n *\n * **Coordinate System**:\n * The `alignCoordinateSystem` flag determines Y-axis orientation:\n * - `true`: Standard screen coordinates (Y increases downward)\n * - `false`: Inverted coordinates (Y increases upward)\n *\n * This interface extends BaseContext from the @ue-too/being state machine library,\n * inheriting setup() and cleanup() lifecycle methods.\n *\n * @category Input State Machine\n */\nexport interface KmtInputContext extends BaseContext {\n /** Whether to use standard screen coordinate system (vs inverted Y-axis) */\n alignCoordinateSystem: boolean;\n /** Canvas accessor for dimensions and cursor control */\n canvas: Canvas;\n /** Sets the initial cursor position when starting a pan gesture */\n setInitialCursorPosition: (position: Point) => void;\n /** Cancels the current action and resets cursor position */\n cancelCurrentAction: () => void;\n /** The cursor position when a pan gesture started */\n initialCursorPosition: Point;\n /** Score tracking input modality: >0 for mouse, <0 for trackpad, 0 for undetermined */\n kmtTrackpadTrackScore: number;\n /** Decreases the score toward trackpad */\n subtractKmtTrackpadTrackScore: () => void;\n /** Increases the score toward mouse */\n addKmtTrackpadTrackScore: () => void;\n /** Sets the determined input modality */\n setMode: (mode: 'kmt' | 'trackpad' | 'TBD') => void;\n /** The current input modality: 'kmt' (mouse), 'trackpad', or 'TBD' (to be determined) */\n mode: 'kmt' | 'trackpad' | 'TBD';\n}\n\n/**\n * No-op implementation of KmtInputContext for web worker relay scenarios.\n *\n * @remarks\n * Used when the input state machine is configured to relay events to a web worker\n * rather than process them locally. The state machine requires a context, but in\n * the relay scenario, no actual state tracking is needed - events are simply forwarded.\n *\n * All methods are no-ops and all properties return default values.\n *\n * @category Input State Machine\n *\n * @see {@link DummyCanvas}\n */\nexport class DummyKmtInputContext implements KmtInputContext {\n\n public alignCoordinateSystem: boolean = false;\n public canvas: Canvas = new DummyCanvas();\n public initialCursorPosition: Point = {x: 0, y: 0};\n\n constructor(){\n\n }\n\n toggleOnEdgeAutoCameraInput: () => void = NO_OP;\n toggleOffEdgeAutoCameraInput: () => void = NO_OP;\n setCursorPosition: (position: Point) => void = NO_OP;\n\n setInitialCursorPosition(position: Point): void {\n }\n\n cleanup(): void {\n }\n\n setup(): void {\n }\n\n get kmtTrackpadTrackScore(): number {\n return 0;\n }\n\n subtractKmtTrackpadTrackScore(): void {\n }\n\n addKmtTrackpadTrackScore(): void {\n }\n\n setMode(mode: 'kmt' | 'trackpad' | 'TBD'): void {\n }\n\n get mode(): 'kmt' | 'trackpad' | 'TBD' {\n return 'kmt';\n }\n\n cancelCurrentAction(): void {\n }\n}\n\n/**\n * Production implementation of KmtInputContext that tracks input state for the state machine.\n *\n * @remarks\n * This class provides the concrete implementation of the KMT input context, maintaining\n * all state required by the state machine to recognize and track gestures:\n *\n * **State Tracking**:\n * - Initial cursor position for calculating pan deltas\n * - Input modality score to distinguish mouse vs trackpad\n * - Determined input mode (kmt/trackpad/TBD)\n * - Coordinate system alignment preference\n *\n * **Input Modality Detection**:\n * The `kmtTrackpadTrackScore` accumulates evidence about the input device:\n * - Positive values indicate mouse behavior (middle-click, no horizontal scroll)\n * - Negative values indicate trackpad behavior (horizontal scroll, two-finger gestures)\n * - Score is used to determine zoom behavior (Ctrl+Scroll for mouse vs Scroll for trackpad)\n *\n * **Design Pattern**:\n * This class follows the Context pattern from the @ue-too/being state machine library,\n * providing stateful data and operations that states can access and modify during transitions.\n *\n * @category Input State Machine\n *\n * @example\n * ```typescript\n * const canvasProxy = new CanvasProxy(canvasElement);\n * const context = new ObservableInputTracker(canvasProxy);\n * const stateMachine = createKmtInputStateMachine(context);\n *\n * // Context tracks state as the state machine processes events\n * stateMachine.happens(\"leftPointerDown\", {x: 100, y: 200});\n * console.log(context.initialCursorPosition); // {x: 100, y: 200}\n * ```\n */\nexport class ObservableInputTracker implements KmtInputContext {\n\n private _alignCoordinateSystem: boolean;\n private _canvasOperator: Canvas;\n private _initialCursorPosition: Point;\n private _kmtTrackpadTrackScore: number; // > 0 for kmt; < 0 for trackpad; 0 for TBD;\n private _mode: 'kmt' | 'trackpad' | 'TBD';\n\n constructor(canvasOperator: Canvas){\n this._alignCoordinateSystem = true;\n this._canvasOperator = canvasOperator;\n this._initialCursorPosition = {x: 0, y: 0};\n this._kmtTrackpadTrackScore = 0;\n this._mode = 'TBD';\n }\n\n get mode(): 'kmt' | 'trackpad' | 'TBD' {\n return this._mode;\n }\n\n setMode(mode: 'kmt' | 'trackpad' | 'TBD'): void {\n this._mode = mode;\n }\n\n get kmtTrackpadTrackScore(): number {\n return this._kmtTrackpadTrackScore;\n }\n\n subtractKmtTrackpadTrackScore(): void {\n this._kmtTrackpadTrackScore--;\n }\n\n addKmtTrackpadTrackScore(): void {\n this._kmtTrackpadTrackScore++;\n }\n\n get alignCoordinateSystem(): boolean {\n return this._alignCoordinateSystem;\n }\n\n get canvas(): Canvas {\n return this._canvasOperator;\n }\n\n get initialCursorPosition(): Point {\n return this._initialCursorPosition;\n }\n\n set alignCoordinateSystem(value: boolean){\n this._alignCoordinateSystem = value;\n }\n\n cancelCurrentAction(): void {\n this._initialCursorPosition = {x: 0, y: 0};\n }\n\n setInitialCursorPosition(position: Point): void {\n this._initialCursorPosition = position;\n }\n\n cleanup(): void {\n }\n\n setup(): void {\n }\n}\n\nfunction withinEdgeOfCanvas(position: Point, boundingBox: {left: number, top: number, width: number, height: number}, padding: number): boolean {\n return position.x <= boundingBox.left + padding || position.x >= boundingBox.left + boundingBox.width - padding || position.y <= boundingBox.top + padding || position.y >= boundingBox.top + boundingBox.height - padding;\n}\n\nfunction pointInWhichHorizontalEdgeOfCanvas(position: Point, boundingBox: {left: number, top: number, width: number, height: number}, padding: number): 'left' | 'right' | 'none' {\n if(position.x <= boundingBox.left + padding){\n return 'left';\n }\n if(position.x >= boundingBox.left + boundingBox.width - padding){\n return 'right';\n }\n return 'none';\n}\n\nfunction pointInWhichVerticalEdgeOfCanvas(position: Point, boundingBox: {left: number, top: number, width: number, height: number}, padding: number): 'up' | 'down' | 'none' {\n if(position.y <= boundingBox.top + padding){\n return 'up';\n }\n if(position.y >= boundingBox.top + boundingBox.height - padding){\n return 'down';\n }\n return 'none';\n}\n",
36
+ "import { PointCal } from \"@ue-too/math\";\n\nimport { \n createDefaultPanByHandler, \n createDefaultPanToHandler, \n PanByHandlerFunction, \n PanHandlerConfig, \n PanToHandlerFunction } from \"./pan-handler\";\nimport { \n ZoomHandlerConfig, \n ZoomToHandlerFunction, \n createDefaultZoomToOnlyHandler, \n ZoomByHandlerFunction, \n createDefaultZoomByOnlyHandler, \n} from \"./zoom-handler\";\nimport DefaultBoardCamera from \"../default-camera\";\nimport { createDefaultRotateToHandler, createDefaultRotateByHandler } from \"./rotation-handler\";\nimport type { RotateToHandlerFunction, RotateByHandlerFunction, RotationHandlerConfig } from \"./rotation-handler\";\nimport { ObservableBoardCamera } from \"../interface\";\nimport { Point } from \"@ue-too/math\";\nimport { convertDeltaInViewPortToWorldSpace } from \"../utils\";\nimport type { BaseContext } from \"@ue-too/being\";\n\n/**\n * Configuration for camera rig behavior combining pan, zoom, and rotation settings.\n * Composed from individual handler configs.\n *\n * @remarks\n * This type merges configuration from:\n * - {@link PanHandlerConfig} - Pan clamping and boundaries\n * - {@link ZoomHandlerConfig} - Zoom limits and restrictions\n * - {@link RotationHandlerConfig} - Rotation constraints\n *\n * @category Camera Rig\n * @see {@link PanHandlerConfig}\n * @see {@link ZoomHandlerConfig}\n * @see {@link RotationHandlerConfig}\n */\nexport type CameraRigConfig = PanHandlerConfig & ZoomHandlerConfig & RotationHandlerConfig;\n\n/**\n * High-level camera control interface providing intuitive methods for pan, zoom, and rotation.\n * The camera rig acts as a facade over the camera, handling coordinate conversions and constraints.\n *\n * @remarks\n * CameraRig provides:\n * - **Coordinate-aware methods**: Separate methods for viewport and world coordinates\n * - **Anchor-point zooming**: Keep points stationary during zoom (zoom-to-cursor)\n * - **Configuration management**: Unified config for all camera operations\n * - **Handler composition**: Combines pan, zoom, rotation handlers with proper sequencing\n *\n * The rig ensures correct transformation order when combining operations\n * (e.g., zoom-at-point requires zoom followed by pan compensation).\n *\n * @category Camera Rig\n * @see {@link DefaultCameraRig} for the default implementation\n * @see {@link createDefaultCameraRig} for a factory function\n */\nexport interface CameraRig extends BaseContext {\n /** The underlying observable camera being controlled */\n camera: ObservableBoardCamera;\n\n /** Current configuration for all camera operations */\n config: CameraRigConfig;\n\n /**\n * Updates the camera rig configuration.\n * @param config - Partial configuration to merge with current config\n */\n configure(config: Partial<CameraRigConfig>): void;\n\n /**\n * Updates the camera rig state (called per frame if needed).\n */\n update(): void;\n\n /**\n * Pans the camera by a delta in viewport coordinates.\n * @param delta - Movement delta in viewport space (CSS pixels, origin at center)\n */\n panByViewPort: (delta: Point) => void;\n\n /**\n * Pans the camera to a target position in viewport coordinates.\n * @param target - Target position in viewport space\n */\n panToViewPort: (target: Point) => void;\n\n /**\n * Pans the camera by a delta in world coordinates.\n * @param delta - Movement delta in world space\n */\n panByWorld: (delta: Point) => void;\n\n /**\n * Pans the camera to a target position in world coordinates.\n * @param target - Target position in world space\n */\n panToWorld: (target: Point) => void;\n\n /**\n * Rotates the camera by a delta angle.\n * @param delta - Rotation delta in radians\n */\n rotateBy: (delta: number) => void;\n\n /**\n * Rotates the camera to a target angle.\n * @param target - Target rotation in radians\n */\n rotateTo: (target: number) => void;\n\n /**\n * Zooms to a target level, keeping a viewport point stationary.\n * @param targetZoom - Target zoom level\n * @param at - Anchor point in viewport coordinates\n */\n zoomToAt: (targetZoom: number, at: Point) => void;\n\n /**\n * Zooms by a delta, keeping a viewport point stationary.\n * @param delta - Zoom delta\n * @param at - Anchor point in viewport coordinates\n */\n zoomByAt: (delta: number, at: Point) => void;\n\n /**\n * Zooms to a target level at viewport center.\n * @param targetZoom - Target zoom level\n */\n zoomTo: (targetZoom: number) => void;\n\n /**\n * Zooms by a delta at viewport center.\n * @param delta - Zoom delta\n */\n zoomBy: (delta: number) => void;\n\n /**\n * Zooms to a target level, keeping a world point stationary.\n * @param targetZoom - Target zoom level\n * @param at - Anchor point in world coordinates\n */\n zoomToAtWorld: (targetZoom: number, at: Point) => void;\n\n /**\n * Zooms by a delta, keeping a world point stationary.\n * @param delta - Zoom delta\n * @param at - Anchor point in world coordinates\n */\n zoomByAtWorld: (delta: number, at: Point) => void;\n}\n\n/**\n * Default implementation of the camera rig providing comprehensive camera control.\n * Composes pan, zoom, and rotation handlers into a unified, easy-to-use API.\n *\n * @remarks\n * DefaultCameraRig serves as:\n * - **Context for state machines**: Passed to pan/zoom state machines as execution context\n * - **Handler composition**: Combines individual pan/zoom/rotation handlers\n * - **Coordinate conversion**: Manages conversions between viewport and world space\n * - **Configuration management**: Applies constraints and limits through handlers\n *\n * The rig ensures proper transformation sequencing:\n * 1. For anchor-point zoom: Apply zoom, then compensate camera position to keep anchor stationary\n * 2. For rotation: Transform coordinates based on current camera rotation\n * 3. For pan: Apply clamping and boundary constraints\n *\n * @example\n * ```typescript\n * const camera = new DefaultBoardCamera();\n * const rig = new DefaultCameraRig({\n * limitEntireViewPort: true,\n * clampTranslation: true,\n * clampZoom: true,\n * restrictZoom: false\n * }, camera);\n *\n * // Pan in viewport coordinates\n * rig.panByViewPort({ x: 50, y: -30 });\n *\n * // Zoom at cursor position\n * rig.zoomByAt(0.1, mousePosition);\n *\n * // Rotate camera\n * rig.rotateBy(Math.PI / 4);\n * ```\n *\n * @category Camera Rig\n * @see {@link CameraRig} for the interface definition\n * @see {@link createDefaultCameraRig} for a convenient factory function\n */\nexport class DefaultCameraRig implements CameraRig {\n\n private _panBy: PanByHandlerFunction;\n private _panTo: PanToHandlerFunction;\n private _zoomTo: ZoomToHandlerFunction;\n private _zoomBy: ZoomByHandlerFunction;\n private _rotateBy: RotateByHandlerFunction;\n private _rotateTo: RotateToHandlerFunction;\n private _config: CameraRigConfig;\n private _camera: ObservableBoardCamera;\n\n /**\n * Creates a new DefaultCameraRig with specified configuration and camera.\n *\n * @param config - Camera rig configuration for pan and zoom constraints\n * @param camera - Observable camera instance to control (defaults to new DefaultBoardCamera)\n *\n * @remarks\n * The constructor initializes:\n * - Default pan, zoom, and rotation handler functions\n * - Rotation config with `restrictRotation: false` and `clampRotation: true`\n * - Handler functions that will be used to process and constrain all camera operations\n *\n * @example\n * ```typescript\n * const rig = new DefaultCameraRig({\n * limitEntireViewPort: true,\n * clampTranslation: true,\n * clampZoom: true,\n * restrictZoom: false,\n * restrictXTranslation: false,\n * restrictYTranslation: false\n * });\n * ```\n */\n constructor(config: PanHandlerConfig & ZoomHandlerConfig, camera: ObservableBoardCamera = new DefaultBoardCamera()){\n this._panBy = createDefaultPanByHandler();\n this._panTo = createDefaultPanToHandler();\n this._zoomTo = createDefaultZoomToOnlyHandler();\n this._zoomBy = createDefaultZoomByOnlyHandler();\n this._rotateBy = createDefaultRotateByHandler();\n this._rotateTo = createDefaultRotateToHandler();\n this._config = {...config, restrictRotation: false, clampRotation: true};\n this._camera = camera;\n }\n\n /**\n * Zooms to a target level while keeping a viewport point stationary (zoom-to-cursor).\n *\n * @param targetZoom - Target zoom level to reach\n * @param at - Anchor point in viewport coordinates (center-anchored, CSS pixels)\n *\n * @remarks\n * This implements the \"zoom to cursor\" behavior commonly seen in map applications.\n * The algorithm:\n * 1. Converts anchor point from viewport to world space (before zoom)\n * 2. Applies zoom transformation (may be clamped by config)\n * 3. Converts anchor point from viewport to world space (after zoom)\n * 4. Calculates position difference and pans camera to compensate\n *\n * The anchor point remains stationary on screen, while the world zooms around it.\n *\n * @example\n * ```typescript\n * // Zoom to 2x at mouse cursor position\n * rig.zoomToAt(2.0, { x: mouseX, y: mouseY });\n *\n * // The world point under the cursor stays in place\n * ```\n */\n zoomToAt(targetZoom: number, at: Point): void {\n let originalAnchorInWorld = this._camera.convertFromViewPort2WorldSpace(at);\n const transformTarget = this._zoomTo(targetZoom, this._camera, this._config);\n this._camera.setZoomLevel(transformTarget);\n let anchorInWorldAfterZoom = this._camera.convertFromViewPort2WorldSpace(at);\n const cameraPositionDiff = PointCal.subVector(originalAnchorInWorld, anchorInWorldAfterZoom);\n const transformedCameraPositionDiff = this._panBy(cameraPositionDiff, this._camera, this._config);\n this._camera.setPosition(PointCal.addVector(this._camera.position, transformedCameraPositionDiff));\n }\n\n /**\n * Zooms by a relative delta while keeping a viewport point stationary.\n *\n * @param delta - Relative zoom delta (multiplied by current zoom level)\n * @param at - Anchor point in viewport coordinates (center-anchored, CSS pixels)\n *\n * @remarks\n * This method is ideal for mouse wheel zoom interactions where the delta\n * represents a relative change rather than an absolute target.\n *\n * The delta is scaled by current zoom level: `actualDelta = delta * currentZoom`\n * This provides consistent zoom \"speed\" regardless of current zoom level.\n *\n * Like {@link zoomToAt}, this keeps the anchor point stationary during zoom.\n *\n * @example\n * ```typescript\n * // Zoom in by 10% at cursor position (mouse wheel up)\n * rig.zoomByAt(0.1, cursorPosition);\n *\n * // Zoom out by 10% at cursor position (mouse wheel down)\n * rig.zoomByAt(-0.1, cursorPosition);\n * ```\n *\n * @see {@link zoomToAt} for zooming to an absolute level\n */\n zoomByAt(delta: number, at: Point): void {\n const convertedDelta = delta * this._camera.zoomLevel;\n let originalAnchorInWorld = this._camera.convertFromViewPort2WorldSpace(at);\n const transformedDelta = this._zoomBy(convertedDelta, this._camera, this._config);\n this._camera.setZoomLevel(this._camera.zoomLevel + transformedDelta);\n let anchorInWorldAfterZoom = this._camera.convertFromViewPort2WorldSpace(at);\n const diff = PointCal.subVector(originalAnchorInWorld, anchorInWorldAfterZoom);\n const transformedDiff = this._panBy(diff, this._camera, this._config);\n this._camera.setPosition(PointCal.addVector(this._camera.position, transformedDiff));\n }\n\n /**\n * Zooms to a target level with the viewport center as the anchor point.\n *\n * @param targetZoom - Target zoom level to reach\n *\n * @remarks\n * This is a simpler version of {@link zoomToAt} that always zooms relative to the\n * viewport center. The camera position remains unchanged, so the center point of\n * the viewport stays fixed in world space.\n *\n * Use this when you want straightforward zoom without anchor-point tracking,\n * such as zoom controls in a UI toolbar.\n *\n * @example\n * ```typescript\n * // Zoom to 2x, centered on current view\n * rig.zoomTo(2.0);\n *\n * // Zoom to fit (100%)\n * rig.zoomTo(1.0);\n * ```\n *\n * @see {@link zoomToAt} for zoom with custom anchor point\n */\n zoomTo(targetZoom: number): void {\n const transformedTarget = this._zoomTo(targetZoom, this._camera, this._config);\n this._camera.setZoomLevel(transformedTarget);\n }\n\n /**\n * Zooms by a relative delta with the viewport center as the anchor point.\n *\n * @param delta - Zoom delta (added to current zoom level)\n *\n * @remarks\n * Unlike {@link zoomByAt}, the delta is NOT scaled by current zoom level.\n * This provides absolute delta changes, useful for programmatic zoom adjustments.\n *\n * The camera position remains unchanged, keeping the viewport center fixed in world space.\n *\n * @example\n * ```typescript\n * // Increase zoom by 0.5\n * rig.zoomBy(0.5);\n *\n * // Decrease zoom by 0.2\n * rig.zoomBy(-0.2);\n * ```\n *\n * @see {@link zoomByAt} for zoom with custom anchor point and scaling\n */\n zoomBy(delta: number): void {\n const transformedDelta = this._zoomBy(delta, this._camera, this._config);\n this._camera.setZoomLevel(this._camera.zoomLevel + transformedDelta);\n }\n\n /**\n * Zooms to a target level while keeping a world-space point stationary.\n *\n * @param targetZoom - Target zoom level to reach\n * @param at - Anchor point in world coordinates\n *\n * @remarks\n * Similar to {@link zoomToAt}, but accepts world-space coordinates instead of viewport coordinates.\n * Useful when you want to zoom to keep a specific world object or location centered,\n * rather than a screen position.\n *\n * The algorithm:\n * 1. Converts world anchor to viewport space (before zoom)\n * 2. Applies zoom transformation\n * 3. Converts world anchor to viewport space (after zoom)\n * 4. Calculates viewport movement and converts to world space\n * 5. Pans camera to compensate\n *\n * @example\n * ```typescript\n * // Zoom to 3x while keeping a specific world object in place\n * const objectWorldPos = { x: 1000, y: 500 };\n * rig.zoomToAtWorld(3.0, objectWorldPos);\n * ```\n *\n * @see {@link zoomToAt} for viewport-space variant\n */\n zoomToAtWorld(targetZoom: number, at: Point): void {\n let originalAnchorInViewPort = this._camera.convertFromWorld2ViewPort(at);\n const transformedTarget = this._zoomTo(targetZoom, this._camera, this._config);\n this._camera.setZoomLevel(transformedTarget);\n let anchorInViewPortAfterZoom = this._camera.convertFromWorld2ViewPort(at);\n const cameraPositionDiffInViewPort = PointCal.subVector(anchorInViewPortAfterZoom, originalAnchorInViewPort);\n const cameraPositionDiffInWorld = convertDeltaInViewPortToWorldSpace(cameraPositionDiffInViewPort, this._camera.zoomLevel, this._camera.rotation);\n const transformedCameraPositionDiff = this._panBy(cameraPositionDiffInWorld, this._camera, this._config);\n this._camera.setPosition(PointCal.addVector(this._camera.position, transformedCameraPositionDiff));\n }\n\n /**\n * Zooms by a delta while keeping a world-space point stationary.\n *\n * @param delta - Zoom delta (added to current zoom level, not scaled)\n * @param at - Anchor point in world coordinates\n *\n * @remarks\n * World-space variant of {@link zoomByAt}. The delta is NOT scaled by current zoom level,\n * unlike the viewport-space version.\n *\n * Use this when programmatically zooming around specific world objects or coordinates.\n *\n * @example\n * ```typescript\n * // Zoom in by 0.5 while keeping a world landmark stationary\n * const landmarkPos = { x: 2000, y: 1500 };\n * rig.zoomByAtWorld(0.5, landmarkPos);\n * ```\n *\n * @see {@link zoomByAt} for viewport-space variant with scaled delta\n */\n zoomByAtWorld(delta: number, at: Point): void {\n let anchorInViewPortBeforeZoom = this._camera.convertFromWorld2ViewPort(at);\n const transformedDelta = this._zoomBy(delta, this._camera, this._config);\n this._camera.setZoomLevel(this._camera.zoomLevel + transformedDelta);\n let anchorInViewPortAfterZoom = this._camera.convertFromWorld2ViewPort(at);\n const diffInViewPort = PointCal.subVector(anchorInViewPortAfterZoom, anchorInViewPortBeforeZoom);\n const diffInWorld = convertDeltaInViewPortToWorldSpace(diffInViewPort, this._camera.zoomLevel, this._camera.rotation);\n const transformedDiff = this._panBy(diffInWorld, this._camera, this._config);\n this._camera.setPosition(PointCal.addVector(this._camera.position, transformedDiff));\n }\n\n /**\n * Pans the camera by a delta in viewport coordinates.\n *\n * @param delta - Movement delta in viewport space (center-anchored, CSS pixels)\n *\n * @remarks\n * This is the most common pan method for user input (mouse drag, touch pan).\n * The delta is in screen/viewport coordinates and gets converted to world space\n * accounting for current camera rotation and zoom.\n *\n * Conversion formula:\n * 1. Rotate delta by camera rotation\n * 2. Scale by inverse zoom (1 / zoomLevel)\n * 3. Apply as world-space pan\n *\n * @example\n * ```typescript\n * // Pan camera when user drags mouse\n * canvas.addEventListener('mousemove', (e) => {\n * if (isDragging) {\n * const delta = { x: e.movementX, y: e.movementY };\n * rig.panByViewPort(delta);\n * }\n * });\n * ```\n *\n * @see {@link panByWorld} for world-space panning\n */\n panByViewPort(delta: Point): void {\n const diffInWorld = PointCal.multiplyVectorByScalar(PointCal.rotatePoint(delta, this._camera.rotation), 1 / this._camera.zoomLevel);\n this.panByWorld(diffInWorld);\n }\n\n /**\n * Pans the camera by a delta in world coordinates.\n *\n * @param delta - Movement delta in world space\n *\n * @remarks\n * Use this for programmatic camera movement or when you already have world-space\n * coordinates (e.g., moving camera to follow a world object).\n *\n * The delta is passed through the pan handler which may apply:\n * - Boundary clamping\n * - Movement restrictions (restrictXTranslation, restrictYTranslation)\n * - Other constraints from {@link CameraRigConfig}\n *\n * @example\n * ```typescript\n * // Move camera 100 units right, 50 units up in world space\n * rig.panByWorld({ x: 100, y: -50 });\n *\n * // Follow a moving object\n * const objectMovement = { x: obj.dx, y: obj.dy };\n * rig.panByWorld(objectMovement);\n * ```\n *\n * @see {@link panByViewPort} for viewport-space panning\n */\n panByWorld(delta: Point): void {\n const transformedDelta = this._panBy(delta, this._camera, this._config);\n this._camera.setPosition(PointCal.addVector(this._camera.position, transformedDelta));\n }\n\n /**\n * Pans the camera to an absolute position in world coordinates.\n *\n * @param target - Target camera position in world space\n *\n * @remarks\n * Sets the camera position directly (subject to constraints).\n * Unlike pan-by methods, this is an absolute positioning operation.\n *\n * The target is passed through the pan handler which may apply:\n * - Boundary clamping\n * - Position restrictions\n *\n * Use this for:\n * - \"Go to location\" features\n * - Centering camera on specific world coordinates\n * - Resetting camera to a known position\n *\n * @example\n * ```typescript\n * // Center camera on world origin\n * rig.panToWorld({ x: 0, y: 0 });\n *\n * // Go to specific landmark\n * const landmark = { x: 1000, y: 500 };\n * rig.panToWorld(landmark);\n * ```\n *\n * @see {@link panToViewPort} for viewport-space variant\n */\n panToWorld(target: Point): void {\n const transformedTarget = this._panTo(target, this._camera, this._config);\n this._camera.setPosition(transformedTarget);\n }\n\n /**\n * Pans the camera to position a viewport point at a specific location.\n *\n * @param target - Target position in viewport coordinates (center-anchored, CSS pixels)\n *\n * @remarks\n * Moves the camera so that the specified viewport point ends up at the viewport center.\n * This is less commonly used than world-space pan-to operations.\n *\n * The method converts the viewport target to world space, then uses {@link panToWorld}.\n *\n * @example\n * ```typescript\n * // Center the camera on what's currently at the top-left of viewport\n * rig.panToViewPort({ x: -400, y: -300 });\n * ```\n *\n * @see {@link panToWorld} for world-space variant (more commonly used)\n */\n panToViewPort(target: Point): void {\n const targetInWorld = this._camera.convertFromViewPort2WorldSpace(target);\n this.panToWorld(targetInWorld);\n }\n\n /**\n * Rotates the camera by a delta angle.\n *\n * @param delta - Rotation delta in radians (positive = counter-clockwise)\n *\n * @remarks\n * Applies a relative rotation to the camera. The delta is passed through the\n * rotation handler which may apply clamping or restrictions based on {@link CameraRigConfig}.\n *\n * Camera rotation affects:\n * - How viewport coordinates map to world coordinates\n * - The orientation of pan operations\n * - Visual rendering of the world\n *\n * @example\n * ```typescript\n * // Rotate 45 degrees counter-clockwise\n * rig.rotateBy(Math.PI / 4);\n *\n * // Rotate 90 degrees clockwise\n * rig.rotateBy(-Math.PI / 2);\n * ```\n *\n * @see {@link rotateTo} for absolute rotation\n */\n rotateBy(delta: number): void {\n const transformedDelta = this._rotateBy(delta, this._camera, this._config);\n this._camera.setRotation(this._camera.rotation + transformedDelta);\n }\n\n /**\n * Rotates the camera to an absolute angle.\n *\n * @param target - Target rotation in radians (0 = no rotation, positive = counter-clockwise)\n *\n * @remarks\n * Sets the camera rotation to a specific angle (subject to constraints).\n * The target is passed through the rotation handler which may apply clamping.\n *\n * Use this for:\n * - Resetting camera to north-up orientation (0 radians)\n * - Snapping to cardinal directions\n * - Setting rotation from UI controls\n *\n * @example\n * ```typescript\n * // Reset to north-up\n * rig.rotateTo(0);\n *\n * // Rotate to 90 degrees\n * rig.rotateTo(Math.PI / 2);\n * ```\n *\n * @see {@link rotateBy} for relative rotation\n */\n rotateTo(target: number): void {\n const transformedTarget = this._rotateTo(target, this._camera, this._config);\n this._camera.setRotation(transformedTarget);\n }\n\n /**\n * Sets whether the entire viewport must remain within boundaries.\n *\n * @remarks\n * When true, pan boundaries ensure the entire viewport stays within configured limits.\n * When false, only the camera center point is constrained.\n *\n * This is a convenience setter for {@link CameraRigConfig}.limitEntireViewPort.\n */\n set limitEntireViewPort(limit: boolean){\n this._config.limitEntireViewPort = limit;\n }\n\n /**\n * Gets whether the entire viewport must remain within boundaries.\n *\n * @returns True if entire viewport is constrained, false if only center is constrained\n */\n get limitEntireViewPort(): boolean {\n return this._config.limitEntireViewPort;\n }\n\n /**\n * Gets the underlying observable camera instance.\n *\n * @returns The camera being controlled by this rig\n */\n get camera(): ObservableBoardCamera {\n return this._camera;\n }\n\n /**\n * Sets the underlying camera instance.\n *\n * @param camera - New camera to control\n *\n * @remarks\n * Use this to swap cameras at runtime, though this is uncommon.\n * Usually you create a new rig instead.\n */\n set camera(camera: ObservableBoardCamera){\n this._camera = camera;\n }\n\n /**\n * Gets the current camera rig configuration.\n *\n * @returns Current configuration object\n *\n * @remarks\n * Returns a reference to the internal config. Modifications will affect rig behavior.\n * For safer updates, use {@link configure} instead.\n */\n get config(): CameraRigConfig {\n return this._config;\n }\n\n /**\n * Sets the camera rig configuration.\n *\n * @param config - New configuration object\n *\n * @remarks\n * Creates a shallow copy of the provided config.\n * For partial updates, use {@link configure} instead.\n */\n set config(config: CameraRigConfig){\n this._config = {...config};\n }\n\n /**\n * Updates camera rig configuration with partial settings.\n *\n * @param config - Partial configuration to merge with current config\n *\n * @remarks\n * This is the recommended way to update configuration at runtime.\n * Only provided properties are updated; others remain unchanged.\n *\n * @example\n * ```typescript\n * // Enable zoom restrictions without changing other settings\n * rig.configure({\n * restrictZoom: true,\n * zoomLevelLimits: { min: 0.5, max: 5.0 }\n * });\n *\n * // Disable position clamping\n * rig.configure({ clampTranslation: false });\n * ```\n */\n configure(config: Partial<CameraRigConfig>){\n this._config = {...this._config, ...config};\n }\n\n /**\n * Cleans up resources used by the camera rig.\n *\n * @remarks\n * Currently a no-op as DefaultCameraRig has no resources to clean up.\n * Implements {@link BaseContext} interface for consistency with other systems.\n */\n cleanup(): void {\n }\n\n /**\n * Sets up the camera rig.\n *\n * @remarks\n * Currently a no-op as DefaultCameraRig requires no setup.\n * Implements {@link BaseContext} interface for consistency with other systems.\n */\n setup(): void {\n }\n\n /**\n * Updates the camera rig state.\n *\n * @remarks\n * Currently a no-op as DefaultCameraRig has no per-frame update logic.\n * Implements {@link BaseContext} interface for consistency with other systems.\n *\n * In stateful rig implementations, this might handle:\n * - Animation interpolation\n * - Momentum/inertia\n * - Smooth camera following\n */\n update(): void {\n }\n}\n\n/**\n * Creates a camera rig with sensible default configuration.\n *\n * @param camera - Observable camera instance to control\n * @returns Configured camera rig ready for use\n *\n * @remarks\n * This factory function creates a {@link DefaultCameraRig} with a balanced default configuration:\n *\n * **Enabled by default:**\n * - `limitEntireViewPort: true` - Entire viewport stays within boundaries\n * - `clampTranslation: true` - Position is clamped to boundaries\n * - `clampZoom: true` - Zoom is clamped to limits\n *\n * **Disabled by default:**\n * - All movement restrictions (`restrictXTranslation`, `restrictYTranslation`, etc.)\n * - Zoom restrictions (`restrictZoom`)\n * - Relative translation restrictions\n *\n * This configuration allows free camera movement with boundary enforcement,\n * suitable for most infinite canvas applications.\n *\n * @example\n * ```typescript\n * const camera = new DefaultBoardCamera(1920, 1080);\n * const rig = createDefaultCameraRig(camera);\n *\n * // Ready to use with sensible defaults\n * rig.configure({\n * boundaries: {\n * min: { x: -1000, y: -1000 },\n * max: { x: 1000, y: 1000 }\n * }\n * });\n *\n * rig.panByViewPort({ x: 100, y: 50 });\n * rig.zoomByAt(0.1, mousePosition);\n * ```\n *\n * @category Camera Rig\n * @see {@link DefaultCameraRig} for the implementation\n * @see {@link CameraRigConfig} for all available configuration options\n */\nexport function createDefaultCameraRig(camera: ObservableBoardCamera): CameraRig{\n return new DefaultCameraRig({\n limitEntireViewPort: true,\n restrictRelativeXTranslation: false,\n restrictRelativeYTranslation: false,\n restrictXTranslation: false,\n restrictYTranslation: false,\n restrictZoom: false,\n clampTranslation: true,\n clampZoom: true,\n }, camera);\n}\n",
37
+ "import { Point } from \"@ue-too/math\";\nimport { BaseContext, NO_OP } from \"@ue-too/being\";\nimport { CanvasPositionDimensionPublisher, getTrueRect, Observable, Observer, SubscriptionOptions, SvgPositionDimensionPublisher, SynchronousObservable } from \"../../utils\";\n\n/**\n * Cursor styles used to provide visual feedback for different input states.\n *\n * @remarks\n * These cursor styles indicate the current interaction mode to users:\n * - **GRAB**: Indicates the canvas is ready to be panned (spacebar pressed, no drag yet)\n * - **GRABBING**: Indicates active panning is in progress\n * - **DEFAULT**: Normal cursor state when no special interaction is active\n *\n * @category Input State Machine\n */\nexport enum CursorStyle {\n GRAB = \"grab\",\n DEFAULT = \"default\",\n GRABBING = \"grabbing\"\n}\n\n/**\n * Canvas dimension and position information.\n *\n * @property width - The canvas width in CSS pixels\n * @property height - The canvas height in CSS pixels\n * @property position - The top-left position of the canvas in window coordinates\n *\n * @category Input State Machine\n */\nexport type CanvasDimensions = {width: number, height: number, position: Point};\n\n/**\n * Abstraction interface for canvas element access and manipulation.\n *\n * @remarks\n * This interface provides a decoupled way to access canvas properties without direct DOM access.\n * Multiple implementations exist to support different use cases:\n * - **CanvasProxy**: Full implementation for HTML canvas elements with dimension tracking\n * - **SvgProxy**: Implementation for SVG elements\n * - **DummyCanvas**: No-op implementation for web worker contexts\n * - **WorkerRelayCanvas**: Relays canvas dimension updates to web workers\n * - **CanvasCacheInWebWorker**: Caches canvas dimensions within a web worker\n *\n * The abstraction enables:\n * - Coordinate system transformations (window → canvas → viewport)\n * - Canvas dimension tracking without repeated DOM queries\n * - Cursor style management\n * - Support for both canvas and SVG rendering contexts\n *\n * @category Input State Machine\n */\nexport interface Canvas {\n /** The canvas width in CSS pixels */\n width: number;\n /** The canvas height in CSS pixels */\n height: number;\n /** The top-left position of the canvas in window coordinates */\n position: Point;\n /** Sets the CSS cursor style for visual feedback */\n setCursor: (style: CursorStyle) => void;\n /** Combined dimensions and position information */\n dimensions: CanvasDimensions;\n /** Whether the canvas is currently detached from the DOM */\n detached: boolean;\n /** Cleanup method to dispose of resources and event listeners */\n tearDown: () => void;\n}\n\n/**\n * No-op implementation of Canvas interface for web worker relay contexts.\n *\n * @remarks\n * This class is used when an input state machine is configured to relay events to a web worker\n * rather than perform actual canvas operations. The state machine requires a Canvas in its context,\n * but in the relay scenario, no actual canvas operations are needed - events are simply forwarded\n * to the worker thread.\n *\n * All properties return default/empty values and all methods are no-ops.\n *\n * @category Input State Machine\n *\n * @see {@link DummyKmtInputContext}\n */\nexport class DummyCanvas implements Canvas {\n width: number = 0;\n height: number = 0;\n position: Point = {x: 0, y: 0};\n setCursor: (style: CursorStyle) => void = NO_OP;\n dimensions: {width: number, height: number, position: Point} = {width: 0, height: 0, position: {x: 0, y: 0}};\n detached: boolean = false;\n tearDown: () => void = NO_OP;\n}\n\nexport class CanvasCacheInWebWorker implements Canvas {\n\n private _width: number;\n private _height: number;\n private _position: Point;\n private _postMessageFunction: typeof postMessage;\n\n constructor(postMessageFunction: typeof postMessage){\n this._width = 0;\n this._height = 0;\n this._position = {x: 0, y: 0};\n this._postMessageFunction = postMessageFunction;\n }\n\n get dimensions(): {width: number, height: number, position: Point} {\n return {width: this._width, height: this._height, position: this._position};\n }\n\n tearDown(): void {\n }\n\n set width(width: number){\n this._width = width;\n }\n\n set height(height: number){\n this._height = height;\n }\n\n set position(position: Point){\n this._position = position;\n }\n\n get width(): number {\n return this._width;\n }\n\n get height(): number {\n return this._height;\n }\n\n get position(): Point {\n return this._position;\n }\n\n setCursor(style: \"grab\" | \"default\" | \"grabbing\"): void {\n this._postMessageFunction({type: \"setCursor\", style});\n }\n\n get detached(): boolean {\n return false;\n }\n}\n\n/**\n * A proxy for the canvas element to prevent constant invoking of the getBoundingClientRect method.\n * @remarks This is mainly used as a proxy to the canvas to prevent invoking the getBoundingClientRect method on the canvas every time a pointer event is triggered or a coordinate conversion is needed. Also to autoscale the canvas buffer depending on the device pixel ratio. It's important to note that in normal circumstances, you would not need to set the size of the canvas manually; you should use the css style width and height to set the size of the canvas.\n * @category Input State Machine\n * @see {@link Canvas} for the interface that this class implements\n * @see {@link Observable} for the observable that this class emits\n * @see {@link Observer} for the observer that can subscribe to the observable\n * @see {@link SubscriptionOptions} for the options that can be passed to the subscribe method\n * @see {@link SynchronousObservable} for the synchronous observable that this class emits\n * @see {@link CanvasPositionDimensionPublisher} for the publisher that is used to publish the canvas dimensions\n */\nexport class CanvasProxy implements Canvas, Observable<[CanvasDimensions]> {\n\n private _width: number = 0;\n private _height: number = 0;\n private _position: Point = {x: 0, y: 0};\n private _canvasPositionDimensionPublisher: CanvasPositionDimensionPublisher;\n private _canvas: HTMLCanvasElement | undefined;\n private _internalSizeUpdateObservable: Observable<[CanvasDimensions]>;\n\n constructor(canvas?: HTMLCanvasElement) {\n this._internalSizeUpdateObservable = new SynchronousObservable<[CanvasDimensions]>();\n\n if(canvas){\n const boundingRect = canvas.getBoundingClientRect();\n const trueRect = getTrueRect(boundingRect, window.getComputedStyle(canvas));\n this._width = trueRect.width;\n this._height = trueRect.height;\n this._position = {x: trueRect.left, y: trueRect.top};\n this._canvas = canvas;\n }\n\n this._canvasPositionDimensionPublisher = new CanvasPositionDimensionPublisher(canvas);\n this._canvasPositionDimensionPublisher.onPositionUpdate((rect)=>{\n // the rect is the canvas dimension in the DOM (the width and height attribute would need to multiply by the device pixel ratio)\n if(this._canvas == undefined){\n console.error('is not attached to any canvas should not have getting any updates');\n return;\n }\n\n this._width = rect.width;\n this._height = rect.height;\n this._position = {x: rect.left, y: rect.top};\n\n this._internalSizeUpdateObservable.notify({\n width: this._width,\n height: this._height,\n position: this._position\n });\n });\n }\n\n subscribe(observer: Observer<[CanvasDimensions]>, options?: SubscriptionOptions): () => void {\n return this._internalSizeUpdateObservable.subscribe(observer, options);\n }\n\n notify(...data: [CanvasDimensions]): void {\n this._internalSizeUpdateObservable.notify(...data);\n }\n\n get detached(): boolean {\n return this._canvas === undefined;\n }\n\n get dimensions(): {width: number, height: number, position: Point} {\n return {width: this._width, height: this._height, position: this._position};\n }\n\n get width(): number {\n return this._width;\n }\n\n /**\n * set the width of the canvas\n * the width is synonymous with the canvas style width not the canvas width\n */\n setWidth(width: number){\n if(this._canvas){\n this._canvas.width = width * window.devicePixelRatio;\n this._canvas.style.width = width + \"px\";\n }\n }\n\n setCanvasWidth(width: number){\n if(this._canvas && this._canvas.style.width === '' && this._canvas.style.height === ''){\n const aspectRatio = this._width / this._height;\n this._canvas.style.aspectRatio = aspectRatio.toString();\n }\n if(this._canvas && this._canvas.style.width !== ''){\n this._canvas.width = width * window.devicePixelRatio;\n } else {\n this.setWidth(width);\n }\n }\n\n /**\n * set the height of the canvas\n * the height is synonymous with the canvas style height not the canvas height\n */\n setHeight(height: number){\n if(this._canvas){\n this._canvas.height = height * window.devicePixelRatio;\n this._canvas.style.height = height + \"px\";\n }\n }\n\n setCanvasHeight(height: number){\n if(this._canvas && this._canvas.style.width === '' && this._canvas.style.height === ''){\n const aspectRatio = this._width / this._height;\n this._canvas.style.aspectRatio = aspectRatio.toString();\n }\n if(this._canvas && this._canvas.style.height !== ''){\n this._canvas.height = height * window.devicePixelRatio;\n } else {\n this.setHeight(height);\n }\n }\n\n get height(): number {\n return this._height;\n }\n\n get position(): Point {\n return this._position;\n }\n\n setCursor(style: \"grab\" | \"default\" | \"grabbing\"): void {\n if(this._canvas){\n this._canvas.style.cursor = style;\n }\n }\n\n tearDown(): void {\n this._canvasPositionDimensionPublisher.dispose();\n this._canvas = undefined;\n this._width = 0;\n this._height = 0;\n this._position = {x: 0, y: 0};\n }\n\n attach(canvas: HTMLCanvasElement){\n this._canvas = canvas;\n this._canvasPositionDimensionPublisher.attach(canvas);\n }\n\n logCanvasTrueSize(){\n if(this._canvas === undefined){\n return;\n }\n console.log('canvas true size');\n console.log('style width', this._canvas.style.width);\n console.log('style height', this._canvas.style.height);\n console.log('width', this._canvas.width);\n console.log('height', this._canvas.height);\n console.log('proxy width', this._width);\n console.log('proxy height', this._height);\n }\n\n}\n\nexport class SvgProxy implements Canvas, Observable<[CanvasDimensions]> {\n\n private _width: number = 0;\n private _height: number = 0;\n private _position: Point = {x: 0, y: 0};\n private _svgPositionDimensionPublisher: SvgPositionDimensionPublisher;\n private _svg: SVGSVGElement | undefined;\n private _internalSizeUpdateObservable: Observable<[CanvasDimensions]>;\n\n constructor(svg?: SVGSVGElement) {\n this._internalSizeUpdateObservable = new SynchronousObservable<[CanvasDimensions]>();\n\n if(svg){\n const boundingRect = svg.getBoundingClientRect();\n const trueRect = getTrueRect(boundingRect, window.getComputedStyle(svg));\n this._width = trueRect.width;\n this._height = trueRect.height;\n this._position = {x: trueRect.left, y: trueRect.top};\n this._svg = svg;\n }\n\n this._svgPositionDimensionPublisher = new SvgPositionDimensionPublisher(svg);\n this._svgPositionDimensionPublisher.onPositionUpdate((rect)=>{\n // the rect is the canvas dimension in the DOM (the width and height attribute would need to multiply by the device pixel ratio)\n if(this._svg == undefined){\n console.error('is not attached to any canvas should not have getting any updates');\n return;\n }\n\n this._width = rect.width;\n this._height = rect.height;\n this._position = {x: rect.left, y: rect.top};\n\n this._internalSizeUpdateObservable.notify({\n width: this._width,\n height: this._height,\n position: this._position\n });\n });\n }\n\n subscribe(observer: Observer<[CanvasDimensions]>, options?: SubscriptionOptions): () => void {\n return this._internalSizeUpdateObservable.subscribe(observer, options);\n }\n\n notify(...data: [CanvasDimensions]): void {\n this._internalSizeUpdateObservable.notify(...data);\n }\n\n get detached(): boolean {\n return this._svg === undefined;\n }\n\n get dimensions(): {width: number, height: number, position: Point} {\n return {width: this._width, height: this._height, position: this._position};\n }\n\n get width(): number {\n return this._width;\n }\n\n /**\n * set the width of the canvas\n * the width is synonymous with the canvas style width not the canvas width\n */\n setWidth(width: number){\n if(this._svg){\n this._svg.style.width = width + \"px\";\n }\n }\n\n /**\n * set the height of the canvas\n * the height is synonymous with the canvas style height not the canvas height\n */\n setHeight(height: number){\n if(this._svg){\n this._svg.style.height = height + \"px\";\n }\n }\n\n get height(): number {\n return this._height;\n }\n\n get position(): Point {\n return this._position;\n }\n\n setCursor(style: \"grab\" | \"default\" | \"grabbing\"): void {\n if(this._svg){\n this._svg.style.cursor = style;\n }\n }\n\n tearDown(): void {\n this._svgPositionDimensionPublisher.dispose();\n this._svg = undefined;\n this._width = 0;\n this._height = 0;\n this._position = {x: 0, y: 0};\n }\n\n attach(svg: SVGSVGElement){\n this._svgPositionDimensionPublisher.attach(svg);\n this._svg = svg;\n const boundingRect = svg.getBoundingClientRect();\n const trueRect = getTrueRect(boundingRect, window.getComputedStyle(svg));\n this._svg.style.width = trueRect.width + \"px\";\n this._svg.style.height = trueRect.height + \"px\";\n this._width = trueRect.width;\n this._height = trueRect.height;\n this._position = {x: trueRect.left, y: trueRect.top};\n this._internalSizeUpdateObservable.notify({\n width: this._width,\n height: this._height,\n position: this._position\n });\n }\n\n logCanvasTrueSize(){\n if(this._svg === undefined){\n return;\n }\n console.log('canvas true size');\n console.log('style width', this._svg.style.width);\n console.log('style height', this._svg.style.height);\n console.log('width', this._svg.width);\n console.log('height', this._svg.height);\n console.log('proxy width', this._width);\n console.log('proxy height', this._height);\n }\n\n}\n\n/**\n * @description A proxy for the canvas that is used to communicate with the web worker.\n * The primary purpose of this class is to cache the canvas dimensions and position in the DOM to reduce the calling of the getBoundingClientRect method.\n * This class only serves as a relay of the updated canvas dimensions and position to the web worker.\n * \n */\nexport class WorkerRelayCanvas implements Canvas {\n\n private _width: number;\n private _height: number;\n private _position: Point;\n private _webWorker: Worker;\n private _canvas: HTMLCanvasElement;\n private _canvasDiemsionPublisher: CanvasPositionDimensionPublisher;\n\n constructor(canvas: HTMLCanvasElement, webWorker: Worker, canvasDiemsionPublisher: CanvasPositionDimensionPublisher){\n const boundingRect = canvas.getBoundingClientRect();\n this._canvas = canvas;\n this._webWorker = webWorker;\n const trueRect = getTrueRect(boundingRect, window.getComputedStyle(canvas));\n this._width = trueRect.width;\n this._height = trueRect.height;\n this._position = {x: trueRect.left, y: trueRect.top};\n this._webWorker.postMessage({type: \"setCanvasDimensions\", width: boundingRect.width, height: boundingRect.height, position: {x: boundingRect.left, y: boundingRect.top}});\n canvasDiemsionPublisher.onPositionUpdate((rect)=>{\n this._width = rect.width;\n this._height = rect.height;\n this._position = {x: rect.left, y: rect.top};\n this._webWorker.postMessage({type: \"updateCanvasDimensions\", width: rect.width, height: rect.height, position: {x: rect.left, y: rect.top}});\n });\n this._canvasDiemsionPublisher = canvasDiemsionPublisher;\n }\n\n get width(): number {\n return this._width;\n }\n\n get height(): number {\n return this._height;\n }\n\n tearDown(): void {\n this._canvasDiemsionPublisher.dispose();\n }\n\n get position(): Point {\n return this._position;\n }\n\n get dimensions(): {width: number, height: number, position: Point} {\n return {width: this._width, height: this._height, position: this._position};\n }\n\n get detached(): boolean {\n return false;\n }\n\n setCursor(style: \"grab\" | \"default\" | \"grabbing\"): void {\n this._canvas.style.cursor = style;\n }\n}\n\n/**\n * Context interface for the Keyboard/Mouse/Trackpad (KMT) input state machine.\n *\n * @remarks\n * This context provides the state and behavior needed by the KMT state machine to:\n * 1. Track cursor positions for calculating pan deltas\n * 2. Distinguish between mouse and trackpad input modalities\n * 3. Access canvas dimensions for coordinate transformations\n * 4. Manage coordinate system alignment (inverted Y-axis handling)\n *\n * **Input Modality Detection**:\n * The context uses a scoring system (`kmtTrackpadTrackScore`) to differentiate between\n * mouse and trackpad input, which have different zoom behaviors:\n * - Mouse: Ctrl+Scroll = zoom, Scroll = pan\n * - Trackpad: Scroll = zoom (no Ctrl needed), Two-finger gesture = pan\n *\n * **Coordinate System**:\n * The `alignCoordinateSystem` flag determines Y-axis orientation:\n * - `true`: Standard screen coordinates (Y increases downward)\n * - `false`: Inverted coordinates (Y increases upward)\n *\n * This interface extends BaseContext from the \\@ue-too/being state machine library,\n * inheriting setup() and cleanup() lifecycle methods.\n *\n * @category Input State Machine\n */\nexport interface KmtInputContext extends BaseContext {\n /** Whether to use standard screen coordinate system (vs inverted Y-axis) */\n alignCoordinateSystem: boolean;\n /** Canvas accessor for dimensions and cursor control */\n canvas: Canvas;\n /** Sets the initial cursor position when starting a pan gesture */\n setInitialCursorPosition: (position: Point) => void;\n /** Cancels the current action and resets cursor position */\n cancelCurrentAction: () => void;\n /** The cursor position when a pan gesture started */\n initialCursorPosition: Point;\n /** Score tracking input modality: >0 for mouse, <0 for trackpad, 0 for undetermined */\n kmtTrackpadTrackScore: number;\n /** Decreases the score toward trackpad */\n subtractKmtTrackpadTrackScore: () => void;\n /** Increases the score toward mouse */\n addKmtTrackpadTrackScore: () => void;\n /** Sets the determined input modality */\n setMode: (mode: 'kmt' | 'trackpad' | 'TBD') => void;\n /** The current input modality: 'kmt' (mouse), 'trackpad', or 'TBD' (to be determined) */\n mode: 'kmt' | 'trackpad' | 'TBD';\n}\n\n/**\n * No-op implementation of KmtInputContext for web worker relay scenarios.\n *\n * @remarks\n * Used when the input state machine is configured to relay events to a web worker\n * rather than process them locally. The state machine requires a context, but in\n * the relay scenario, no actual state tracking is needed - events are simply forwarded.\n *\n * All methods are no-ops and all properties return default values.\n *\n * @category Input State Machine\n *\n * @see {@link DummyCanvas}\n */\nexport class DummyKmtInputContext implements KmtInputContext {\n\n public alignCoordinateSystem: boolean = false;\n public canvas: Canvas = new DummyCanvas();\n public initialCursorPosition: Point = {x: 0, y: 0};\n\n constructor(){\n\n }\n\n toggleOnEdgeAutoCameraInput: () => void = NO_OP;\n toggleOffEdgeAutoCameraInput: () => void = NO_OP;\n setCursorPosition: (position: Point) => void = NO_OP;\n\n setInitialCursorPosition(position: Point): void {\n }\n\n cleanup(): void {\n }\n\n setup(): void {\n }\n\n get kmtTrackpadTrackScore(): number {\n return 0;\n }\n\n subtractKmtTrackpadTrackScore(): void {\n }\n\n addKmtTrackpadTrackScore(): void {\n }\n\n setMode(mode: 'kmt' | 'trackpad' | 'TBD'): void {\n }\n\n get mode(): 'kmt' | 'trackpad' | 'TBD' {\n return 'kmt';\n }\n\n cancelCurrentAction(): void {\n }\n}\n\n/**\n * Production implementation of KmtInputContext that tracks input state for the state machine.\n *\n * @remarks\n * This class provides the concrete implementation of the KMT input context, maintaining\n * all state required by the state machine to recognize and track gestures:\n *\n * **State Tracking**:\n * - Initial cursor position for calculating pan deltas\n * - Input modality score to distinguish mouse vs trackpad\n * - Determined input mode (kmt/trackpad/TBD)\n * - Coordinate system alignment preference\n *\n * **Input Modality Detection**:\n * The `kmtTrackpadTrackScore` accumulates evidence about the input device:\n * - Positive values indicate mouse behavior (middle-click, no horizontal scroll)\n * - Negative values indicate trackpad behavior (horizontal scroll, two-finger gestures)\n * - Score is used to determine zoom behavior (Ctrl+Scroll for mouse vs Scroll for trackpad)\n *\n * **Design Pattern**:\n * This class follows the Context pattern from the @ue-too/being state machine library,\n * providing stateful data and operations that states can access and modify during transitions.\n *\n * @category Input State Machine\n *\n * @example\n * ```typescript\n * const canvasProxy = new CanvasProxy(canvasElement);\n * const context = new ObservableInputTracker(canvasProxy);\n * const stateMachine = createKmtInputStateMachine(context);\n *\n * // Context tracks state as the state machine processes events\n * stateMachine.happens(\"leftPointerDown\", {x: 100, y: 200});\n * console.log(context.initialCursorPosition); // {x: 100, y: 200}\n * ```\n */\nexport class ObservableInputTracker implements KmtInputContext {\n\n private _alignCoordinateSystem: boolean;\n private _canvasOperator: Canvas;\n private _initialCursorPosition: Point;\n private _kmtTrackpadTrackScore: number; // > 0 for kmt; < 0 for trackpad; 0 for TBD;\n private _mode: 'kmt' | 'trackpad' | 'TBD';\n private _deciding: boolean = true;\n\n constructor(canvasOperator: Canvas){\n this._alignCoordinateSystem = true;\n this._canvasOperator = canvasOperator;\n this._initialCursorPosition = {x: 0, y: 0};\n this._kmtTrackpadTrackScore = 0;\n this._mode = 'TBD';\n }\n\n get mode(): 'kmt' | 'trackpad' | 'TBD' {\n return this._mode;\n }\n\n setMode(mode: 'kmt' | 'trackpad' | 'TBD'): void {\n this._deciding = false;\n this._mode = mode;\n }\n\n enableInputModeDetection(): void {\n this._deciding = true;\n this._kmtTrackpadTrackScore = 0;\n }\n\n get kmtTrackpadTrackScore(): number {\n return this._kmtTrackpadTrackScore;\n }\n\n subtractKmtTrackpadTrackScore(): void {\n if(!this._deciding){\n return;\n }\n this._kmtTrackpadTrackScore--;\n if(this._kmtTrackpadTrackScore < -5){\n this._kmtTrackpadTrackScore = 0;\n // this._mode = 'trackpad';\n }\n }\n\n addKmtTrackpadTrackScore(): void {\n if(!this._deciding){\n return;\n }\n this._kmtTrackpadTrackScore++;\n if(this._kmtTrackpadTrackScore > 5){\n this._kmtTrackpadTrackScore = 0;\n // this._mode = 'kmt';\n }\n }\n\n get alignCoordinateSystem(): boolean {\n return this._alignCoordinateSystem;\n }\n\n get canvas(): Canvas {\n return this._canvasOperator;\n }\n\n get initialCursorPosition(): Point {\n return this._initialCursorPosition;\n }\n\n set alignCoordinateSystem(value: boolean){\n this._alignCoordinateSystem = value;\n }\n\n cancelCurrentAction(): void {\n this._initialCursorPosition = {x: 0, y: 0};\n }\n\n setInitialCursorPosition(position: Point): void {\n this._initialCursorPosition = position;\n }\n\n cleanup(): void {\n }\n\n setup(): void {\n }\n}\n\nfunction withinEdgeOfCanvas(position: Point, boundingBox: {left: number, top: number, width: number, height: number}, padding: number): boolean {\n return position.x <= boundingBox.left + padding || position.x >= boundingBox.left + boundingBox.width - padding || position.y <= boundingBox.top + padding || position.y >= boundingBox.top + boundingBox.height - padding;\n}\n\nfunction pointInWhichHorizontalEdgeOfCanvas(position: Point, boundingBox: {left: number, top: number, width: number, height: number}, padding: number): 'left' | 'right' | 'none' {\n if(position.x <= boundingBox.left + padding){\n return 'left';\n }\n if(position.x >= boundingBox.left + boundingBox.width - padding){\n return 'right';\n }\n return 'none';\n}\n\nfunction pointInWhichVerticalEdgeOfCanvas(position: Point, boundingBox: {left: number, top: number, width: number, height: number}, padding: number): 'up' | 'down' | 'none' {\n if(position.y <= boundingBox.top + padding){\n return 'up';\n }\n if(position.y >= boundingBox.top + boundingBox.height - padding){\n return 'down';\n }\n return 'none';\n}\n",
38
38
  "import { BaseContext } from \"@ue-too/being\";\nimport { Canvas } from \"./kmt-input-context\";\n\n/**\n * Represents a single touch point in window coordinates.\n *\n * @property ident - The unique identifier for this touch point (from TouchEvent.identifier)\n * @property x - X coordinate in window space\n * @property y - Y coordinate in window space\n *\n * @remarks\n * Touch points are tracked by their identifiers to maintain consistency across touch events.\n * Each finger/contact point maintains its identifier for the duration of the touch interaction.\n *\n * @category Input State Machine - Touch\n */\nexport type TouchPoints = {\n ident: number,\n x: number,\n y: number,\n}\n\n/**\n * Context interface for the touch input state machine.\n *\n * @remarks\n * This context manages the state required for multi-touch gesture recognition:\n *\n * **Touch Point Tracking**:\n * - Maintains a map of active touch points by identifier\n * - Stores initial positions to calculate deltas for pan gestures\n * - Stores initial distances to calculate zoom factors\n *\n * **Gesture Recognition**:\n * - Single-finger: Not handled (reserved for UI interactions)\n * - Two-finger: Pan and pinch-to-zoom gestures\n * - Three+ fingers: Currently not handled\n *\n * **Coordinate System**:\n * Similar to KMT, the `alignCoordinateSystem` flag controls Y-axis orientation.\n *\n * This interface extends BaseContext from the @ue-too/being state machine library.\n *\n * @category Input State Machine - Touch\n */\nexport interface TouchContext extends BaseContext{\n /** Adds new touch points to tracking */\n addTouchPoints: (points: TouchPoints[]) => void;\n /** Removes touch points from tracking by identifier */\n removeTouchPoints: (idents: number[]) => void;\n /** Returns the current number of active touch points */\n getCurrentTouchPointsCount: () => number;\n /** Retrieves the initial positions of specific touch points */\n getInitialTouchPointsPositions: (idents: number[]) => TouchPoints[];\n /** Updates the current positions of touch points */\n updateTouchPoints: (pointsMoved: TouchPoints[]) => void;\n /** Whether to use standard screen coordinate system (vs inverted Y-axis) */\n alignCoordinateSystem: boolean;\n /** Canvas accessor for dimensions and coordinate transformations */\n canvas: Canvas;\n}\n\n/**\n * Production implementation of TouchContext that tracks multi-touch state.\n *\n * @remarks\n * This class maintains a map of active touch points, storing their initial positions\n * to enable gesture recognition. The state machine uses this context to:\n *\n * - Calculate pan deltas (difference between initial and current midpoint)\n * - Calculate zoom factors (change in distance between two touch points)\n * - Determine gesture type (pan vs zoom based on relative magnitudes)\n *\n * **Touch Point Lifecycle**:\n * 1. `addTouchPoints`: Called on touchstart to register new touches\n * 2. `updateTouchPoints`: Called on touchmove to update current positions\n * 3. `removeTouchPoints`: Called on touchend/touchcancel to unregister touches\n *\n * The initial positions are preserved until the touch ends, allowing continuous\n * calculation of deltas throughout the gesture.\n *\n * @category Input State Machine - Touch\n *\n * @example\n * ```typescript\n * const canvasProxy = new CanvasProxy(canvasElement);\n * const context = new TouchInputTracker(canvasProxy);\n * const stateMachine = createTouchInputStateMachine(context);\n *\n * // When a two-finger touch starts\n * context.addTouchPoints([\n * {ident: 0, x: 100, y: 200},\n * {ident: 1, x: 300, y: 200}\n * ]);\n * console.log(context.getCurrentTouchPointsCount()); // 2\n * ```\n */\nexport class TouchInputTracker implements TouchContext {\n\n private _touchPointsMap: Map<number, TouchPoints> = new Map<number, TouchPoints>();\n private _canvas: Canvas;\n private _alignCoordinateSystem: boolean;\n\n constructor(canvas: Canvas) {\n this._canvas = canvas;\n this._alignCoordinateSystem = true;\n }\n\n addTouchPoints(points: TouchPoints[]): void {\n points.forEach((point)=>{\n this._touchPointsMap.set(point.ident, {...point});\n });\n }\n\n removeTouchPoints(identifiers: number[]): void {\n identifiers.forEach((ident)=>{\n if(this._touchPointsMap.has(ident)){\n this._touchPointsMap.delete(ident);\n }\n });\n }\n\n getCurrentTouchPointsCount(): number {\n return this._touchPointsMap.size;\n }\n\n getInitialTouchPointsPositions(idents: number[]): TouchPoints[] {\n const res: TouchPoints[] = [];\n idents.forEach((ident)=>{\n if(this._touchPointsMap.has(ident)){\n const point = this._touchPointsMap.get(ident);\n if(point){\n res.push(point);\n }\n }\n });\n return res; \n }\n\n updateTouchPoints(pointsMoved: TouchPoints[]): void {\n pointsMoved.forEach((point)=>{\n if(this._touchPointsMap.has(point.ident)){\n this._touchPointsMap.set(point.ident, {...point});\n }\n });\n }\n\n get alignCoordinateSystem(): boolean {\n return this._alignCoordinateSystem;\n }\n\n set alignCoordinateSystem(value: boolean) {\n this._alignCoordinateSystem = value;\n }\n\n get canvas(): Canvas {\n return this._canvas;\n }\n\n cleanup(): void {\n }\n\n setup(): void {\n }\n}\n",
39
- "import { PointCal } from \"@ue-too/math\";\nimport { EventReactions, EventGuards, Guard, TemplateState, TemplateStateMachine } from \"@ue-too/being\";\nimport { TouchContext, TouchPoints } from \"./touch-input-context\";\nimport type { Point } from \"@ue-too/math\";\n\n/**\n * Possible states of the touch input state machine.\n *\n * @remarks\n * State transitions:\n * - **IDLE**: No touches active, or single touch (reserved for UI)\n * - **PENDING**: Exactly two touches active, waiting for movement to determine gesture type\n * - **IN_PROGRESS**: Two-finger gesture in progress (pan or zoom)\n *\n * The state machine only handles two-finger gestures. Single-finger touches are ignored\n * to avoid interfering with UI interactions (button clicks, text selection, etc.).\n *\n * @category Input State Machine - Touch\n */\nexport type TouchStates = \"IDLE\" | \"PENDING\" | \"IN_PROGRESS\";\n\n/**\n * Payload for touch events containing active touch points.\n *\n * @property points - Array of touch points involved in this event\n *\n * @category Input State Machine - Touch\n */\nexport type TouchEventPayload = {\n points: TouchPoints[];\n};\n\n/**\n * Output events produced by the touch state machine for the orchestrator.\n *\n * @remarks\n * Touch gestures are recognized from two-finger interactions:\n *\n * **Pan Gesture**:\n * - Two fingers move in the same direction\n * - Delta is calculated from the midpoint movement\n * - Triggers when midpoint delta > distance delta\n *\n * **Zoom Gesture**:\n * - Two fingers move toward/away from each other (pinch)\n * - Delta is calculated from distance change between fingers\n * - Anchor point is the midpoint between fingers\n * - Triggers when distance delta > midpoint delta\n *\n * **Coordinate Spaces**:\n * - Pan delta is in window pixels\n * - Zoom anchor point is in viewport coordinates\n *\n * @category Input State Machine - Touch\n */\nexport type TouchOutputEvent =\n | { type: \"pan\", delta: Point }\n | { type: \"zoom\", delta: number, anchorPointInViewPort: Point }\n | { type: \"none\" };\n\n/**\n * Event mapping for the touch input state machine.\n *\n * @remarks\n * Maps touch event names to their payload types. The state machine handles\n * the three core touch events: touchstart, touchmove, and touchend.\n *\n * @category Input State Machine - Touch\n */\nexport type TouchEventMapping = {\n touchstart: TouchEventPayload;\n touchmove: TouchEventPayload;\n touchend: TouchEventPayload;\n}\n\n/**\n * Mapping of events to their output types.\n *\n * @remarks\n * Only touchmove produces outputs (pan or zoom gestures).\n * touchstart and touchend only manage state transitions.\n *\n * @category Input State Machine - Touch\n */\nexport type TouchInputEventOutputMapping = {\n touchmove: TouchOutputEvent;\n}\n\n/**\n * IDLE state - waiting for two-finger touch.\n *\n * @remarks\n * This state handles touch lifecycle but only transitions to PENDING when exactly\n * two touches are active. Single touches and three+ touches are ignored.\n *\n * **Guard Condition**:\n * Transitions to PENDING only when `getCurrentTouchPointsCount() === 2`.\n * This ensures the state machine only handles two-finger gestures.\n *\n * @category Input State Machine - Touch\n */\nexport class IdleState extends TemplateState<TouchEventMapping, TouchContext, TouchStates, TouchInputEventOutputMapping> {\n\n private _eventReactions: EventReactions<TouchEventMapping, TouchContext, TouchStates, TouchInputEventOutputMapping> = {\n touchstart: {\n action: this.touchstart,\n defaultTargetState: \"IDLE\",\n },\n touchend: {\n action: this.touchend,\n defaultTargetState: \"IDLE\",\n },\n };\n\n protected _guards: Guard<TouchContext, \"touchPointsCount\"> = {\n touchPointsCount: ((context: TouchContext) => {\n return context.getCurrentTouchPointsCount() === 2;\n }).bind(this)\n };\n\n protected _eventGuards: Partial<EventGuards<TouchEventMapping, TouchStates, TouchContext, typeof this._guards>> = {\n touchstart: [{\n guard: \"touchPointsCount\",\n target: \"PENDING\",\n }],\n touchend: [{\n guard: \"touchPointsCount\",\n target: \"PENDING\",\n }],\n };\n\n get eventReactions(): EventReactions<TouchEventMapping, TouchContext, TouchStates, TouchInputEventOutputMapping> {\n return this._eventReactions;\n }\n\n touchstart(context: TouchContext, payload: TouchEventPayload): void {\n context.addTouchPoints(payload.points);\n }\n\n touchend(context: TouchContext, payload: TouchEventPayload): void {\n context.removeTouchPoints(payload.points.map(p => p.ident));\n }\n}\n\n/**\n * @description The pending state of the touch input state machine.\n *\n * @category Input State Machine\n */\nexport class PendingState extends TemplateState<TouchEventMapping, TouchContext, TouchStates, TouchInputEventOutputMapping> {\n\n private _eventReactions: EventReactions<TouchEventMapping, TouchContext, TouchStates, TouchInputEventOutputMapping> = {\n touchstart: {\n action: this.touchstart,\n defaultTargetState: \"IDLE\",\n },\n touchend: {\n action: this.touchend,\n defaultTargetState: \"IDLE\",\n },\n touchmove: {\n action: this.touchmove,\n defaultTargetState: \"IN_PROGRESS\",\n },\n };\n\n get eventReactions(): EventReactions<TouchEventMapping, TouchContext, TouchStates, TouchInputEventOutputMapping> {\n return this._eventReactions;\n }\n\n touchstart(context: TouchContext, payload: TouchEventPayload): void {\n context.addTouchPoints(payload.points);\n }\n\n touchend(context: TouchContext, payload: TouchEventPayload): void {\n context.removeTouchPoints(payload.points.map(p => p.ident));\n }\n\n touchmove(context: TouchContext, payload: TouchEventPayload): TouchOutputEvent {\n const idents = payload.points.map(p => p.ident);\n const initialPositions = context.getInitialTouchPointsPositions(idents);\n const currentPositions = payload.points;\n const initialStartAndEndDistance = PointCal.distanceBetweenPoints(initialPositions[0], initialPositions[1]);\n const currentStartAndEndDistance = PointCal.distanceBetweenPoints(currentPositions[0], currentPositions[1]);\n const midPoint = PointCal.linearInterpolation(initialPositions[0], initialPositions[1], 0.5);\n const currentMidPoint = PointCal.linearInterpolation(currentPositions[0], currentPositions[1], 0.5);\n const midPointDelta = PointCal.subVector(midPoint, currentMidPoint);\n const cameraCenterInWindow = {x: context.canvas.position.x + context.canvas.width / 2, y: context.canvas.position.y + context.canvas.height / 2};\n const midPointInViewPort = PointCal.subVector(midPoint, cameraCenterInWindow);\n let panZoom = Math.abs(currentStartAndEndDistance - initialStartAndEndDistance) > PointCal.distanceBetweenPoints(midPoint, currentMidPoint) ? \"ZOOMING\" : \"PANNING\";\n\n context.updateTouchPoints(currentPositions);\n switch(panZoom){\n case \"ZOOMING\":\n return {\n type: \"zoom\",\n delta: (currentStartAndEndDistance - initialStartAndEndDistance) * 0.005,\n anchorPointInViewPort: midPointInViewPort\n };\n case \"PANNING\":\n return {\n type: \"pan\",\n delta: midPointDelta\n };\n default:\n console.warn(\"Unknown panZoom state\", panZoom);\n return { type: \"none\" };\n }\n }\n}\n\n/**\n * @description The in progress state of the touch input state machine.\n *\n * @category Input State Machine\n */\nexport class InProgressState extends TemplateState<TouchEventMapping, TouchContext, TouchStates, TouchInputEventOutputMapping> {\n\n private _eventReactions: EventReactions<TouchEventMapping, TouchContext, TouchStates, TouchInputEventOutputMapping> = {\n touchmove: {\n action: this.touchmove,\n defaultTargetState: \"IN_PROGRESS\",\n },\n touchend: {\n action: this.touchend,\n defaultTargetState: \"IDLE\",\n },\n touchstart: {\n action: ()=> \"IDLE\",\n defaultTargetState: \"IDLE\",\n },\n };\n\n get eventReactions(): EventReactions<TouchEventMapping, TouchContext, TouchStates, TouchInputEventOutputMapping> {\n return this._eventReactions;\n }\n\n touchmove(context: TouchContext, payload: TouchEventPayload): TouchOutputEvent {\n const idents = payload.points.map(p => p.ident);\n const initialPositions = context.getInitialTouchPointsPositions(idents);\n const currentPositions = payload.points;\n const initialStartAndEndDistance = PointCal.distanceBetweenPoints(initialPositions[0], initialPositions[1]);\n const currentStartAndEndDistance = PointCal.distanceBetweenPoints(currentPositions[0], currentPositions[1]);\n const midPoint = PointCal.linearInterpolation(initialPositions[0], initialPositions[1], 0.5);\n const currentMidPoint = PointCal.linearInterpolation(currentPositions[0], currentPositions[1], 0.5);\n const midPointDelta = PointCal.subVector(midPoint, currentMidPoint);\n const cameraCenterInWindow = {x: context.canvas.position.x + context.canvas.width / 2, y: context.canvas.position.y + context.canvas.height / 2};\n const midPointInViewPort = PointCal.subVector(midPoint, cameraCenterInWindow);\n let panZoom = Math.abs(currentStartAndEndDistance - initialStartAndEndDistance) > PointCal.distanceBetweenPoints(midPoint, currentMidPoint) ? \"ZOOMING\" : \"PANNING\";\n\n context.updateTouchPoints(currentPositions);\n switch(panZoom){\n case \"ZOOMING\":\n if(!context.alignCoordinateSystem){\n midPointInViewPort.y = -midPointInViewPort.y;\n }\n return {\n type: \"zoom\",\n delta: -(initialStartAndEndDistance - currentStartAndEndDistance) * 0.005,\n anchorPointInViewPort: midPointInViewPort\n };\n case \"PANNING\":\n if(!context.alignCoordinateSystem){\n midPointDelta.y = -midPointDelta.y;\n }\n return {\n type: \"pan\",\n delta: midPointDelta\n };\n default:\n console.warn(\"Unknown panZoom state\", panZoom);\n return { type: \"none\" };\n }\n }\n\n touchend(context: TouchContext, payload: TouchEventPayload): void {\n context.removeTouchPoints(payload.points.map(p => p.ident));\n }\n}\n\n/**\n * Type alias for the touch input state machine.\n *\n * @category Input State Machine - Touch\n */\nexport type TouchInputStateMachine = TemplateStateMachine<TouchEventMapping, TouchContext, TouchStates, TouchInputEventOutputMapping>;\n\n/**\n * Creates a new touch input state machine for multi-touch gesture recognition.\n *\n * @param context - The context providing touch point tracking and canvas access\n * @returns A configured state machine ready to process touch events\n *\n * @remarks\n * This factory creates a state machine that recognizes two-finger pan and pinch-to-zoom gestures.\n *\n * **State Flow**:\n * ```\n * IDLE → (2 touches start) → PENDING → (touch move) → IN_PROGRESS\n * IN_PROGRESS → (touch end) → IDLE\n * ```\n *\n * **Gesture Recognition Algorithm**:\n * 1. Wait for exactly 2 touches (IDLE → PENDING)\n * 2. On first move, determine gesture type:\n * - If distance change > midpoint change: ZOOM\n * - If midpoint change > distance change: PAN\n * 3. Continue producing pan/zoom outputs until touches end\n *\n * **Pan Gesture**:\n * Delta = current midpoint - initial midpoint\n *\n * **Zoom Gesture**:\n * Delta = (current distance - initial distance) * 0.005\n * Anchor = midpoint in viewport coordinates\n *\n * @category Input State Machine - Touch\n *\n * @example\n * ```typescript\n * const canvasProxy = new CanvasProxy(canvasElement);\n * const context = new TouchInputTracker(canvasProxy);\n * const stateMachine = createTouchInputStateMachine(context);\n *\n * // Process a touch start event with 2 fingers\n * const result = stateMachine.happens(\"touchstart\", {\n * points: [\n * {ident: 0, x: 100, y: 200},\n * {ident: 1, x: 300, y: 200}\n * ]\n * });\n * console.log(result.nextState); // \"PENDING\"\n * ```\n */\nexport function createTouchInputStateMachine(context: TouchContext): TouchInputStateMachine {\n return new TemplateStateMachine<TouchEventMapping, TouchContext, TouchStates, TouchInputEventOutputMapping>(\n {\n IDLE: new IdleState(),\n PENDING: new PendingState(),\n IN_PROGRESS: new InProgressState(),\n }, \"IDLE\", context);\n}\n",
40
- "import { EventReactions, EventGuards, Guard, TemplateState, TemplateStateMachine, NO_OP, EventArgs, EventResult, CreateStateType } from \"@ue-too/being\";\nimport type { Point } from \"@ue-too/math\";\nimport { CursorStyle, DummyKmtInputContext, KmtInputContext } from \"./kmt-input-context\";\nimport { convertFromWindow2ViewPortWithCanvasOperator } from \"../../utils/coorindate-conversion\";\n\nconst KMT_INPUT_STATES = [\"IDLE\", \"READY_TO_PAN_VIA_SPACEBAR\", \"READY_TO_PAN_VIA_SCROLL_WHEEL\", \"PAN\", \"INITIAL_PAN\", \"PAN_VIA_SCROLL_WHEEL\", \"DISABLED\"] as const;\n\n/**\n * Possible states of the Keyboard/Mouse/Trackpad input state machine.\n *\n * @remarks\n * State transitions:\n * - **IDLE**: Default state, waiting for user input\n * - **READY_TO_PAN_VIA_SPACEBAR**: Spacebar pressed, ready to pan with left-click drag\n * - **INITIAL_PAN**: First frame of pan via spacebar (detects accidental clicks)\n * - **PAN**: Active panning via spacebar + left-click drag\n * - **READY_TO_PAN_VIA_SCROLL_WHEEL**: Middle mouse button pressed, ready to pan\n * - **PAN_VIA_SCROLL_WHEEL**: Active panning via middle-click drag\n * - **DISABLED**: Input temporarily disabled (e.g., during UI interactions)\n *\n * @category Input State Machine - KMT\n */\nexport type KmtInputStates = CreateStateType<typeof KMT_INPUT_STATES>;\n\n/**\n * Payload for pointer events (mouse button press/release/move).\n *\n * @property x - X coordinate in window space\n * @property y - Y coordinate in window space\n *\n * @category Input State Machine - KMT\n */\nexport type PointerEventPayload = {\n x: number;\n y: number;\n}\n\n/**\n * @internal\n */\ntype EmptyPayload = {};\n\n/**\n * Payload for scroll wheel events.\n *\n * @property deltaX - Horizontal scroll delta\n * @property deltaY - Vertical scroll delta\n *\n * @category Input State Machine - KMT\n */\nexport type ScrollEventPayload = {\n deltaX: number;\n deltaY: number;\n}\n\n/**\n * Payload for scroll events combined with ctrl key (zoom gesture).\n *\n * @property deltaX - Horizontal scroll delta\n * @property deltaY - Vertical scroll delta\n * @property x - Cursor X coordinate in window space (zoom anchor point)\n * @property y - Cursor Y coordinate in window space (zoom anchor point)\n *\n * @category Input State Machine - KMT\n */\nexport type ScrollWithCtrlEventPayload = {\n deltaX: number;\n deltaY: number;\n x: number;\n y: number;\n}\n\n/**\n * Event mapping for the KMT input state machine.\n *\n * @remarks\n * Maps event names to their payload types. Used by the state machine framework\n * to provide type-safe event handling.\n *\n * Key events:\n * - **leftPointerDown/Up/Move**: Left mouse button interactions\n * - **middlePointerDown/Up/Move**: Middle mouse button interactions (pan)\n * - **spacebarDown/Up**: Spacebar for pan mode\n * - **scroll**: Regular scroll (pan or zoom depending on device)\n * - **scrollWithCtrl**: Ctrl + scroll (always zoom)\n * - **disable/enable**: Temporarily disable/enable input processing\n *\n * @category Input State Machine - KMT\n */\nexport type KmtInputEventMapping = {\n leftPointerDown: PointerEventPayload;\n leftPointerUp: PointerEventPayload;\n leftPointerMove: PointerEventPayload;\n spacebarDown: EmptyPayload;\n spacebarUp: EmptyPayload;\n stayIdle: EmptyPayload;\n cursorOnElement: EmptyPayload;\n scroll: ScrollWithCtrlEventPayload;\n scrollWithCtrl: ScrollWithCtrlEventPayload;\n middlePointerDown: PointerEventPayload;\n middlePointerUp: PointerEventPayload;\n middlePointerMove: PointerEventPayload;\n disable: EmptyPayload;\n enable: EmptyPayload;\n pointerMove: PointerEventPayload;\n}\n\n/**\n * @internal\n */\ntype PanEventOutput = {\n type: \"pan\";\n delta: Point;\n};\n\n/**\n * @internal\n */\ntype ZoomEventOutput = {\n type: \"zoom\";\n delta: number;\n anchorPoint: Point;\n};\n\n/**\n * Output events produced by the KMT state machine for the orchestrator.\n *\n * @remarks\n * These high-level gesture events are the result of recognizing patterns in raw DOM events.\n * The orchestrator receives these events and coordinates camera control and observer notification.\n *\n * **Event Types**:\n * - **pan**: Camera translation with delta in viewport coordinates\n * - **zoom**: Camera scale change with anchor point in viewport coordinates\n * - **rotate**: Camera rotation change (currently unused in KMT)\n * - **cursor**: Cursor style change request (handled by state uponEnter/beforeExit)\n * - **none**: No action required\n *\n * **Coordinate Spaces**:\n * - Pan delta is in viewport pixels\n * - Zoom anchor point is in viewport coordinates (origin at viewport center)\n *\n * @category Input State Machine - KMT\n */\nexport type KmtOutputEvent =\n | { type: \"pan\", delta: Point }\n | { type: \"zoom\", delta: number, anchorPointInViewPort: Point }\n | { type: \"rotate\", deltaRotation: number }\n | { type: \"cursor\", style: CursorStyle }\n | { type: \"none\" };\n\n/**\n * Mapping of events to their output types.\n *\n * @remarks\n * Defines which events produce outputs. Not all events produce outputs - some only\n * cause state transitions. This mapping is used by the state machine framework for\n * type-safe output handling.\n *\n * @category Input State Machine - KMT\n */\nexport type KmtInputEventOutputMapping = {\n spacebarDown: number;\n middlePointerMove: KmtOutputEvent;\n scroll: KmtOutputEvent;\n scrollWithCtrl: KmtOutputEvent;\n leftPointerMove: KmtOutputEvent;\n}\n\n/**\n * @internal\n */\nexport type KmtIdleStatePossibleTargetStates = \"IDLE\" | \"READY_TO_PAN_VIA_SPACEBAR\" | \"READY_TO_PAN_VIA_SCROLL_WHEEL\" | \"DISABLED\";\n\n/**\n * IDLE state - default state waiting for user input.\n *\n * @remarks\n * This is the default state of the KMT input state machine. It handles scroll events\n * for panning and zooming, and transitions to pan-ready states when the user presses\n * spacebar or middle-click.\n *\n * **Responsibilities**:\n * - Process scroll events (pan or zoom depending on device and modifiers)\n * - Detect spacebar press to enter pan mode\n * - Detect middle-click to enter pan mode\n * - Distinguish between mouse and trackpad input modalities\n *\n * **Scroll Behavior**:\n * - Ctrl + Scroll: Always zoom (both mouse and trackpad)\n * - Scroll (no Ctrl): Pan (trackpad) or Zoom (mouse, determined by modality detection)\n *\n * **Input Modality Detection**:\n * The state tracks horizontal scroll deltas to distinguish trackpads (which produce deltaX)\n * from mice (which typically only produce deltaY). This affects zoom behavior.\n *\n * @category Input State Machine - KMT\n */\nexport class KmtIdleState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n\n constructor() {\n super();\n }\n\n protected _guards: Guard<KmtInputContext, \"isIdle\"> = {\n isIdle: () => true,\n }\n\n protected _eventGuards: Partial<EventGuards<KmtInputEventMapping, KmtInputStates, KmtInputContext, Guard<KmtInputContext>>> = {\n }\n\n // Arrow function properties must be defined before _eventReactions to ensure proper initialization order\n scrollPan = (context: KmtInputContext, payload: ScrollEventPayload): KmtOutputEvent => {\n const delta = {...payload}\n if(!context.alignCoordinateSystem){\n delta.deltaY = -delta.deltaY;\n }\n return {\n type: \"pan\",\n delta: {x: delta.deltaX, y: delta.deltaY}\n };\n }\n\n scrollZoom = (context: KmtInputContext, payload: ScrollWithCtrlEventPayload): KmtOutputEvent => {\n let scrollSensitivity = 0.005;\n if(Math.abs(payload.deltaY) > 100){\n scrollSensitivity = 0.0005;\n }\n const zoomAmount = payload.deltaY * scrollSensitivity;\n const cursorPosition = {x: payload.x, y: payload.y};\n const anchorPointInViewPort = convertFromWindow2ViewPortWithCanvasOperator(cursorPosition, context.canvas, {x: context.canvas.width / 2, y: context.canvas.height / 2}, !context.alignCoordinateSystem);\n return {\n type: \"zoom\",\n delta: -(zoomAmount * 5),\n anchorPointInViewPort,\n };\n }\n\n scrollHandler = (context: KmtInputContext, payload: ScrollWithCtrlEventPayload): KmtOutputEvent => {\n if(payload.deltaX !== 0){\n // probably from a trackpad\n context.subtractKmtTrackpadTrackScore();\n }\n if(context.mode === \"kmt\"){\n return this.scrollZoom(context, payload);\n } else {\n return this.scrollPan(context, payload);\n }\n }\n\n scrollWithCtrlHandler = (context: KmtInputContext, payload: ScrollWithCtrlEventPayload): KmtOutputEvent => {\n return this.scrollZoom(context, payload);\n }\n\n get eventReactions(): EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n return this._eventReactions;\n }\n\n protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> = {\n spacebarDown: {\n action: this.spacebarDownHandler,\n defaultTargetState: \"READY_TO_PAN_VIA_SPACEBAR\",\n },\n scroll: {\n action: this.scrollHandler,\n defaultTargetState: \"IDLE\",\n },\n scrollWithCtrl: {\n action: this.scrollWithCtrlHandler,\n defaultTargetState: \"IDLE\",\n },\n middlePointerDown: {\n action: this.middlePointerDownHandler,\n defaultTargetState: \"READY_TO_PAN_VIA_SCROLL_WHEEL\",\n },\n disable: {\n action: NO_OP,\n defaultTargetState: \"DISABLED\",\n },\n }\n\n uponEnter(context: KmtInputContext): void {\n context.canvas.setCursor(CursorStyle.DEFAULT);\n }\n\n spacebarDownHandler(context: KmtInputContext, payload: EmptyPayload): number {\n // context.canvas.setCursor(CursorStyle.GRAB);\n return 1;\n }\n\n middlePointerDownHandler(context: KmtInputContext, payload: PointerEventPayload): void {\n // probably from kmt\n context.addKmtTrackpadTrackScore();\n if(context.mode === \"TBD\") {\n context.setMode(\"kmt\");\n }\n context.setInitialCursorPosition({x: payload.x, y: payload.y});\n }\n\n}\n\nexport class DisabledState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n constructor() {\n super();\n }\n\n uponEnter(context: KmtInputContext): void {\n context.canvas.setCursor(CursorStyle.DEFAULT);\n // context.toggleOnEdgeAutoCameraInput();\n }\n\n beforeExit(context: KmtInputContext): void {\n // context.toggleOffEdgeAutoCameraInput();\n }\n\n get eventReactions(): EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n return {\n \"enable\": {\n action: NO_OP,\n defaultTargetState: \"IDLE\",\n },\n };\n }\n}\n\n/**\n * @description The ready to pan via space bar state of the keyboard mouse and trackpad input state machine.\n * \n * @category Input State Machine\n */\nexport class ReadyToPanViaSpaceBarState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n\n constructor() {\n super();\n }\n\n protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> = {\n spacebarUp: {\n action: NO_OP,\n defaultTargetState: \"IDLE\",\n },\n leftPointerDown: {\n action: this.leftPointerDownHandler,\n defaultTargetState: \"INITIAL_PAN\",\n },\n disable: {\n action: (context) => context.cancelCurrentAction(),\n defaultTargetState: \"DISABLED\",\n },\n leftPointerMove: {\n action: NO_OP,\n defaultTargetState: \"READY_TO_PAN_VIA_SPACEBAR\",\n }\n }\n\n uponEnter(context: KmtInputContext): void {\n context.canvas.setCursor(CursorStyle.GRAB);\n }\n\n get eventReactions(): EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n return this._eventReactions;\n }\n\n leftPointerDownHandler(context: KmtInputContext, payload: PointerEventPayload): void {\n context.setInitialCursorPosition({x: payload.x, y: payload.y});\n }\n}\n\n/**\n * @description The initial pan state of the keyboard mouse and trackpad input state machine.\n * \n * @category Input State Machine\n */\nexport class InitialPanState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n\n constructor() {\n super();\n }\n\n protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> = {\n leftPointerUp: {\n action: NO_OP,\n defaultTargetState: \"READY_TO_PAN_VIA_SPACEBAR\",\n },\n leftPointerMove: {\n action: this.leftPointerMoveHandler,\n defaultTargetState: \"PAN\",\n },\n spacebarUp: {\n action: () => \"IDLE\",\n defaultTargetState: \"IDLE\",\n },\n leftPointerDown: {\n action: () => \"PAN\",\n defaultTargetState: \"PAN\",\n },\n }\n\n get eventReactions(): EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n return this._eventReactions;\n }\n\n uponEnter(context: KmtInputContext): void {\n context.canvas.setCursor(CursorStyle.GRABBING);\n }\n\n leftPointerMoveHandler(context: KmtInputContext, payload: PointerEventPayload): KmtOutputEvent {\n const delta = {\n x: context.initialCursorPosition.x - payload.x,\n y: context.initialCursorPosition.y - payload.y,\n };\n if(!context.alignCoordinateSystem){\n delta.y = -delta.y;\n }\n context.setInitialCursorPosition({x: payload.x, y: payload.y});\n return {\n type: \"pan\",\n delta: delta\n };\n }\n}\n\n/**\n * @description The ready to pan via scroll wheel state of the keyboard mouse and trackpad input state machine.\n * \n * @category Input State Machine\n */\nexport class ReadyToPanViaScrollWheelState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n\n constructor() {\n super();\n }\n\n protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> = {\n middlePointerUp: {\n action: NO_OP,\n defaultTargetState: \"IDLE\",\n },\n middlePointerMove: {\n action: NO_OP,\n defaultTargetState: \"PAN_VIA_SCROLL_WHEEL\",\n },\n }\n\n get eventReactions(): EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n return this._eventReactions;\n }\n\n uponEnter(context: KmtInputContext): void {\n context.canvas.setCursor(CursorStyle.GRABBING);\n }\n}\n\n/**\n * @description The pan state of the keyboard mouse and trackpad input state machine.\n * \n * @category Input State Machine\n */\nexport class PanState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n\n constructor() {\n super();\n }\n\n protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> = {\n leftPointerUp: {\n action: NO_OP,\n defaultTargetState: \"READY_TO_PAN_VIA_SPACEBAR\",\n },\n leftPointerMove: {\n action: this.leftPointerMoveHandler,\n defaultTargetState: \"PAN\",\n },\n spacebarUp: {\n action: NO_OP, \n defaultTargetState: \"IDLE\",\n },\n }\n\n get eventReactions(): EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n return this._eventReactions;\n }\n\n uponEnter(context: KmtInputContext): void {\n context.canvas.setCursor(CursorStyle.GRABBING);\n }\n\n beforeExit(context: KmtInputContext): void {\n context.canvas.setCursor(CursorStyle.DEFAULT);\n }\n\n leftPointerMoveHandler(context: KmtInputContext, payload: PointerEventPayload): KmtOutputEvent {\n const delta = {\n x: context.initialCursorPosition.x - payload.x,\n y: context.initialCursorPosition.y - payload.y,\n };\n if(!context.alignCoordinateSystem){\n delta.y = -delta.y;\n }\n context.setInitialCursorPosition({x: payload.x, y: payload.y});\n return {\n type: \"pan\",\n delta: delta\n };\n }\n}\n\n/**\n * @description The pan via scroll wheel state of the keyboard mouse and trackpad input state machine.\n * \n * @category Input State Machine\n */\nexport class PanViaScrollWheelState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n\n protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> = {\n middlePointerUp: {\n action: NO_OP,\n defaultTargetState: \"IDLE\",\n },\n middlePointerMove: {\n action: this.middlePointerMoveHandler,\n defaultTargetState: \"PAN_VIA_SCROLL_WHEEL\",\n },\n }\n\n get eventReactions(): EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n return this._eventReactions;\n }\n\n middlePointerMoveHandler(context: KmtInputContext, payload: PointerEventPayload): KmtOutputEvent {\n const delta = {\n x: context.initialCursorPosition.x - payload.x,\n y: context.initialCursorPosition.y - payload.y,\n };\n if(!context.alignCoordinateSystem){\n delta.y = -delta.y;\n }\n context.setInitialCursorPosition({x: payload.x, y: payload.y});\n return {\n type: \"pan\",\n delta: delta,\n };\n }\n\n uponEnter(context: KmtInputContext): void {\n context.canvas.setCursor(CursorStyle.GRABBING);\n }\n}\n\nexport class KmtEmptyState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n\n constructor() {\n super();\n }\n\n get eventReactions(): EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n return {};\n }\n \n}\n\n/**\n * Type alias for the KMT input state machine.\n *\n * @category Input State Machine - KMT\n */\nexport type KmtInputStateMachine = TemplateStateMachine<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping>;\n\n/**\n * Creates a new KMT (Keyboard/Mouse/Trackpad) input state machine.\n *\n * @param context - The context providing state and canvas access for the state machine\n * @returns A configured state machine ready to process KMT input events\n *\n * @remarks\n * This factory function creates a fully configured state machine with all KMT gesture\n * recognition states. The state machine processes raw input events and produces\n * high-level gesture outputs (pan, zoom, rotate).\n *\n * **State Flow**:\n * ```\n * IDLE → (spacebar) → READY_TO_PAN_VIA_SPACEBAR → (click) → INITIAL_PAN → PAN\n * IDLE → (middle-click) → READY_TO_PAN_VIA_SCROLL_WHEEL → PAN_VIA_SCROLL_WHEEL\n * IDLE → (scroll) → [produces pan or zoom output, stays in IDLE]\n * ```\n *\n * **Gesture Recognition**:\n * - **Pan via spacebar**: Spacebar + left-click drag\n * - **Pan via middle-click**: Middle-click drag\n * - **Zoom**: Ctrl + scroll (mouse) or scroll (trackpad, auto-detected)\n * - **Pan via scroll**: Scroll (trackpad) or scroll without Ctrl (varies by device)\n *\n * @category Input State Machine - KMT\n *\n * @example\n * ```typescript\n * const canvasProxy = new CanvasProxy(canvasElement);\n * const context = new ObservableInputTracker(canvasProxy);\n * const stateMachine = createKmtInputStateMachine(context);\n *\n * // Process an event\n * const result = stateMachine.happens(\"scroll\", {\n * deltaX: 0,\n * deltaY: 10,\n * x: 500,\n * y: 300\n * });\n *\n * // Check for output\n * if (result.output) {\n * console.log(\"Gesture recognized:\", result.output.type);\n * }\n * ```\n */\nexport function createKmtInputStateMachine(context: KmtInputContext): KmtInputStateMachine {\n const states = {\n IDLE: new KmtIdleState(),\n READY_TO_PAN_VIA_SPACEBAR: new ReadyToPanViaSpaceBarState(),\n INITIAL_PAN: new InitialPanState(),\n PAN: new PanState(),\n READY_TO_PAN_VIA_SCROLL_WHEEL: new ReadyToPanViaScrollWheelState(),\n PAN_VIA_SCROLL_WHEEL: new PanViaScrollWheelState(),\n DISABLED: new DisabledState(),\n }\n return new TemplateStateMachine<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping>(states, \"IDLE\", context);\n}\n\nexport class KmtInputStateMachineWebWorkerProxy extends TemplateStateMachine<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n\n private _webworker: Worker;\n\n constructor(webworker: Worker){\n super({\n \"IDLE\": new KmtEmptyState(),\n \"READY_TO_PAN_VIA_SPACEBAR\": new KmtEmptyState(),\n \"INITIAL_PAN\": new KmtEmptyState(),\n \"PAN\": new KmtEmptyState(),\n \"READY_TO_PAN_VIA_SCROLL_WHEEL\": new KmtEmptyState(),\n \"PAN_VIA_SCROLL_WHEEL\": new KmtEmptyState(),\n \"DISABLED\": new DisabledState(),\n }, \"IDLE\", new DummyKmtInputContext());\n this._webworker = webworker;\n }\n\n happens(...args: EventArgs<KmtInputEventMapping, keyof KmtInputEventMapping | string>): EventResult<KmtInputStates> { \n this._webworker.postMessage({\n type: \"kmtInputStateMachine\",\n event: args[0],\n payload: args[1],\n });\n return {handled: true, nextState: \"IDLE\"};\n }\n}\n",
41
- "import type {Point} from \"@ue-too/math\";\nimport {KmtOutputEvent} from \"./input-state-machine/kmt-input-state-machine\";\nimport {TouchOutputEvent} from \"./input-state-machine/touch-input-state-machine\";\nimport {UserInputPublisher} from \"./raw-input-publisher/raw-input-publisher\";\nimport {CameraMux, CameraMuxPanOutput, CameraMuxZoomOutput, CameraMuxRotationOutput} from \"../camera/camera-mux\";\nimport {CameraRig} from \"../camera/camera-rig\";\n\n/**\n * Union type of all output events from state machines.\n *\n * @remarks\n * This type represents the unified output from both KMT (Keyboard/Mouse/Trackpad) and Touch state machines.\n * By unifying these outputs, the orchestrator can handle events from different input modalities uniformly.\n *\n * @category Input Interpretation\n */\nexport type OutputEvent = KmtOutputEvent | TouchOutputEvent;\n\n/**\n * Central orchestrator that coordinates input interpretation and camera control for the infinite canvas.\n *\n * @remarks\n * The InputOrchestrator serves as the mediator between input state machines and camera control systems.\n * It implements a permission-based architecture where:\n *\n * 1. **Event Flow**: State machines produce high-level gesture events (pan, zoom, rotate)\n * 2. **Permission Check**: Events are sent to CameraMux for permission validation\n * 3. **Execution**: If allowed, gestures are executed on CameraRig\n * 4. **Broadcasting**: Raw events are simultaneously broadcast to observers via UserInputPublisher\n *\n * **Architecture Pattern**:\n * ```\n * State Machines → Orchestrator → CameraMux (permission) → CameraRig (execution)\n * ↓\n * UserInputPublisher (observers)\n * ```\n *\n * This design decouples state machines from camera control, allowing state machines to focus solely\n * on gesture recognition while the orchestrator handles the complexities of camera coordination,\n * permission management, and event distribution.\n *\n * **Key Benefits**:\n * - Single point of control for all camera operations\n * - State machines remain unaware of camera implementation\n * - Parallel path for observers to react to raw input events\n * - Consistent handling of KMT and Touch input modalities\n *\n * @category Input Interpretation\n *\n * @example\n * ```typescript\n * // Create the orchestrator\n * const cameraMux = new CameraMux();\n * const cameraRig = new CameraRig(camera, viewport);\n * const publisher = new RawUserInputPublisher();\n * const orchestrator = new InputOrchestrator(cameraMux, cameraRig, publisher);\n *\n * // State machines send their output to the orchestrator\n * const kmtStateMachine = createKmtInputStateMachine(kmtContext);\n * const result = kmtStateMachine.happens(\"leftPointerMove\", {x: 100, y: 200});\n * orchestrator.processInputEventOutput(result.output);\n *\n * // Observers can subscribe to raw input events\n * publisher.on(\"pan\", (event) => {\n * console.log(\"Pan gesture detected:\", event.diff);\n * });\n * ```\n */\nexport class InputOrchestrator {\n private _cameraMux: CameraMux;\n private _cameraRig: CameraRig;\n private _publisher?: UserInputPublisher;\n\n /**\n * Creates a new InputOrchestrator instance.\n *\n * @param cameraMux - The camera multiplexer that validates and controls camera operation permissions\n * @param cameraRig - The camera rig that executes camera transformations\n * @param publisher - Optional publisher for broadcasting raw input events to observers\n *\n * @remarks\n * The publisher parameter is optional to support scenarios where event broadcasting is not needed.\n * When provided, all input events are broadcast in parallel to camera control execution.\n */\n constructor(cameraMux: CameraMux, cameraRig: CameraRig, publisher?: UserInputPublisher) {\n this._cameraMux = cameraMux;\n this._cameraRig = cameraRig;\n this._publisher = publisher;\n }\n\n /**\n * Processes output events from state machines and routes them to camera control and observers.\n *\n * @param output - The output from a state machine, can be a single event, array of events, or any value\n *\n * @remarks\n * This method serves as the main entry point for state machine outputs. It:\n * 1. Validates whether the output is a valid OutputEvent\n * 2. Handles both single events and arrays of events\n * 3. Routes each valid event through the camera control pipeline\n * 4. Broadcasts events to observers via the publisher\n *\n * Called by event parsers after the state machine processes an input and produces output.\n * The method uses type guards to ensure type safety when handling dynamic output types.\n *\n * @example\n * ```typescript\n * const result = stateMachine.happens(\"scroll\", {deltaX: 0, deltaY: 10, x: 100, y: 200});\n * orchestrator.processInputEventOutput(result.output);\n * ```\n */\n public processInputEventOutput(output: any): void {\n // Handle different output types\n if (this.isOutputEvent(output)) {\n this.handleStateMachineOutput(output);\n } else if (Array.isArray(output)) {\n // Handle multiple outputs\n output.forEach(item => {\n if (this.isOutputEvent(item)) {\n this.handleStateMachineOutput(item);\n }\n });\n }\n }\n\n /**\n * Type guard to check if an output value is a valid OutputEvent.\n *\n * @param output - The value to check\n * @returns True if the output is a valid OutputEvent with a type property\n *\n * @remarks\n * This type guard ensures type safety when processing state machine outputs.\n * It checks for the presence of a 'type' property which is common to all OutputEvent variants.\n */\n private isOutputEvent(output: any): output is OutputEvent {\n return output && typeof output === 'object' && 'type' in output;\n }\n\n /**\n * Handles individual output events from state machines by routing to camera control and observers.\n *\n * @param event - The output event from a state machine (pan, zoom, rotate, cursor, or none)\n *\n * @remarks\n * This method implements a dual-path architecture:\n *\n * **Parallel Path 1 - Observer Notification**:\n * - Immediately broadcasts the event to all subscribers via UserInputPublisher\n * - This allows external systems to react to user input in real-time\n * - Independent of camera permission/execution\n *\n * **Parallel Path 2 - Camera Control**:\n * - Requests permission from CameraMux for the operation\n * - CameraMux may modify the event (e.g., clamp values, deny operation)\n * - If permitted, executes the transformation on CameraRig\n *\n * Event types:\n * - **pan**: Translates the camera viewport\n * - **zoom**: Scales the camera around an anchor point\n * - **rotate**: Rotates the camera view\n * - **cursor**: Changes cursor appearance (handled by state machine)\n * - **none**: No operation needed\n */\n private handleStateMachineOutput(event: OutputEvent): void {\n switch (event.type) {\n case \"pan\":\n // Publish to observers (parallel path)\n this._publisher?.notifyPan(event.delta);\n // Ask CameraMux for permission and process its output\n const panOutput = this._cameraMux.notifyPanInput(event.delta);\n this.processPanMuxOutput(panOutput);\n break;\n case \"zoom\":\n // Publish to observers (parallel path)\n this._publisher?.notifyZoom(event.delta, event.anchorPointInViewPort);\n // Ask CameraMux for permission and process its output\n const zoomOutput = this._cameraMux.notifyZoomInput(event.delta, event.anchorPointInViewPort);\n this.processZoomMuxOutput(zoomOutput);\n break;\n case \"rotate\":\n // Publish to observers (parallel path)\n this._publisher?.notifyRotate(event.deltaRotation);\n // Ask CameraMux for permission and process its output\n const rotateOutput = this._cameraMux.notifyRotationInput(event.deltaRotation);\n this.processRotateMuxOutput(rotateOutput);\n break;\n case \"cursor\":\n // Cursor changes are handled by the state machine's uponEnter/beforeExit methods\n // This case is here for future extension\n break;\n case \"none\":\n // No action needed\n break;\n }\n }\n\n /**\n * Processes pan output from CameraMux and executes the pan operation if permitted.\n *\n * @param output - The pan output from CameraMux containing permission and potentially modified delta\n *\n * @remarks\n * CameraMux may deny the operation (allowPassThrough = false) or modify the delta value\n * to enforce constraints like viewport bounds or animation states.\n * Only when permission is granted does the pan execute on CameraRig.\n */\n private processPanMuxOutput(output: CameraMuxPanOutput): void {\n if (output.allowPassThrough) {\n this._cameraRig.panByViewPort(output.delta);\n }\n }\n\n /**\n * Processes zoom output from CameraMux and executes the zoom operation if permitted.\n *\n * @param output - The zoom output from CameraMux containing permission and potentially modified parameters\n *\n * @remarks\n * CameraMux may deny the operation or modify zoom parameters to enforce constraints\n * like minimum/maximum zoom levels or animation states. The anchor point determines\n * the center of the zoom transformation in viewport coordinates.\n */\n private processZoomMuxOutput(output: CameraMuxZoomOutput): void {\n if (output.allowPassThrough) {\n this._cameraRig.zoomByAt(output.delta, output.anchorPoint);\n }\n }\n\n /**\n * Processes rotation output from CameraMux and executes the rotation operation if permitted.\n *\n * @param output - The rotation output from CameraMux containing permission and potentially modified delta\n *\n * @remarks\n * CameraMux may deny the operation or modify the rotation delta to enforce constraints\n * like rotation limits or animation states.\n */\n private processRotateMuxOutput(output: CameraMuxRotationOutput): void {\n if (output.allowPassThrough) {\n this._cameraRig.rotateBy(output.delta);\n }\n }\n\n /**\n * Gets the UserInputPublisher for direct access to event subscription.\n *\n * @returns The publisher instance, or undefined if not configured\n *\n * @remarks\n * Allows external code to subscribe to raw input events without going through the orchestrator.\n */\n get publisher(): UserInputPublisher | undefined {\n return this._publisher;\n }\n\n /**\n * Gets the CameraMux instance for direct access to permission control.\n *\n * @returns The camera multiplexer instance\n */\n get cameraMux(): CameraMux {\n return this._cameraMux;\n }\n\n /**\n * Sets a new CameraMux instance.\n *\n * @param cameraMux - The new camera multiplexer to use for permission control\n *\n * @remarks\n * Allows dynamic reconfiguration of camera permission logic at runtime.\n */\n set cameraMux(cameraMux: CameraMux){\n this._cameraMux = cameraMux;\n }\n}\n",
39
+ "import { PointCal } from \"@ue-too/math\";\nimport { EventReactions, EventGuards, Guard, TemplateState, TemplateStateMachine } from \"@ue-too/being\";\nimport { TouchContext, TouchPoints } from \"./touch-input-context\";\nimport type { Point } from \"@ue-too/math\";\n\n/**\n * Possible states of the touch input state machine.\n *\n * @remarks\n * State transitions:\n * - **IDLE**: No touches active, or single touch (reserved for UI)\n * - **PENDING**: Exactly two touches active, waiting for movement to determine gesture type\n * - **IN_PROGRESS**: Two-finger gesture in progress (pan or zoom)\n *\n * The state machine only handles two-finger gestures. Single-finger touches are ignored\n * to avoid interfering with UI interactions (button clicks, text selection, etc.).\n *\n * @category Input State Machine - Touch\n */\nexport type TouchStates = \"IDLE\" | \"PENDING\" | \"IN_PROGRESS\";\n\n/**\n * Payload for touch events containing active touch points.\n *\n * @property points - Array of touch points involved in this event\n *\n * @category Input State Machine - Touch\n */\nexport type TouchEventPayload = {\n points: TouchPoints[];\n};\n\n/**\n * Output events produced by the touch state machine for the orchestrator.\n *\n * @remarks\n * Touch gestures are recognized from two-finger interactions:\n *\n * **Pan Gesture**:\n * - Two fingers move in the same direction\n * - Delta is calculated from the midpoint movement\n * - Triggers when midpoint delta > distance delta\n *\n * **Zoom Gesture**:\n * - Two fingers move toward/away from each other (pinch)\n * - Delta is calculated from distance change between fingers\n * - Anchor point is the midpoint between fingers\n * - Triggers when distance delta > midpoint delta\n *\n * **Coordinate Spaces**:\n * - Pan delta is in window pixels\n * - Zoom anchor point is in viewport coordinates\n *\n * @category Input State Machine - Touch\n */\nexport type TouchOutputEvent =\n | { type: \"pan\", delta: Point }\n | { type: \"zoom\", delta: number, anchorPointInViewPort: Point }\n | { type: \"none\" };\n\n/**\n * Event mapping for the touch input state machine.\n *\n * @remarks\n * Maps touch event names to their payload types. The state machine handles\n * the three core touch events: touchstart, touchmove, and touchend.\n *\n * @category Input State Machine - Touch\n */\nexport type TouchEventMapping = {\n touchstart: TouchEventPayload;\n touchmove: TouchEventPayload;\n touchend: TouchEventPayload;\n}\n\n/**\n * Mapping of events to their output types.\n *\n * @remarks\n * Only touchmove produces outputs (pan or zoom gestures).\n * touchstart and touchend only manage state transitions.\n *\n * @category Input State Machine - Touch\n */\nexport type TouchInputEventOutputMapping = {\n touchmove: TouchOutputEvent;\n}\n\n/**\n * IDLE state - waiting for two-finger touch.\n *\n * @remarks\n * This state handles touch lifecycle but only transitions to PENDING when exactly\n * two touches are active. Single touches and three+ touches are ignored.\n *\n * **Guard Condition**:\n * Transitions to PENDING only when `getCurrentTouchPointsCount() === 2`.\n * This ensures the state machine only handles two-finger gestures.\n *\n * @category Input State Machine - Touch\n */\nexport class IdleState extends TemplateState<TouchEventMapping, TouchContext, TouchStates, TouchInputEventOutputMapping> {\n\n protected _eventReactions: EventReactions<TouchEventMapping, TouchContext, TouchStates, TouchInputEventOutputMapping> = {\n touchstart: {\n action: this.touchstart,\n defaultTargetState: \"IDLE\",\n },\n touchend: {\n action: this.touchend,\n defaultTargetState: \"IDLE\",\n },\n };\n\n protected _guards: Guard<TouchContext, \"touchPointsCount\"> = {\n touchPointsCount: ((context: TouchContext) => {\n return context.getCurrentTouchPointsCount() === 2;\n }).bind(this)\n };\n\n protected _eventGuards: Partial<EventGuards<TouchEventMapping, TouchStates, TouchContext, typeof this._guards>> = {\n touchstart: [{\n guard: \"touchPointsCount\",\n target: \"PENDING\",\n }],\n touchend: [{\n guard: \"touchPointsCount\",\n target: \"PENDING\",\n }],\n };\n\n touchstart(context: TouchContext, payload: TouchEventPayload): void {\n context.addTouchPoints(payload.points);\n }\n\n touchend(context: TouchContext, payload: TouchEventPayload): void {\n context.removeTouchPoints(payload.points.map(p => p.ident));\n }\n}\n\n/**\n * @description The pending state of the touch input state machine.\n *\n * @category Input State Machine\n */\nexport class PendingState extends TemplateState<TouchEventMapping, TouchContext, TouchStates, TouchInputEventOutputMapping> {\n\n protected _eventReactions: EventReactions<TouchEventMapping, TouchContext, TouchStates, TouchInputEventOutputMapping> = {\n touchstart: {\n action: this.touchstart,\n defaultTargetState: \"IDLE\",\n },\n touchend: {\n action: this.touchend,\n defaultTargetState: \"IDLE\",\n },\n touchmove: {\n action: this.touchmove,\n defaultTargetState: \"IN_PROGRESS\",\n },\n };\n\n touchstart(context: TouchContext, payload: TouchEventPayload): void {\n context.addTouchPoints(payload.points);\n }\n\n touchend(context: TouchContext, payload: TouchEventPayload): void {\n context.removeTouchPoints(payload.points.map(p => p.ident));\n }\n\n touchmove(context: TouchContext, payload: TouchEventPayload): TouchOutputEvent {\n const idents = payload.points.map(p => p.ident);\n const initialPositions = context.getInitialTouchPointsPositions(idents);\n const currentPositions = payload.points;\n const initialStartAndEndDistance = PointCal.distanceBetweenPoints(initialPositions[0], initialPositions[1]);\n const currentStartAndEndDistance = PointCal.distanceBetweenPoints(currentPositions[0], currentPositions[1]);\n const midPoint = PointCal.linearInterpolation(initialPositions[0], initialPositions[1], 0.5);\n const currentMidPoint = PointCal.linearInterpolation(currentPositions[0], currentPositions[1], 0.5);\n const midPointDelta = PointCal.subVector(midPoint, currentMidPoint);\n const cameraCenterInWindow = {x: context.canvas.position.x + context.canvas.width / 2, y: context.canvas.position.y + context.canvas.height / 2};\n const midPointInViewPort = PointCal.subVector(midPoint, cameraCenterInWindow);\n let panZoom = Math.abs(currentStartAndEndDistance - initialStartAndEndDistance) > PointCal.distanceBetweenPoints(midPoint, currentMidPoint) ? \"ZOOMING\" : \"PANNING\";\n\n context.updateTouchPoints(currentPositions);\n switch(panZoom){\n case \"ZOOMING\":\n return {\n type: \"zoom\",\n delta: (currentStartAndEndDistance - initialStartAndEndDistance) * 0.005,\n anchorPointInViewPort: midPointInViewPort\n };\n case \"PANNING\":\n return {\n type: \"pan\",\n delta: midPointDelta\n };\n default:\n console.warn(\"Unknown panZoom state\", panZoom);\n return { type: \"none\" };\n }\n }\n}\n\n/**\n * @description The in progress state of the touch input state machine.\n *\n * @category Input State Machine\n */\nexport class InProgressState extends TemplateState<TouchEventMapping, TouchContext, TouchStates, TouchInputEventOutputMapping> {\n\n protected _eventReactions: EventReactions<TouchEventMapping, TouchContext, TouchStates, TouchInputEventOutputMapping> = {\n touchmove: {\n action: this.touchmove,\n defaultTargetState: \"IN_PROGRESS\",\n },\n touchend: {\n action: this.touchend,\n defaultTargetState: \"IDLE\",\n },\n touchstart: {\n action: ()=> \"IDLE\",\n defaultTargetState: \"IDLE\",\n },\n };\n\n touchmove(context: TouchContext, payload: TouchEventPayload): TouchOutputEvent {\n const idents = payload.points.map(p => p.ident);\n const initialPositions = context.getInitialTouchPointsPositions(idents);\n const currentPositions = payload.points;\n const initialStartAndEndDistance = PointCal.distanceBetweenPoints(initialPositions[0], initialPositions[1]);\n const currentStartAndEndDistance = PointCal.distanceBetweenPoints(currentPositions[0], currentPositions[1]);\n const midPoint = PointCal.linearInterpolation(initialPositions[0], initialPositions[1], 0.5);\n const currentMidPoint = PointCal.linearInterpolation(currentPositions[0], currentPositions[1], 0.5);\n const midPointDelta = PointCal.subVector(midPoint, currentMidPoint);\n const cameraCenterInWindow = {x: context.canvas.position.x + context.canvas.width / 2, y: context.canvas.position.y + context.canvas.height / 2};\n const midPointInViewPort = PointCal.subVector(midPoint, cameraCenterInWindow);\n let panZoom = Math.abs(currentStartAndEndDistance - initialStartAndEndDistance) > PointCal.distanceBetweenPoints(midPoint, currentMidPoint) ? \"ZOOMING\" : \"PANNING\";\n\n context.updateTouchPoints(currentPositions);\n switch(panZoom){\n case \"ZOOMING\":\n if(!context.alignCoordinateSystem){\n midPointInViewPort.y = -midPointInViewPort.y;\n }\n return {\n type: \"zoom\",\n delta: -(initialStartAndEndDistance - currentStartAndEndDistance) * 0.005,\n anchorPointInViewPort: midPointInViewPort\n };\n case \"PANNING\":\n if(!context.alignCoordinateSystem){\n midPointDelta.y = -midPointDelta.y;\n }\n return {\n type: \"pan\",\n delta: midPointDelta\n };\n default:\n console.warn(\"Unknown panZoom state\", panZoom);\n return { type: \"none\" };\n }\n }\n\n touchend(context: TouchContext, payload: TouchEventPayload): void {\n context.removeTouchPoints(payload.points.map(p => p.ident));\n }\n}\n\n/**\n * Type alias for the touch input state machine.\n *\n * @category Input State Machine - Touch\n */\nexport type TouchInputStateMachine = TemplateStateMachine<TouchEventMapping, TouchContext, TouchStates, TouchInputEventOutputMapping>;\n\n/**\n * Creates a new touch input state machine for multi-touch gesture recognition.\n *\n * @param context - The context providing touch point tracking and canvas access\n * @returns A configured state machine ready to process touch events\n *\n * @remarks\n * This factory creates a state machine that recognizes two-finger pan and pinch-to-zoom gestures.\n *\n * **State Flow**:\n * ```\n * IDLE → (2 touches start) → PENDING → (touch move) → IN_PROGRESS\n * IN_PROGRESS → (touch end) → IDLE\n * ```\n *\n * **Gesture Recognition Algorithm**:\n * 1. Wait for exactly 2 touches (IDLE → PENDING)\n * 2. On first move, determine gesture type:\n * - If distance change > midpoint change: ZOOM\n * - If midpoint change > distance change: PAN\n * 3. Continue producing pan/zoom outputs until touches end\n *\n * **Pan Gesture**:\n * Delta = current midpoint - initial midpoint\n *\n * **Zoom Gesture**:\n * Delta = (current distance - initial distance) * 0.005\n * Anchor = midpoint in viewport coordinates\n *\n * @category Input State Machine - Touch\n *\n * @example\n * ```typescript\n * const canvasProxy = new CanvasProxy(canvasElement);\n * const context = new TouchInputTracker(canvasProxy);\n * const stateMachine = createTouchInputStateMachine(context);\n *\n * // Process a touch start event with 2 fingers\n * const result = stateMachine.happens(\"touchstart\", {\n * points: [\n * {ident: 0, x: 100, y: 200},\n * {ident: 1, x: 300, y: 200}\n * ]\n * });\n * console.log(result.nextState); // \"PENDING\"\n * ```\n */\nexport function createTouchInputStateMachine(context: TouchContext): TouchInputStateMachine {\n return new TemplateStateMachine<TouchEventMapping, TouchContext, TouchStates, TouchInputEventOutputMapping>(\n {\n IDLE: new IdleState(),\n PENDING: new PendingState(),\n IN_PROGRESS: new InProgressState(),\n }, \"IDLE\", context);\n}\n",
40
+ "import { EventReactions, EventGuards, Guard, TemplateState, TemplateStateMachine, NO_OP, EventArgs, EventResult, CreateStateType } from \"@ue-too/being\";\nimport type { Point } from \"@ue-too/math\";\nimport { Canvas, CursorStyle, DummyKmtInputContext, KmtInputContext, ObservableInputTracker } from \"./kmt-input-context\";\nimport { convertFromWindow2ViewPortWithCanvasOperator } from \"../../utils/coorindate-conversion\";\n\nconst KMT_INPUT_STATES = [\"IDLE\", \"READY_TO_PAN_VIA_SPACEBAR\", \"READY_TO_PAN_VIA_SCROLL_WHEEL\", \"PAN\", \"INITIAL_PAN\", \"PAN_VIA_SCROLL_WHEEL\", \"DISABLED\"] as const;\n\n/**\n * Possible states of the Keyboard/Mouse/Trackpad input state machine.\n *\n * @remarks\n * State transitions:\n * - **IDLE**: Default state, waiting for user input\n * - **READY_TO_PAN_VIA_SPACEBAR**: Spacebar pressed, ready to pan with left-click drag\n * - **INITIAL_PAN**: First frame of pan via spacebar (detects accidental clicks)\n * - **PAN**: Active panning via spacebar + left-click drag\n * - **READY_TO_PAN_VIA_SCROLL_WHEEL**: Middle mouse button pressed, ready to pan\n * - **PAN_VIA_SCROLL_WHEEL**: Active panning via middle-click drag\n * - **DISABLED**: Input temporarily disabled (e.g., during UI interactions)\n *\n * @category Input State Machine - KMT\n */\nexport type KmtInputStates = CreateStateType<typeof KMT_INPUT_STATES>;\n\n/**\n * Payload for pointer events (mouse button press/release/move).\n *\n * @property x - X coordinate in window space\n * @property y - Y coordinate in window space\n *\n * @category Input State Machine - KMT\n */\nexport type PointerEventPayload = {\n x: number;\n y: number;\n}\n\n/**\n * @internal\n */\ntype EmptyPayload = {};\n\n/**\n * Payload for scroll wheel events.\n *\n * @property deltaX - Horizontal scroll delta\n * @property deltaY - Vertical scroll delta\n *\n * @category Input State Machine - KMT\n */\nexport type ScrollEventPayload = {\n deltaX: number;\n deltaY: number;\n}\n\n/**\n * Payload for scroll events combined with ctrl key (zoom gesture).\n *\n * @property deltaX - Horizontal scroll delta\n * @property deltaY - Vertical scroll delta\n * @property x - Cursor X coordinate in window space (zoom anchor point)\n * @property y - Cursor Y coordinate in window space (zoom anchor point)\n *\n * @category Input State Machine - KMT\n */\nexport type ScrollWithCtrlEventPayload = {\n deltaX: number;\n deltaY: number;\n x: number;\n y: number;\n}\n\n/**\n * Event mapping for the KMT input state machine.\n *\n * @remarks\n * Maps event names to their payload types. Used by the state machine framework\n * to provide type-safe event handling.\n *\n * Key events:\n * - **leftPointerDown/Up/Move**: Left mouse button interactions\n * - **middlePointerDown/Up/Move**: Middle mouse button interactions (pan)\n * - **spacebarDown/Up**: Spacebar for pan mode\n * - **scroll**: Regular scroll (pan or zoom depending on device)\n * - **scrollWithCtrl**: Ctrl + scroll (always zoom)\n * - **disable/enable**: Temporarily disable/enable input processing\n *\n * @category Input State Machine - KMT\n */\nexport type KmtInputEventMapping = {\n leftPointerDown: PointerEventPayload;\n leftPointerUp: PointerEventPayload;\n leftPointerMove: PointerEventPayload;\n spacebarDown: EmptyPayload;\n spacebarUp: EmptyPayload;\n stayIdle: EmptyPayload;\n cursorOnElement: EmptyPayload;\n scroll: ScrollWithCtrlEventPayload;\n scrollWithCtrl: ScrollWithCtrlEventPayload;\n middlePointerDown: PointerEventPayload;\n middlePointerUp: PointerEventPayload;\n middlePointerMove: PointerEventPayload;\n disable: EmptyPayload;\n enable: EmptyPayload;\n pointerMove: PointerEventPayload;\n}\n\n/**\n * @internal\n */\ntype PanEventOutput = {\n type: \"pan\";\n delta: Point;\n};\n\n/**\n * @internal\n */\ntype ZoomEventOutput = {\n type: \"zoom\";\n delta: number;\n anchorPoint: Point;\n};\n\n/**\n * Output events produced by the KMT state machine for the orchestrator.\n *\n * @remarks\n * These high-level gesture events are the result of recognizing patterns in raw DOM events.\n * The orchestrator receives these events and coordinates camera control and observer notification.\n *\n * **Event Types**:\n * - **pan**: Camera translation with delta in viewport coordinates\n * - **zoom**: Camera scale change with anchor point in viewport coordinates\n * - **rotate**: Camera rotation change (currently unused in KMT)\n * - **cursor**: Cursor style change request (handled by state uponEnter/beforeExit)\n * - **none**: No action required\n *\n * **Coordinate Spaces**:\n * - Pan delta is in viewport pixels\n * - Zoom anchor point is in viewport coordinates (origin at viewport center)\n *\n * @category Input State Machine - KMT\n */\nexport type KmtOutputEvent =\n | { type: \"pan\", delta: Point }\n | { type: \"zoom\", delta: number, anchorPointInViewPort: Point }\n | { type: \"rotate\", deltaRotation: number }\n | { type: \"cursor\", style: CursorStyle }\n | { type: \"none\" };\n\n/**\n * Mapping of events to their output types.\n *\n * @remarks\n * Defines which events produce outputs. Not all events produce outputs - some only\n * cause state transitions. This mapping is used by the state machine framework for\n * type-safe output handling.\n *\n * @category Input State Machine - KMT\n */\nexport type KmtInputEventOutputMapping = {\n spacebarDown: number;\n middlePointerMove: KmtOutputEvent;\n scroll: KmtOutputEvent;\n scrollWithCtrl: KmtOutputEvent;\n leftPointerMove: KmtOutputEvent;\n}\n\n/**\n * @internal\n */\nexport type KmtIdleStatePossibleTargetStates = \"IDLE\" | \"READY_TO_PAN_VIA_SPACEBAR\" | \"READY_TO_PAN_VIA_SCROLL_WHEEL\" | \"DISABLED\";\n\n/**\n * IDLE state - default state waiting for user input.\n *\n * @remarks\n * This is the default state of the KMT input state machine. It handles scroll events\n * for panning and zooming, and transitions to pan-ready states when the user presses\n * spacebar or middle-click.\n *\n * **Responsibilities**:\n * - Process scroll events (pan or zoom depending on device and modifiers)\n * - Detect spacebar press to enter pan mode\n * - Detect middle-click to enter pan mode\n * - Distinguish between mouse and trackpad input modalities\n *\n * **Scroll Behavior**:\n * - Ctrl + Scroll: Always zoom (both mouse and trackpad)\n * - Scroll (no Ctrl): Pan (trackpad) or Zoom (mouse, determined by modality detection)\n *\n * **Input Modality Detection**:\n * The state tracks horizontal scroll deltas to distinguish trackpads (which produce deltaX)\n * from mice (which typically only produce deltaY). This affects zoom behavior.\n *\n * @category Input State Machine - KMT\n */\nexport class KmtIdleState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n\n constructor() {\n super();\n }\n\n protected _guards: Guard<KmtInputContext, \"isIdle\"> = {\n isIdle: () => true,\n }\n\n protected _eventGuards: Partial<EventGuards<KmtInputEventMapping, KmtInputStates, KmtInputContext, Guard<KmtInputContext>>> = {\n }\n\n // Arrow function properties must be defined before _eventReactions to ensure proper initialization order\n scrollPan = (context: KmtInputContext, payload: ScrollEventPayload): KmtOutputEvent => {\n const delta = {...payload}\n if(!context.alignCoordinateSystem){\n delta.deltaY = -delta.deltaY;\n }\n return {\n type: \"pan\",\n delta: {x: delta.deltaX, y: delta.deltaY}\n };\n }\n\n scrollZoom = (context: KmtInputContext, payload: ScrollWithCtrlEventPayload): KmtOutputEvent => {\n let scrollSensitivity = 0.005;\n if(Math.abs(payload.deltaY) > 100){\n scrollSensitivity = 0.0005;\n }\n const zoomAmount = payload.deltaY * scrollSensitivity;\n const cursorPosition = {x: payload.x, y: payload.y};\n const anchorPointInViewPort = convertFromWindow2ViewPortWithCanvasOperator(cursorPosition, context.canvas, {x: context.canvas.width / 2, y: context.canvas.height / 2}, !context.alignCoordinateSystem);\n return {\n type: \"zoom\",\n delta: -(zoomAmount * 5),\n anchorPointInViewPort,\n };\n }\n\n scrollHandler = (context: KmtInputContext, payload: ScrollWithCtrlEventPayload): KmtOutputEvent => {\n if(payload.deltaX === 0 && payload.deltaY !== 0){\n context.addKmtTrackpadTrackScore();\n } else if (payload.deltaX !== 0 && payload.deltaY !== 0){\n context.subtractKmtTrackpadTrackScore();\n }\n if(context.mode === \"kmt\"){\n return this.scrollZoom(context, payload);\n } else {\n return this.scrollPan(context, payload);\n }\n }\n\n scrollWithCtrlHandler = (context: KmtInputContext, payload: ScrollWithCtrlEventPayload): KmtOutputEvent => {\n return this.scrollZoom(context, payload);\n }\n\n protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> = {\n spacebarDown: {\n action: this.spacebarDownHandler,\n defaultTargetState: \"READY_TO_PAN_VIA_SPACEBAR\",\n },\n scroll: {\n action: this.scrollHandler,\n defaultTargetState: \"IDLE\",\n },\n scrollWithCtrl: {\n action: this.scrollWithCtrlHandler,\n defaultTargetState: \"IDLE\",\n },\n middlePointerDown: {\n action: this.middlePointerDownHandler,\n defaultTargetState: \"READY_TO_PAN_VIA_SCROLL_WHEEL\",\n },\n disable: {\n action: NO_OP,\n defaultTargetState: \"DISABLED\",\n },\n }\n\n uponEnter(context: KmtInputContext): void {\n context.canvas.setCursor(CursorStyle.DEFAULT);\n }\n\n spacebarDownHandler(context: KmtInputContext, payload: EmptyPayload): number {\n // context.canvas.setCursor(CursorStyle.GRAB);\n return 1;\n }\n\n middlePointerDownHandler(context: KmtInputContext, payload: PointerEventPayload): void {\n // probably from kmt\n context.addKmtTrackpadTrackScore();\n if(context.mode === \"TBD\") {\n context.setMode(\"kmt\");\n }\n context.setInitialCursorPosition({x: payload.x, y: payload.y});\n }\n\n}\n\nexport class DisabledState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n constructor() {\n super();\n }\n\n uponEnter(context: KmtInputContext): void {\n context.canvas.setCursor(CursorStyle.DEFAULT);\n // context.toggleOnEdgeAutoCameraInput();\n }\n\n beforeExit(context: KmtInputContext): void {\n // context.toggleOffEdgeAutoCameraInput();\n }\n\n protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> = {\n enable: {\n action: NO_OP,\n defaultTargetState: \"IDLE\",\n },\n }\n}\n\n/**\n * @description The ready to pan via space bar state of the keyboard mouse and trackpad input state machine.\n * \n * @category Input State Machine\n */\nexport class ReadyToPanViaSpaceBarState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n\n constructor() {\n super();\n }\n\n protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> = {\n spacebarUp: {\n action: NO_OP,\n defaultTargetState: \"IDLE\",\n },\n leftPointerDown: {\n action: this.leftPointerDownHandler,\n defaultTargetState: \"INITIAL_PAN\",\n },\n disable: {\n action: (context) => context.cancelCurrentAction(),\n defaultTargetState: \"DISABLED\",\n },\n leftPointerMove: {\n action: NO_OP,\n defaultTargetState: \"READY_TO_PAN_VIA_SPACEBAR\",\n }\n }\n\n uponEnter(context: KmtInputContext): void {\n context.canvas.setCursor(CursorStyle.GRAB);\n }\n\n leftPointerDownHandler(context: KmtInputContext, payload: PointerEventPayload): void {\n context.setInitialCursorPosition({x: payload.x, y: payload.y});\n }\n}\n\n/**\n * @description The initial pan state of the keyboard mouse and trackpad input state machine.\n * \n * @category Input State Machine\n */\nexport class InitialPanState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n\n constructor() {\n super();\n }\n\n protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> = {\n leftPointerUp: {\n action: NO_OP,\n defaultTargetState: \"READY_TO_PAN_VIA_SPACEBAR\",\n },\n leftPointerMove: {\n action: this.leftPointerMoveHandler,\n defaultTargetState: \"PAN\",\n },\n spacebarUp: {\n action: () => \"IDLE\",\n defaultTargetState: \"IDLE\",\n },\n leftPointerDown: {\n action: () => \"PAN\",\n defaultTargetState: \"PAN\",\n },\n }\n\n uponEnter(context: KmtInputContext): void {\n context.canvas.setCursor(CursorStyle.GRABBING);\n }\n\n leftPointerMoveHandler(context: KmtInputContext, payload: PointerEventPayload): KmtOutputEvent {\n const delta = {\n x: context.initialCursorPosition.x - payload.x,\n y: context.initialCursorPosition.y - payload.y,\n };\n if(!context.alignCoordinateSystem){\n delta.y = -delta.y;\n }\n context.setInitialCursorPosition({x: payload.x, y: payload.y});\n return {\n type: \"pan\",\n delta: delta\n };\n }\n}\n\n/**\n * @description The ready to pan via scroll wheel state of the keyboard mouse and trackpad input state machine.\n * \n * @category Input State Machine\n */\nexport class ReadyToPanViaScrollWheelState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n\n constructor() {\n super();\n }\n\n protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> = {\n middlePointerUp: {\n action: NO_OP,\n defaultTargetState: \"IDLE\",\n },\n middlePointerMove: {\n action: NO_OP,\n defaultTargetState: \"PAN_VIA_SCROLL_WHEEL\",\n },\n }\n\n uponEnter(context: KmtInputContext): void {\n context.canvas.setCursor(CursorStyle.GRABBING);\n }\n}\n\n/**\n * @description The pan state of the keyboard mouse and trackpad input state machine.\n * \n * @category Input State Machine\n */\nexport class PanState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n\n constructor() {\n super();\n }\n\n protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> = {\n leftPointerUp: {\n action: NO_OP,\n defaultTargetState: \"READY_TO_PAN_VIA_SPACEBAR\",\n },\n leftPointerMove: {\n action: this.leftPointerMoveHandler,\n defaultTargetState: \"PAN\",\n },\n spacebarUp: {\n action: NO_OP, \n defaultTargetState: \"IDLE\",\n },\n }\n\n uponEnter(context: KmtInputContext): void {\n context.canvas.setCursor(CursorStyle.GRABBING);\n }\n\n beforeExit(context: KmtInputContext): void {\n context.canvas.setCursor(CursorStyle.DEFAULT);\n }\n\n leftPointerMoveHandler(context: KmtInputContext, payload: PointerEventPayload): KmtOutputEvent {\n const delta = {\n x: context.initialCursorPosition.x - payload.x,\n y: context.initialCursorPosition.y - payload.y,\n };\n if(!context.alignCoordinateSystem){\n delta.y = -delta.y;\n }\n context.setInitialCursorPosition({x: payload.x, y: payload.y});\n return {\n type: \"pan\",\n delta: delta\n };\n }\n}\n\n/**\n * @description The pan via scroll wheel state of the keyboard mouse and trackpad input state machine.\n * \n * @category Input State Machine\n */\nexport class PanViaScrollWheelState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n\n protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> = {\n middlePointerUp: {\n action: NO_OP,\n defaultTargetState: \"IDLE\",\n },\n middlePointerMove: {\n action: this.middlePointerMoveHandler,\n defaultTargetState: \"PAN_VIA_SCROLL_WHEEL\",\n },\n }\n\n middlePointerMoveHandler(context: KmtInputContext, payload: PointerEventPayload): KmtOutputEvent {\n const delta = {\n x: context.initialCursorPosition.x - payload.x,\n y: context.initialCursorPosition.y - payload.y,\n };\n if(!context.alignCoordinateSystem){\n delta.y = -delta.y;\n }\n context.setInitialCursorPosition({x: payload.x, y: payload.y});\n return {\n type: \"pan\",\n delta: delta,\n };\n }\n\n uponEnter(context: KmtInputContext): void {\n context.canvas.setCursor(CursorStyle.GRABBING);\n }\n}\n\nexport class KmtEmptyState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n\n constructor() {\n super();\n }\n\n}\n\n/**\n * Type alias for the KMT input state machine.\n *\n * @category Input State Machine - KMT\n */\nexport type KmtInputStateMachine = TemplateStateMachine<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping>;\n\n/**\n * Creates a new KMT (Keyboard/Mouse/Trackpad) input state machine.\n *\n * @param context - The context providing state and canvas access for the state machine\n * @returns A configured state machine ready to process KMT input events\n *\n * @remarks\n * This factory function creates a fully configured state machine with all KMT gesture\n * recognition states. The state machine processes raw input events and produces\n * high-level gesture outputs (pan, zoom, rotate).\n *\n * **State Flow**:\n * ```\n * IDLE → (spacebar) → READY_TO_PAN_VIA_SPACEBAR → (click) → INITIAL_PAN → PAN\n * IDLE → (middle-click) → READY_TO_PAN_VIA_SCROLL_WHEEL → PAN_VIA_SCROLL_WHEEL\n * IDLE → (scroll) → [produces pan or zoom output, stays in IDLE]\n * ```\n *\n * **Gesture Recognition**:\n * - **Pan via spacebar**: Spacebar + left-click drag\n * - **Pan via middle-click**: Middle-click drag\n * - **Zoom**: Ctrl + scroll (mouse) or scroll (trackpad, auto-detected)\n * - **Pan via scroll**: Scroll (trackpad) or scroll without Ctrl (varies by device)\n *\n * @category Input State Machine - KMT\n *\n * @example\n * ```typescript\n * const canvasProxy = new CanvasProxy(canvasElement);\n * const context = new ObservableInputTracker(canvasProxy);\n * const stateMachine = createKmtInputStateMachine(context);\n *\n * // Process an event\n * const result = stateMachine.happens(\"scroll\", {\n * deltaX: 0,\n * deltaY: 10,\n * x: 500,\n * y: 300\n * });\n *\n * // Check for output\n * if (result.output) {\n * console.log(\"Gesture recognized:\", result.output.type);\n * }\n * ```\n */\nexport function createKmtInputStateMachine(context: KmtInputContext): KmtInputStateMachine {\n const states = {\n IDLE: new KmtIdleState(),\n READY_TO_PAN_VIA_SPACEBAR: new ReadyToPanViaSpaceBarState(),\n INITIAL_PAN: new InitialPanState(),\n PAN: new PanState(),\n READY_TO_PAN_VIA_SCROLL_WHEEL: new ReadyToPanViaScrollWheelState(),\n PAN_VIA_SCROLL_WHEEL: new PanViaScrollWheelState(),\n DISABLED: new DisabledState(),\n }\n return new TemplateStateMachine<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping>(states, \"IDLE\", context);\n}\n\nexport function createKmtInputStateMachineWithCanvas(canvas: Canvas): KmtInputStateMachine {\n const context = new ObservableInputTracker(canvas);\n\n return createKmtInputStateMachine(context);\n}\n\nexport class KmtInputStateMachineWebWorkerProxy extends TemplateStateMachine<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {\n\n private _webworker: Worker;\n\n constructor(webworker: Worker){\n super({\n \"IDLE\": new KmtEmptyState(),\n \"READY_TO_PAN_VIA_SPACEBAR\": new KmtEmptyState(),\n \"INITIAL_PAN\": new KmtEmptyState(),\n \"PAN\": new KmtEmptyState(),\n \"READY_TO_PAN_VIA_SCROLL_WHEEL\": new KmtEmptyState(),\n \"PAN_VIA_SCROLL_WHEEL\": new KmtEmptyState(),\n \"DISABLED\": new DisabledState(),\n }, \"IDLE\", new DummyKmtInputContext());\n this._webworker = webworker;\n }\n\n happens(...args: EventArgs<KmtInputEventMapping, keyof KmtInputEventMapping | string>): EventResult<KmtInputStates> { \n this._webworker.postMessage({\n type: \"kmtInputStateMachine\",\n event: args[0],\n payload: args[1],\n });\n return {handled: true, nextState: \"IDLE\"};\n }\n}\n",
41
+ "import type {Point} from \"@ue-too/math\";\nimport {KmtOutputEvent} from \"./input-state-machine/kmt-input-state-machine\";\nimport {TouchOutputEvent} from \"./input-state-machine/touch-input-state-machine\";\nimport {UserInputPublisher} from \"./raw-input-publisher/raw-input-publisher\";\nimport {CameraMux, CameraMuxPanOutput, CameraMuxZoomOutput, CameraMuxRotationOutput} from \"../camera/camera-mux\";\nimport {CameraRig} from \"../camera/camera-rig\";\n\n/**\n * Union type of all output events from state machines.\n *\n * @remarks\n * This type represents the unified output from both KMT (Keyboard/Mouse/Trackpad) and Touch state machines.\n * By unifying these outputs, the orchestrator can handle events from different input modalities uniformly.\n *\n * @category Input Interpretation\n */\nexport type OutputEvent = KmtOutputEvent | TouchOutputEvent;\n\n/**\n * Central orchestrator that coordinates input interpretation and camera control for the infinite canvas.\n *\n * @remarks\n * The InputOrchestrator serves as the mediator between input state machines and camera control systems.\n * It implements a permission-based architecture where:\n *\n * 1. **Event Flow**: State machines produce high-level gesture events (pan, zoom, rotate)\n * 2. **Permission Check**: Events are sent to CameraMux for permission validation\n * 3. **Execution**: If allowed, gestures are executed on CameraRig\n * 4. **Broadcasting**: Raw events are simultaneously broadcast to observers via UserInputPublisher\n *\n * **Architecture Pattern**:\n * ```\n * State Machines → Orchestrator → CameraMux (permission) → CameraRig (execution)\n * ↓\n * UserInputPublisher (observers)\n * ```\n *\n * This design decouples state machines from camera control, allowing state machines to focus solely\n * on gesture recognition while the orchestrator handles the complexities of camera coordination,\n * permission management, and event distribution.\n *\n * **Key Benefits**:\n * - Single point of control for all camera operations\n * - State machines remain unaware of camera implementation\n * - Parallel path for observers to react to raw input events\n * - Consistent handling of KMT and Touch input modalities\n *\n * @category Input Interpretation\n *\n * @example\n * ```typescript\n * // Create the orchestrator\n * const cameraMux = new CameraMux();\n * const cameraRig = new CameraRig(camera, viewport);\n * const publisher = new RawUserInputPublisher();\n * const orchestrator = new InputOrchestrator(cameraMux, cameraRig, publisher);\n *\n * // State machines send their output to the orchestrator\n * const kmtStateMachine = createKmtInputStateMachine(kmtContext);\n * const result = kmtStateMachine.happens(\"leftPointerMove\", {x: 100, y: 200});\n * orchestrator.processInputEventOutput(result.output);\n *\n * // Observers can subscribe to raw input events\n * publisher.on(\"pan\", (event) => {\n * console.log(\"Pan gesture detected:\", event.diff);\n * });\n * ```\n */\nexport class InputOrchestrator {\n private _cameraMux: CameraMux;\n private _cameraRig: CameraRig;\n private _publisher?: UserInputPublisher;\n\n /**\n * Creates a new InputOrchestrator instance.\n *\n * @param cameraMux - The camera multiplexer that validates and controls camera operation permissions\n * @param cameraRig - The camera rig that executes camera transformations\n * @param publisher - Optional publisher for broadcasting raw input events to observers\n *\n * @remarks\n * The publisher parameter is optional to support scenarios where event broadcasting is not needed.\n * When provided, all input events are broadcast in parallel to camera control execution.\n */\n constructor(cameraMux: CameraMux, cameraRig: CameraRig, publisher?: UserInputPublisher) {\n this._cameraMux = cameraMux;\n this._cameraRig = cameraRig;\n this._publisher = publisher;\n }\n\n /**\n * Processes output events from state machines and routes them to camera control and observers.\n *\n * @param output - The output from a state machine, can be a single event, array of events, or any value\n *\n * @remarks\n * This method serves as the main entry point for state machine outputs. It:\n * 1. Validates whether the output is a valid OutputEvent\n * 2. Handles both single events and arrays of events\n * 3. Routes each valid event through the camera control pipeline\n * 4. Broadcasts events to observers via the publisher\n *\n * Called by event parsers after the state machine processes an input and produces output.\n * The method uses type guards to ensure type safety when handling dynamic output types.\n *\n * @example\n * ```typescript\n * const result = stateMachine.happens(\"scroll\", {deltaX: 0, deltaY: 10, x: 100, y: 200});\n * orchestrator.processInputEventOutput(result.output);\n * ```\n */\n public processInputEventOutput(output: any): void {\n // Handle different output types\n if (this.isOutputEvent(output)) {\n this.handleStateMachineOutput(output);\n } else if (Array.isArray(output)) {\n // Handle multiple outputs\n output.forEach(item => {\n if (this.isOutputEvent(item)) {\n this.handleStateMachineOutput(item);\n }\n });\n }\n }\n\n public processInputEvent(input: OutputEvent): void {\n this.handleStateMachineOutput(input);\n }\n\n /**\n * Type guard to check if an output value is a valid OutputEvent.\n *\n * @param output - The value to check\n * @returns True if the output is a valid OutputEvent with a type property\n *\n * @remarks\n * This type guard ensures type safety when processing state machine outputs.\n * It checks for the presence of a 'type' property which is common to all OutputEvent variants.\n */\n private isOutputEvent(output: any): output is OutputEvent {\n return output && typeof output === 'object' && 'type' in output;\n }\n\n /**\n * Handles individual output events from state machines by routing to camera control and observers.\n *\n * @param event - The output event from a state machine (pan, zoom, rotate, cursor, or none)\n *\n * @remarks\n * This method implements a dual-path architecture:\n *\n * **Parallel Path 1 - Observer Notification**:\n * - Immediately broadcasts the event to all subscribers via UserInputPublisher\n * - This allows external systems to react to user input in real-time\n * - Independent of camera permission/execution\n *\n * **Parallel Path 2 - Camera Control**:\n * - Requests permission from CameraMux for the operation\n * - CameraMux may modify the event (e.g., clamp values, deny operation)\n * - If permitted, executes the transformation on CameraRig\n *\n * Event types:\n * - **pan**: Translates the camera viewport\n * - **zoom**: Scales the camera around an anchor point\n * - **rotate**: Rotates the camera view\n * - **cursor**: Changes cursor appearance (handled by state machine)\n * - **none**: No operation needed\n */\n private handleStateMachineOutput(event: OutputEvent): void {\n switch (event.type) {\n case \"pan\":\n // Publish to observers (parallel path)\n this._publisher?.notifyPan(event.delta);\n // Ask CameraMux for permission and process its output\n const panOutput = this._cameraMux.notifyPanInput(event.delta);\n this.processPanMuxOutput(panOutput);\n break;\n case \"zoom\":\n // Publish to observers (parallel path)\n this._publisher?.notifyZoom(event.delta, event.anchorPointInViewPort);\n // Ask CameraMux for permission and process its output\n const zoomOutput = this._cameraMux.notifyZoomInput(event.delta, event.anchorPointInViewPort);\n this.processZoomMuxOutput(zoomOutput);\n break;\n case \"rotate\":\n // Publish to observers (parallel path)\n this._publisher?.notifyRotate(event.deltaRotation);\n // Ask CameraMux for permission and process its output\n const rotateOutput = this._cameraMux.notifyRotationInput(event.deltaRotation);\n this.processRotateMuxOutput(rotateOutput);\n break;\n case \"cursor\":\n // Cursor changes are handled by the state machine's uponEnter/beforeExit methods\n // This case is here for future extension\n break;\n case \"none\":\n // No action needed\n break;\n }\n }\n\n /**\n * Processes pan output from CameraMux and executes the pan operation if permitted.\n *\n * @param output - The pan output from CameraMux containing permission and potentially modified delta\n *\n * @remarks\n * CameraMux may deny the operation (allowPassThrough = false) or modify the delta value\n * to enforce constraints like viewport bounds or animation states.\n * Only when permission is granted does the pan execute on CameraRig.\n */\n private processPanMuxOutput(output: CameraMuxPanOutput): void {\n if (output.allowPassThrough) {\n this._cameraRig.panByViewPort(output.delta);\n }\n }\n\n /**\n * Processes zoom output from CameraMux and executes the zoom operation if permitted.\n *\n * @param output - The zoom output from CameraMux containing permission and potentially modified parameters\n *\n * @remarks\n * CameraMux may deny the operation or modify zoom parameters to enforce constraints\n * like minimum/maximum zoom levels or animation states. The anchor point determines\n * the center of the zoom transformation in viewport coordinates.\n */\n private processZoomMuxOutput(output: CameraMuxZoomOutput): void {\n if (output.allowPassThrough) {\n this._cameraRig.zoomByAt(output.delta, output.anchorPoint);\n }\n }\n\n /**\n * Processes rotation output from CameraMux and executes the rotation operation if permitted.\n *\n * @param output - The rotation output from CameraMux containing permission and potentially modified delta\n *\n * @remarks\n * CameraMux may deny the operation or modify the rotation delta to enforce constraints\n * like rotation limits or animation states.\n */\n private processRotateMuxOutput(output: CameraMuxRotationOutput): void {\n if (output.allowPassThrough) {\n this._cameraRig.rotateBy(output.delta);\n }\n }\n\n /**\n * Gets the UserInputPublisher for direct access to event subscription.\n *\n * @returns The publisher instance, or undefined if not configured\n *\n * @remarks\n * Allows external code to subscribe to raw input events without going through the orchestrator.\n */\n get publisher(): UserInputPublisher | undefined {\n return this._publisher;\n }\n\n /**\n * Gets the CameraMux instance for direct access to permission control.\n *\n * @returns The camera multiplexer instance\n */\n get cameraMux(): CameraMux {\n return this._cameraMux;\n }\n\n /**\n * Sets a new CameraMux instance.\n *\n * @param cameraMux - The new camera multiplexer to use for permission control\n *\n * @remarks\n * Allows dynamic reconfiguration of camera permission logic at runtime.\n */\n set cameraMux(cameraMux: CameraMux){\n this._cameraMux = cameraMux;\n }\n}\n",
42
42
  "import { CameraMux } from \"./camera-mux\";\nimport { PointCal } from \"@ue-too/math\";\n\n/**\n * Automatic camera panning triggered by cursor proximity to viewport edges.\n * Commonly used in strategy games, map editors, and design tools.\n *\n * @remarks\n * This class implements edge-scrolling behavior where the camera automatically\n * pans when the cursor approaches the viewport edges. The panning speed is\n * constant and direction-based (no acceleration).\n *\n * The camera moves in viewport space, meaning the speed is consistent regardless\n * of zoom level. The actual world-space movement will vary with zoom.\n *\n * @example\n * ```typescript\n * const cameraMux = new CameraMux(camera);\n * const edgeScroll = new EdgeAutoCameraInput(cameraMux);\n *\n * // Track mouse position relative to viewport edges\n * canvas.addEventListener('mousemove', (e) => {\n * const rect = canvas.getBoundingClientRect();\n * const edgeMargin = 50; // pixels from edge\n *\n * let horizontal: 'left' | 'right' | 'none' = 'none';\n * let vertical: 'up' | 'down' | 'none' = 'none';\n *\n * if (e.clientX - rect.left < edgeMargin) horizontal = 'left';\n * if (rect.right - e.clientX < edgeMargin) horizontal = 'right';\n * if (e.clientY - rect.top < edgeMargin) vertical = 'up';\n * if (rect.bottom - e.clientY < edgeMargin) vertical = 'down';\n *\n * edgeScroll.setDirection(horizontal, vertical);\n * edgeScroll.toggleOn();\n * });\n *\n * // Stop scrolling when mouse leaves\n * canvas.addEventListener('mouseleave', () => {\n * edgeScroll.toggleOff();\n * });\n *\n * // Update in render loop\n * function render(deltaTime: number) {\n * edgeScroll.update(deltaTime / 1000); // convert ms to seconds\n * }\n * ```\n *\n * @category Camera\n * @see {@link CameraMux} for the camera input multiplexer this feeds into\n */\nexport class EdgeAutoCameraInput {\n\n private _cameraMux: CameraMux;\n private _state: 'idle' | 'moving' = 'idle';\n private _speed: number = 100; // pixels per second in viewport space\n\n private _horizontalDirection: 'left' | 'right' | 'none' = 'none';\n private _verticalDirection: 'up' | 'down' | 'none' = 'none';\n\n /**\n * Creates a new edge auto-scroll input controller.\n *\n * @param cameraMux - The camera multiplexer to send pan inputs to\n */\n constructor(cameraMux: CameraMux) {\n this._cameraMux = cameraMux;\n }\n\n /**\n * Disables edge scrolling.\n * The camera will stop panning even if direction is set.\n */\n toggleOff(){\n this._state = 'idle';\n }\n\n /**\n * Enables edge scrolling.\n * The camera will pan according to the current direction setting.\n */\n toggleOn(){\n this._state = 'moving';\n }\n\n /**\n * Sets the scrolling direction based on cursor position relative to edges.\n *\n * @param horizontalDirection - Horizontal scroll direction ('left', 'right', or 'none')\n * @param verticalDirection - Vertical scroll direction ('up', 'down', or 'none')\n *\n * @remarks\n * Directions can be combined for diagonal scrolling.\n * Set both to 'none' to stop scrolling without disabling via {@link toggleOff}.\n *\n * @example\n * ```typescript\n * edgeScroll.setDirection('left', 'none'); // Scroll left only\n * edgeScroll.setDirection('right', 'up'); // Scroll diagonally up-right\n * edgeScroll.setDirection('none', 'none'); // Stop scrolling\n * ```\n */\n setDirection(horizontalDirection: 'left' | 'right' | 'none', verticalDirection: 'up' | 'down' | 'none'): void {\n this._horizontalDirection = horizontalDirection;\n this._verticalDirection = verticalDirection;\n }\n\n /**\n * Updates the camera position based on elapsed time and current direction.\n * Call this in your render loop or update tick.\n *\n * @param deltaTime - Time elapsed since last update in seconds\n *\n * @remarks\n * The camera pans at a constant speed of 100 pixels/second in viewport space.\n * This is independent of zoom level - world-space movement varies with zoom.\n *\n * If the state is 'idle', this method does nothing.\n *\n * @example\n * ```typescript\n * // In animation frame callback\n * let lastTime = performance.now();\n *\n * function animate(currentTime: number) {\n * const deltaTime = (currentTime - lastTime) / 1000; // Convert to seconds\n * lastTime = currentTime;\n *\n * edgeScroll.update(deltaTime);\n *\n * requestAnimationFrame(animate);\n * }\n * ```\n */\n update(deltaTime: number){\n\n if(this._state === 'idle') {\n return;\n }\n\n const direction = {\n x: this._horizontalDirection === 'left' ? -1 : this._horizontalDirection === 'right' ? 1 : 0,\n y: this._verticalDirection === 'up' ? -1 : this._verticalDirection === 'down' ? 1 : 0\n };\n\n const distance = this._speed * deltaTime;\n\n const deltaVector = PointCal.multiplyVectorByScalar(direction, distance);\n\n this._cameraMux.notifyPanInput(deltaVector);\n }\n}\n"
43
43
  ],
44
- "mappings": "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",
45
- "debugId": "5A3EA2BE7215FE0464756E2164756E21",
44
+ "mappings": "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",
45
+ "debugId": "AA86369A6BE56E1864756E2164756E21",
46
46
  "names": []
47
47
  }