@blorkfield/overlay-core 0.3.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/README.md +417 -0
- package/dist/index.cjs +2519 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +700 -0
- package/dist/index.d.ts +700 -0
- package/dist/index.js +2474 -0
- package/dist/index.js.map +1 -0
- package/package.json +35 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/OverlayScene.ts","../src/engine.ts","../src/bodies.ts","../src/logger.ts","../src/imageClip.ts","../src/fontLoader.ts","../src/entity.ts","../src/EffectManager.ts"],"sourcesContent":["import Matter from 'matter-js';\nimport { createEngine, createRender } from './engine';\nimport { createBoundaries, createBoundariesWithFloorConfig, createEntity, createEntityAsync, createObstacle, createObstacleAsync, createBoxObstacleWithInfo, getImageDimensions } from './bodies';\nimport { tintImage } from './imageClip';\nimport { loadFont, getGlyphData, getKerning, type LoadedFont } from './fontLoader';\nimport { logger } from './logger';\nimport { applyMouseForce, wrapHorizontal } from './entity';\nimport { EffectManager } from './EffectManager';\nimport type {\n OverlaySceneConfig,\n ObjectConfig,\n UpdateCallback,\n UpdateCallbackData,\n DynamicObject,\n ContainerOptions,\n Bounds,\n EffectConfig,\n DespawnEffectConfig,\n TextObstacleConfig,\n TextObstacleResult,\n TTFTextObstacleConfig,\n FontInfo,\n FontManifest,\n LetterDebugInfo\n} from './types';\n\ninterface TTFGlyphRenderInfo {\n char: string;\n fontSize: number;\n fontFamily: string;\n fillColor: string;\n // Offset from body center to baseline position for fillText\n offsetX: number;\n offsetY: number;\n}\n\n/**\n * Internal representation of a scene object.\n * Behavior is determined by tags:\n * - 'falling': Object is dynamic (not static), affected by gravity\n * - 'follow': Object follows mouse when grounded\n * - 'grabable': Object can be dragged via mouse constraint\n */\ninterface ObjectEntry {\n id: string;\n body: Matter.Body;\n tags: string[];\n spawnTime: number;\n ttl?: number;\n despawnEffect?: DespawnEffectConfig;\n /** TTF glyph rendering info (for text objects) */\n ttfGlyph?: TTFGlyphRenderInfo;\n /** Pressure threshold - when reached, this obstacle becomes dynamic */\n pressureThreshold?: number;\n /** If set, collapse all letters with this word tag together when word total reaches threshold */\n wordCollapseTag?: string;\n /** Weight for pressure calculation (default: 1). Higher weight = more pressure contribution */\n weight: number;\n /** Shadow config - when set, a washed-out static copy is left behind on collapse */\n shadow?: { opacity: number };\n /** Original position (for shadow placement) */\n originalPosition?: { x: number; y: number };\n /** Image URL (for shadow rendering) */\n imageUrl?: string;\n /** Image size (for shadow rendering) */\n imageSize?: number;\n /** Clicks remaining before this obstacle collapses (undefined = no click behavior) */\n clicksRemaining?: number;\n}\n\nexport class OverlayScene {\n private engine: Matter.Engine;\n private render: Matter.Render;\n private runner: Matter.Runner;\n private canvas: HTMLCanvasElement;\n /** All scene objects (unified - no more entity/obstacle distinction) */\n private objects: Map<string, ObjectEntry> = new Map();\n private boundaries: Matter.Body[] = [];\n private updateCallbacks: UpdateCallback[] = [];\n private mouseX: number = 0;\n private config: OverlaySceneConfig;\n private animationFrameId: number | null = null;\n private mouse: Matter.Mouse | null = null;\n private mouseConstraint: Matter.MouseConstraint | null = null;\n private effectManager: EffectManager;\n private fonts: FontInfo[] = [];\n private fontsInitialized: boolean = false;\n private letterDebugInfo: Map<string, LetterDebugInfo[]> = new Map(); // wordTag -> debug info\n\n // Pressure tracking: maps obstacle ID -> Set of dynamic object IDs resting on it\n private obstaclePressure: Map<string, Set<string>> = new Map();\n private previousPressure: Map<string, number> = new Map();\n private pressureLogTimer: number = 0;\n // Floor segment tracking\n private floorSegments: Matter.Body[] = [];\n private floorSegmentPressure: Map<number, Set<string>> = new Map(); // segment index -> object IDs\n private collapsedSegments: Set<number> = new Set();\n\n static createContainer(\n parent: HTMLElement,\n options: ContainerOptions = {}\n ): { canvas: HTMLCanvasElement; bounds: Bounds } {\n const canvas = document.createElement('canvas');\n\n let width: number;\n let height: number;\n\n if (options.fullscreen !== false && !options.width && !options.height) {\n // Default: fullscreen (fill parent)\n width = parent.clientWidth;\n height = parent.clientHeight;\n canvas.style.width = '100%';\n canvas.style.height = '100%';\n } else {\n // Fixed size\n width = options.width ?? 800;\n height = options.height ?? 600;\n }\n\n canvas.width = width;\n canvas.height = height;\n parent.appendChild(canvas);\n\n return {\n canvas,\n bounds: { top: 0, bottom: height, left: 0, right: width }\n };\n }\n\n constructor(canvas: HTMLCanvasElement, config: OverlaySceneConfig) {\n this.canvas = canvas;\n this.config = {\n gravity: 1,\n wrapHorizontal: true,\n debug: false,\n background: 'transparent',\n ...config\n };\n this.engine = createEngine(this.config.gravity!);\n this.render = createRender(this.engine, canvas, this.config);\n this.runner = Matter.Runner.create();\n\n // Create boundaries with optional floor segments\n const boundariesResult = createBoundariesWithFloorConfig(this.config.bounds, this.config.floorConfig);\n this.boundaries = [...boundariesResult.walls, ...boundariesResult.floorSegments];\n this.floorSegments = boundariesResult.floorSegments;\n Matter.Composite.add(this.engine.world, this.boundaries);\n\n // Setup mouse interaction\n this.mouse = Matter.Mouse.create(canvas);\n this.mouseConstraint = Matter.MouseConstraint.create(this.engine, {\n mouse: this.mouse,\n constraint: {\n stiffness: 0.2,\n render: { visible: false }\n }\n });\n Matter.Composite.add(this.engine.world, this.mouseConstraint);\n\n // Filter grabbing based on 'grabable' tag\n Matter.Events.on(this.mouseConstraint, 'startdrag', this.handleStartDrag);\n\n // Handle clicks for click-to-fall behavior\n canvas.addEventListener('click', this.handleCanvasClick);\n\n // Keep render in sync with mouse for pixel ratio\n this.render.mouse = this.mouse;\n\n // Setup effect manager - uses async spawning for image clipping support\n this.effectManager = new EffectManager(\n this.config.bounds,\n (cfg) => this.spawnObjectAsync(cfg),\n (id) => this.objects.get(id)?.body ?? null\n );\n }\n\n /** Filter drag events - only allow grabbing objects with 'grabable' tag */\n private handleStartDrag = (event: Matter.IEvent<Matter.MouseConstraint> & { body?: Matter.Body }): void => {\n const body = event.body;\n if (!body) return;\n\n // Find the object entry for this body\n const entry = this.findObjectByBody(body);\n\n // If object doesn't have 'grabable' tag, release the constraint immediately\n if (!entry || !entry.tags.includes('grabable')) {\n if (this.mouseConstraint) {\n this.mouseConstraint.constraint.bodyB = null;\n }\n }\n };\n\n /** Handle canvas clicks for click-to-fall behavior */\n private handleCanvasClick = (event: MouseEvent): void => {\n const rect = this.canvas.getBoundingClientRect();\n const x = event.clientX - rect.left;\n const y = event.clientY - rect.top;\n\n // Find all bodies at the click position\n const bodies = Matter.Query.point(\n Matter.Composite.allBodies(this.engine.world),\n { x, y }\n );\n\n // Process each clicked body\n for (const body of bodies) {\n const entry = this.findObjectByBody(body);\n if (!entry) continue;\n\n // Skip if already falling or no click behavior\n if (entry.tags.includes('falling')) continue;\n if (entry.clicksRemaining === undefined) continue;\n\n // Decrement clicks remaining\n entry.clicksRemaining--;\n\n const name = this.getObstacleDisplayName(entry);\n logger.debug('OverlayScene', `Click on ${name}: ${entry.clicksRemaining} clicks remaining`);\n\n // Collapse if no clicks remaining\n if (entry.clicksRemaining <= 0) {\n this.collapseObstacle(entry);\n }\n }\n };\n\n /** Get a display name for an obstacle (letter char or short ID) */\n private getObstacleDisplayName(entry: ObjectEntry): string {\n const letterTag = entry.tags.find(t => t.startsWith('letter-') && !t.startsWith('letter-index-'));\n if (letterTag) return letterTag.replace('letter-', '');\n if (entry.ttfGlyph) return entry.ttfGlyph.char;\n return entry.id.slice(0, 4);\n }\n\n /** Update pressure tracking - check which dynamic objects rest on static obstacles */\n private updatePressure(): void {\n // Collect static obstacles and dynamic objects\n const obstacles: ObjectEntry[] = [];\n const dynamics: ObjectEntry[] = [];\n\n for (const entry of this.objects.values()) {\n if (entry.tags.includes('falling')) {\n // Only count resting objects (low velocity)\n if (Math.abs(entry.body.velocity.y) < 2) {\n dynamics.push(entry);\n }\n } else {\n obstacles.push(entry);\n }\n }\n\n // Build new pressure map\n const newPressure: Map<string, Set<string>> = new Map();\n\n for (const obstacle of obstacles) {\n const resting = new Set<string>();\n const obsBounds = obstacle.body.bounds;\n\n for (const dyn of dynamics) {\n const dynBounds = dyn.body.bounds;\n\n // Check if dynamic object is resting on/in obstacle:\n // Dynamic's bottom is within the obstacle's vertical range (with some tolerance above)\n // AND horizontal positions overlap\n const tolerance = 10; // pixels above obstacle top\n const dynBottom = dynBounds.max.y;\n const obsTop = obsBounds.min.y;\n const obsBottom = obsBounds.max.y;\n\n // Dynamic is \"on\" obstacle if its bottom is between (slightly above top) and bottom\n const verticallyOn = dynBottom >= obsTop - tolerance && dynBottom <= obsBottom;\n\n const horizontalOverlap =\n dynBounds.max.x > obsBounds.min.x &&\n dynBounds.min.x < obsBounds.max.x;\n\n if (verticallyOn && horizontalOverlap) {\n resting.add(dyn.id);\n }\n }\n\n if (resting.size > 0) {\n newPressure.set(obstacle.id, resting);\n }\n }\n\n // Check thresholds and collapse obstacles that exceed them\n this.checkPressureThresholds(obstacles, newPressure);\n\n // Track floor segment pressure\n this.updateFloorSegmentPressure(dynamics);\n\n // Check floor segment thresholds\n this.checkFloorSegmentThresholds();\n\n // Update stored state\n this.obstaclePressure = newPressure;\n this.previousPressure.clear();\n for (const [id, set] of newPressure) {\n this.previousPressure.set(id, set.size);\n }\n\n // Timed summary log every ~2 seconds (120 frames at 60fps)\n this.pressureLogTimer++;\n if (this.pressureLogTimer >= 120) {\n this.pressureLogTimer = 0;\n this.logPressureSummary();\n }\n }\n\n /** Update pressure tracking for each floor segment */\n private updateFloorSegmentPressure(dynamics: ObjectEntry[]): void {\n // Clear and rebuild segment pressure\n this.floorSegmentPressure.clear();\n\n // Build set of object IDs that are resting on obstacles (letters)\n // These should NOT count toward floor pressure\n const onObstacles = new Set<string>();\n for (const objectIds of this.obstaclePressure.values()) {\n for (const id of objectIds) {\n onObstacles.add(id);\n }\n }\n\n for (let i = 0; i < this.floorSegments.length; i++) {\n if (this.collapsedSegments.has(i)) continue;\n\n const segment = this.floorSegments[i];\n const segmentBounds = segment.bounds;\n const resting = new Set<string>();\n\n for (const dyn of dynamics) {\n // Skip objects resting on obstacles - they don't contribute to floor pressure\n if (onObstacles.has(dyn.id)) continue;\n\n const dynBounds = dyn.body.bounds;\n\n // Count resting objects in this segment's horizontal column\n const horizontalOverlap =\n dynBounds.max.x > segmentBounds.min.x &&\n dynBounds.min.x < segmentBounds.max.x;\n\n if (horizontalOverlap) {\n resting.add(dyn.id);\n }\n }\n\n if (resting.size > 0) {\n this.floorSegmentPressure.set(i, resting);\n }\n }\n }\n\n /** Check floor segment thresholds and collapse segments that exceed them */\n private checkFloorSegmentThresholds(): void {\n const floorConfig = this.config.floorConfig;\n // Support legacy floorThreshold for backward compatibility\n const legacyThreshold = this.config.floorThreshold;\n\n if (!floorConfig?.threshold && legacyThreshold === undefined) return;\n\n for (let i = 0; i < this.floorSegments.length; i++) {\n if (this.collapsedSegments.has(i)) continue;\n\n // Determine threshold for this segment\n let threshold: number | undefined;\n if (floorConfig?.threshold !== undefined) {\n threshold = Array.isArray(floorConfig.threshold)\n ? floorConfig.threshold[i]\n : floorConfig.threshold;\n } else if (legacyThreshold !== undefined) {\n threshold = legacyThreshold;\n }\n\n if (threshold === undefined) continue;\n\n const objectIds = this.floorSegmentPressure.get(i);\n const pressure = objectIds ? this.calculateWeightedPressure(objectIds) : 0;\n\n if (pressure >= threshold) {\n this.collapseFloorSegment(i, pressure, threshold);\n }\n }\n }\n\n /** Collapse a single floor segment */\n private collapseFloorSegment(index: number, pressure: number, threshold: number): void {\n if (this.collapsedSegments.has(index)) return;\n this.collapsedSegments.add(index);\n\n const segment = this.floorSegments[index];\n Matter.Composite.remove(this.engine.world, segment);\n console.log(`[Pressure] Floor segment ${index} collapsed! (pressure: ${pressure} >= ${threshold})`);\n }\n\n /** Log a summary of pressure on all obstacles, grouped by word */\n private logPressureSummary(): void {\n if (this.obstaclePressure.size === 0 && this.floorSegmentPressure.size === 0 && this.collapsedSegments.size === 0) return;\n\n // Group by word tag\n const wordPressure: Map<string, string[]> = new Map();\n\n for (const [obstacleId, objectIds] of this.obstaclePressure) {\n const entry = this.objects.get(obstacleId);\n if (!entry) continue;\n\n // Find word tag\n const wordTag = entry.tags.find(t => t.includes('-word-'));\n const groupKey = wordTag ?? 'other';\n\n // Get letter display name and weighted pressure\n const letter = this.getObstacleDisplayName(entry);\n const pressure = this.calculateWeightedPressure(objectIds);\n\n if (!wordPressure.has(groupKey)) {\n wordPressure.set(groupKey, []);\n }\n wordPressure.get(groupKey)!.push(`${letter}:${pressure}`);\n }\n\n // Format output\n const parts: string[] = [];\n for (const [wordTag, letters] of wordPressure) {\n // Extract word index from tag like \"str-abc123-word-0\"\n const match = wordTag.match(/-word-(\\d+)$/);\n const wordLabel = match ? `w${match[1]}` : wordTag.slice(0, 8);\n parts.push(`[${wordLabel}: ${letters.join(' ')}]`);\n }\n\n // Add floor segment pressure if any\n if (this.floorSegmentPressure.size > 0 || this.collapsedSegments.size > 0) {\n const floorConfig = this.config.floorConfig;\n const legacyThreshold = this.config.floorThreshold;\n\n // Get threshold for display\n let thresholdDisplay: number | string = '∞';\n if (floorConfig?.threshold !== undefined && !Array.isArray(floorConfig.threshold)) {\n thresholdDisplay = floorConfig.threshold;\n } else if (legacyThreshold !== undefined) {\n thresholdDisplay = legacyThreshold;\n }\n\n // Build segment pressure display: \"seg0=42 seg1=X seg2=55\"\n const segmentParts: string[] = [];\n for (let i = 0; i < this.floorSegments.length; i++) {\n if (this.collapsedSegments.has(i)) {\n segmentParts.push(`s${i}=X`);\n continue;\n }\n\n const objectIds = this.floorSegmentPressure.get(i);\n const pressure = objectIds ? this.calculateWeightedPressure(objectIds) : 0;\n if (pressure > 0) {\n segmentParts.push(`s${i}=${pressure}`);\n }\n }\n\n if (segmentParts.length > 0) {\n parts.push(`[floor(t=${thresholdDisplay}): ${segmentParts.join(' ')}]`);\n }\n }\n\n if (parts.length > 0) {\n console.log('[Pressure]', parts.join(' '));\n }\n }\n\n /** Calculate weighted pressure from a set of object IDs */\n private calculateWeightedPressure(objectIds: Set<string>): number {\n let total = 0;\n for (const id of objectIds) {\n const entry = this.objects.get(id);\n if (entry) {\n total += entry.weight;\n }\n }\n return total;\n }\n\n /** Check pressure thresholds and collapse obstacles that exceed them */\n private checkPressureThresholds(obstacles: ObjectEntry[], pressure: Map<string, Set<string>>): void {\n // Track word-level pressure for wordCollapse mode\n const wordPressure: Map<string, number> = new Map();\n const wordObstacles: Map<string, ObjectEntry[]> = new Map();\n\n // First pass: calculate per-letter and word-level pressure (using weights)\n for (const obstacle of obstacles) {\n if (obstacle.pressureThreshold === undefined) continue;\n\n const objectsOnObstacle = pressure.get(obstacle.id);\n const obstaclePressure = objectsOnObstacle ? this.calculateWeightedPressure(objectsOnObstacle) : 0;\n\n if (obstacle.wordCollapseTag) {\n // Accumulate word-level pressure\n const currentTotal = wordPressure.get(obstacle.wordCollapseTag) ?? 0;\n wordPressure.set(obstacle.wordCollapseTag, currentTotal + obstaclePressure);\n\n // Track obstacles in this word\n if (!wordObstacles.has(obstacle.wordCollapseTag)) {\n wordObstacles.set(obstacle.wordCollapseTag, []);\n }\n wordObstacles.get(obstacle.wordCollapseTag)!.push(obstacle);\n } else {\n // Per-letter mode: check individual threshold\n if (obstaclePressure >= obstacle.pressureThreshold) {\n this.collapseObstacle(obstacle);\n }\n }\n }\n\n // Second pass: check word-level thresholds\n for (const [wordTag, total] of wordPressure) {\n const wordObs = wordObstacles.get(wordTag);\n if (!wordObs || wordObs.length === 0) continue;\n\n // All letters in a word share the same threshold (from config)\n const threshold = wordObs[0].pressureThreshold;\n if (threshold !== undefined && total >= threshold) {\n // Collapse all letters in this word\n for (const obs of wordObs) {\n this.collapseObstacle(obs);\n }\n console.log(`[Pressure] Word collapsed! ${wordTag} (total: ${total} >= ${threshold})`);\n }\n }\n }\n\n /** Convert a static obstacle to dynamic (make it fall) */\n private collapseObstacle(entry: ObjectEntry): void {\n // Skip if already falling\n if (entry.tags.includes('falling')) return;\n\n const name = this.getObstacleDisplayName(entry);\n console.log(`[Pressure] Collapsed: ${name}`);\n\n // Create shadow if configured\n if (entry.shadow && entry.originalPosition) {\n this.createShadow(entry);\n }\n\n // Add falling tag\n entry.tags.push('falling');\n\n // Make body dynamic\n Matter.Body.setStatic(entry.body, false);\n\n // Clear threshold so it doesn't trigger again\n entry.pressureThreshold = undefined;\n entry.wordCollapseTag = undefined;\n }\n\n /** Create a static shadow copy of an obstacle at its original position */\n private async createShadow(entry: ObjectEntry): Promise<void> {\n if (!entry.originalPosition) return;\n\n const opacity = entry.shadow?.opacity ?? 0.3;\n const shadowId = `shadow-${entry.id}`;\n\n // Handle TTF glyph shadows (canvas-rendered text)\n if (entry.ttfGlyph) {\n // For TTF glyphs, create a minimal static body and store shadow glyph info\n const body = Matter.Bodies.circle(entry.originalPosition.x, entry.originalPosition.y, 1, {\n isStatic: true,\n isSensor: true, // Don't collide\n label: `shadow:${shadowId}`,\n render: { visible: false }\n });\n\n const shadowEntry: ObjectEntry = {\n id: shadowId,\n body,\n tags: ['shadow'],\n spawnTime: performance.now(),\n weight: 0,\n ttfGlyph: {\n ...entry.ttfGlyph,\n fillColor: this.applyOpacityToColor(entry.ttfGlyph.fillColor, opacity)\n }\n };\n this.objects.set(shadowId, shadowEntry);\n Matter.Composite.add(this.engine.world, body);\n return;\n }\n\n // Handle image-based shadows\n if (!entry.imageUrl) return;\n\n const result = await createBoxObstacleWithInfo(shadowId, {\n x: entry.originalPosition.x,\n y: entry.originalPosition.y,\n imageUrl: entry.imageUrl,\n size: entry.imageSize ?? 50,\n tags: ['shadow']\n }, true);\n\n // Make shadow non-colliding (purely visual)\n result.body.isSensor = true;\n\n // Set shadow opacity via render\n if (result.body.render.sprite) {\n result.body.render.opacity = opacity;\n }\n\n // Store shadow as object (static, no pressure tracking)\n const shadowEntry: ObjectEntry = {\n id: shadowId,\n body: result.body,\n tags: ['shadow'],\n spawnTime: performance.now(),\n weight: 0 // Shadows don't contribute to pressure\n };\n this.objects.set(shadowId, shadowEntry);\n Matter.Composite.add(this.engine.world, result.body);\n }\n\n /** Apply opacity to a CSS color string */\n private applyOpacityToColor(color: string, opacity: number): string {\n // Handle hex colors\n if (color.startsWith('#')) {\n const hex = color.slice(1);\n let r, g, b;\n if (hex.length === 3) {\n r = parseInt(hex[0] + hex[0], 16);\n g = parseInt(hex[1] + hex[1], 16);\n b = parseInt(hex[2] + hex[2], 16);\n } else {\n r = parseInt(hex.slice(0, 2), 16);\n g = parseInt(hex.slice(2, 4), 16);\n b = parseInt(hex.slice(4, 6), 16);\n }\n return `rgba(${r}, ${g}, ${b}, ${opacity})`;\n }\n // Handle rgb/rgba\n if (color.startsWith('rgb')) {\n const match = color.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/);\n if (match) {\n return `rgba(${match[1]}, ${match[2]}, ${match[3]}, ${opacity})`;\n }\n }\n // Fallback: return with alpha\n return color;\n }\n\n /** Find an object entry by its Matter.js body (handles compound body parts) */\n private findObjectByBody(body: Matter.Body): ObjectEntry | null {\n // For compound bodies (created by fromVertices), collision events report parts\n // Use the parent property to find the root body we're tracking\n const rootBody = body.parent ?? body;\n\n for (const entry of this.objects.values()) {\n if (entry.body === body || entry.body === rootBody) {\n return entry;\n }\n }\n return null;\n }\n\n /** Check if a body is grounded (low vertical velocity indicates resting on something) */\n private isGrounded(body: Matter.Body): boolean {\n return Math.abs(body.velocity.y) < 0.5;\n }\n\n start(): void {\n Matter.Render.run(this.render);\n Matter.Runner.run(this.runner, this.engine);\n this.loop();\n }\n\n stop(): void {\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n Matter.Render.stop(this.render);\n Matter.Runner.stop(this.runner);\n }\n\n destroy(): void {\n this.stop();\n if (this.mouseConstraint) {\n Matter.Events.off(this.mouseConstraint, 'startdrag', this.handleStartDrag);\n }\n this.canvas.removeEventListener('click', this.handleCanvasClick);\n Matter.Engine.clear(this.engine);\n this.objects.clear();\n this.obstaclePressure.clear();\n this.previousPressure.clear();\n this.pressureLogTimer = 0;\n this.floorSegmentPressure.clear();\n this.collapsedSegments.clear();\n this.updateCallbacks = [];\n }\n\n setDebug(enabled: boolean): void {\n this.config.debug = enabled;\n this.render.options.wireframes = enabled;\n\n // Toggle TTF glyph body visibility (show collision shapes in debug mode)\n for (const [, entry] of this.objects) {\n if (entry.ttfGlyph && entry.body.render) {\n entry.body.render.visible = enabled;\n }\n }\n }\n\n resize(width: number, height: number): void {\n // Update canvas dimensions\n this.canvas.width = width;\n this.canvas.height = height;\n\n // Update config bounds\n this.config.bounds = { top: 0, bottom: height, left: 0, right: width };\n\n // Remove old boundaries\n Matter.Composite.remove(this.engine.world, this.boundaries);\n\n // Create and add new boundaries with floor segments\n const boundariesResult = createBoundariesWithFloorConfig(this.config.bounds, this.config.floorConfig);\n this.boundaries = [...boundariesResult.walls, ...boundariesResult.floorSegments];\n this.floorSegments = boundariesResult.floorSegments;\n this.collapsedSegments.clear();\n this.floorSegmentPressure.clear();\n Matter.Composite.add(this.engine.world, this.boundaries);\n\n // Update render bounds\n this.render.options.width = width;\n this.render.options.height = height;\n this.render.canvas.width = width;\n this.render.canvas.height = height;\n\n // Update effect manager bounds\n this.effectManager.setBounds(this.config.bounds);\n }\n\n // ==================== OBJECT METHODS ====================\n\n /**\n * Spawn an object synchronously.\n * Object behavior is determined by tags:\n * - 'falling': Object is dynamic (affected by gravity)\n * - 'follow': Object follows mouse when grounded\n * - 'grabable': Object can be dragged\n * Without 'falling' tag, object is static.\n */\n spawnObject(config: ObjectConfig): string {\n const id = crypto.randomUUID();\n const tags = config.tags ?? [];\n const isStatic = !tags.includes('falling');\n\n logger.debug('OverlayScene', `Spawning object`, {\n id,\n tags,\n isStatic,\n shape: config.shape?.type ?? (config.radius ? 'circle' : 'rectangle'),\n ttl: config.ttl\n });\n\n // Determine if this is an \"entity-style\" object (has radius) or \"obstacle-style\" (has width/height)\n let body: Matter.Body;\n if (config.radius) {\n body = createEntity(id, config);\n // If it should be static, set it\n if (isStatic) {\n Matter.Body.setStatic(body, true);\n }\n } else {\n body = createObstacle(id, config, isStatic);\n }\n\n const entry: ObjectEntry = {\n id,\n body,\n tags,\n spawnTime: performance.now(),\n ttl: config.ttl,\n despawnEffect: config.despawnEffect,\n weight: config.weight ?? 1\n };\n this.objects.set(id, entry);\n Matter.Composite.add(this.engine.world, body);\n return id;\n }\n\n /**\n * Spawn an object asynchronously. Required for image-based shapes that need\n * shape extraction from image alpha channel.\n */\n async spawnObjectAsync(config: ObjectConfig): Promise<string> {\n const id = crypto.randomUUID();\n const tags = config.tags ?? [];\n const isStatic = !tags.includes('falling');\n\n logger.debug('OverlayScene', `Spawning object async`, {\n id,\n tags,\n isStatic,\n shape: config.shape?.type ?? (config.radius ? 'circle' : 'rectangle'),\n ttl: config.ttl\n });\n\n let body: Matter.Body;\n if (config.radius) {\n body = await createEntityAsync(id, config);\n if (isStatic) {\n Matter.Body.setStatic(body, true);\n }\n } else {\n body = await createObstacleAsync(id, config, isStatic);\n }\n\n const entry: ObjectEntry = {\n id,\n body,\n tags,\n spawnTime: performance.now(),\n ttl: config.ttl,\n despawnEffect: config.despawnEffect,\n weight: config.weight ?? 1\n };\n this.objects.set(id, entry);\n Matter.Composite.add(this.engine.world, body);\n return id;\n }\n\n /**\n * Add 'falling' tag to an object, making it dynamic (affected by gravity).\n * Also adds 'grabable' tag so released objects can be dragged.\n * This is the tag-based replacement for releaseObstacle().\n */\n addFallingTag(id: string): void {\n const entry = this.objects.get(id);\n if (!entry) return;\n if (!entry.tags.includes('falling')) {\n entry.tags.push('falling');\n Matter.Body.setStatic(entry.body, false);\n }\n if (!entry.tags.includes('grabable')) {\n entry.tags.push('grabable');\n }\n }\n\n /**\n * Add a tag to an object.\n */\n addTag(id: string, tag: string): void {\n const entry = this.objects.get(id);\n if (!entry) return;\n if (!entry.tags.includes(tag)) {\n entry.tags.push(tag);\n // Handle special tag behaviors\n if (tag === 'falling') {\n Matter.Body.setStatic(entry.body, false);\n }\n }\n }\n\n /**\n * Remove a tag from an object.\n */\n removeTag(id: string, tag: string): void {\n const entry = this.objects.get(id);\n if (!entry) return;\n const index = entry.tags.indexOf(tag);\n if (index !== -1) {\n entry.tags.splice(index, 1);\n // Handle special tag behaviors\n if (tag === 'falling') {\n Matter.Body.setStatic(entry.body, true);\n }\n }\n }\n\n /**\n * Release an object (add 'falling' tag to make it dynamic).\n * Convenience method - equivalent to addFallingTag().\n */\n releaseObject(id: string): void {\n this.addFallingTag(id);\n }\n\n /**\n * Release multiple objects by their IDs.\n */\n releaseObjects(ids: string[]): void {\n for (const id of ids) {\n this.releaseObject(id);\n }\n }\n\n /**\n * Release all static objects (add 'falling' and 'grabable' tags).\n */\n releaseAllObjects(): void {\n for (const [id] of this.objects) {\n this.addFallingTag(id);\n }\n }\n\n /**\n * Release objects by tag (add 'falling' and 'grabable' tags to matching objects).\n */\n releaseObjectsByTag(tag: string): void {\n for (const [id, entry] of this.objects) {\n if (entry.tags.includes(tag)) {\n this.addFallingTag(id);\n }\n }\n }\n\n removeObject(id: string): void {\n const entry = this.objects.get(id);\n if (!entry) return;\n Matter.Composite.remove(this.engine.world, entry.body);\n this.objects.delete(id);\n }\n\n removeObjects(ids: string[]): void {\n for (const id of ids) {\n this.removeObject(id);\n }\n }\n\n removeAllObjects(): void {\n for (const entry of this.objects.values()) {\n Matter.Composite.remove(this.engine.world, entry.body);\n }\n this.objects.clear();\n }\n\n removeObjectsByTag(tag: string): void {\n const toRemove: string[] = [];\n for (const [id, entry] of this.objects) {\n if (entry.tags.includes(tag)) {\n Matter.Composite.remove(this.engine.world, entry.body);\n toRemove.push(id);\n }\n }\n toRemove.forEach((id) => this.objects.delete(id));\n // Clean up letter debug info if this was a word tag\n this.letterDebugInfo.delete(tag);\n }\n\n getObjectIds(): string[] {\n return Array.from(this.objects.keys());\n }\n\n getObjectIdsByTag(tag: string): string[] {\n const ids: string[] = [];\n for (const [id, entry] of this.objects) {\n if (entry.tags.includes(tag)) {\n ids.push(id);\n }\n }\n return ids;\n }\n\n /**\n * Get all unique tags currently in use by objects in the scene.\n */\n getAllTags(): string[] {\n const tagsSet = new Set<string>();\n for (const entry of this.objects.values()) {\n for (const tag of entry.tags) {\n tagsSet.add(tag);\n }\n }\n return Array.from(tagsSet).sort();\n }\n\n setMousePosition(x: number, _y: number): void {\n this.mouseX = x;\n }\n\n // ==================== PRESSURE TRACKING METHODS ====================\n\n /**\n * Get the current pressure (number of objects resting) on an obstacle.\n * @param obstacleId - The ID of the obstacle\n * @returns Number of objects currently resting on the obstacle\n */\n getPressure(obstacleId: string): number {\n return this.obstaclePressure.get(obstacleId)?.size ?? 0;\n }\n\n /**\n * Get the IDs of all objects currently resting on an obstacle.\n * @param obstacleId - The ID of the obstacle\n * @returns Array of object IDs resting on the obstacle\n */\n getObjectsRestingOn(obstacleId: string): string[] {\n const set = this.obstaclePressure.get(obstacleId);\n return set ? Array.from(set) : [];\n }\n\n /**\n * Get all obstacles that have pressure (at least one object resting on them).\n * @returns Map of obstacle ID -> pressure count\n */\n getAllPressure(): Map<string, number> {\n const result = new Map<string, number>();\n for (const [id, set] of this.obstaclePressure) {\n if (set.size > 0) {\n result.set(id, set.size);\n }\n }\n return result;\n }\n\n /**\n * Get pressure summary for all obstacles with their display names (letters).\n * Useful for debugging and visualization.\n * @returns Array of { id, name, pressure } objects\n */\n getPressureSummary(): { id: string; name: string; pressure: number }[] {\n const summary: { id: string; name: string; pressure: number }[] = [];\n for (const [id, set] of this.obstaclePressure) {\n if (set.size > 0) {\n const entry = this.objects.get(id);\n summary.push({\n id,\n name: entry ? this.getObstacleDisplayName(entry) : id.slice(0, 4),\n pressure: set.size\n });\n }\n }\n return summary;\n }\n\n // ==================== FONT MANAGEMENT METHODS ====================\n\n /**\n * Initialize fonts by loading the font manifest.\n * Should be called before using text obstacles if you want automatic font detection.\n * @param fontsBasePath Base URL path for fonts directory (default: '/fonts/')\n */\n async initializeFonts(fontsBasePath: string = '/fonts/'): Promise<void> {\n if (this.fontsInitialized) {\n return;\n }\n\n try {\n const manifestUrl = `${fontsBasePath}fonts.json`;\n const response = await fetch(manifestUrl);\n\n if (!response.ok) {\n logger.warn('OverlayScene', `Failed to load fonts manifest from ${manifestUrl}: ${response.status}`);\n this.fonts = [];\n this.fontsInitialized = true;\n return;\n }\n\n const manifest: FontManifest = await response.json();\n this.fonts = manifest.fonts || [];\n\n // Load TTF fonts via FontFace API so they're available for canvas fillText\n for (const font of this.fonts) {\n if (font.type === 'ttf' && font.fontUrl) {\n try {\n const fontFace = new FontFace(font.name, `url(${font.fontUrl})`);\n await fontFace.load();\n document.fonts.add(fontFace);\n logger.debug('OverlayScene', `Loaded TTF font: ${font.name}`);\n } catch (err) {\n logger.warn('OverlayScene', `Failed to load TTF font ${font.name}: ${err}`);\n }\n }\n }\n\n this.fontsInitialized = true;\n\n logger.info('OverlayScene', `Loaded ${this.fonts.length} fonts`, { fonts: this.fonts.map(f => f.name) });\n } catch (error) {\n logger.warn('OverlayScene', `Error loading fonts manifest: ${error}`);\n this.fonts = [];\n this.fontsInitialized = true;\n }\n }\n\n /**\n * Get list of available fonts.\n * Returns empty array if fonts have not been initialized.\n */\n getAvailableFonts(): FontInfo[] {\n return [...this.fonts];\n }\n\n /**\n * Get font by index. Returns undefined if index is out of bounds.\n * @param index Font index (0-based)\n */\n getFontByIndex(index: number): FontInfo | undefined {\n return this.fonts[index];\n }\n\n /**\n * Get font by name. Returns undefined if font not found.\n * @param name Font name to find\n */\n getFontByName(name: string): FontInfo | undefined {\n return this.fonts.find(f => f.name === name);\n }\n\n /**\n * Get the default font (first available font).\n * Returns undefined if no fonts are available.\n */\n getDefaultFont(): FontInfo | undefined {\n return this.fonts[0];\n }\n\n /**\n * Check if fonts have been initialized.\n */\n areFontsInitialized(): boolean {\n return this.fontsInitialized;\n }\n\n // ==================== TEXT OBSTACLE METHODS ====================\n\n /**\n * Create text obstacles from a string. Each character becomes an individual obstacle\n * with shape extracted from the corresponding letter PNG image.\n * Supported characters: A-Z, a-z, 0-9 (spaces handled, unsupported chars skipped)\n * Case is preserved: uses lowercase PNG if available, falls back to uppercase (and vice versa).\n * Supports multiline text with \\n characters.\n *\n * Letter positioning is based on original PNG dimensions:\n * - Each letter's PNG width controls its horizontal spacing\n * - The clipped shape is positioned correctly within the original bounds\n * - This allows fine control of letter spacing via PNG canvas size\n */\n async addTextObstacles(config: TextObstacleConfig): Promise<TextObstacleResult> {\n // Convert literal \\n strings to actual newlines (preserve case for lowercase support)\n const text = config.text.replace(/\\\\n/g, '\\n');\n const letterSize = config.letterSize;\n const lineHeight = config.lineHeight ?? letterSize * 1.2;\n const fontsBasePath = config.fontsBasePath ?? '/fonts/';\n const fontName = config.fontName ?? this.getDefaultFont()?.name ?? 'handwritten';\n const basePath = `${fontsBasePath}${fontName}/`;\n const stringTag = config.stringTag ?? `str-${crypto.randomUUID().slice(0, 8)}`;\n // Determine if static based on tags (no 'falling' tag = static)\n const baseTags = config.tags ?? [];\n const isStatic = !baseTags.includes('falling');\n const letterColor = config.letterColor;\n\n const letterIds: string[] = [];\n const letterMap = new Map<string, string>();\n const debugInfo: LetterDebugInfo[] = [];\n\n // Track word boundaries for word-level tagging\n const wordTagsSet = new Set<string>();\n let currentWordIndex = 0;\n let inWord = false;\n\n // Split text into lines\n const lines = text.split('\\n');\n\n // First pass: collect all unique characters and load their image dimensions\n const uniqueChars = new Set<string>();\n for (const line of lines) {\n for (const char of line) {\n if (/^[A-Za-z0-9]$/.test(char)) {\n uniqueChars.add(char);\n }\n }\n }\n\n // Load dimensions for all unique characters in parallel\n // Try exact case first, then fallback to opposite case (a->A or A->a)\n const charDimensions = new Map<string, { width: number; height: number }>();\n const charFileNames = new Map<string, string>(); // Maps input char to resolved filename char\n await Promise.all(\n Array.from(uniqueChars).map(async (char) => {\n const imageUrl = `${basePath}${char}.png`;\n try {\n const dims = await getImageDimensions(imageUrl);\n charDimensions.set(char, dims);\n charFileNames.set(char, char);\n } catch {\n // Try opposite case for letters (not digits)\n if (/^[A-Za-z]$/.test(char)) {\n const fallbackChar = char === char.toLowerCase() ? char.toUpperCase() : char.toLowerCase();\n const fallbackUrl = `${basePath}${fallbackChar}.png`;\n try {\n const dims = await getImageDimensions(fallbackUrl);\n charDimensions.set(char, dims);\n charFileNames.set(char, fallbackChar);\n logger.debug('OverlayScene', `Using fallback ${fallbackChar} for ${char}`);\n } catch (fallbackError) {\n logger.warn('OverlayScene', `Failed to load char ${char} (tried ${fallbackChar} too)`, { error: String(fallbackError) });\n charDimensions.set(char, { width: 100, height: 100 });\n charFileNames.set(char, char);\n }\n } else {\n logger.warn('OverlayScene', `Failed to load dimensions for char ${char}`);\n charDimensions.set(char, { width: 100, height: 100 });\n charFileNames.set(char, char);\n }\n }\n })\n );\n\n // Calculate the scale factor: letterSize is the target max dimension\n // Find the max dimension among all letters to determine base scale\n let maxDimension = 0;\n for (const dims of charDimensions.values()) {\n maxDimension = Math.max(maxDimension, dims.width, dims.height);\n }\n // If no letters found, default to 100\n if (maxDimension === 0) maxDimension = 100;\n\n // Track Y position for each line\n let currentY = config.y;\n let globalCharIndex = 0;\n\n for (const line of lines) {\n const chars = line.split('');\n let currentX = config.x;\n\n // New line = end of current word (if we were in one)\n if (inWord) {\n currentWordIndex++;\n inWord = false;\n }\n\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i];\n\n // Handle spaces - use average letter width or letterSpacing config\n if (char === ' ') {\n // Use explicit letterSpacing if provided, otherwise use letterSize as space width\n // TODO Replace this with a configured value\n currentX += 20;\n globalCharIndex++;\n // Space ends the current word\n if (inWord) {\n currentWordIndex++;\n inWord = false;\n }\n continue;\n }\n\n // Only process A-Z, a-z, and 0-9\n if (!/^[A-Za-z0-9]$/.test(char)) {\n globalCharIndex++;\n continue;\n }\n\n // We're now in a word\n inWord = true;\n const wordTag = `${stringTag}-word-${currentWordIndex}`;\n wordTagsSet.add(wordTag);\n\n // Get this letter's original dimensions\n const dims = charDimensions.get(char)!;\n const scale = letterSize / Math.max(dims.width, dims.height);\n const scaledWidth = dims.width * scale;\n const scaledHeight = dims.height * scale;\n\n // Letter box position (top-left of the original dimension box)\n const boxX = currentX;\n const boxY = currentY - scaledHeight / 2; // Center vertically on currentY\n\n // Letter center position (center of the original dimension box)\n const centerX = currentX + scaledWidth / 2;\n const centerY = currentY;\n\n // Prepare image URL (with optional tinting), using resolved filename with fallback\n const resolvedChar = charFileNames.get(char) ?? char;\n const originalImageUrl = `${basePath}${resolvedChar}.png`;\n const imageUrl = letterColor\n ? await tintImage(originalImageUrl, letterColor)\n : originalImageUrl;\n const tags = [...(config.tags ?? []), stringTag, wordTag, `letter-${char}`, `letter-index-${globalCharIndex}`];\n\n const id = crypto.randomUUID();\n\n // Create clipped letter body at the center position\n const objectConfig: ObjectConfig = {\n x: centerX,\n y: centerY,\n imageUrl,\n size: letterSize,\n tags,\n ttl: config.ttl\n };\n\n const result = await createBoxObstacleWithInfo(id, objectConfig, isStatic);\n\n // Determine pressure threshold for this letter\n let pressureThreshold: number | undefined;\n let wordCollapseTag: string | undefined;\n if (config.pressureThreshold) {\n const pt = config.pressureThreshold;\n if (Array.isArray(pt.value)) {\n // Per-letter thresholds by index\n pressureThreshold = pt.value[letterIds.length];\n } else {\n pressureThreshold = pt.value;\n if (pt.wordCollapse) {\n wordCollapseTag = wordTag;\n }\n }\n }\n\n // Determine weight for this letter\n let weight = 1;\n if (config.weight) {\n if (Array.isArray(config.weight.value)) {\n weight = config.weight.value[letterIds.length] ?? 1;\n } else {\n weight = config.weight.value;\n }\n }\n\n // Determine shadow config\n const shadow = config.shadow ? { opacity: config.shadow.opacity ?? 0.3 } : undefined;\n\n // Determine click to fall config\n const clicksRemaining = config.clickToFall?.clicks;\n\n const entry: ObjectEntry = {\n id,\n body: result.body,\n tags,\n spawnTime: performance.now(),\n ttl: config.ttl,\n pressureThreshold,\n wordCollapseTag,\n weight,\n shadow,\n originalPosition: shadow || clicksRemaining !== undefined ? { x: centerX, y: centerY } : undefined,\n imageUrl: shadow || clicksRemaining !== undefined ? imageUrl : undefined,\n imageSize: shadow || clicksRemaining !== undefined ? letterSize : undefined,\n clicksRemaining\n };\n this.objects.set(id, entry);\n Matter.Composite.add(this.engine.world, result.body);\n\n letterIds.push(id);\n letterMap.set(`${char}-${globalCharIndex}`, id);\n\n // Store debug info\n debugInfo.push({\n char,\n id,\n originalWidth: dims.width,\n originalHeight: dims.height,\n scaledWidth,\n scaledHeight,\n boxX,\n boxY,\n centerX,\n centerY\n });\n\n // Advance X by this letter's scaled width (plus optional extra spacing)\n const extraSpacing = config.letterSpacing !== undefined ? config.letterSpacing - scaledWidth : 0;\n currentX += scaledWidth + Math.max(0, extraSpacing);\n\n globalCharIndex++;\n }\n\n // Move to next line\n currentY += lineHeight;\n }\n\n // Store debug info for this string\n this.letterDebugInfo.set(stringTag, debugInfo);\n\n const wordTags = Array.from(wordTagsSet);\n logger.info('OverlayScene', `Created text obstacles`, {\n text: text.replace(/\\n/g, '\\\\n'),\n fontName,\n letterCount: letterIds.length,\n stringTag,\n wordTags,\n letterColor,\n lineCount: lines.length\n });\n\n return {\n letterIds,\n stringTag,\n wordTags,\n letterMap,\n letterDebugInfo: debugInfo\n };\n }\n\n /**\n * Spawn falling text objects from a string.\n * Same as addTextObstacles but with 'falling' tag (objects fall with gravity).\n */\n async spawnFallingTextObstacles(config: TextObstacleConfig): Promise<TextObstacleResult> {\n const tags = [...(config.tags ?? [])];\n if (!tags.includes('falling')) tags.push('falling');\n return this.addTextObstacles({ ...config, tags });\n }\n\n /**\n * Release all letters in a word (add 'falling' tag so they fall).\n * @param wordTag - The word tag returned from addTextObstacles\n */\n releaseTextObstacles(wordTag: string): void {\n this.releaseObjectsByTag(wordTag);\n }\n\n /**\n * Release letters one by one with a delay between each.\n * @param wordTag - The word tag returned from addTextObstacles\n * @param delayMs - Delay between releasing each letter (default: 100ms)\n * @param reverse - If true, release from end to start (default: false)\n */\n async releaseTextObstaclesSequentially(wordTag: string, delayMs: number = 100, reverse: boolean = false): Promise<void> {\n const ids = this.getObjectIdsByTag(wordTag);\n if (reverse) ids.reverse();\n\n for (const id of ids) {\n this.releaseObject(id);\n if (delayMs > 0) {\n await new Promise(resolve => setTimeout(resolve, delayMs));\n }\n }\n }\n\n /**\n * Get letter debug info for a word.\n * Returns the debug info array for the given word tag, or undefined if not found.\n * Debug info includes original dimension boxes for each letter.\n */\n getLetterDebugInfo(wordTag: string): LetterDebugInfo[] | undefined {\n return this.letterDebugInfo.get(wordTag);\n }\n\n /**\n * Get all stored letter debug info (all words).\n * Returns a map of wordTag -> debug info array.\n */\n getAllLetterDebugInfo(): Map<string, LetterDebugInfo[]> {\n return new Map(this.letterDebugInfo);\n }\n\n // ==================== TTF FONT TEXT METHODS ====================\n\n /**\n * Create text obstacles from a TTF/OTF font file.\n * Uses proper font metrics for spacing, kerning, and glyph outlines for collision.\n * Supports multiline text with \\n characters.\n */\n async addTTFTextObstacles(config: TTFTextObstacleConfig): Promise<TextObstacleResult> {\n const { x, y, fontSize, fontUrl } = config;\n // Convert literal \\n strings to actual newlines\n const text = config.text.replace(/\\\\n/g, '\\n');\n const stringTag = config.stringTag ?? `str-${crypto.randomUUID().slice(0, 8)}`;\n // Determine if static based on tags (no 'falling' tag = static)\n const baseTags = config.tags ?? [];\n const isStatic = !baseTags.includes('falling');\n const fillColor = config.fillColor ?? '#ffffff';\n const lineHeight = config.lineHeight ?? fontSize * 1.2;\n\n const letterIds: string[] = [];\n const letterMap = new Map<string, string>();\n\n // Track word boundaries for word-level tagging\n const wordTagsSet = new Set<string>();\n let currentWordIndex = 0;\n let inWord = false;\n\n // Load the font\n const loadedFont = await loadFont(fontUrl);\n\n // Find font family name for canvas rendering\n const fontInfo = this.fonts.find(f => f.fontUrl === fontUrl);\n const fontFamily = fontInfo?.name ?? 'sans-serif';\n\n // Split text into lines\n const lines = text.split('\\n');\n\n // Track current Y position for each line\n let currentY = y;\n let globalCharIndex = 0;\n\n for (const line of lines) {\n // Track current X position as we place each glyph\n let currentX = x;\n\n // New line = end of current word (if we were in one)\n if (inWord) {\n currentWordIndex++;\n inWord = false;\n }\n\n const chars = line.split('');\n\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i];\n\n // Get glyph data (vertices, advance width, etc.)\n const glyphData = getGlyphData(loadedFont, char, fontSize);\n\n // Skip if no vertices (space or unsupported char)\n if (glyphData.vertices.length < 3) {\n // Still advance by the glyph's advance width\n currentX += glyphData.advanceWidth;\n\n // Add kerning with next character\n if (i < chars.length - 1) {\n currentX += getKerning(loadedFont, char, chars[i + 1], fontSize);\n }\n globalCharIndex++;\n // Space/unsupported char ends the current word\n if (char === ' ' && inWord) {\n currentWordIndex++;\n inWord = false;\n }\n continue;\n }\n\n // We're now in a word\n inWord = true;\n const wordTag = `${stringTag}-word-${currentWordIndex}`;\n wordTagsSet.add(wordTag);\n\n const id = crypto.randomUUID();\n const tags = [...(config.tags ?? []), stringTag, wordTag, `letter-${char}`, `letter-index-${globalCharIndex}`];\n\n // Calculate glyph center from bounding box\n const bbox = glyphData.boundingBox;\n const glyphWidth = bbox ? bbox.x2 - bbox.x1 : glyphData.advanceWidth;\n const glyphHeight = bbox ? bbox.y2 - bbox.y1 : fontSize;\n const glyphCenterX = currentX + (bbox ? bbox.x1 + glyphWidth / 2 : glyphData.advanceWidth / 2);\n const glyphCenterY = currentY - (bbox ? (bbox.y1 + bbox.y2) / 2 : fontSize / 2);\n\n // Create world-positioned vertices\n const worldVertices = glyphData.vertices.map(v => ({\n x: currentX + v.x,\n y: currentY + v.y\n }));\n\n // Create body from vertices at the target position\n // fromVertices will calculate centroid and position body there\n const body = Matter.Bodies.fromVertices(glyphCenterX, glyphCenterY, [worldVertices], {\n isStatic,\n label: `obstacle:${id}`,\n render: {\n visible: false\n }\n });\n\n // Store and add to world\n // Calculate offset from ACTUAL body position to baseline for fillText rendering\n // (fromVertices positions body at vertex centroid, not where we specified)\n const offsetX = currentX - body.position.x;\n const offsetY = currentY - body.position.y;\n\n // Determine pressure threshold for this letter\n let pressureThreshold: number | undefined;\n let wordCollapseTag: string | undefined;\n if (config.pressureThreshold) {\n const pt = config.pressureThreshold;\n if (Array.isArray(pt.value)) {\n // Per-letter thresholds by index\n pressureThreshold = pt.value[letterIds.length];\n } else {\n pressureThreshold = pt.value;\n if (pt.wordCollapse) {\n wordCollapseTag = wordTag;\n }\n }\n }\n\n // Determine weight for this letter\n let weight = 1;\n if (config.weight) {\n if (Array.isArray(config.weight.value)) {\n weight = config.weight.value[letterIds.length] ?? 1;\n } else {\n weight = config.weight.value;\n }\n }\n\n // Determine shadow config\n const shadow = config.shadow ? { opacity: config.shadow.opacity ?? 0.3 } : undefined;\n\n // Determine click to fall config\n const clicksRemaining = config.clickToFall?.clicks;\n\n const entry: ObjectEntry = {\n id,\n body,\n tags,\n spawnTime: performance.now(),\n ttl: config.ttl,\n ttfGlyph: {\n char,\n fontSize,\n fontFamily,\n fillColor,\n offsetX,\n offsetY\n },\n pressureThreshold,\n wordCollapseTag,\n weight,\n shadow,\n originalPosition: shadow || clicksRemaining !== undefined ? { x: body.position.x, y: body.position.y } : undefined,\n clicksRemaining\n };\n this.objects.set(id, entry);\n Matter.Composite.add(this.engine.world, body);\n\n letterIds.push(id);\n letterMap.set(`${char}-${globalCharIndex}`, id);\n\n // Advance X position by glyph's advance width\n currentX += glyphData.advanceWidth;\n\n // Add kerning with next character\n if (i < chars.length - 1) {\n currentX += getKerning(loadedFont, char, chars[i + 1], fontSize);\n }\n\n globalCharIndex++;\n }\n\n // Move to next line\n currentY += lineHeight;\n }\n\n const wordTags = Array.from(wordTagsSet);\n logger.info('OverlayScene', `Created TTF text obstacles`, {\n text: text.replace(/\\n/g, '\\\\n'),\n fontUrl,\n fontSize,\n letterCount: letterIds.length,\n stringTag,\n wordTags,\n lineCount: lines.length\n });\n\n // TTF fonts use font metrics, not PNG dimensions, so debug info is empty\n return {\n letterIds,\n stringTag,\n wordTags,\n letterMap,\n letterDebugInfo: []\n };\n }\n\n /**\n * Spawn falling TTF text objects.\n * Same as addTTFTextObstacles but with 'falling' tag (objects fall with gravity).\n */\n async spawnFallingTTFTextObstacles(config: TTFTextObstacleConfig): Promise<TextObstacleResult> {\n const tags = [...(config.tags ?? [])];\n if (!tags.includes('falling')) tags.push('falling');\n return this.addTTFTextObstacles({ ...config, tags });\n }\n\n // ==================== COMBINED TAG METHODS ====================\n\n removeAllByTag(tag: string): void {\n this.removeObjectsByTag(tag);\n }\n\n removeAll(): void {\n this.removeAllObjects();\n }\n\n // ==================== CALLBACKS ====================\n\n onUpdate(callback: UpdateCallback): void {\n this.updateCallbacks.push(callback);\n }\n\n // ==================== EFFECT METHODS ====================\n\n /**\n * Add or update an effect configuration.\n * Effects are persistent spawning mechanisms that run until disabled.\n */\n setEffect(config: EffectConfig): void {\n this.effectManager.setEffect(config);\n }\n\n /**\n * Remove an effect by ID\n */\n removeEffect(id: string): void {\n this.effectManager.removeEffect(id);\n }\n\n /**\n * Enable or disable an effect\n */\n setEffectEnabled(id: string, enabled: boolean): void {\n this.effectManager.setEffectEnabled(id, enabled);\n }\n\n /**\n * Get an effect configuration by ID\n */\n getEffect(id: string): EffectConfig | undefined {\n return this.effectManager.getEffect(id);\n }\n\n /**\n * Get all effect IDs\n */\n getEffectIds(): string[] {\n return this.effectManager.getEffectIds();\n }\n\n /**\n * Check if an effect is currently enabled\n */\n isEffectEnabled(id: string): boolean {\n return this.effectManager.isEffectEnabled(id);\n }\n\n // ==================== PRIVATE ====================\n\n private loop = (): void => {\n // Update effects (spawn objects)\n this.effectManager.update();\n\n // Check for TTL expiration\n this.checkTTLExpiration();\n\n // Check for objects fallen below floor (despawn them)\n this.checkDespawnBelowFloor();\n\n // Update pressure tracking\n this.updatePressure();\n\n // Apply tag-based behaviors to all objects\n const mouseX = this.mouse?.position.x ?? this.mouseX;\n\n for (const entry of this.objects.values()) {\n // Only apply mouse force to objects with 'follow' tag, and not if being dragged\n const isDragging = this.mouseConstraint?.body === entry.body;\n if (!isDragging && entry.tags.includes('follow')) {\n applyMouseForce(entry.body, mouseX, this.isGrounded(entry.body));\n }\n\n // Apply horizontal wrapping to dynamic objects (objects with 'falling' tag)\n if (this.config.wrapHorizontal && entry.tags.includes('falling')) {\n wrapHorizontal(entry.body, this.config.bounds);\n }\n }\n\n // Draw TTF glyphs using canvas fillText (clean text rendering)\n // Only when not in debug mode - debug mode shows collision shapes instead\n if (!this.config.debug) {\n this.drawTTFGlyphs();\n }\n\n // Draw debug overlays after Matter.js renders\n if (this.config.debug) {\n this.drawDebugOverlays();\n }\n\n this.fireUpdateCallbacks();\n this.animationFrameId = requestAnimationFrame(this.loop);\n };\n\n /**\n * Draw debug overlays for letter original dimension boxes\n */\n private drawDebugOverlays(): void {\n const ctx = this.canvas.getContext('2d');\n if (!ctx) return;\n\n // Draw original dimension boxes for all letters\n for (const [, debugInfos] of this.letterDebugInfo) {\n for (const info of debugInfos) {\n // Check if the object still exists\n const object = this.objects.get(info.id);\n if (!object) continue;\n\n // Get current body position (letters may have moved if dynamic)\n const body = object.body;\n\n // Draw the original dimension box (cyan dashed outline)\n ctx.save();\n ctx.strokeStyle = '#00ffff';\n ctx.lineWidth = 2;\n ctx.setLineDash([5, 5]);\n\n // Translate and rotate with the body\n ctx.translate(body.position.x, body.position.y);\n ctx.rotate(body.angle);\n\n // Draw rectangle centered on body (box dimensions relative to center)\n const halfWidth = info.scaledWidth / 2;\n const halfHeight = info.scaledHeight / 2;\n ctx.strokeRect(-halfWidth, -halfHeight, info.scaledWidth, info.scaledHeight);\n\n ctx.restore();\n }\n }\n }\n\n /**\n * Draw TTF glyphs using canvas fillText for clean text rendering\n */\n private drawTTFGlyphs(): void {\n const ctx = this.canvas.getContext('2d');\n if (!ctx) return;\n\n for (const [, entry] of this.objects) {\n if (!entry.ttfGlyph) continue;\n\n const { char, fontSize, fontFamily, fillColor, offsetX, offsetY } = entry.ttfGlyph;\n const body = entry.body;\n\n ctx.save();\n\n // Move to body position and rotate\n ctx.translate(body.position.x, body.position.y);\n ctx.rotate(body.angle);\n\n // Set up font\n ctx.font = `${fontSize}px \"${fontFamily}\"`;\n ctx.fillStyle = fillColor;\n ctx.textBaseline = 'alphabetic';\n\n // Draw at offset from body center (offset points to baseline position)\n ctx.fillText(char, offsetX, offsetY);\n\n ctx.restore();\n }\n }\n\n private checkTTLExpiration(): void {\n const now = performance.now();\n\n // Check all objects for expiration\n const expiredObjects: string[] = [];\n for (const [id, entry] of this.objects) {\n if (entry.ttl !== undefined && now - entry.spawnTime >= entry.ttl) {\n // TODO: Trigger despawn effect when implemented\n // if (entry.despawnEffect) { ... }\n expiredObjects.push(id);\n }\n }\n for (const id of expiredObjects) {\n this.removeObject(id);\n }\n }\n\n /** Despawn objects that have fallen below the floor by the configured distance */\n private checkDespawnBelowFloor(): void {\n // Default to 100% of container height below floor\n const despawnDistance = this.config.despawnBelowFloor ?? 1.0;\n const containerHeight = this.config.bounds.bottom - this.config.bounds.top;\n const despawnY = this.config.bounds.bottom + (containerHeight * despawnDistance);\n\n const toDespawn: string[] = [];\n for (const [id, entry] of this.objects) {\n if (entry.body.position.y > despawnY) {\n toDespawn.push(id);\n }\n }\n\n for (const id of toDespawn) {\n this.removeObject(id);\n }\n }\n\n private fireUpdateCallbacks(): void {\n // Collect all dynamic objects (objects with 'falling' tag)\n const objects: DynamicObject[] = [];\n this.objects.forEach((entry) => {\n if (entry.tags.includes('falling')) {\n objects.push({\n id: entry.id,\n x: entry.body.position.x,\n y: entry.body.position.y,\n angle: entry.body.angle,\n tags: entry.tags\n });\n }\n });\n\n const data: UpdateCallbackData = { objects };\n this.updateCallbacks.forEach((cb) => cb(data));\n }\n\n}\n","import Matter from 'matter-js';\nimport type { OverlaySceneConfig } from './types';\n\nexport function createEngine(gravity: number): Matter.Engine {\n const engine = Matter.Engine.create();\n engine.gravity.y = gravity;\n return engine;\n}\n\nexport function createRender(\n engine: Matter.Engine,\n canvas: HTMLCanvasElement,\n config: OverlaySceneConfig\n): Matter.Render {\n const render = Matter.Render.create({\n canvas,\n engine,\n options: {\n width: config.bounds.right - config.bounds.left,\n height: config.bounds.bottom - config.bounds.top,\n wireframes: config.debug ?? false,\n background: config.background ?? 'transparent'\n }\n });\n return render;\n}\n","import Matter from 'matter-js';\nimport type { Bounds, ObjectConfig, ShapeConfig, FloorConfig } from './types';\nimport { getVerticesFromImage, getVerticesAndDimensionsFromImage, loadImage, Vector2D, ImageClipResult, ClipBounds } from './imageClip';\nimport { logger } from './logger';\n\n// Re-export for convenience\nexport type { ImageClipResult, ClipBounds };\n\nconst BOUNDARY_THICKNESS = 50;\nconst LOG_PREFIX = 'Bodies';\nconst DEFAULT_RADIUS = 20;\n\n// Preset shape side counts\nconst SHAPE_SIDES: Record<string, number> = {\n triangle: 3,\n rectangle: 4,\n pentagon: 5,\n hexagon: 6,\n octagon: 8\n};\n\n/**\n * Generate polygon vertices centered at origin\n * @param sides - number of sides\n * @param radius - distance from center to vertices\n * @param aspectRatio - for non-regular polygons (only applies to 4-sided rectangles)\n */\nfunction generatePolygonVertices(sides: number, radius: number, aspectRatio?: number): Vector2D[] {\n // Rectangle with aspect ratio is a special 4-sided case\n if (sides === 4 && aspectRatio !== undefined && aspectRatio !== 1) {\n const width = radius * Math.sqrt(2 * aspectRatio / (1 + aspectRatio));\n const height = width / aspectRatio;\n return [\n { x: -width, y: -height },\n { x: width, y: -height },\n { x: width, y: height },\n { x: -width, y: height }\n ];\n }\n\n // Regular polygon\n const vertices: Vector2D[] = [];\n const angleStep = (2 * Math.PI) / sides;\n const startAngle = -Math.PI / 2; // Start from top for nicer orientation\n\n for (let i = 0; i < sides; i++) {\n const angle = startAngle + i * angleStep;\n vertices.push({\n x: radius * Math.cos(angle),\n y: radius * Math.sin(angle)\n });\n }\n return vertices;\n}\n\n/**\n * Get vertices for a shape config (non-async presets only)\n * Returns null only if shape is invalid - caller should fall back to circle\n */\nfunction getShapeVertices(shape: ShapeConfig, radius: number): Vector2D[] | null {\n // Custom vertices take priority\n if (shape.vertices && shape.vertices.length >= 3) {\n logger.debug(LOG_PREFIX, `Using custom vertices`, { count: shape.vertices.length });\n return shape.vertices;\n }\n\n // Get side count - either explicit or from preset\n const sides = shape.sides ?? SHAPE_SIDES[shape.type];\n\n if (!sides || sides < 3) {\n logger.warn(LOG_PREFIX, `Invalid polygon: need sides >= 3`, { type: shape.type, sides });\n return null;\n }\n\n logger.debug(LOG_PREFIX, `Generating polygon`, { type: shape.type, sides, aspectRatio: shape.aspectRatio });\n return generatePolygonVertices(sides, radius, shape.aspectRatio);\n}\n\n/**\n * Create a Matter.js body from vertices\n */\nfunction createBodyFromVertices(\n id: string,\n x: number,\n y: number,\n vertices: Vector2D[],\n renderOptions: Matter.IBodyRenderOptions\n): Matter.Body {\n const matterVertices = vertices.map(v => ({ x: v.x, y: v.y }));\n\n const body = Matter.Bodies.fromVertices(x, y, [matterVertices], {\n restitution: 0.3,\n friction: 0.1,\n frictionAir: 0.01,\n label: `entity:${id}`,\n render: renderOptions\n });\n\n // fromVertices can return a compound body if the vertices are concave\n // Matter.js will decompose them. We need to ensure the body is positioned correctly\n Matter.Body.setPosition(body, { x, y });\n\n return body;\n}\n\nexport function createBoundaries(bounds: Bounds): Matter.Body[] {\n const width = bounds.right - bounds.left;\n const height = bounds.bottom - bounds.top;\n const options = { isStatic: true, render: { visible: false } };\n return [\n // Ground\n Matter.Bodies.rectangle(\n bounds.left + width / 2,\n bounds.bottom + BOUNDARY_THICKNESS / 2,\n width,\n BOUNDARY_THICKNESS,\n { ...options, label: 'ground' }\n ),\n // Left wall\n Matter.Bodies.rectangle(\n bounds.left - BOUNDARY_THICKNESS / 2,\n bounds.top + height / 2,\n BOUNDARY_THICKNESS,\n height,\n { ...options, label: 'leftWall' }\n ),\n // Right wall\n Matter.Bodies.rectangle(\n bounds.right + BOUNDARY_THICKNESS / 2,\n bounds.top + height / 2,\n BOUNDARY_THICKNESS,\n height,\n { ...options, label: 'rightWall' }\n )\n ];\n}\n\nexport interface BoundariesResult {\n walls: Matter.Body[];\n floorSegments: Matter.Body[];\n}\n\nexport function createBoundariesWithFloorConfig(bounds: Bounds, floorConfig?: FloorConfig): BoundariesResult {\n const width = bounds.right - bounds.left;\n const height = bounds.bottom - bounds.top;\n const options = { isStatic: true, render: { visible: false } };\n\n // Create walls (left and right)\n const walls = [\n // Left wall\n Matter.Bodies.rectangle(\n bounds.left - BOUNDARY_THICKNESS / 2,\n bounds.top + height / 2,\n BOUNDARY_THICKNESS,\n height,\n { ...options, label: 'leftWall' }\n ),\n // Right wall\n Matter.Bodies.rectangle(\n bounds.right + BOUNDARY_THICKNESS / 2,\n bounds.top + height / 2,\n BOUNDARY_THICKNESS,\n height,\n { ...options, label: 'rightWall' }\n )\n ];\n\n // Create floor segment(s)\n const segmentCount = floorConfig?.segments ?? 1;\n const segmentWidth = width / segmentCount;\n const floorSegments: Matter.Body[] = [];\n\n for (let i = 0; i < segmentCount; i++) {\n const segmentX = bounds.left + (i + 0.5) * segmentWidth;\n floorSegments.push(\n Matter.Bodies.rectangle(\n segmentX,\n bounds.bottom + BOUNDARY_THICKNESS / 2,\n segmentWidth,\n BOUNDARY_THICKNESS,\n { ...options, label: `floor-segment-${i}` }\n )\n );\n }\n\n return { walls, floorSegments };\n}\n\n/**\n * Create render options for entity (non-image)\n */\nfunction createFillRenderOptions(config: ObjectConfig): Matter.IBodyRenderOptions {\n return {\n fillStyle: config.fillStyle ?? '#ff0000'\n };\n}\n\n/**\n * Create render options for entity with sprite, using actual image dimensions\n */\nfunction createSpriteRenderOptions(config: ObjectConfig, imageWidth: number, imageHeight: number): Matter.IBodyRenderOptions {\n const radius = config.radius ?? DEFAULT_RADIUS;\n const targetSize = radius * 2;\n const maxDim = Math.max(imageWidth, imageHeight);\n const spriteScale = targetSize / maxDim;\n\n return {\n sprite: {\n texture: config.imageUrl!,\n xScale: spriteScale,\n yScale: spriteScale\n }\n };\n}\n\n/**\n * Create a circle body (default fallback) - no image\n */\nfunction createCircleEntity(id: string, config: ObjectConfig): Matter.Body {\n const radius = config.radius ?? DEFAULT_RADIUS;\n logger.debug(LOG_PREFIX, `Creating circle entity`, { id, radius });\n return Matter.Bodies.circle(config.x, config.y, radius, {\n restitution: 0.3,\n friction: 0.1,\n frictionAir: 0.01,\n label: `entity:${id}`,\n render: createFillRenderOptions(config)\n });\n}\n\n/**\n * Create a circle body with sprite - requires image dimensions\n */\nfunction createCircleEntityWithSprite(id: string, config: ObjectConfig, imageWidth: number, imageHeight: number): Matter.Body {\n const radius = config.radius ?? DEFAULT_RADIUS;\n logger.debug(LOG_PREFIX, `Creating circle entity with sprite`, { id, radius, imageWidth, imageHeight });\n return Matter.Bodies.circle(config.x, config.y, radius, {\n restitution: 0.3,\n friction: 0.1,\n frictionAir: 0.01,\n label: `entity:${id}`,\n render: createSpriteRenderOptions(config, imageWidth, imageHeight)\n });\n}\n\n/**\n * Synchronous entity creation - handles circles and preset polygon shapes\n * For image-based shape extraction, use createEntityAsync instead\n */\nexport function createEntity(id: string, config: ObjectConfig): Matter.Body {\n const shape = config.shape;\n\n // No shape config or circle - use circle\n if (!shape || shape.type === 'circle') {\n return createCircleEntity(id, config);\n }\n\n // If there's an imageUrl, warn that they should use async for shape extraction\n if (config.imageUrl) {\n logger.warn(LOG_PREFIX, `Image provided but using sync createEntity - shape won't be extracted. Use createEntityAsync for image shape extraction.`);\n }\n\n // Get vertices for polygon shape\n const radius = config.radius ?? DEFAULT_RADIUS;\n const vertices = getShapeVertices(shape, radius);\n if (!vertices) {\n logger.warn(LOG_PREFIX, `Failed to get vertices, falling back to circle`, { type: shape.type });\n return createCircleEntity(id, config);\n }\n\n logger.info(LOG_PREFIX, `Creating polygon entity`, { id, type: shape.type, vertices: vertices.length });\n return createBodyFromVertices(id, config.x, config.y, vertices, createFillRenderOptions(config));\n}\n\n/**\n * Async entity creation - automatically extracts shape from image if imageUrl provided\n * Falls back to circle if extraction fails\n */\nexport async function createEntityAsync(id: string, config: ObjectConfig): Promise<Matter.Body> {\n const shape = config.shape;\n\n // If explicit circle requested, use circle\n if (shape?.type === 'circle') {\n logger.info(LOG_PREFIX, `Creating circle entity (explicit)`, { id });\n return createCircleEntity(id, config);\n }\n\n // If imageUrl provided, try to extract shape from it\n if (config.imageUrl) {\n const radius = config.radius ?? DEFAULT_RADIUS;\n logger.debug(LOG_PREFIX, `Attempting to extract shape from image`, { id, imageUrl: config.imageUrl });\n const { vertices, imageWidth, imageHeight } = await getVerticesAndDimensionsFromImage(config.imageUrl, radius * 2);\n\n if (vertices.length >= 3) {\n logger.debug(LOG_PREFIX, `Image shape extraction succeeded`, { id, vertices: vertices.length, imageWidth, imageHeight });\n return createBodyFromVertices(id, config.x, config.y, vertices, createSpriteRenderOptions(config, imageWidth, imageHeight));\n }\n\n logger.warn(LOG_PREFIX, `Image shape extraction failed, falling back to circle`, { id, verticesFound: vertices.length });\n // Still use sprite for the circle fallback\n return createCircleEntityWithSprite(id, config, imageWidth, imageHeight);\n }\n\n // No image - check for shape config (polygon presets, custom vertices)\n if (shape) {\n const shapeRadius = config.radius ?? DEFAULT_RADIUS;\n const vertices = getShapeVertices(shape, shapeRadius);\n if (vertices) {\n logger.info(LOG_PREFIX, `Creating polygon entity`, { id, type: shape.type, vertices: vertices.length });\n return createBodyFromVertices(id, config.x, config.y, vertices, createFillRenderOptions(config));\n }\n logger.warn(LOG_PREFIX, `Failed to get vertices from shape config, falling back to circle`, { type: shape.type });\n }\n\n // Default: circle\n logger.info(LOG_PREFIX, `Creating circle entity (default)`, { id });\n return createCircleEntity(id, config);\n}\n\nexport function createObstacle(id: string, config: ObjectConfig, isStatic: boolean = true): Matter.Body {\n const width = config.width ?? 100;\n const height = config.height ?? 20;\n return Matter.Bodies.rectangle(config.x, config.y, width, height, {\n isStatic,\n label: `obstacle:${id}`,\n render: {\n visible: true,\n fillStyle: config.fillStyle ?? '#4a4a6a'\n }\n });\n}\n\n/**\n * Get image dimensions without extracting vertices.\n * Useful for calculating letter spacing before creating obstacles.\n */\nexport async function getImageDimensions(imageUrl: string): Promise<{ width: number; height: number }> {\n const img = await loadImage(imageUrl);\n return { width: img.width, height: img.height };\n}\n\n/**\n * Result from createBoxObstacleWithInfo\n */\nexport interface BoxObstacleResult {\n body: Matter.Body;\n /** Original image dimensions */\n imageWidth: number;\n imageHeight: number;\n /** Scaled dimensions (how large the image appears at target size) */\n scaledWidth: number;\n scaledHeight: number;\n /** Clip bounds within the original image */\n clipBounds: ClipBounds;\n /** Offset from image center to clip center (in scaled coordinates) */\n clipOffset: Vector2D;\n}\n\n/**\n * Create an image-clipped obstacle centered at (config.x, config.y).\n * Image center goes at that position, not the shape centroid.\n */\nexport async function createBoxObstacle(id: string, config: ObjectConfig, isStatic: boolean = true): Promise<Matter.Body> {\n const result = await createBoxObstacleWithInfo(id, config, isStatic);\n return result.body;\n}\n\n/**\n * Create an image-clipped obstacle with full positioning info.\n * Returns the body plus dimension info for debug rendering.\n */\nexport async function createBoxObstacleWithInfo(id: string, config: ObjectConfig, isStatic: boolean = true): Promise<BoxObstacleResult> {\n const size = config.size ?? 50;\n\n const { vertices, imageWidth, imageHeight, clipBounds, clipOffset } = await getVerticesAndDimensionsFromImage(config.imageUrl!, size);\n\n const maxDim = Math.max(imageWidth, imageHeight);\n const spriteScale = size / maxDim;\n const scaledWidth = imageWidth * spriteScale;\n const scaledHeight = imageHeight * spriteScale;\n\n let body: Matter.Body;\n\n if (vertices.length >= 3) {\n // Translate vertices to world position (like TTF glyph approach)\n const worldVertices = vertices.map(v => ({\n x: config.x + v.x,\n y: config.y + v.y\n }));\n\n // Use fromVertices - it will position body at centroid of these world vertices\n body = Matter.Bodies.fromVertices(config.x, config.y, [worldVertices], {\n isStatic,\n label: `obstacle:${id}`,\n render: {\n sprite: {\n texture: config.imageUrl!,\n xScale: spriteScale,\n yScale: spriteScale\n }\n }\n });\n\n // Calculate sprite offset AFTER body creation (we now know where centroid landed)\n // Offset sprite to compensate for body being at centroid instead of image center\n const spriteOffsetX = (config.x - body.position.x) / scaledWidth;\n const spriteOffsetY = (config.y - body.position.y) / scaledHeight;\n\n if (body.render.sprite) {\n (body.render.sprite as unknown as Record<string, unknown>).xOffset = 0.5 + spriteOffsetX;\n (body.render.sprite as unknown as Record<string, unknown>).yOffset = 0.5 + spriteOffsetY;\n }\n } else {\n // Fallback to rectangle\n body = Matter.Bodies.rectangle(config.x, config.y, scaledWidth, scaledHeight, {\n isStatic,\n label: `obstacle:${id}`,\n render: {\n sprite: {\n texture: config.imageUrl!,\n xScale: spriteScale,\n yScale: spriteScale\n }\n }\n });\n }\n\n return {\n body,\n imageWidth,\n imageHeight,\n scaledWidth,\n scaledHeight,\n clipBounds,\n clipOffset\n };\n}\n\n/**\n * Create an image-based obstacle asynchronously.\n * Extracts shape from image alpha channel.\n */\nexport async function createObstacleAsync(id: string, config: ObjectConfig, isStatic: boolean = true): Promise<Matter.Body> {\n // If no imageUrl, fall back to rectangle\n if (!config.imageUrl) {\n return createObstacle(id, config, isStatic);\n }\n\n const size = config.size ?? 50;\n logger.info(LOG_PREFIX, `Creating image-based obstacle`, { id, imageUrl: config.imageUrl, size });\n\n const { vertices, imageWidth, imageHeight } = await getVerticesAndDimensionsFromImage(config.imageUrl, size);\n\n if (vertices.length >= 3) {\n logger.info(LOG_PREFIX, `Image obstacle shape extraction succeeded`, { id, vertices: vertices.length, imageWidth, imageHeight });\n const matterVertices = vertices.map(v => ({ x: v.x, y: v.y }));\n\n const maxDim = Math.max(imageWidth, imageHeight);\n const spriteScale = size / maxDim;\n\n const body = Matter.Bodies.fromVertices(config.x, config.y, [matterVertices], {\n isStatic,\n label: `obstacle:${id}`,\n render: {\n sprite: {\n texture: config.imageUrl,\n xScale: spriteScale,\n yScale: spriteScale\n }\n }\n });\n\n // Vertices are now centered on image dimensions, so setPosition aligns correctly\n Matter.Body.setPosition(body, { x: config.x, y: config.y });\n return body;\n }\n\n // Fall back to rectangle if shape extraction fails\n logger.warn(LOG_PREFIX, `Image obstacle shape extraction failed, falling back to rectangle`, { id });\n return createObstacle(id, config, isStatic);\n}\n","type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3\n};\n\nlet currentLevel: LogLevel = 'info';\n\nexport function setLogLevel(level: LogLevel): void {\n currentLevel = level;\n}\n\nexport function getLogLevel(): LogLevel {\n return currentLevel;\n}\n\nfunction shouldLog(level: LogLevel): boolean {\n return LOG_LEVELS[level] >= LOG_LEVELS[currentLevel];\n}\n\nfunction formatMessage(prefix: string, message: string, data?: unknown): string {\n const timestamp = new Date().toISOString().split('T')[1].slice(0, -1);\n const base = `[${timestamp}] [${prefix}] ${message}`;\n return data !== undefined ? `${base} ${JSON.stringify(data)}` : base;\n}\n\nexport const logger = {\n debug(prefix: string, message: string, data?: unknown): void {\n if (shouldLog('debug')) {\n console.debug(formatMessage(prefix, message, data));\n }\n },\n\n info(prefix: string, message: string, data?: unknown): void {\n if (shouldLog('info')) {\n console.info(formatMessage(prefix, message, data));\n }\n },\n\n warn(prefix: string, message: string, data?: unknown): void {\n if (shouldLog('warn')) {\n console.warn(formatMessage(prefix, message, data));\n }\n },\n\n error(prefix: string, message: string, data?: unknown): void {\n if (shouldLog('error')) {\n console.error(formatMessage(prefix, message, data));\n }\n }\n};\n","import { logger } from './logger';\n\nexport interface Vector2D {\n x: number;\n y: number;\n}\n\nconst LOG_PREFIX = 'ImageClip';\nconst ALPHA_THRESHOLD = 128;\n\n// Bounding box of the clipped content within the original image\nexport interface ClipBounds {\n minX: number;\n minY: number;\n maxX: number;\n maxY: number;\n}\n\n// Cache for extracted vertices - keyed by imageUrl\n// We cache the raw contour data (before scaling) so it can be reused at different sizes\ninterface ContourCacheEntry {\n vertices: Vector2D[]; // Normalized to unit size (-0.5 to 0.5 range), centered on clip bounds\n imageWidth: number;\n imageHeight: number;\n clipBounds: ClipBounds; // Bounding box of non-transparent content\n timestamp: number;\n}\n\nconst contourCache: Map<string, ContourCacheEntry> = new Map();\nconst CACHE_MAX_SIZE = 100;\nconst CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\n\nexport function loadImage(url: string): Promise<HTMLImageElement> {\n logger.debug(LOG_PREFIX, `Loading image: ${url}`);\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.onload = () => {\n logger.debug(LOG_PREFIX, `Image loaded successfully`, { width: img.width, height: img.height });\n resolve(img);\n };\n img.onerror = () => {\n logger.error(LOG_PREFIX, `Failed to load image: ${url}`);\n reject(new Error(`Failed to load image: ${url}`));\n };\n img.src = url;\n });\n}\n\nexport function getImageAlphaData(img: HTMLImageElement): { data: Uint8ClampedArray; width: number; height: number } {\n const canvas = document.createElement('canvas');\n canvas.width = img.width;\n canvas.height = img.height;\n const ctx = canvas.getContext('2d')!;\n ctx.drawImage(img, 0, 0);\n const imageData = ctx.getImageData(0, 0, img.width, img.height);\n return { data: imageData.data, width: img.width, height: img.height };\n}\n\nfunction getAlpha(data: Uint8ClampedArray, width: number, x: number, y: number): number {\n if (x < 0 || y < 0 || x >= width) return 0;\n const idx = (y * width + x) * 4 + 3;\n return data[idx] ?? 0;\n}\n\nfunction isSolid(data: Uint8ClampedArray, width: number, x: number, y: number): boolean {\n return getAlpha(data, width, x, y) >= ALPHA_THRESHOLD;\n}\n\n// Marching squares to extract contour\nexport function extractContour(data: Uint8ClampedArray, width: number, height: number): Vector2D[] {\n logger.debug(LOG_PREFIX, `Extracting contour from image`, { width, height });\n\n // Find starting point (first solid pixel from top-left)\n let startX = -1;\n let startY = -1;\n\n outer: for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n if (isSolid(data, width, x, y)) {\n startX = x;\n startY = y;\n break outer;\n }\n }\n }\n\n if (startX === -1) {\n logger.warn(LOG_PREFIX, `No solid pixels found in image`);\n return [];\n }\n\n logger.debug(LOG_PREFIX, `Found starting point`, { startX, startY });\n\n const contour: Vector2D[] = [];\n let x = startX;\n let y = startY;\n let dir = 0; // 0=right, 1=down, 2=left, 3=up\n\n const dx = [1, 0, -1, 0];\n const dy = [0, 1, 0, -1];\n\n do {\n contour.push({ x, y });\n\n // Try to turn left first, then straight, then right, then back\n for (let i = 0; i < 4; i++) {\n const newDir = (dir + 3 + i) % 4; // left, straight, right, back\n const nx = x + dx[newDir];\n const ny = y + dy[newDir];\n\n if (nx >= 0 && nx < width && ny >= 0 && ny < height && isSolid(data, width, nx, ny)) {\n x = nx;\n y = ny;\n dir = newDir;\n break;\n }\n }\n\n // Prevent infinite loops\n if (contour.length > width * height) {\n logger.warn(LOG_PREFIX, `Contour extraction hit safety limit, breaking`);\n break;\n }\n } while (x !== startX || y !== startY);\n\n logger.debug(LOG_PREFIX, `Contour extracted`, { pointCount: contour.length });\n return contour;\n}\n\n// Simplify contour using Ramer-Douglas-Peucker algorithm\nexport function simplifyContour(points: Vector2D[], epsilon: number): Vector2D[] {\n if (points.length < 3) return points;\n\n let maxDist = 0;\n let maxIdx = 0;\n\n const first = points[0];\n const last = points[points.length - 1];\n\n for (let i = 1; i < points.length - 1; i++) {\n const dist = perpendicularDistance(points[i], first, last);\n if (dist > maxDist) {\n maxDist = dist;\n maxIdx = i;\n }\n }\n\n if (maxDist > epsilon) {\n const left = simplifyContour(points.slice(0, maxIdx + 1), epsilon);\n const right = simplifyContour(points.slice(maxIdx), epsilon);\n return left.slice(0, -1).concat(right);\n }\n\n return [first, last];\n}\n\nfunction perpendicularDistance(point: Vector2D, lineStart: Vector2D, lineEnd: Vector2D): number {\n const dx = lineEnd.x - lineStart.x;\n const dy = lineEnd.y - lineStart.y;\n const mag = Math.sqrt(dx * dx + dy * dy);\n\n if (mag === 0) {\n return Math.sqrt((point.x - lineStart.x) ** 2 + (point.y - lineStart.y) ** 2);\n }\n\n const u = ((point.x - lineStart.x) * dx + (point.y - lineStart.y) * dy) / (mag * mag);\n const closestX = lineStart.x + u * dx;\n const closestY = lineStart.y + u * dy;\n\n return Math.sqrt((point.x - closestX) ** 2 + (point.y - closestY) ** 2);\n}\n\n/**\n * Calculate the bounding box of a set of vertices (the clipped content bounds)\n */\nexport function getClipBounds(vertices: Vector2D[]): ClipBounds {\n if (vertices.length === 0) {\n return { minX: 0, minY: 0, maxX: 0, maxY: 0 };\n }\n\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;\n for (const v of vertices) {\n minX = Math.min(minX, v.x);\n minY = Math.min(minY, v.y);\n maxX = Math.max(maxX, v.x);\n maxY = Math.max(maxY, v.y);\n }\n return { minX, minY, maxX, maxY };\n}\n\n// Scale and center vertices for Matter.js body creation\n// When imageWidth/imageHeight are provided, centers based on image dimensions (for consistent alignment)\n// Otherwise falls back to centering based on shape bounding box\nexport function normalizeVertices(\n vertices: Vector2D[],\n targetSize: number,\n imageWidth?: number,\n imageHeight?: number\n): Vector2D[] {\n if (vertices.length === 0) return [];\n\n // Use image dimensions if provided, otherwise calculate from shape bounds\n let centerX: number;\n let centerY: number;\n let refWidth: number;\n let refHeight: number;\n\n if (imageWidth !== undefined && imageHeight !== undefined) {\n // Center based on image dimensions - all same-size images will align\n centerX = imageWidth / 2;\n centerY = imageHeight / 2;\n refWidth = imageWidth;\n refHeight = imageHeight;\n } else {\n // Fallback: center based on shape bounding box\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;\n for (const v of vertices) {\n minX = Math.min(minX, v.x);\n minY = Math.min(minY, v.y);\n maxX = Math.max(maxX, v.x);\n maxY = Math.max(maxY, v.y);\n }\n centerX = (minX + maxX) / 2;\n centerY = (minY + maxY) / 2;\n refWidth = maxX - minX;\n refHeight = maxY - minY;\n }\n\n const scale = targetSize / Math.max(refWidth, refHeight);\n\n return vertices.map(v => ({\n x: (v.x - centerX) * scale,\n y: (v.y - centerY) * scale\n }));\n}\n\n/**\n * Scale cached unit vertices to target size\n */\nfunction scaleVertices(vertices: Vector2D[], targetSize: number): Vector2D[] {\n return vertices.map(v => ({\n x: v.x * targetSize,\n y: v.y * targetSize\n }));\n}\n\n/**\n * Clean up expired cache entries\n */\nfunction cleanupCache(): void {\n const now = Date.now();\n for (const [key, entry] of contourCache) {\n if (now - entry.timestamp > CACHE_TTL_MS) {\n contourCache.delete(key);\n }\n }\n // If still over limit, remove oldest entries\n if (contourCache.size > CACHE_MAX_SIZE) {\n const entries = Array.from(contourCache.entries())\n .sort((a, b) => a[1].timestamp - b[1].timestamp);\n const toRemove = entries.slice(0, entries.length - CACHE_MAX_SIZE);\n for (const [key] of toRemove) {\n contourCache.delete(key);\n }\n }\n}\n\n/**\n * Result from getVerticesAndDimensionsFromImage\n */\nexport interface ImageClipResult {\n /** Collision vertices, scaled to targetSize and centered on image center */\n vertices: Vector2D[];\n /** Original image width in pixels */\n imageWidth: number;\n /** Original image height in pixels */\n imageHeight: number;\n /** Bounding box of the non-transparent content within the original image */\n clipBounds: ClipBounds;\n /** Offset from image center to clip center (in target size coordinates) */\n clipOffset: Vector2D;\n}\n\n/**\n * Extract vertices from image, with caching for repeated calls.\n * Vertices are cached normalized to unit size and scaled to targetSize on retrieval.\n */\nexport async function getVerticesFromImage(imageUrl: string, targetSize: number): Promise<Vector2D[]> {\n const result = await getVerticesAndDimensionsFromImage(imageUrl, targetSize);\n return result.vertices;\n}\n\n/**\n * Extract vertices from image along with original image dimensions and clip bounds.\n * Returns information needed for proper letter spacing and positioning.\n */\nexport async function getVerticesAndDimensionsFromImage(imageUrl: string, targetSize: number): Promise<ImageClipResult> {\n // Check cache first\n const cached = contourCache.get(imageUrl);\n if (cached) {\n logger.debug(LOG_PREFIX, `Cache hit for image`, { imageUrl, targetSize, cachedVertices: cached.vertices.length });\n cached.timestamp = Date.now(); // Refresh TTL on access\n\n // Calculate clip offset in target coordinates\n const scale = targetSize / Math.max(cached.imageWidth, cached.imageHeight);\n const imageCenterX = cached.imageWidth / 2;\n const imageCenterY = cached.imageHeight / 2;\n const clipCenterX = (cached.clipBounds.minX + cached.clipBounds.maxX) / 2;\n const clipCenterY = (cached.clipBounds.minY + cached.clipBounds.maxY) / 2;\n\n return {\n vertices: scaleVertices(cached.vertices, targetSize),\n imageWidth: cached.imageWidth,\n imageHeight: cached.imageHeight,\n clipBounds: cached.clipBounds,\n clipOffset: {\n x: (clipCenterX - imageCenterX) * scale,\n y: (clipCenterY - imageCenterY) * scale\n }\n };\n }\n\n logger.info(LOG_PREFIX, `Extracting vertices from image (cache miss)`, { imageUrl, targetSize });\n\n try {\n const img = await loadImage(imageUrl);\n const { data, width, height } = getImageAlphaData(img);\n\n const contour = extractContour(data, width, height);\n if (contour.length < 3) {\n logger.warn(LOG_PREFIX, `Contour has insufficient points`, { pointCount: contour.length });\n const emptyClipBounds = { minX: 0, minY: 0, maxX: width, maxY: height };\n return {\n vertices: [],\n imageWidth: width,\n imageHeight: height,\n clipBounds: emptyClipBounds,\n clipOffset: { x: 0, y: 0 }\n };\n }\n\n // Calculate clip bounds before simplification (for accurate bounds)\n const clipBounds = getClipBounds(contour);\n\n // Simplify based on image size - larger images need more aggressive simplification\n const epsilon = Math.max(width, height) / 50;\n const simplified = simplifyContour(contour, epsilon);\n logger.debug(LOG_PREFIX, `Simplified contour`, { original: contour.length, simplified: simplified.length, epsilon });\n\n // Ensure we have at least 3 vertices for a valid polygon\n if (simplified.length < 3) {\n logger.warn(LOG_PREFIX, `Simplified contour has insufficient points`, { pointCount: simplified.length });\n return {\n vertices: [],\n imageWidth: width,\n imageHeight: height,\n clipBounds,\n clipOffset: { x: 0, y: 0 }\n };\n }\n\n // Normalize to unit size, centered on IMAGE dimensions (not shape bounds)\n // This ensures all same-size images align consistently\n const unitVertices = normalizeVertices(simplified, 1, width, height);\n\n // Cache the unit-sized vertices along with image dimensions and clip bounds\n contourCache.set(imageUrl, {\n vertices: unitVertices,\n imageWidth: width,\n imageHeight: height,\n clipBounds,\n timestamp: Date.now()\n });\n cleanupCache();\n\n logger.info(LOG_PREFIX, `Vertices extracted and cached`, {\n imageUrl,\n vertexCount: unitVertices.length,\n width,\n height,\n clipBounds\n });\n\n // Calculate clip offset in target coordinates\n const scale = targetSize / Math.max(width, height);\n const imageCenterX = width / 2;\n const imageCenterY = height / 2;\n const clipCenterX = (clipBounds.minX + clipBounds.maxX) / 2;\n const clipCenterY = (clipBounds.minY + clipBounds.maxY) / 2;\n\n // Scale to requested size and return\n return {\n vertices: scaleVertices(unitVertices, targetSize),\n imageWidth: width,\n imageHeight: height,\n clipBounds,\n clipOffset: {\n x: (clipCenterX - imageCenterX) * scale,\n y: (clipCenterY - imageCenterY) * scale\n }\n };\n } catch (error) {\n logger.error(LOG_PREFIX, `Failed to extract vertices from image`, { error: String(error) });\n return {\n vertices: [],\n imageWidth: 0,\n imageHeight: 0,\n clipBounds: { minX: 0, minY: 0, maxX: 0, maxY: 0 },\n clipOffset: { x: 0, y: 0 }\n };\n }\n}\n\n/**\n * Clear the vertex cache (useful for testing or memory management)\n */\nexport function clearVertexCache(): void {\n contourCache.clear();\n logger.debug(LOG_PREFIX, `Vertex cache cleared`);\n}\n\n/**\n * Get cache statistics\n */\nexport function getVertexCacheStats(): { size: number; maxSize: number } {\n return { size: contourCache.size, maxSize: CACHE_MAX_SIZE };\n}\n\n// Cache for tinted images - keyed by \"imageUrl:color\"\nconst tintedImageCache: Map<string, string> = new Map();\nconst TINTED_CACHE_MAX_SIZE = 200;\n\n/**\n * Parse a CSS color string to RGB values\n */\nfunction parseColor(color: string): { r: number; g: number; b: number } | null {\n // Create a temporary canvas to parse the color\n const canvas = document.createElement('canvas');\n canvas.width = 1;\n canvas.height = 1;\n const ctx = canvas.getContext('2d')!;\n ctx.fillStyle = color;\n ctx.fillRect(0, 0, 1, 1);\n const data = ctx.getImageData(0, 0, 1, 1).data;\n return { r: data[0], g: data[1], b: data[2] };\n}\n\n/**\n * Tint an image by replacing all non-transparent pixels with the specified color,\n * preserving the original alpha values.\n *\n * @param imageUrl - URL of the image to tint\n * @param color - CSS color string (e.g., '#ff0000', 'red', 'rgb(255,0,0)')\n * @returns Data URL of the tinted image\n */\nexport async function tintImage(imageUrl: string, color: string): Promise<string> {\n const cacheKey = `${imageUrl}:${color}`;\n\n // Check cache first\n const cached = tintedImageCache.get(cacheKey);\n if (cached) {\n logger.debug(LOG_PREFIX, `Tinted image cache hit`, { imageUrl, color });\n return cached;\n }\n\n logger.debug(LOG_PREFIX, `Tinting image`, { imageUrl, color });\n\n try {\n const img = await loadImage(imageUrl);\n const canvas = document.createElement('canvas');\n canvas.width = img.width;\n canvas.height = img.height;\n const ctx = canvas.getContext('2d')!;\n\n // Draw original image\n ctx.drawImage(img, 0, 0);\n\n // Get image data\n const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);\n const data = imageData.data;\n\n // Parse the target color\n const rgb = parseColor(color);\n if (!rgb) {\n logger.warn(LOG_PREFIX, `Failed to parse color, returning original image`, { color });\n return imageUrl;\n }\n\n // Replace colors while preserving alpha\n for (let i = 0; i < data.length; i += 4) {\n const alpha = data[i + 3];\n if (alpha > 0) {\n data[i] = rgb.r; // R\n data[i + 1] = rgb.g; // G\n data[i + 2] = rgb.b; // B\n // data[i + 3] stays the same (alpha)\n }\n }\n\n // Put modified data back\n ctx.putImageData(imageData, 0, 0);\n\n // Convert to data URL\n const dataUrl = canvas.toDataURL('image/png');\n\n // Cache the result\n tintedImageCache.set(cacheKey, dataUrl);\n\n // Clean up cache if too large\n if (tintedImageCache.size > TINTED_CACHE_MAX_SIZE) {\n const firstKey = tintedImageCache.keys().next().value;\n if (firstKey) {\n tintedImageCache.delete(firstKey);\n }\n }\n\n logger.debug(LOG_PREFIX, `Image tinted successfully`, { imageUrl, color });\n return dataUrl;\n } catch (error) {\n logger.error(LOG_PREFIX, `Failed to tint image`, { error: String(error) });\n return imageUrl; // Return original on error\n }\n}\n\n/**\n * Clear the tinted image cache\n */\nexport function clearTintedImageCache(): void {\n tintedImageCache.clear();\n logger.debug(LOG_PREFIX, `Tinted image cache cleared`);\n}\n","import opentype from 'opentype.js';\nimport { logger } from './logger';\n\nconst LOG_PREFIX = 'FontLoader';\n\nexport interface Vector2D {\n x: number;\n y: number;\n}\n\nexport interface GlyphData {\n vertices: Vector2D[];\n advanceWidth: number;\n leftSideBearing: number;\n boundingBox: { x1: number; y1: number; x2: number; y2: number } | null;\n}\n\nexport interface LoadedFont {\n font: opentype.Font;\n unitsPerEm: number;\n ascender: number;\n descender: number;\n}\n\n// Cache for loaded fonts\nconst fontCache: Map<string, LoadedFont> = new Map();\n\n/**\n * Load a TTF/OTF font from a URL\n */\nexport async function loadFont(fontUrl: string): Promise<LoadedFont> {\n const cached = fontCache.get(fontUrl);\n if (cached) {\n logger.debug(LOG_PREFIX, `Font cache hit`, { fontUrl });\n return cached;\n }\n\n logger.info(LOG_PREFIX, `Loading font`, { fontUrl });\n\n const font = await opentype.load(fontUrl);\n\n const loadedFont: LoadedFont = {\n font,\n unitsPerEm: font.unitsPerEm,\n ascender: font.ascender,\n descender: font.descender\n };\n\n fontCache.set(fontUrl, loadedFont);\n logger.info(LOG_PREFIX, `Font loaded`, { fontUrl, unitsPerEm: font.unitsPerEm });\n\n return loadedFont;\n}\n\n/**\n * Convert a glyph path to vertices for Matter.js collision\n * Returns vertices scaled to the target font size\n */\nexport function glyphToVertices(\n glyph: opentype.Glyph,\n fontSize: number,\n unitsPerEm: number\n): Vector2D[] {\n const path = glyph.getPath(0, 0, fontSize);\n const commands = path.commands;\n\n if (commands.length === 0) {\n return [];\n }\n\n const vertices: Vector2D[] = [];\n let currentX = 0;\n let currentY = 0;\n\n for (const cmd of commands) {\n switch (cmd.type) {\n case 'M': // Move to\n currentX = cmd.x;\n currentY = cmd.y;\n vertices.push({ x: currentX, y: currentY });\n break;\n\n case 'L': // Line to\n currentX = cmd.x;\n currentY = cmd.y;\n vertices.push({ x: currentX, y: currentY });\n break;\n\n case 'Q': // Quadratic bezier - sample points along curve\n {\n const steps = 4;\n for (let t = 1; t <= steps; t++) {\n const tNorm = t / steps;\n const x = (1 - tNorm) * (1 - tNorm) * currentX +\n 2 * (1 - tNorm) * tNorm * cmd.x1 +\n tNorm * tNorm * cmd.x;\n const y = (1 - tNorm) * (1 - tNorm) * currentY +\n 2 * (1 - tNorm) * tNorm * cmd.y1 +\n tNorm * tNorm * cmd.y;\n vertices.push({ x, y });\n }\n currentX = cmd.x;\n currentY = cmd.y;\n }\n break;\n\n case 'C': // Cubic bezier - sample points along curve\n {\n const steps = 6;\n for (let t = 1; t <= steps; t++) {\n const tNorm = t / steps;\n const mt = 1 - tNorm;\n const x = mt * mt * mt * currentX +\n 3 * mt * mt * tNorm * cmd.x1 +\n 3 * mt * tNorm * tNorm * cmd.x2 +\n tNorm * tNorm * tNorm * cmd.x;\n const y = mt * mt * mt * currentY +\n 3 * mt * mt * tNorm * cmd.y1 +\n 3 * mt * tNorm * tNorm * cmd.y2 +\n tNorm * tNorm * tNorm * cmd.y;\n vertices.push({ x, y });\n }\n currentX = cmd.x;\n currentY = cmd.y;\n }\n break;\n\n case 'Z': // Close path\n // Path closed, don't add duplicate vertex\n break;\n }\n }\n\n // Simplify vertices to reduce count (remove colinear points)\n return simplifyVertices(vertices, fontSize / 50);\n}\n\n/**\n * Simplify vertices by removing points that are too close together\n */\nfunction simplifyVertices(vertices: Vector2D[], minDistance: number): Vector2D[] {\n if (vertices.length < 3) return vertices;\n\n const result: Vector2D[] = [vertices[0]];\n\n for (let i = 1; i < vertices.length; i++) {\n const last = result[result.length - 1];\n const curr = vertices[i];\n const dist = Math.sqrt((curr.x - last.x) ** 2 + (curr.y - last.y) ** 2);\n\n if (dist >= minDistance) {\n result.push(curr);\n }\n }\n\n return result;\n}\n\n/**\n * Get glyph data for a character including vertices and metrics\n */\nexport function getGlyphData(\n loadedFont: LoadedFont,\n char: string,\n fontSize: number\n): GlyphData {\n const { font, unitsPerEm } = loadedFont;\n const glyph = font.charToGlyph(char);\n\n if (!glyph) {\n logger.warn(LOG_PREFIX, `Glyph not found for character`, { char });\n return {\n vertices: [],\n advanceWidth: fontSize / 2,\n leftSideBearing: 0,\n boundingBox: null\n };\n }\n\n const scale = fontSize / unitsPerEm;\n const advanceWidth = (glyph.advanceWidth ?? 0) * scale;\n const leftSideBearing = (glyph.leftSideBearing ?? 0) * scale;\n\n const bbox = glyph.getBoundingBox();\n const boundingBox = bbox ? {\n x1: bbox.x1 * scale,\n y1: bbox.y1 * scale,\n x2: bbox.x2 * scale,\n y2: bbox.y2 * scale\n } : null;\n\n const vertices = glyphToVertices(glyph, fontSize, unitsPerEm);\n\n return {\n vertices,\n advanceWidth,\n leftSideBearing,\n boundingBox\n };\n}\n\n/**\n * Get kerning adjustment between two characters\n */\nexport function getKerning(\n loadedFont: LoadedFont,\n char1: string,\n char2: string,\n fontSize: number\n): number {\n const { font, unitsPerEm } = loadedFont;\n const glyph1 = font.charToGlyph(char1);\n const glyph2 = font.charToGlyph(char2);\n\n if (!glyph1 || !glyph2) return 0;\n\n const kerning = font.getKerningValue(glyph1, glyph2);\n const scale = fontSize / unitsPerEm;\n\n return kerning * scale;\n}\n\n/**\n * Calculate total width of a string with proper spacing and kerning\n */\nexport function measureText(\n loadedFont: LoadedFont,\n text: string,\n fontSize: number\n): number {\n let width = 0;\n const chars = text.split('');\n\n for (let i = 0; i < chars.length; i++) {\n const glyphData = getGlyphData(loadedFont, chars[i], fontSize);\n width += glyphData.advanceWidth;\n\n // Add kerning with next character\n if (i < chars.length - 1) {\n width += getKerning(loadedFont, chars[i], chars[i + 1], fontSize);\n }\n }\n\n return width;\n}\n\n/**\n * Clear the font cache\n */\nexport function clearFontCache(): void {\n fontCache.clear();\n logger.debug(LOG_PREFIX, `Font cache cleared`);\n}\n","import Matter from 'matter-js';\nimport type { Bounds } from './types';\n\nconst MOUSE_FORCE = 0.001;\n\nexport function applyMouseForce(entity: Matter.Body, mouseX: number, grounded: boolean): void {\n if (!grounded) return;\n const direction = Math.sign(mouseX - entity.position.x);\n Matter.Body.applyForce(entity, entity.position, { x: MOUSE_FORCE * direction, y: 0 });\n}\n\nexport function wrapHorizontal(entity: Matter.Body, bounds: Bounds): void {\n if (entity.position.x < bounds.left) {\n Matter.Body.setPosition(entity, { x: bounds.right, y: entity.position.y });\n } else if (entity.position.x > bounds.right) {\n Matter.Body.setPosition(entity, { x: bounds.left, y: entity.position.y });\n }\n}\n\nexport function isGrounded(entity: Matter.Body, groundY: number, threshold: number = 5): boolean {\n return entity.position.y >= groundY - threshold - 20; // 20 = approximate radius buffer\n}\n","import Matter from 'matter-js';\nimport type {\n EffectConfig,\n BurstEffectConfig,\n RainEffectConfig,\n StreamEffectConfig,\n EffectObjectConfig,\n ObjectConfig,\n Bounds\n} from './types';\nimport { logger } from './logger';\n\ntype SpawnObjectAsyncFn = (config: ObjectConfig) => Promise<string>;\ntype GetBodyFn = (id: string) => Matter.Body | null;\n\ninterface EffectState {\n config: EffectConfig;\n lastSpawnTime: number;\n /** For rain: accumulated fractional spawns */\n spawnAccumulator: number;\n}\n\n/**\n * Manages spawning effects (burst, rain, stream) that create objects over time.\n * Effects are persistent and run until disabled.\n */\nexport class EffectManager {\n private effects: Map<string, EffectState> = new Map();\n private bounds: Bounds;\n private spawnObjectAsync: SpawnObjectAsyncFn;\n private getBody: GetBodyFn;\n\n constructor(bounds: Bounds, spawnObjectAsync: SpawnObjectAsyncFn, getBody: GetBodyFn) {\n this.bounds = bounds;\n this.spawnObjectAsync = spawnObjectAsync;\n this.getBody = getBody;\n }\n\n /**\n * Update bounds when scene resizes\n */\n setBounds(bounds: Bounds): void {\n this.bounds = bounds;\n }\n\n /**\n * Add or update an effect configuration\n */\n setEffect(config: EffectConfig): void {\n const existing = this.effects.get(config.id);\n if (existing) {\n // Reset timing if effect is being enabled (was disabled, now enabled)\n const wasDisabled = !existing.config.enabled;\n const nowEnabled = config.enabled;\n if (wasDisabled && nowEnabled) {\n existing.lastSpawnTime = Date.now();\n existing.spawnAccumulator = 0;\n }\n existing.config = config;\n logger.debug('EffectManager', `Updated effect: ${config.id}`, { type: config.type, enabled: config.enabled });\n } else {\n this.effects.set(config.id, {\n config,\n lastSpawnTime: Date.now(),\n spawnAccumulator: 0\n });\n logger.debug('EffectManager', `Added effect: ${config.id}`, { type: config.type, enabled: config.enabled });\n }\n }\n\n /**\n * Remove an effect\n */\n removeEffect(id: string): void {\n this.effects.delete(id);\n logger.debug('EffectManager', `Removed effect: ${id}`);\n }\n\n /**\n * Enable or disable an effect\n */\n setEffectEnabled(id: string, enabled: boolean): void {\n const state = this.effects.get(id);\n if (state) {\n state.config.enabled = enabled;\n // Reset timing when enabling to prevent immediate burst\n if (enabled) {\n state.lastSpawnTime = Date.now();\n state.spawnAccumulator = 0;\n }\n logger.debug('EffectManager', `Effect ${id} ${enabled ? 'enabled' : 'disabled'}`);\n }\n }\n\n /**\n * Get current effect configuration\n */\n getEffect(id: string): EffectConfig | undefined {\n return this.effects.get(id)?.config;\n }\n\n /**\n * Get all effect IDs\n */\n getEffectIds(): string[] {\n return Array.from(this.effects.keys());\n }\n\n /**\n * Check if an effect is enabled\n */\n isEffectEnabled(id: string): boolean {\n return this.effects.get(id)?.config.enabled ?? false;\n }\n\n /**\n * Called each frame to update effects and spawn objects\n */\n update(): void {\n const now = Date.now();\n\n for (const state of this.effects.values()) {\n if (!state.config.enabled) continue;\n\n switch (state.config.type) {\n case 'burst':\n this.updateBurstEffect(state, now);\n break;\n case 'rain':\n this.updateRainEffect(state, now);\n break;\n case 'stream':\n this.updateStreamEffect(state, now);\n break;\n }\n }\n }\n\n private updateBurstEffect(state: EffectState, now: number): void {\n const config = state.config as BurstEffectConfig;\n const elapsed = now - state.lastSpawnTime;\n\n if (elapsed >= config.burstInterval) {\n state.lastSpawnTime = now;\n // Fire-and-forget async burst spawn\n this.spawnBurst(config);\n }\n }\n\n private updateRainEffect(state: EffectState, now: number): void {\n const config = state.config as RainEffectConfig;\n // Cap elapsed time to 100ms to prevent burst spawning after pauses/re-enables\n const elapsed = Math.min(now - state.lastSpawnTime, 100);\n state.lastSpawnTime = now;\n\n // Calculate how many objects to spawn this frame\n const deltaSeconds = elapsed / 1000;\n state.spawnAccumulator += config.spawnRate * deltaSeconds;\n\n // Spawn whole objects\n while (state.spawnAccumulator >= 1) {\n state.spawnAccumulator -= 1;\n this.spawnRainObject(config);\n }\n }\n\n private async spawnBurst(config: BurstEffectConfig): Promise<void> {\n const { bounds } = this;\n\n // Determine burst origin\n const originX = config.origin?.x ?? this.randomInRange(bounds.left + 50, bounds.right - 50);\n const originY = config.origin?.y ?? this.randomInRange(bounds.top + 50, bounds.bottom - 100);\n\n logger.debug('EffectManager', `Spawning burst at (${originX.toFixed(0)}, ${originY.toFixed(0)})`, { count: config.burstCount });\n\n // Prepare all spawn configs with their random angles\n const spawnData: Array<{ config: ObjectConfig; angle: number }> = [];\n for (let i = 0; i < config.burstCount; i++) {\n const objectConfig = this.selectObjectConfig(config.objectConfigs);\n if (!objectConfig) continue;\n\n const radius = this.calculateRadius(objectConfig);\n // Ensure 'falling' tag is present for dynamic behavior\n const tags = [...(objectConfig.objectConfig.tags ?? [])];\n if (!tags.includes('falling')) tags.push('falling');\n\n const fullConfig: ObjectConfig = {\n ...objectConfig.objectConfig,\n x: originX,\n y: originY,\n radius,\n tags\n };\n\n spawnData.push({\n config: fullConfig,\n angle: Math.random() * Math.PI * 2\n });\n }\n\n // Spawn all objects in parallel (uses cached image clipping for same images)\n const ids = await Promise.all(\n spawnData.map(data => this.spawnObjectAsync(data.config))\n );\n\n // Apply forces to all spawned objects\n for (let i = 0; i < ids.length; i++) {\n const body = this.getBody(ids[i]);\n if (body) {\n const angle = spawnData[i].angle;\n const force = config.burstForce;\n Matter.Body.setVelocity(body, {\n x: Math.cos(angle) * force,\n y: Math.sin(angle) * force\n });\n }\n }\n }\n\n private spawnRainObject(config: RainEffectConfig): void {\n const { bounds } = this;\n const spawnWidth = config.spawnWidth ?? 1;\n\n // Calculate spawn area\n const totalWidth = bounds.right - bounds.left;\n const spawnAreaWidth = totalWidth * spawnWidth;\n const spawnAreaStart = bounds.left + (totalWidth - spawnAreaWidth) / 2;\n\n const objectConfig = this.selectObjectConfig(config.objectConfigs);\n if (!objectConfig) return;\n\n const radius = this.calculateRadius(objectConfig);\n const x = this.randomInRange(spawnAreaStart + radius, spawnAreaStart + spawnAreaWidth - radius);\n const y = bounds.top - radius; // Spawn just above visible area\n\n // Ensure 'falling' tag is present for dynamic behavior\n const tags = [...(objectConfig.objectConfig.tags ?? [])];\n if (!tags.includes('falling')) tags.push('falling');\n\n const fullConfig: ObjectConfig = {\n ...objectConfig.objectConfig,\n x,\n y,\n radius,\n tags\n };\n\n // Fire-and-forget async spawn (no force needed for rain)\n this.spawnObjectAsync(fullConfig);\n }\n\n private updateStreamEffect(state: EffectState, now: number): void {\n const config = state.config as StreamEffectConfig;\n // Cap elapsed time to 100ms to prevent burst spawning after pauses/re-enables\n const elapsed = Math.min(now - state.lastSpawnTime, 100);\n state.lastSpawnTime = now;\n\n // Calculate how many objects to spawn this frame\n const deltaSeconds = elapsed / 1000;\n state.spawnAccumulator += config.spawnRate * deltaSeconds;\n\n // Spawn whole objects\n while (state.spawnAccumulator >= 1) {\n state.spawnAccumulator -= 1;\n this.spawnStreamObject(config);\n }\n }\n\n private async spawnStreamObject(config: StreamEffectConfig): Promise<void> {\n const objectConfig = this.selectObjectConfig(config.objectConfigs);\n if (!objectConfig) return;\n\n const radius = this.calculateRadius(objectConfig);\n\n // Ensure 'falling' tag is present for dynamic behavior\n const tags = [...(objectConfig.objectConfig.tags ?? [])];\n if (!tags.includes('falling')) tags.push('falling');\n\n // Spawn at origin\n const fullConfig: ObjectConfig = {\n ...objectConfig.objectConfig,\n x: config.origin.x,\n y: config.origin.y,\n radius,\n tags\n };\n\n // Normalize the direction vector\n const dirLength = Math.sqrt(config.direction.x ** 2 + config.direction.y ** 2);\n const normalizedDir = dirLength > 0\n ? { x: config.direction.x / dirLength, y: config.direction.y / dirLength }\n : { x: 0, y: 1 }; // Default to downward if zero vector\n\n // Calculate base angle from normalized direction\n const baseAngle = Math.atan2(normalizedDir.y, normalizedDir.x);\n\n // Add random spread within cone angle (-coneAngle to +coneAngle)\n const spreadAngle = (Math.random() * 2 - 1) * config.coneAngle;\n const finalAngle = baseAngle + spreadAngle;\n\n // Spawn object and apply velocity\n const id = await this.spawnObjectAsync(fullConfig);\n const body = this.getBody(id);\n if (body) {\n Matter.Body.setVelocity(body, {\n x: Math.cos(finalAngle) * config.force,\n y: Math.sin(finalAngle) * config.force\n });\n }\n }\n\n /**\n * Select an object config based on probability weights\n */\n private selectObjectConfig(configs: EffectObjectConfig[]): EffectObjectConfig | null {\n if (configs.length === 0) return null;\n\n const totalWeight = configs.reduce((sum, c) => sum + c.probability, 0);\n if (totalWeight === 0) return configs[0];\n\n let random = Math.random() * totalWeight;\n for (const config of configs) {\n random -= config.probability;\n if (random <= 0) return config;\n }\n return configs[configs.length - 1];\n }\n\n /**\n * Calculate radius based on scale range\n */\n private calculateRadius(config: EffectObjectConfig): number {\n const baseRadius = config.baseRadius ?? 20;\n const scale = this.randomInRange(config.minScale, config.maxScale);\n return baseRadius * scale;\n }\n\n private randomInRange(min: number, max: number): number {\n return min + Math.random() * (max - min);\n }\n}\n"],"mappings":"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","names":["Matter","Matter","y","x","LOG_PREFIX","Matter","Matter","LOG_PREFIX","Matter","LOG_PREFIX","LOG_PREFIX","Matter","Matter","Matter","Matter","shadowEntry"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@blorkfield/overlay-core",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"matter-js": "^0.19.0",
|
|
20
|
+
"opentype.js": "^1.3.4"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@changesets/cli": "^2.27.0",
|
|
24
|
+
"@types/matter-js": "^0.19.6",
|
|
25
|
+
"@types/opentype.js": "^1.3.8",
|
|
26
|
+
"concurrently": "^9.2.1",
|
|
27
|
+
"tsup": "^8.0.0",
|
|
28
|
+
"typescript": "^5.3.0"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsup",
|
|
32
|
+
"dev": "concurrently \"tsup --watch\" \"pnpm -C test-app dev\"",
|
|
33
|
+
"typecheck": "tsc --noEmit"
|
|
34
|
+
}
|
|
35
|
+
}
|