simple-circuit-engine 0.0.12 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/AGENTS.md +2 -1
  2. package/CLAUDE.md +2 -0
  3. package/dist/core/index.js +83 -73
  4. package/dist/core/setup.d.ts +8 -0
  5. package/dist/core/simulation/behaviors/arithmetic/AdderBehavior.d.ts +28 -0
  6. package/dist/core/simulation/behaviors/arithmetic/ArithmeticBehaviorMixin.d.ts +63 -0
  7. package/dist/core/simulation/behaviors/arithmetic/EightBitAdderBehavior.d.ts +51 -0
  8. package/dist/core/simulation/behaviors/arithmetic/EightBitOnesComplementBehavior.d.ts +29 -0
  9. package/dist/core/simulation/behaviors/arithmetic/HalfAdderBehavior.d.ts +22 -0
  10. package/dist/core/simulation/behaviors/arithmetic/index.d.ts +7 -0
  11. package/dist/core/simulation/behaviors/index.d.ts +4 -0
  12. package/dist/core/simulation/states/arithmetic/AdderState.d.ts +16 -0
  13. package/dist/core/simulation/states/arithmetic/ArithmeticState.d.ts +53 -0
  14. package/dist/core/simulation/states/arithmetic/EightBitAdderState.d.ts +25 -0
  15. package/dist/core/simulation/states/arithmetic/EightBitOnesComplementState.d.ts +18 -0
  16. package/dist/core/simulation/states/arithmetic/HalfAdderState.d.ts +16 -0
  17. package/dist/core/simulation/states/arithmetic/index.d.ts +7 -0
  18. package/dist/core/simulation/states/index.d.ts +5 -0
  19. package/dist/core/simulation/types.d.ts +1 -1
  20. package/dist/core/topology/Component.d.ts +2 -1
  21. package/dist/core/topology/types.d.ts +19 -5
  22. package/dist/{core-Bjta9Y7_.js → core-b6Q8w2sn.js} +1505 -652
  23. package/dist/core-b6Q8w2sn.js.map +1 -0
  24. package/dist/i18n/index.d.ts +882 -0
  25. package/dist/i18n/locales/en.json.d.ts +268 -0
  26. package/dist/i18n/locales/fr.json.d.ts +268 -0
  27. package/dist/index.d.ts +1 -0
  28. package/dist/index.js +137 -120
  29. package/dist/scene/CircuitEngine.d.ts +13 -0
  30. package/dist/scene/index.d.ts +1 -1
  31. package/dist/scene/index.js +50 -45
  32. package/dist/scene/setup.d.ts +8 -0
  33. package/dist/scene/shared/AbstractCircuitController.d.ts +6 -0
  34. package/dist/scene/shared/components/arithmetic/AdderVisualFactory.d.ts +54 -0
  35. package/dist/scene/shared/components/arithmetic/EightBitAdderVisualFactory.d.ts +45 -0
  36. package/dist/scene/shared/components/arithmetic/EightBitOnesComplementVisualFactory.d.ts +63 -0
  37. package/dist/scene/shared/components/arithmetic/HalfAdderVisualFactory.d.ts +55 -0
  38. package/dist/scene/shared/components/arithmetic/index.d.ts +4 -0
  39. package/dist/scene/shared/components/index.d.ts +4 -0
  40. package/dist/scene/shared/types.d.ts +0 -2
  41. package/dist/scene/shared/utils/GeometryUtils.d.ts +76 -0
  42. package/dist/scene/static/CircuitController.d.ts +1 -0
  43. package/dist/scene/static/PinTooltipWidget.d.ts +5 -0
  44. package/dist/scene/static/tools/BuildTool.d.ts +4 -0
  45. package/dist/scene/static/tools/ComponentPickerWidget.d.ts +7 -0
  46. package/dist/scene/static/tools/ConfigPanelWidget.d.ts +14 -0
  47. package/dist/{scene-CVsDdySt.js → scene-D4QcWeiq.js} +2487 -1099
  48. package/dist/scene-D4QcWeiq.js.map +1 -0
  49. package/package.json +14 -9
  50. package/dist/core-Bjta9Y7_.js.map +0 -1
  51. package/dist/scene-CVsDdySt.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-D4QcWeiq.js","names":[],"sources":["../src/scene/shared/EventEmitter.ts","../src/scene/shared/utils/GeometryUtils.ts","../src/i18n/locales/en.json","../src/i18n/locales/fr.json","../src/i18n/index.ts","../src/scene/static/tools/ComponentPickerWidget.ts","../src/scene/static/tools/BuildTool.ts","../src/scene/static/tools/MultiSelectTool.ts","../src/scene/shared/SelectionManager.ts","../src/scene/static/CircuitWriter.ts","../src/scene/shared/utils/CameraUtils.ts","../src/scene/shared/utils/LightingUtils.ts","../src/scene/shared/utils/LayerConstants.ts","../src/scene/shared/HoverManager.ts","../src/scene/shared/BranchingPointVisualFactory.ts","../src/scene/shared/utils/MaterialUtils.ts","../src/scene/shared/WireVisualManager.ts","../src/scene/shared/utils/Options.ts","../src/scene/shared/utils/ControlsUtils.ts","../src/scene/shared/AbstractCircuitController.ts","../src/scene/static/tools/ConfigPanelWidget.ts","../src/scene/static/PinTooltipWidget.ts","../src/scene/static/CircuitController.ts","../src/scene/simulation/CircuitRunnerController.ts","../src/scene/CircuitEngine.ts","../src/scene/shared/components/types.ts","../src/scene/shared/components/FactoryRegistry.ts","../src/scene/shared/components/ComponentVisualFactory.ts","../src/scene/shared/utils/ColorUtils.ts","../src/scene/shared/components/DefaultVisualFactory.ts","../src/scene/shared/components/GroupedFactoryRegistry.ts","../src/scene/shared/components/basic/BatteryVisualFactory.ts","../src/scene/shared/components/basic/LabelVisualFactory.ts","../src/scene/shared/components/basic/LightbulbVisualFactory.ts","../src/scene/shared/components/basic/RectangleLEDVisualFactory.ts","../src/scene/shared/components/basic/RelayVisualFactory.ts","../src/scene/shared/components/basic/SmallLEDVisualFactory.ts","../src/scene/shared/components/basic/SwitchVisualFactory.ts","../src/scene/shared/components/basic/DoubleThrowSwitchVisualFactory.ts","../src/scene/shared/components/basic/ClockVisualFactory.ts","../src/scene/shared/components/gates/InverterVisualFactory.ts","../src/scene/shared/components/gates/NandGateVisualFactory.ts","../src/scene/shared/components/gates/Nand4GateVisualFactory.ts","../src/scene/shared/components/gates/Nand8GateVisualFactory.ts","../src/scene/shared/components/gates/NorGateVisualFactory.ts","../src/scene/shared/components/gates/Nor4GateVisualFactory.ts","../src/scene/shared/components/gates/Nor8GateVisualFactory.ts","../src/scene/shared/components/gates/XorGateVisualFactory.ts","../src/scene/shared/components/gates/Xor4GateVisualFactory.ts","../src/scene/shared/components/gates/Xor8GateVisualFactory.ts","../src/scene/shared/components/arithmetic/HalfAdderVisualFactory.ts","../src/scene/shared/components/arithmetic/AdderVisualFactory.ts","../src/scene/shared/components/arithmetic/EightBitAdderVisualFactory.ts","../src/scene/shared/components/arithmetic/EightBitOnesComplementVisualFactory.ts","../src/scene/setup.ts"],"sourcesContent":["/**\n * Type-safe Event Emitter\n * @module scene/shared/EventEmitter\n *\n * Provides type-safe event handling without external dependencies.\n * Generic EventMap type ensures compile-time type safety for event payloads.\n */\n\n/**\n * Generic event emitter with type-safe event emission and subscription\n *\n * @template EventMap - Map of event names to their payload types\n *\n * @example\n * ```typescript\n * interface MyEvents {\n * click: { x: number; y: number };\n * error: { message: string };\n * }\n *\n * const emitter = new EventEmitter<MyEvents>();\n * emitter.on('click', ({ x, y }) => console.log(x, y)); // Type-safe!\n * emitter.emit('click', { x: 10, y: 20 });\n * ```\n */\nexport class EventEmitter<EventMap extends Record<string, any>> {\n private listeners: Map<keyof EventMap, Function[]> = new Map();\n\n /**\n * Register an event listener\n *\n * @param event - Event name to listen for\n * @param callback - Function to call when event occurs\n *\n * @remarks\n * Same callback can be registered multiple times (will be called multiple times).\n * Callbacks are wrapped in try-catch to prevent errors from breaking emission.\n */\n on<K extends keyof EventMap>(event: K, callback: (payload: EventMap[K]) => void): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, []);\n }\n this.listeners.get(event)!.push(callback);\n }\n\n /**\n * Unregister an event listener\n *\n * @param event - Event name\n * @param callback - Function to remove (must be same reference used in on())\n *\n * @remarks\n * If callback was registered multiple times, only removes one registration.\n */\n off<K extends keyof EventMap>(event: K, callback: (payload: EventMap[K]) => void): void {\n const callbacks = this.listeners.get(event);\n if (callbacks) {\n const index = callbacks.indexOf(callback);\n if (index !== -1) {\n callbacks.splice(index, 1);\n }\n if (callbacks.length === 0) {\n this.listeners.delete(event);\n }\n }\n }\n\n /**\n * Emit an event to all registered listeners\n * This method is public so that tools may emit events directly on behalf of the EventEmitter owner (controller).\n *\n * @param event - Event name to emit\n * @param payload - Event-specific payload\n *\n * @remarks\n * Listeners are called in registration order.\n * Errors in callbacks are caught and logged but do not stop other callbacks.\n */\n emit<K extends keyof EventMap>(event: K, payload: EventMap[K]): void {\n const callbacks = this.listeners.get(event);\n if (callbacks) {\n for (const callback of callbacks) {\n try {\n callback(payload);\n } catch (error) {\n // Prevent callback errors from breaking other callbacks\n console.error(`Error in event listener for '${String(event)}':`, error);\n }\n }\n }\n }\n\n /**\n * Remove all listeners for a specific event or all events\n *\n * @param event - Optional event name. If omitted, removes all listeners for all events.\n */\n removeAllListeners<K extends keyof EventMap>(event?: K): void {\n if (event !== undefined) {\n this.listeners.delete(event);\n } else {\n this.listeners.clear();\n }\n }\n\n /**\n * Get count of listeners for a specific event\n *\n * @param event - Event name\n * @returns Number of registered listeners\n */\n listenerCount<K extends keyof EventMap>(event: K): number {\n return this.listeners.get(event)?.length ?? 0;\n }\n\n /**\n * Register a listener that receives all events\n *\n * @param callback - Function called with event name and payload for every event\n * @returns Cleanup function to remove the listener\n *\n * @remarks\n * Useful for event forwarding/delegation patterns where you want to\n * re-emit all events from one emitter to another.\n *\n * @example\n * ```typescript\n * const cleanup = source.onAny((event, payload) => {\n * target.emit(event, payload);\n * });\n * // Later: cleanup();\n * ```\n */\n onAny(callback: <K extends keyof EventMap>(event: K, payload: EventMap[K]) => void): () => void {\n // Store the original emit\n const originalEmit = this.emit.bind(this);\n\n // Override emit to also call the callback\n this.emit = <K extends keyof EventMap>(event: K, payload: EventMap[K]) => {\n originalEmit(event, payload);\n try {\n callback(event, payload);\n } catch (error) {\n console.error(`Error in onAny listener for '${String(event)}':`, error);\n }\n };\n\n // Return cleanup function that restores original emit\n return () => {\n this.emit = originalEmit;\n };\n }\n}\n","/**\n * Geometry Utilities\n * @module scene/shared/utils/GeometryUtils\n *\n * Helper functions for creating Three.js geometries for grid and circuit elements\n */\n\nimport * as THREE from 'three';\nimport { Position, Rotation } from 'simple-circuit-engine/core';\nimport { ExtrudeGeometry } from 'three';\n\n/**\n * convenience to control standard rotations of meshes on X-Z plan\n */\nexport type Direction2D = 'right' | 'bottom' | 'left' | 'top';\n\n/**\n * Create a grid helper for the scene\n *\n * @param size - Size of the grid\n * @param divisions - Number of grid divisions\n * @param colorCenterLine - Color for center lines\n * @param colorGrid - Color for grid lines\n * @returns GridHelper object\n */\nexport function createGridHelper(\n size: number,\n divisions: number,\n colorCenterLine: number,\n colorGrid: number\n): THREE.GridHelper {\n const grid = new THREE.GridHelper(size, divisions, colorCenterLine, colorGrid);\n grid.position.set(0, 0, 0);\n // set z-index to be behind other objects\n grid.renderOrder = -1;\n return grid;\n}\n\n/**\n * optimal number of grid divisions for a given size\n * @param size\n */\nexport function computeDivisionsForSize(size: number): number {\n if (size <= 10) return size;\n let basis = 10;\n let threshold = 10;\n if (size <= 30) {\n return basis + Math.floor((size - threshold) / 2);\n }\n basis = 20;\n threshold = 30;\n if (size <= 70) {\n return basis + Math.floor((size - threshold) / 4);\n }\n basis = 30;\n threshold = 70;\n if (size <= 150) {\n return basis + Math.floor((size - threshold) / 8);\n }\n basis = 40;\n threshold = 150;\n if (size <= 310) {\n return basis + Math.floor((size - threshold) / 16);\n }\n basis = 50;\n threshold = 310;\n if (size <= 630) {\n return basis + Math.floor((size - threshold) / 32);\n }\n basis = 60;\n threshold = 630;\n return Math.min(70, basis + Math.floor((size - threshold) / 64));\n}\n\n/**\n * Components, branching points and wires intermediate points snap to the nearest integer grid point.\n * @param position\n * @constructor\n */\nexport function nearestWorldSnapPosition(position: THREE.Vector3): THREE.Vector3 {\n return new THREE.Vector3(Math.round(position.x), 0, Math.round(position.z));\n}\n\n/**\n * Converts a world 3D position to the snapped 2D model grid position.\n * @param position\n * @constructor\n */\nexport function worldToGridPosition(position: THREE.Vector3): Position {\n return new Position(Math.round(position.x), Math.round(-position.z));\n}\n\n/**\n * Converts a model grid 2D position to the world 3D position.\n * @param position\n * @constructor\n */\nexport function gridToWorldPosition(position: Position): THREE.Vector3 {\n return new THREE.Vector3(position.x, 0, -position.y);\n}\n\n/**\n * Converts a world 3D rotation to the model grid 2D rotation.\n * @param rotation\n * @constructor\n */\nexport function worldToGridRotation(rotation: THREE.Euler): Rotation {\n return new Rotation(Math.round(THREE.MathUtils.radToDeg(-rotation.y)));\n}\n\n/**\n * Converts model grid 2D rotation to the world 3D rotation.\n * @param rotation\n * @constructor\n */\nexport function gridToWorldRotation(rotation: Rotation): THREE.Euler {\n return new THREE.Euler(0, THREE.MathUtils.degToRad(-rotation.angle), 0);\n}\n\n/**\n * Get the bounding box of a Three.js object in world space\n *\n * @param object - The Three.js object to get bounds for\n * @returns Box3 representing the world-space bounding box\n */\nexport function getObjectBoundingBox(object: THREE.Object3D): THREE.Box3 {\n const box = new THREE.Box3();\n box.setFromObject(object);\n return box;\n}\n\n/**\n * Project a 3D world position to 2D screen coordinates\n *\n * @param worldPosition - Position in world space\n * @param camera - Camera to use for projection\n * @param width - Viewport width in pixels\n * @param height - Viewport height in pixels\n * @returns Screen coordinates {x, y} where (0,0) is top-left\n */\nexport function worldToScreenPosition(\n worldPosition: THREE.Vector3,\n camera: THREE.Camera,\n width: number,\n height: number\n): { x: number; y: number } {\n const vector = worldPosition.clone();\n vector.project(camera);\n\n const x = ((vector.x + 1) / 2) * width;\n const y = ((-vector.y + 1) / 2) * height;\n\n return { x, y };\n}\n\n/**\n * Check if a 3D point (projected to screen space) is inside a 2D screen rectangle\n *\n * @param worldPosition - Position in world space\n * @param camera - Camera to use for projection\n * @param width - Viewport width in pixels\n * @param height - Viewport height in pixels\n * @param rect - Screen rectangle with min/max coordinates\n * @returns true if the projected point is inside the rectangle\n */\nexport function isPointInScreenRect(\n worldPosition: THREE.Vector3,\n camera: THREE.Camera,\n width: number,\n height: number,\n rect: { minX: number; minY: number; maxX: number; maxY: number }\n): boolean {\n const screen = worldToScreenPosition(worldPosition, camera, width, height);\n return (\n screen.x >= rect.minX && screen.x <= rect.maxX && screen.y >= rect.minY && screen.y <= rect.maxY\n );\n}\n\n/**\n * Check if an object's center point is inside a screen rectangle\n * Used for rectangle selection of components and branching points\n *\n * @param object - The Three.js object to check\n * @param camera - Camera to use for projection\n * @param width - Viewport width in pixels\n * @param height - Viewport height in pixels\n * @param rect - Screen rectangle with min/max coordinates\n * @returns true if object's center is inside the rectangle\n */\nexport function isObjectInScreenRect(\n object: THREE.Object3D,\n camera: THREE.Camera,\n width: number,\n height: number,\n rect: { minX: number; minY: number; maxX: number; maxY: number }\n): boolean {\n const worldPosition = new THREE.Vector3();\n object.getWorldPosition(worldPosition);\n return isPointInScreenRect(worldPosition, camera, width, height, rect);\n}\n\n/**\n * Create a ring geometry with given inner/outer radius and depth (y axis)\n * @param innerRadius\n * @param outerRadius\n * @param depth\n * @param steps\n * @constructor\n */\nexport function RingGeometry(\n innerRadius: number,\n outerRadius: number,\n depth: number,\n steps: number\n): ExtrudeGeometry {\n // Create the outer ring shape\n const shape = new THREE.Shape();\n shape.moveTo(outerRadius, 0);\n shape.absarc(0, 0, outerRadius, 0, Math.PI * 2, false);\n\n // Extrude settings\n const extrudeSettings = {\n depth: depth,\n bevelEnabled: false,\n steps: steps,\n };\n // if innerRadius is less than 10% of outerRadius we consider the geometry full (inner hole would be too small)\n if (innerRadius < 0.1 * outerRadius) {\n return new THREE.ExtrudeGeometry(shape, extrudeSettings);\n }\n\n // Create the inner ring path (hole)\n const holePath = new THREE.Path();\n holePath.moveTo(innerRadius, 0);\n holePath.absarc(0, 0, innerRadius, 0, Math.PI * 2, true);\n shape.holes.push(holePath);\n\n // Create the extruded geometry\n return new THREE.ExtrudeGeometry(shape, extrudeSettings);\n}\n\n/**\n * Returns the inner hole path for an AND gate body of the given dimensions.\n * Handles both standard (width > height / 2) and tall (width ≤ height / 2) proportions.\n * Returns null when thickness is large enough that the shape should be solid (no hole).\n *\n * @param width - Total width, from left flat edge to rightmost arc point\n * @param height - Total height\n * @param thickness - Wall thickness of the gate shell\n */\nfunction _andGateHolePath(width: number, height: number, thickness: number): THREE.Path | null {\n const halfW = width / 2;\n const halfH = height / 2;\n if (thickness > 0.9 * Math.min(halfH, halfW)) return null;\n\n const hole = new THREE.Path();\n\n if (width <= height / 2) {\n // Tall case: inner hole uses the same arc formula as _AndGateTallGeometry\n const innerHalfW = halfW - thickness;\n const innerHalfH = halfH - thickness;\n const cx_inner = -(innerHalfH * innerHalfH) / (4 * innerHalfW);\n const radius_inner = (innerHalfH * innerHalfH + 4 * innerHalfW * innerHalfW) / (4 * innerHalfW);\n const xComp_inner = -innerHalfW - cx_inner;\n const angle_top_inner = Math.atan2(innerHalfH, xComp_inner);\n\n hole.moveTo(-halfW + thickness, innerHalfH);\n hole.lineTo(-halfW + thickness, -innerHalfH); // inner left side, going down\n hole.absarc(cx_inner, 0, radius_inner, -angle_top_inner, angle_top_inner, false); // CCW, sweeping right\n // arc ends at (-halfW + thickness, innerHalfH) = moveTo → Three.js auto-closes\n return hole;\n }\n\n // Standard case: inner hole is AND gate shape shrunk by thickness, opposite winding\n const arcCenterX = halfW - halfH;\n const innerHalfH = halfH - thickness;\n hole.moveTo(-halfW + thickness, -innerHalfH);\n hole.lineTo(arcCenterX, -innerHalfH); // inner bottom edge, going right\n hole.absarc(arcCenterX, 0, innerHalfH, -Math.PI / 2, Math.PI / 2, false); // inner right semicircle, CCW\n hole.lineTo(-halfW + thickness, innerHalfH); // inner top edge, going left\n // Three.js auto-closes back to moveTo point (inner left side, going down)\n return hole;\n}\n\n/**\n * Internal variant of AndGateGeometry for tall gates where width ≤ height / 2.\n * In this regime a standard semicircle does not fit, so the entire right side is\n * a single circular arc passing through the top-left corner (-halfW, +halfH),\n * the output tip (+halfW, 0), and the bottom-left corner (-halfW, -halfH).\n *\n * The arc center and radius are derived analytically from those three points.\n * By symmetry the center lies on the x-axis: cx = -halfH² / (4·halfW).\n * The inner hole reuses the same formula applied to the inset dimensions\n * (halfW - thickness, halfH - thickness), so the right-side wall is uniform.\n *\n * @param width - Total width, from left flat edge to rightmost arc point\n * @param height - Total height; also determines the arc radius (= height / 2)\n * @param thickness - Wall thickness of the gate shell\n * @param depth - Extrusion depth\n * @param steps - Number of extrusion steps\n */\nfunction _AndGateTallGeometry(\n width: number,\n height: number,\n thickness: number,\n depth: number,\n steps: number\n): ExtrudeGeometry {\n if (width > height / 2) {\n throw new Error(\n 'this method only handle the tall case : use regular AndGateGeometry in this case.'\n );\n }\n const halfW = width / 2;\n const halfH = height / 2;\n // Unique circle through (-halfW, ±halfH) and (halfW, 0), center on x-axis.\n // From (cx - halfW)² = (cx + halfW)² + halfH² → cx = -halfH² / (4·halfW)\n const cx = -(halfH * halfH) / (4 * halfW);\n const radius = (halfH * halfH + 4 * halfW * halfW) / (4 * halfW);\n // x-distance from center to the back-left corners; always > 0 in the tall regime (halfH ≥ 2·halfW)\n const xComp = -halfW - cx; // = (halfH² - 4·halfW²) / (4·halfW)\n const angle_top = Math.atan2(halfH, xComp);\n\n // Outer shape: flat left side (up) + single right arc CW from top to bottom\n const shape = new THREE.Shape();\n shape.moveTo(-halfW, -halfH);\n shape.lineTo(-halfW, halfH); // left flat side, going up\n shape.absarc(cx, 0, radius, angle_top, -angle_top, true); // right arc CW, sweeping through (halfW, 0)\n // arc ends exactly at (-halfW, -halfH) = moveTo → Three.js auto-closes\n\n const extrudeSettings = {\n depth,\n bevelEnabled: false,\n steps,\n };\n const hole = _andGateHolePath(width, height, thickness);\n if (hole !== null) shape.holes.push(hole);\n return new THREE.ExtrudeGeometry(shape, extrudeSettings);\n}\n\n/**\n * Create an ExtrudeGeometry for the body of an AND gate.\n * Shape is centered at origin: flat side on the left, semicircle on the right,\n * matching the standard logic gate schematic representation.\n * Handles both standard (width > height / 2) and tall (width ≤ height / 2) proportions.\n *\n * Input pins attach on the left flat side (x = -width/2).\n * Output pin attaches at the rightmost point of the arc (x = +width/2).\n *\n * The thickness parameter controls a visual state trick:\n * - LOW state: thin thickness → gate appears as an empty shell\n * - HIGH state: thick thickness → gate appears filled\n * - TRANSITIONING: medium thickness → gate appears half-filled\n *\n * @param width - Total width, from left flat edge to rightmost arc point\n * @param height - Total height; also determines the arc radius (= height / 2)\n * @param thickness - Wall thickness of the gate shell\n * @param depth - Extrusion depth\n * @param steps - Number of extrusion steps\n * @constructor\n */\nexport function AndGateGeometry(\n width: number,\n height: number,\n thickness: number,\n depth: number,\n steps: number = 1\n): ExtrudeGeometry {\n // Tall gate: the semicircle does not fit when width ≤ height / 2; use the arc variant\n if (width <= height / 2) {\n return _AndGateTallGeometry(width, height, thickness, depth, steps);\n }\n\n const halfW = width / 2;\n const halfH = height / 2;\n\n // The semicircle center sits at the boundary between the rectangular and curved portions\n const arcCenterX = halfW - halfH;\n\n // Outer shape: left flat side, top edge, clockwise right semicircle, bottom edge\n const shape = new THREE.Shape();\n shape.moveTo(-halfW, -halfH);\n shape.lineTo(-halfW, halfH); // left flat side, going up\n shape.lineTo(arcCenterX, halfH); // top edge, going right\n shape.absarc(arcCenterX, 0, halfH, Math.PI / 2, -Math.PI / 2, true); // right semicircle, clockwise\n shape.lineTo(-halfW, -halfH);\n\n const extrudeSettings = {\n depth,\n bevelEnabled: false,\n steps,\n };\n const hole = _andGateHolePath(width, height, thickness);\n if (hole !== null) shape.holes.push(hole);\n return new THREE.ExtrudeGeometry(shape, extrudeSettings);\n}\n\n/**\n * Create an ExtrudeGeometry for the inner hole of an AND gate body.\n * Returns null when thickness is large enough that the gate has no hole.\n * Handles both standard (width > height / 2) and tall (width ≤ height / 2) proportions.\n *\n * @param width - Total width, from left flat edge to rightmost arc point\n * @param height - Total height\n * @param thickness - Wall thickness of the gate shell\n * @param depth - Extrusion depth\n * @param steps - Number of extrusion steps\n */\nexport function AndGateHoleGeometry(\n width: number,\n height: number,\n thickness: number,\n depth: number,\n steps: number = 1\n): ExtrudeGeometry | null {\n const hole = _andGateHolePath(width, height, thickness);\n if (hole === null) return null;\n const shape = new THREE.Shape(hole.getPoints(64).reverse());\n return new THREE.ExtrudeGeometry(shape, { depth, bevelEnabled: false, steps });\n}\n\n/**\n * Returns the inner hole path for an OR gate body of the given dimensions.\n * Handles both standard (width > height / 2) and tall (width ≤ height / 2) proportions.\n * Returns null when thickness is large enough that the shape should be solid (no hole).\n *\n * @param width - Total width\n * @param height - Total height\n * @param thickness - Wall thickness of the gate shell\n */\nfunction _orGateHolePath(width: number, height: number, thickness: number): THREE.Path | null {\n const halfW = width / 2;\n const halfH = height / 2;\n if (thickness > 0.9 * Math.min(halfH, halfW)) return null;\n\n // Back arc parameters — shared between standard and tall cases\n const backInset = halfH * 0.4;\n const u_b = (halfH * halfH - backInset * backInset) / (2 * backInset);\n const cx_b = -halfW - u_b;\n const r_b = u_b + backInset;\n\n const innerHalfH = halfH - thickness;\n const backWallThickness = Math.max(0.1, thickness / 2);\n const r_b_inner = r_b + backWallThickness;\n const inner_x_comp = Math.sqrt(r_b_inner * r_b_inner - innerHalfH * innerHalfH);\n const angle_b_inner = Math.atan2(innerHalfH, inner_x_comp);\n const inner_back_x = cx_b + inner_x_comp;\n\n const hole = new THREE.Path();\n\n if (width <= height / 2) {\n // Tall case: inner right arc solved analytically from three points\n const p = halfW - thickness; // inner output tip x-coordinate\n const q = inner_back_x;\n const cx_r = (q * q + innerHalfH * innerHalfH - p * p) / (2 * (q - p));\n const radius_r = p - cx_r;\n const angle_top_r = Math.atan2(innerHalfH, inner_back_x - cx_r);\n\n hole.moveTo(inner_back_x, innerHalfH);\n hole.absarc(cx_b, 0, r_b_inner, angle_b_inner, -angle_b_inner, true); // CW, top to bottom\n hole.absarc(cx_r, 0, radius_r, -angle_top_r, angle_top_r, false); // CCW, bottom to top\n // arc ends at (inner_back_x, innerHalfH) = moveTo → Three.js auto-closes\n return hole;\n }\n\n // Standard case\n const arcCenterX = halfW - halfH;\n hole.moveTo(inner_back_x, innerHalfH);\n hole.absarc(cx_b, 0, r_b_inner, angle_b_inner, -angle_b_inner, true); // CW from top to bottom\n hole.lineTo(arcCenterX, -innerHalfH); // inner bottom edge, going right\n hole.absarc(arcCenterX, 0, innerHalfH, -Math.PI / 2, Math.PI / 2, false); // inner right semicircle, CCW\n hole.lineTo(inner_back_x, innerHalfH); // inner top edge, going left\n // Three.js auto-closes back to moveTo point\n return hole;\n}\n\n/**\n * Internal variant of OrGateGeometry for tall gates where width ≤ height / 2.\n * In this regime the right output semicircle does not fit, so the right side becomes\n * a single circular arc through the back-left corners (-halfW, ±halfH) and (halfW, 0),\n * exactly as in _AndGateTallGeometry — the only difference is the back (left) side\n * which remains the OR gate's characteristic concave arc.\n *\n * The inner hole back arc uses the same concentric offset trick (fixed backWallThickness).\n * The inner right arc is solved from the three points it must pass through:\n * (inner_back_x, ±innerHalfH) and (halfW - thickness, 0), where inner_back_x is where\n * the concentric back arc intersects y = ±innerHalfH. The circle center is found\n * analytically from (p - cx_r)² = (q - cx_r)² + innerHalfH² where p = halfW - thickness\n * and q = inner_back_x.\n */\nfunction _OrGateTallGeometry(\n width: number,\n height: number,\n thickness: number,\n depth: number,\n steps: number\n): ExtrudeGeometry {\n if (width > height / 2) {\n throw new Error(\n '_OrGateTallGeometry only handles the tall case: use regular OrGateGeometry instead.'\n );\n }\n const halfW = width / 2;\n const halfH = height / 2;\n\n // Back (left) concave arc — identical to standard OrGateGeometry\n const backInset = halfH * 0.4;\n const u_b = (halfH * halfH - backInset * backInset) / (2 * backInset);\n const cx_b = -halfW - u_b;\n const r_b = u_b + backInset;\n const angle_b = Math.atan2(halfH, u_b);\n\n // Right arc: unique circle through (-halfW, ±halfH) and (halfW, 0), center on x-axis\n // Same derivation as _AndGateTallGeometry: cx = -halfH² / (4·halfW)\n const cx = -(halfH * halfH) / (4 * halfW);\n const radius = (halfH * halfH + 4 * halfW * halfW) / (4 * halfW);\n const xComp = -halfW - cx;\n const angle_top = Math.atan2(halfH, xComp);\n\n // Outer shape: concave back arc (CCW) flows directly into right arc (CW) — no horizontal edges\n const shape = new THREE.Shape();\n shape.moveTo(-halfW, -halfH);\n shape.absarc(cx_b, 0, r_b, -angle_b, angle_b, false); // concave back, CCW bottom to top\n shape.absarc(cx, 0, radius, angle_top, -angle_top, true); // right arc CW, top to bottom through (halfW, 0)\n // arc ends at (-halfW, -halfH) = moveTo → Three.js auto-closes\n\n const extrudeSettings = { depth, bevelEnabled: false, steps };\n const hole = _orGateHolePath(width, height, thickness);\n if (hole !== null) shape.holes.push(hole);\n return new THREE.ExtrudeGeometry(shape, extrudeSettings);\n}\n\n/**\n * Create an ExtrudeGeometry for the body of an OR gate.\n * Same structure as AndGateGeometry but the left side is a concave arc (curves inward)\n * instead of a flat edge, matching the standard logic gate schematic representation.\n * Handles both standard (width > height / 2) and tall (width ≤ height / 2) proportions.\n *\n * Input pins attach on the left curved side (nominally at x = -width/2).\n * Output pin attaches at the rightmost point of the right arc (x = +width/2).\n *\n * The thickness parameter controls the same visual state trick as AndGateGeometry:\n * - LOW state: thin thickness → gate appears as an empty shell\n * - HIGH state: thick thickness → gate appears filled\n * - TRANSITIONING: medium thickness → gate appears half-filled\n *\n * Back arc geometry: the concave left side bows inward (rightward) by backInset = halfH * 0.4.\n * The arc center sits far to the left; u_b is its x-distance to the back-left corners.\n * The inner hole back arc is concentric with the outer (same center, radius += 0.1), giving a\n * truly constant perpendicular wall thickness of 0.1 along the entire back curve, independent\n * of the thickness parameter.\n *\n * @param width - Total width, from leftmost back curve point to rightmost arc point\n * @param height - Total height; also determines the right arc radius (= height / 2)\n * @param thickness - Wall thickness of the gate shell\n * @param depth - Extrusion depth\n * @param steps - Number of extrusion steps\n * @constructor\n */\nexport function OrGateGeometry(\n width: number,\n height: number,\n thickness: number,\n depth: number,\n steps: number = 1\n): ExtrudeGeometry {\n // Tall gate: the output semicircle does not fit when width ≤ height / 2; use the arc variant\n if (width <= height / 2) {\n return _OrGateTallGeometry(width, height, thickness, depth, steps);\n }\n\n const halfW = width / 2;\n const halfH = height / 2;\n\n // Back (left) concave arc: midpoint bows rightward by backInset from the left edge\n const backInset = halfH * 0.4;\n // u_b: x-distance from arc center (cx_b, 0) to the back-left corners (-halfW, ±halfH)\n // Derived from: arc passes through (-halfW, ±halfH) and midpoint (-halfW + backInset, 0)\n const u_b = (halfH * halfH - backInset * backInset) / (2 * backInset);\n const cx_b = -halfW - u_b; // arc center, far to the left of the gate\n const r_b = u_b + backInset; // = sqrt(u_b² + halfH²), by construction\n const angle_b = Math.atan2(halfH, u_b); // angle from center to the back-left corners\n\n // Right output semicircle center (same as AndGateGeometry)\n const arcCenterX = halfW - halfH;\n\n // Outer shape: concave back arc (CCW, bottom to top) + top edge + CW right semicircle + bottom edge\n const shape = new THREE.Shape();\n shape.moveTo(-halfW, -halfH);\n shape.absarc(cx_b, 0, r_b, -angle_b, angle_b, false); // concave back, CCW bottom to top\n shape.lineTo(arcCenterX, halfH); // top edge, going right\n shape.absarc(arcCenterX, 0, halfH, Math.PI / 2, -Math.PI / 2, true); // right output semicircle, CW\n shape.lineTo(-halfW, -halfH); // bottom edge, back to start\n\n const extrudeSettings = { depth, bevelEnabled: false, steps };\n const hole = _orGateHolePath(width, height, thickness);\n if (hole !== null) shape.holes.push(hole);\n return new THREE.ExtrudeGeometry(shape, extrudeSettings);\n}\n\n/**\n * Create an ExtrudeGeometry for the inner hole of an OR gate body.\n * Returns null when thickness is large enough that the gate has no hole.\n * Handles both standard (width > height / 2) and tall (width ≤ height / 2) proportions.\n *\n * @param width - Total width\n * @param height - Total height\n * @param thickness - Wall thickness of the gate shell\n * @param depth - Extrusion depth\n * @param steps - Number of extrusion steps\n */\nexport function OrGateHoleGeometry(\n width: number,\n height: number,\n thickness: number,\n depth: number,\n steps: number = 1\n): ExtrudeGeometry | null {\n const hole = _orGateHolePath(width, height, thickness);\n if (hole === null) return null;\n const shape = new THREE.Shape(hole.getPoints(64).reverse());\n return new THREE.ExtrudeGeometry(shape, { depth, bevelEnabled: false, steps });\n}\n\n/**\n * Create an ExtrudeGeometry for the XOR gate tail — the distinctive extra curved bar\n * placed to the left of an OrGateGeometry body to form the full XOR gate symbol.\n *\n * Back-arc parameters (backInset, u_b, cx_b, r_b, angle_b) are identical to those in\n * OrGateGeometry so this geometry aligns correctly when placed at the same centre.\n *\n * Note: the tail arc's centre is shifted left by tailWidth (radius stays constant = r_b), so\n * the tail always spans the full gate height regardless of tailWidth.\n * For bars to be visible, barsSeparation must be less than orHeight − 2 × thickness.\n *\n * @param orWidth - Width of the paired OR gate body\n * @param orHeight - Height of the paired OR gate body\n * @param tailWidth - How far left the tail arc's corners are from the OR gate's left corners\n * @param thickness - Wall thickness of the arc shell AND height of each bar\n * @param barsSeparation - Vertical gap between the two bars (centred on y = 0)\n * @param depth - Extrusion depth\n * @param steps - Number of extrusion steps\n */\nexport function XorGateTailGeometry(\n orWidth: number,\n orHeight: number,\n tailWidth: number,\n thickness: number,\n barsSeparation: number,\n depth: number,\n steps: number = 1\n): ExtrudeGeometry {\n const halfW = orWidth / 2;\n const halfH = orHeight / 2;\n\n // OR gate back-arc parameters — identical to OrGateGeometry\n const backInset = halfH * 0.4;\n const u_b = (halfH * halfH - backInset * backInset) / (2 * backInset);\n const cx_b = -halfW - u_b;\n const r_b = u_b + backInset;\n const angle_b = Math.atan2(halfH, u_b);\n\n // Tail arc: SAME radius r_b and angular sweep ±angle_b as the OR gate back arc,\n // but centre shifted left by tailWidth.\n // • arc corners land at (−halfW − tailWidth, ±halfH) — full gate height, always\n // • r_b never changes → never negative regardless of tailWidth value\n // • curvature is identical to the OR gate back arc (concentric shift of the centre)\n const cx_b_tail = cx_b - tailWidth; // = −(halfW + u_b + tailWidth)\n\n // Inner face of the arc shell: larger radius (further right from cx_b_tail = toward OR gate)\n const r_inner = r_b + thickness;\n\n // Corner x of the outer arc at y = ±halfH (= −halfW − tailWidth)\n const x_outer_corner = cx_b_tail + r_b * Math.cos(angle_b);\n // Flat right face aligned with the OR gate left-corner plane\n const x_flat = -halfW;\n\n // Bar vertical boundaries\n const y3 = barsSeparation / 2; // inner edge of bars (middle-gap boundary)\n const y4 = y3 + thickness; // outer edge of bars\n\n // x-coordinate and angle on the inner arc r_inner at height y\n const xInner = (y: number): number =>\n cx_b_tail + Math.sqrt(Math.max(0, r_inner * r_inner - y * y));\n const thetaInner = (y: number): number =>\n Math.atan2(y, Math.sqrt(Math.max(0, r_inner * r_inner - y * y)));\n\n // Whether bars and top/bottom caps are geometrically feasible given the parameters\n const barsExist = y3 < halfH && xInner(y3) < x_flat;\n const capsExist = barsExist && y4 < halfH; // caps sit above/below the bars inside the shape\n\n const extrudeSettings = { depth, bevelEnabled: false, steps };\n\n // Single closed CCW path — NO holes (avoids Three.js hole-border artefacts).\n // Strategy: trace the full solid boundary directly.\n // moveTo(x_outer_corner, -halfH)\n // EAST along bottom → NORTH up the right side (CCW inner arc + bar kinks) → WEST along top → CW outer arc SOUTH\n //\n // Three cases for the right side:\n // • No bars : single CCW inner arc from −halfH to +halfH\n // • Bars, no caps : flat face spans −halfH→+halfH, split by inner arc at ±y3\n // • Bars + caps : inner arc sections join bar segments between ±y3 and ±y4\n\n const shape = new THREE.Shape();\n shape.moveTo(x_outer_corner, -halfH);\n\n if (!barsExist) {\n // ── arc wall only ──────────────────────────────────────────────────────────\n shape.lineTo(xInner(-halfH), -halfH); // E — arc wall bottom\n shape.absarc(cx_b_tail, 0, r_inner, thetaInner(-halfH), thetaInner(halfH), false); // N — CCW inner arc all the way up\n } else if (!capsExist) {\n // ── bars reach the top/bottom of the shape (no cap regions) ───────────────\n // Flat face starts right at y = ±halfH; inner arc only spans the middle gap.\n shape.lineTo(x_flat, -halfH); // E — bottom edge spans arc wall + bar\n shape.lineTo(x_flat, -y3); // N — right face of bottom bar\n shape.lineTo(xInner(-y3), -y3); // W — top of bottom bar → inner arc\n shape.absarc(cx_b_tail, 0, r_inner, thetaInner(-y3), thetaInner(y3), false); // N — CCW inner arc through gap\n shape.lineTo(x_flat, y3); // E — bottom of top bar\n shape.lineTo(x_flat, halfH); // N — right face of top bar\n } else {\n // ── full shape: arc wall + top & bottom caps + two bars ───────────────────\n shape.lineTo(xInner(-halfH), -halfH); // E — arc wall bottom\n shape.absarc(cx_b_tail, 0, r_inner, thetaInner(-halfH), thetaInner(-y4), false); // N — CCW inner arc to bottom cap top\n shape.lineTo(x_flat, -y4); // E — bottom of bottom bar\n shape.lineTo(x_flat, -y3); // N — right face of bottom bar\n shape.lineTo(xInner(-y3), -y3); // W — top of bottom bar → inner arc\n shape.absarc(cx_b_tail, 0, r_inner, thetaInner(-y3), thetaInner(y3), false); // N — CCW inner arc through gap\n shape.lineTo(x_flat, y3); // E — bottom of top bar\n shape.lineTo(x_flat, y4); // N — right face of top bar\n shape.lineTo(xInner(y4), y4); // W — top of top bar → inner arc\n shape.absarc(cx_b_tail, 0, r_inner, thetaInner(y4), thetaInner(halfH), false); // N — CCW inner arc to top\n }\n\n shape.lineTo(x_outer_corner, halfH); // W — top edge\n shape.absarc(cx_b_tail, 0, r_b, angle_b, -angle_b, true); // S — CW outer arc down (left side)\n // Three.js auto-closes back to moveTo at (x_outer_corner, -halfH)\n\n return new THREE.ExtrudeGeometry(shape, extrudeSettings);\n}\n\n/**\n * Create an ExtrudeGeometry shaped like an L (or mirrored L) with a configurable\n * angle between the two arms.\n *\n * Default orientation (`invert = false`) produces a `|_`-like shape:\n * base arm extends to the right, stem arm extends upward at the given angle.\n * When `invert = true` the shape is mirrored horizontally (`_|`-like):\n * base arm extends to the left, stem arm mirrors accordingly.\n *\n * The `angle` parameter (in degrees) controls the inner angle between the two arms:\n * - 90° → standard right-angle L\n * - 120° → obtuse junction (`\\_`-like)\n * - 60° → acute junction\n *\n * Both the inner (concave) and outer (convex) junction corners are rounded\n * equally with `junctionRadius` (similar to CSS border-radius).\n * When 0 both corners are sharp. The arc sweep adapts to the angle: (180° − angle).\n *\n * The geometry is centered on its bounding box.\n *\n * At 90°, `width` and `height` correspond exactly to the bounding box dimensions.\n * At other angles the bounding box changes but the arm lengths remain consistent:\n * base inner arm = width − thickness, stem inner arm = height − thickness.\n *\n * @param width - Base arm length (bounding-box width at 90°)\n * @param height - Stem arm length (bounding-box height at 90°)\n * @param thickness - Arm thickness of the L\n * @param angle - Inner angle between the two arms, in degrees (typically 30–150)\n * @param invert - If true, mirror horizontally\n * @param junctionRadius - Radius of the rounded junction corners, inner and outer (0 = sharp)\n * @param depth - Extrusion depth\n * @param steps - Number of extrusion steps\n */\nexport function LGeometry(\n width: number,\n height: number,\n thickness: number,\n angle: number,\n invert: boolean,\n junctionRadius: number,\n depth: number,\n steps: number = 1\n): ExtrudeGeometry {\n const t = thickness;\n const alpha = THREE.MathUtils.degToRad(angle);\n const halfAlpha = alpha / 2;\n const cosA = Math.cos(alpha);\n const sinA = Math.sin(alpha);\n const cotHalf = Math.cos(halfAlpha) / Math.sin(halfAlpha);\n\n // Inner arm lengths from junction inner corner to arm tips\n const W = width - t;\n const H = height - t;\n\n // Clamp junction radius: tangent points must stay within both arms\n // Tangent distance from inner corner along each arm = r · cot(α/2)\n const maxR = cotHalf > 0 ? Math.min(W, H) / cotHalf : Infinity;\n const r = Math.min(Math.max(0, junctionRadius), maxR);\n\n // Inner arc center offset from inner corner along each arm = r · cot(α/2)\n const arcCX = r * cotHalf;\n\n // Outer arc center: at distance r inside the polygon from both outer edges\n // Center = ((r − t) · cotHalf, r − t) for non-inverted\n const outerCX = (r - t) * cotHalf;\n const outerCY = r - t;\n\n const shape = new THREE.Shape();\n\n if (!invert) {\n // Base arm extends RIGHT, stem arm at angle α from base\n // All coordinates relative to the inner junction corner at origin, CCW winding\n\n if (r > 0) {\n // Start at outer base tangent (on base outer edge, near junction)\n shape.moveTo(outerCX, -t);\n shape.lineTo(W, -t); // base outer edge →\n shape.lineTo(W, 0); // base tip cap ↑\n shape.lineTo(arcCX, 0); // base inner edge ← to inner arc tangent\n shape.absarc(arcCX, r, r, -Math.PI / 2, alpha + Math.PI / 2, true); // CW inner arc\n shape.lineTo(H * cosA, H * sinA); // stem tip inner\n shape.lineTo(H * cosA - t * sinA, H * sinA + t * cosA); // stem tip outer\n // Stem outer edge ↙ to outer arc tangent\n shape.lineTo(outerCX - r * sinA, outerCY + r * cosA);\n // CCW outer arc (convex corner rounding)\n shape.absarc(outerCX, outerCY, r, alpha + Math.PI / 2, -Math.PI / 2, false);\n // auto-close back to outer base tangent\n } else {\n shape.moveTo(-t * cotHalf, -t); // outer junction corner\n shape.lineTo(W, -t); // base outer edge →\n shape.lineTo(W, 0); // base tip cap ↑\n shape.lineTo(0, 0); // sharp inner corner\n shape.lineTo(H * cosA, H * sinA); // stem tip inner\n shape.lineTo(H * cosA - t * sinA, H * sinA + t * cosA); // stem tip outer\n // auto-close: stem outer edge back to outer junction corner\n }\n } else {\n // Mirrored: base arm extends LEFT, stem arm mirrored\n // Stem direction becomes (-cosA, sinA), outward perpendicular (sinA, cosA)\n\n if (r > 0) {\n // Start at outer stem tangent (on stem outer edge, near junction)\n shape.moveTo(-outerCX + r * sinA, outerCY + r * cosA);\n shape.lineTo(-H * cosA + t * sinA, H * sinA + t * cosA); // stem outer edge\n shape.lineTo(-H * cosA, H * sinA); // stem tip inner (cap)\n shape.lineTo(-arcCX * cosA, arcCX * sinA); // stem inner edge to inner arc tangent\n shape.absarc(-arcCX, r, r, Math.PI / 2 - alpha, -Math.PI / 2, true); // CW inner arc\n shape.lineTo(-W, 0); // base tip inner\n shape.lineTo(-W, -t); // base tip outer (cap)\n // Base outer edge → to outer arc tangent\n shape.lineTo(-outerCX, -t);\n // CCW outer arc (convex corner rounding)\n shape.absarc(-outerCX, outerCY, r, -Math.PI / 2, Math.PI / 2 - alpha, false);\n // auto-close back to outer stem tangent\n } else {\n shape.moveTo(t * cotHalf, -t); // outer junction corner\n shape.lineTo(-H * cosA + t * sinA, H * sinA + t * cosA); // stem tip outer\n shape.lineTo(-H * cosA, H * sinA); // stem tip inner (cap)\n shape.lineTo(0, 0); // sharp inner corner\n shape.lineTo(-W, 0); // base tip inner\n shape.lineTo(-W, -t); // base tip outer (cap)\n // auto-close: base outer edge → back to outer junction corner\n }\n }\n\n const geometry = new THREE.ExtrudeGeometry(shape, { depth, bevelEnabled: false, steps });\n /*geometry.rotateX(-0.23);\n geometry.rotateY(0.95);\n geometry.rotateZ(-0.22);*/\n return geometry;\n}\n\n/**\n * Returns the inner hole path for a cyclic trapezoid of the given dimensions.\n * Returns null when the geometry should be solid (no hole).\n *\n * @param width - Total width\n * @param tailHeight - Height of the left (tail) side\n * @param headHeight - Height of the right (head) side (0 for triangle)\n * @param thickness - Wall thickness\n */\nfunction _cyclicTrapezoidHolePath(\n width: number,\n tailHeight: number,\n headHeight: number,\n thickness: number\n): THREE.Path | null {\n const halfW = width / 2;\n const halfTailH = tailHeight / 2;\n const halfHeadH = headHeight / 2;\n\n if (thickness > 0.9 * Math.min(halfTailH, halfW)) return null;\n\n // Perpendicular inset of the slanted edges.\n // Top slant from (-halfW, halfTailH) to (halfW, halfHeadH):\n // dH = halfTailH − halfHeadH (height drop along the slant, ≥ 0)\n // L = slant length = √(width² + dH²)\n // Inner top-left y = halfTailH − thickness·(L + dH) / width\n // Inner top-right y = halfHeadH − thickness·(L − dH) / width\n // (bottom corners symmetric about y = 0)\n const dH = halfTailH - halfHeadH;\n const L = Math.sqrt(width * width + dH * dH);\n const innerTailHalfH = halfTailH - (thickness * (L + dH)) / width;\n if (innerTailHalfH <= 0) return null;\n\n const hole = new THREE.Path();\n\n if (headHeight > 0) {\n const innerHeadHalfH = halfHeadH - (thickness * (L - dH)) / width;\n if (innerHeadHalfH <= 0 || width - 2 * thickness <= 0) return null;\n\n // CW hole: TL → BL → BR → TR\n hole.moveTo(-halfW + thickness, innerTailHalfH);\n hole.lineTo(-halfW + thickness, -innerTailHalfH);\n hole.lineTo(halfW - thickness, -innerHeadHalfH);\n hole.lineTo(halfW - thickness, innerHeadHalfH);\n } else {\n // Triangle case: top and bottom inner slants meet at a single tip on the x-axis.\n // tipX = halfW − thickness·L / halfTailH\n const tipX = halfW - (thickness * L) / halfTailH;\n if (tipX <= -halfW + thickness) return null;\n\n // CW hole: TL → BL → tip\n hole.moveTo(-halfW + thickness, innerTailHalfH);\n hole.lineTo(-halfW + thickness, -innerTailHalfH);\n hole.lineTo(tipX, 0);\n }\n\n return hole;\n}\n\nexport function CyclicTrapezoidGeometry(\n width: number,\n tailHeight: number,\n headHeight: number,\n thickness: number,\n depth: number,\n steps: number = 1\n): ExtrudeGeometry {\n const halfW = width / 2;\n const halfTailH = tailHeight / 2;\n const halfHeadH = headHeight / 2;\n\n // Outer shape (CCW): BL → BR (→ TR if trapezoid) → TL\n const shape = new THREE.Shape();\n shape.moveTo(-halfW, -halfTailH);\n if (headHeight > 0) {\n shape.lineTo(halfW, -halfHeadH);\n shape.lineTo(halfW, halfHeadH);\n } else {\n shape.lineTo(halfW, 0);\n }\n shape.lineTo(-halfW, halfTailH);\n // Three.js auto-closes back to moveTo\n\n const extrudeSettings = { depth, bevelEnabled: false, steps };\n const hole = _cyclicTrapezoidHolePath(width, tailHeight, headHeight, thickness);\n if (hole !== null) shape.holes.push(hole);\n return new THREE.ExtrudeGeometry(shape, extrudeSettings);\n}\n\n/**\n * Create an ExtrudeGeometry shaped like an empty rectangle (frame/border).\n * Shape is centered at origin. The inner hole is a smaller rectangle inset\n * by `thickness` on all four sides.\n *\n * @param width - Total width of the outer rectangle\n * @param height - Total height of the outer rectangle\n * @param thickness - Wall thickness of the frame (inset on each side)\n * @param depth - Extrusion depth\n * @param steps - Number of extrusion steps\n * @constructor\n */\nexport function EmptyRectangleGeometry(\n width: number,\n height: number,\n thickness: number,\n depth: number,\n steps: number = 1\n): ExtrudeGeometry {\n const halfW = width / 2;\n const halfH = height / 2;\n\n // Outer shape (CCW): BL → BR → TR → TL\n const shape = new THREE.Shape();\n shape.moveTo(-halfW, -halfH);\n shape.lineTo(halfW, -halfH);\n shape.lineTo(halfW, halfH);\n shape.lineTo(-halfW, halfH);\n // Three.js auto-closes back to (-halfW, -halfH)\n\n const innerHalfW = halfW - thickness;\n const innerHalfH = halfH - thickness;\n\n if (innerHalfW > 0 && innerHalfH > 0) {\n // Inner hole (CW): TL → BL → BR → TR\n const hole = new THREE.Path();\n hole.moveTo(-innerHalfW, innerHalfH);\n hole.lineTo(-innerHalfW, -innerHalfH);\n hole.lineTo(innerHalfW, -innerHalfH);\n hole.lineTo(innerHalfW, innerHalfH);\n // auto-closes back to (-innerHalfW, innerHalfH)\n shape.holes.push(hole);\n }\n\n return new THREE.ExtrudeGeometry(shape, { depth, bevelEnabled: false, steps });\n}\n\n/**\n * Create an ExtrudeGeometry shaped like a rectangle with a rectangular \"nail\"\n * (intrusion or protrusion)\n * The main rectangle is centered at origin; the nail hangs inside or outside it.\n *\n * @param width - Total width of the rectangle\n * @param height - Height of the main rectangle body\n * @param nailWidth - Width of the nail intrusion\n * @param nailHeight - Height of the nail\n * @param nailX - Horizontal center of the nail, measured from the rectangle's left edge\n * @param protusion - if false nail will enter inside the rectangle (intrusion), else outside (protusion)\n * @param depth - Extrusion depth\n * @param steps - Number of extrusion steps\n * @throws Error if nail dimensions or position are geometrically invalid\n * @constructor\n */\nexport function RectangleWithNailGeometry(\n width: number,\n height: number,\n nailWidth: number,\n nailHeight: number,\n nailX: number,\n protusion: boolean,\n depth: number,\n steps: number = 1\n): ExtrudeGeometry {\n if (!protusion && nailHeight >= height) throw new Error(`when intrusion nailHeight (${nailHeight}) must be < height (${height})`);\n if (nailWidth >= width) throw new Error(`nailWidth (${nailWidth}) must be < width (${width})`);\n if (nailX - nailWidth / 2 <= 0) throw new Error(`nailX - nailWidth/2 (${nailX - nailWidth / 2}) must be > 0`);\n if (nailX + nailWidth / 2 >= width) throw new Error(`nailX + nailWidth/2 (${nailX + nailWidth / 2}) must be < width (${width})`);\n\n const halfW = width / 2;\n const halfH = height / 2;\n const nailL = nailX - halfW - nailWidth / 2; // nail left x in centered coords\n const nailR = nailX - halfW + nailWidth / 2; // nail right x in centered coords\n\n const nailYPos = -halfH + (protusion ? -1 : 1) * nailHeight;\n\n // Outer shape (CCW): BL → nail-slot-left → nail-BL → nail-BR → nail-slot-right → BR → TR → TL\n const shape = new THREE.Shape();\n shape.moveTo(-halfW, -halfH);\n shape.lineTo(nailL, -halfH);\n shape.lineTo(nailL, nailYPos);\n shape.lineTo(nailR, nailYPos);\n shape.lineTo(nailR, -halfH);\n shape.lineTo(halfW, -halfH);\n shape.lineTo(halfW, halfH);\n shape.lineTo(-halfW, halfH);\n // Three.js auto-closes back to (-halfW, -halfH)\n\n return new THREE.ExtrudeGeometry(shape, { depth, bevelEnabled: false, steps });\n}\n\n/**\n * Create an ExtrudeGeometry for the inner hole of a cyclic trapezoid.\n * Returns null when the geometry should be solid (no hole).\n *\n * @param width - Total width\n * @param tailHeight - Height of the left (tail) side\n * @param headHeight - Height of the right (head) side (0 for triangle)\n * @param thickness - Wall thickness\n * @param depth - Extrusion depth\n * @param steps - Number of extrusion steps\n */\nexport function CyclicTrapezoidHoleGeometry(\n width: number,\n tailHeight: number,\n headHeight: number,\n thickness: number,\n depth: number,\n steps: number = 1\n): ExtrudeGeometry | null {\n const hole = _cyclicTrapezoidHolePath(width, tailHeight, headHeight, thickness);\n if (hole === null) return null;\n const shape = new THREE.Shape(hole.getPoints(64).reverse());\n return new THREE.ExtrudeGeometry(shape, { depth, bevelEnabled: false, steps });\n}\n\n/**\n * Returns the inner hole path for a drip (teardrop) shape.\n * Returns null when thickness is large enough that the shape should be solid.\n *\n * @param width - Total width\n * @param height - Total height (must be > width)\n * @param thickness - Wall thickness\n */\nfunction _dripHolePath(width: number, height: number, thickness: number): THREE.Path | null {\n const halfW = width / 2;\n const halfH = height / 2;\n if (thickness > 0.9 * Math.min(halfW, halfH)) return null;\n\n const yc = -halfH + halfW; // bottom circle center y\n const d = height - halfW; // distance from tip to circle center\n const sinA = halfW / d;\n const cosA = Math.sqrt(1 - sinA * sinA);\n const alpha = Math.asin(sinA);\n\n const rInner = halfW - thickness;\n const tipInsetY = halfH - thickness / sinA; // inner tip y (moved down from outer tip)\n const innerTanY = yc - rInner * sinA; // y of inner tangent points\n\n if (rInner <= 0) return null;\n if (tipInsetY <= innerTanY) return null; // tip would fall below tangent points\n\n // Hole path (CCW): inner tip → inner T_left → [CCW arc through bottom] → inner T_right → auto-close to tip\n const hole = new THREE.Path();\n hole.moveTo(0, tipInsetY); // inner tip\n hole.lineTo(-rInner * cosA, innerTanY); // inner T_left\n hole.absarc(0, yc, rInner, alpha - Math.PI, -alpha, false); // CCW arc through bottom\n // Arc ends at inner T_right = (rInner * cosA, innerTanY)\n // Three.js auto-closes back to inner tip\n return hole;\n}\n\n/**\n * Create an ExtrudeGeometry for a drip (teardrop) shape.\n * The shape has a sharp pointed tip at the top and a rounded bottom.\n * Shape is centered at origin.\n *\n * Construction: the rounded bottom is a circle of radius width/2 centered at\n * y = -height/2 + width/2. Two straight tangent lines connect the circle to the\n * tip at (0, +height/2). This requires height > width so the tip sits above the\n * circle and the tangent lines exist.\n *\n * @param width - Total width\n * @param height - Total height (must be > width)\n * @param thickness - Wall thickness of the shell\n * @param depth - Extrusion depth\n * @param steps - Number of extrusion steps\n * @throws Error if height <= width (tip too low for tangent construction)\n * @constructor\n */\nexport function DripGeometry(\n width: number,\n height: number,\n thickness: number,\n depth: number,\n steps: number = 1\n): ExtrudeGeometry {\n if (height <= width) throw new Error(`DripGeometry: height (${height}) must be > width (${width})`);\n\n const halfW = width / 2;\n const halfH = height / 2;\n const yc = -halfH + halfW; // bottom circle center y\n const d = height - halfW; // distance from tip to circle center\n const sinA = halfW / d;\n const cosA = Math.sqrt(1 - sinA * sinA);\n const alpha = Math.asin(sinA);\n const theta_r = -alpha; // angle of right tangent point on circle\n const theta_l = alpha - Math.PI; // angle of left tangent point on circle (= -(π − alpha))\n\n // Outer shape: tip → T_right → [CW arc through bottom] → T_left → [auto-close to tip]\n const shape = new THREE.Shape();\n shape.moveTo(0, halfH); // tip\n shape.lineTo(halfW * cosA, yc - halfW * sinA); // T_right\n shape.absarc(0, yc, halfW, theta_r, theta_l, true); // CW arc through bottom\n // Arc ends at T_left = (-halfW * cosA, yc - halfW * sinA)\n // Three.js auto-closes back to tip (left side tangent line)\n\n const hole = _dripHolePath(width, height, thickness);\n if (hole !== null) shape.holes.push(hole);\n\n return new THREE.ExtrudeGeometry(shape, { depth, bevelEnabled: false, steps });\n}\n\n/**\n * Create an ExtrudeGeometry for the inner hole of a drip shape.\n * Returns null when thickness is large enough that the drip has no hole.\n *\n * @param width - Total width\n * @param height - Total height (must be > width)\n * @param thickness - Wall thickness\n * @param depth - Extrusion depth\n * @param steps - Number of extrusion steps\n * @throws Error if height <= width\n */\nexport function DripHoleGeometry(\n width: number,\n height: number,\n thickness: number,\n depth: number,\n steps: number = 1\n): ExtrudeGeometry | null {\n if (height <= width) throw new Error(`DripHoleGeometry: height (${height}) must be > width (${width})`);\n const hole = _dripHolePath(width, height, thickness);\n if (hole === null) return null;\n const shape = new THREE.Shape(hole.getPoints(64).reverse());\n return new THREE.ExtrudeGeometry(shape, { depth, bevelEnabled: false, steps });\n}\n\n/**\n * Create an ExtrudeGeometry shaped like japanese hiragana Ku and katakana Ko (くコ)\n * Shape is centered horizontally at the junction between the two and vertically at the middle height\n * by `kuThickness` on all four sides.\n *\n * @param kuWidth - width of the left ku part (outer)\n * @param koWidth - width of the right ko part (outer)\n * @param height - Total height of the outer geometry\n * @param kuThickness - Wall kuThickness of the frame (inset on ku side)\n * @param koThickness - Wall koThickness of the frame (inset on ko side)\n * @param depth - Extrusion depth\n * @param steps - Number of extrusion steps\n * @constructor\n */\nexport function KuKoGeometry(\n kuWidth: number,\n koWidth: number,\n height: number,\n kuThickness: number,\n koThickness: number,\n depth: number,\n steps: number = 1\n): ExtrudeGeometry {\n const halfH = height / 2;\n\n // Outer shape\n const shape = new THREE.Shape();\n shape.moveTo(0, -halfH);\n shape.lineTo(-kuWidth, 0);\n shape.lineTo(0, halfH);\n shape.lineTo(koWidth, halfH);\n shape.lineTo(koWidth, -halfH);\n // Three.js auto-closes back to (0, -halfH)\n\n let hasKuHole = kuThickness < 0.45*kuWidth && kuThickness < 0.45*height;\n let hasKoHole = koThickness < 0.45*koWidth && koThickness < 0.45*height;\n\n if(hasKuHole && hasKoHole){\n const hole = new THREE.Path();\n hole.moveTo(0, -halfH + koThickness);\n hole.lineTo(-kuWidth + kuThickness, 0);\n hole.lineTo(0, halfH - koThickness);\n hole.lineTo(koWidth - koThickness, halfH - koThickness);\n hole.lineTo(koWidth - koThickness, -halfH + koThickness);\n // Three.js auto-closes back to (0, -halfH + kuThickness)\n shape.holes.push(hole);\n }\n else if(!hasKuHole && hasKoHole){\n const hole = new THREE.Path();\n hole.moveTo(0, -halfH + koThickness);\n hole.lineTo(0, halfH - koThickness);\n hole.lineTo(koWidth - koThickness, halfH - koThickness);\n hole.lineTo(koWidth - koThickness, -halfH + koThickness);\n // Three.js auto-closes back to (0, -halfH + kuThickness)\n shape.holes.push(hole);\n }\n else if(hasKuHole && !hasKoHole){\n const hole = new THREE.Path();\n hole.moveTo(0, -halfH + kuThickness);\n hole.lineTo(-kuWidth + kuThickness, 0);\n hole.lineTo(0, halfH - kuThickness);\n // Three.js auto-closes back to (0, -halfH + kuThickness)\n shape.holes.push(hole);\n }\n console.log(shape);\n // both false : no hole -> whole form\n return new THREE.ExtrudeGeometry(shape, { depth, bevelEnabled: false, steps });\n}\n","{\n \"components\": {\n \"groups\": {\n \"basic\": {\n \"name\": \"Basic Components\"\n },\n \"gates\": {\n \"name\": \"Logic Gates\"\n },\n \"arithmetic\": {\n \"name\": \"Arithmetic\"\n }\n },\n \"battery\": {\n \"name\": \"Battery\"\n },\n \"clock\": {\n \"name\": \"Clock\",\n \"config\": {\n \"fields\": {\n \"startHigh\": { \"name\": \"Start High ?\" },\n \"halfPeriod\": { \"name\": \"Half Period\" }\n }\n }\n },\n \"label\": {\n \"name\": \"Label\",\n \"config\": {\n \"fields\": {\n \"text\": { \"name\": \"Text\" },\n \"size\": { \"name\": \"Size\" }\n }\n }\n },\n \"switch\": {\n \"name\": \"Switch\",\n \"config\": {\n \"fields\": {\n \"initialState\": { \"name\": \"Open at start\" },\n \"transitionSpan\": { \"name\": \"Delay (ticks)\" },\n \"size\": { \"name\": \"Size\" }\n }\n }\n },\n \"doubleThrowSwitch\": {\n \"name\": \"2-1 Switch\",\n \"config\": {\n \"fields\": {\n \"initialState\": { \"name\": \"Input1 at start\" },\n \"transitionSpan\": { \"name\": \"Delay (ticks)\" },\n \"size\": { \"name\": \"Size\" }\n }\n }\n },\n \"lightbulb\": {\n \"name\": \"Lightbulb\",\n \"config\": {\n \"fields\": {\n \"transitionSpan\": { \"name\": \"Lit delay (ticks)\" },\n \"size\": { \"name\": \"Size\" }\n }\n }\n },\n \"rectangleLED\": {\n \"name\": \"Rectangle LED\",\n \"config\": {\n \"fields\": {\n \"transitionSpan\": { \"name\": \"Lit delay (ticks)\" },\n \"idleColor\": { \"name\": \"Idle Color\" },\n \"activeColor\": { \"name\": \"Active Color\" },\n \"size\": { \"name\": \"Size\" },\n \"hwRatio\": { \"name\": \"Ratio H/W\" },\n \"ywRatio\": { \"name\": \"Ratio Y/W\" }\n }\n }\n },\n \"relay\": {\n \"name\": \"Relay\",\n \"config\": {\n \"fields\": {\n \"activationLogic\": { \"name\": \"Activation Logic\" },\n \"transitionSpan\": { \"name\": \"Transition Span (ticks)\" },\n \"size\": { \"name\": \"Size\" },\n \"initializationOrder\": { \"name\": \"Init Order\" }\n }\n }\n },\n \"smallLED\": {\n \"name\": \"Small LED\",\n \"config\": {\n \"fields\": {\n \"transitionSpan\": { \"name\": \"Lit delay (ticks)\" },\n \"idleColor\": { \"name\": \"Idle Color\" },\n \"activeColor\": { \"name\": \"Active Color\" },\n \"size\": { \"name\": \"Size\" },\n \"ywRatio\": { \"name\": \"Ratio Y/W\" }\n }\n }\n },\n \"inverter\": {\n \"name\": \"Inverter\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Logic Family\" },\n \"activationLogic\": { \"name\": \"Activation Logic\" },\n \"transitionSpan\": { \"name\": \"Propagation delay (ticks)\" },\n \"initializationOrder\": { \"name\": \"Init Order\" }\n }\n }\n },\n \"nandGate\": {\n \"name\": \"NAND Gate\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Logic Family\" },\n \"activationLogic\": { \"name\": \"Activation Logic\" },\n \"transitionSpan\": { \"name\": \"Propagation delay (ticks)\" },\n \"initializationOrder\": { \"name\": \"Init Order\" }\n }\n }\n },\n \"nand4Gate\": {\n \"name\": \"4-input NAND Gate\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Logic Family\" },\n \"activationLogic\": { \"name\": \"Activation Logic\" },\n \"transitionSpan\": { \"name\": \"Propagation delay (ticks)\" },\n \"initializationOrder\": { \"name\": \"Init Order\" }\n }\n }\n },\n \"nand8Gate\": {\n \"name\": \"8-input NAND Gate\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Logic Family\" },\n \"activationLogic\": { \"name\": \"Activation Logic\" },\n \"transitionSpan\": { \"name\": \"Propagation delay (ticks)\" },\n \"initializationOrder\": { \"name\": \"Init Order\" }\n }\n }\n },\n \"norGate\": {\n \"name\": \"NOR Gate\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Logic Family\" },\n \"activationLogic\": { \"name\": \"Activation Logic\" },\n \"transitionSpan\": { \"name\": \"Propagation delay (ticks)\" },\n \"initializationOrder\": { \"name\": \"Init Order\" }\n }\n }\n },\n \"nor4Gate\": {\n \"name\": \"4-input NOR Gate\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Logic Family\" },\n \"activationLogic\": { \"name\": \"Activation Logic\" },\n \"transitionSpan\": { \"name\": \"Propagation delay (ticks)\" },\n \"initializationOrder\": { \"name\": \"Init Order\" }\n }\n }\n },\n \"nor8Gate\": {\n \"name\": \"8-input NOR Gate\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Logic Family\" },\n \"activationLogic\": { \"name\": \"Activation Logic\" },\n \"transitionSpan\": { \"name\": \"Propagation delay (ticks)\" },\n \"initializationOrder\": { \"name\": \"Init Order\" }\n }\n }\n },\n \"xorGate\": {\n \"name\": \"XOR Gate\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Logic Family\" },\n \"activationLogic\": { \"name\": \"Activation Logic\" },\n \"transitionSpan\": { \"name\": \"Propagation delay (ticks)\" },\n \"initializationOrder\": { \"name\": \"Init Order\" }\n }\n }\n },\n \"xor4Gate\": {\n \"name\": \"4-input XOR Gate\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Logic Family\" },\n \"activationLogic\": { \"name\": \"Activation Logic\" },\n \"transitionSpan\": { \"name\": \"Propagation delay (ticks)\" },\n \"initializationOrder\": { \"name\": \"Init Order\" }\n }\n }\n },\n \"xor8Gate\": {\n \"name\": \"8-input XOR Gate\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Logic Family\" },\n \"activationLogic\": { \"name\": \"Activation Logic\" },\n \"transitionSpan\": { \"name\": \"Propagation delay (ticks)\" },\n \"initializationOrder\": { \"name\": \"Init Order\" }\n }\n }\n },\n \"halfAdder\": {\n \"name\": \"Half Adder\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Logic Family\" },\n \"transitionSpan\": { \"name\": \"Propagation delay (ticks)\" },\n \"initializationOrder\": { \"name\": \"Init Order\" }\n }\n }\n },\n \"adder\": {\n \"name\": \"Full Adder\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Logic Family\" },\n \"transitionSpan\": { \"name\": \"Propagation delay (ticks)\" },\n \"initializationOrder\": { \"name\": \"Init Order\" }\n }\n }\n },\n \"eightBitAdder\": {\n \"name\": \"8-Bit Adder\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Logic Family\" },\n \"transitionSpan\": { \"name\": \"Propagation delay (ticks)\" },\n \"initializationOrder\": { \"name\": \"Init Order\" }\n }\n }\n },\n \"eightBitOnesComplement\": {\n \"name\": \"8-Bit Ones Complement\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Logic Family\" },\n \"transitionSpan\": { \"name\": \"Propagation delay (ticks)\" },\n \"initializationOrder\": { \"name\": \"Init Order\" }\n }\n }\n },\n \"default\": {\n \"config\": {\n \"fields\": {\n \"color\": { \"name\": \"Color\" }\n }\n }\n }\n },\n \"picker\": {\n \"title\": \"Components\",\n \"branchingPoint\": \"Branching Point\"\n },\n \"config\": {\n \"title\": \"Configuration: {{name}}\"\n }\n}\n","{\n \"components\": {\n \"groups\": {\n \"basic\": {\n \"name\": \"Composants basiques\"\n },\n \"gates\": {\n \"name\": \"Portes logiques\"\n },\n \"arithmetic\": {\n \"name\": \"Arithmétique\"\n }\n },\n \"battery\": {\n \"name\": \"Pile\"\n },\n \"clock\": {\n \"name\": \"Horloge\",\n \"config\": {\n \"fields\": {\n \"startHigh\": { \"name\": \"Démarrer haut ?\" },\n \"halfPeriod\": { \"name\": \"Demi-période\" }\n }\n }\n },\n \"label\": {\n \"name\": \"Libellé\",\n \"config\": {\n \"fields\": {\n \"text\": { \"name\": \"Texte\" },\n \"size\": { \"name\": \"Taille\" }\n }\n }\n },\n \"switch\": {\n \"name\": \"Interrupteur\",\n \"config\": {\n \"fields\": {\n \"initialState\": { \"name\": \"Ouvert au démarrage\" },\n \"transitionSpan\": { \"name\": \"Délai (ticks)\" },\n \"size\": { \"name\": \"Taille\" }\n }\n }\n },\n \"doubleThrowSwitch\": {\n \"name\": \"Interrupteur 2-1\",\n \"config\": {\n \"fields\": {\n \"initialState\": { \"name\": \"Entrée 1 au démarrage\" },\n \"transitionSpan\": { \"name\": \"Délai (ticks)\" },\n \"size\": { \"name\": \"Taille\" }\n }\n }\n },\n \"lightbulb\": {\n \"name\": \"Ampoule\",\n \"config\": {\n \"fields\": {\n \"transitionSpan\": { \"name\": \"Délai d'allumage (ticks)\" },\n \"size\": { \"name\": \"Taille\" }\n }\n }\n },\n \"rectangleLED\": {\n \"name\": \"LED rectangulaire\",\n \"config\": {\n \"fields\": {\n \"transitionSpan\": { \"name\": \"Délai d'allumage (ticks)\" },\n \"idleColor\": { \"name\": \"Couleur inactive\" },\n \"activeColor\": { \"name\": \"Couleur active\" },\n \"size\": { \"name\": \"Taille\" },\n \"hwRatio\": { \"name\": \"Ratio H/L\" },\n \"ywRatio\": { \"name\": \"Ratio Y/L\" }\n }\n }\n },\n \"relay\": {\n \"name\": \"Relais\",\n \"config\": {\n \"fields\": {\n \"activationLogic\": { \"name\": \"Logique d'activation\" },\n \"transitionSpan\": { \"name\": \"Durée de transition (ticks)\" },\n \"size\": { \"name\": \"Taille\" },\n \"initializationOrder\": { \"name\": \"Ordre d'initialisation\" }\n }\n }\n },\n \"smallLED\": {\n \"name\": \"Petite LED\",\n \"config\": {\n \"fields\": {\n \"transitionSpan\": { \"name\": \"Délai d'allumage (ticks)\" },\n \"idleColor\": { \"name\": \"Couleur inactive\" },\n \"activeColor\": { \"name\": \"Couleur active\" },\n \"size\": { \"name\": \"Taille\" },\n \"ywRatio\": { \"name\": \"Ratio Y/L\" }\n }\n }\n },\n \"inverter\": {\n \"name\": \"Inverseur\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Famille logique\" },\n \"activationLogic\": { \"name\": \"Logique d'activation\" },\n \"transitionSpan\": { \"name\": \"Délai de propagation (ticks)\" },\n \"initializationOrder\": { \"name\": \"Ordre d'initialisation\" }\n }\n }\n },\n \"nandGate\": {\n \"name\": \"Porte NAND\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Famille logique\" },\n \"activationLogic\": { \"name\": \"Logique d'activation\" },\n \"transitionSpan\": { \"name\": \"Délai de propagation (ticks)\" },\n \"initializationOrder\": { \"name\": \"Ordre d'initialisation\" }\n }\n }\n },\n \"nand4Gate\": {\n \"name\": \"Porte NAND 4 entrées\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Famille logique\" },\n \"activationLogic\": { \"name\": \"Logique d'activation\" },\n \"transitionSpan\": { \"name\": \"Délai de propagation (ticks)\" },\n \"initializationOrder\": { \"name\": \"Ordre d'initialisation\" }\n }\n }\n },\n \"nand8Gate\": {\n \"name\": \"Porte NAND 8 entrées\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Famille logique\" },\n \"activationLogic\": { \"name\": \"Logique d'activation\" },\n \"transitionSpan\": { \"name\": \"Délai de propagation (ticks)\" },\n \"initializationOrder\": { \"name\": \"Ordre d'initialisation\" }\n }\n }\n },\n \"norGate\": {\n \"name\": \"Porte NOR\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Famille logique\" },\n \"activationLogic\": { \"name\": \"Logique d'activation\" },\n \"transitionSpan\": { \"name\": \"Délai de propagation (ticks)\" },\n \"initializationOrder\": { \"name\": \"Ordre d'initialisation\" }\n }\n }\n },\n \"nor4Gate\": {\n \"name\": \"Porte NOR 4 entrées\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Famille logique\" },\n \"activationLogic\": { \"name\": \"Logique d'activation\" },\n \"transitionSpan\": { \"name\": \"Délai de propagation (ticks)\" },\n \"initializationOrder\": { \"name\": \"Ordre d'initialisation\" }\n }\n }\n },\n \"nor8Gate\": {\n \"name\": \"Porte NOR 8 entrées\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Famille logique\" },\n \"activationLogic\": { \"name\": \"Logique d'activation\" },\n \"transitionSpan\": { \"name\": \"Délai de propagation (ticks)\" },\n \"initializationOrder\": { \"name\": \"Ordre d'initialisation\" }\n }\n }\n },\n \"xorGate\": {\n \"name\": \"Porte XOR\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Famille logique\" },\n \"activationLogic\": { \"name\": \"Logique d'activation\" },\n \"transitionSpan\": { \"name\": \"Délai de propagation (ticks)\" },\n \"initializationOrder\": { \"name\": \"Ordre d'initialisation\" }\n }\n }\n },\n \"xor4Gate\": {\n \"name\": \"Porte XOR 4 entrées\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Famille logique\" },\n \"activationLogic\": { \"name\": \"Logique d'activation\" },\n \"transitionSpan\": { \"name\": \"Délai de propagation (ticks)\" },\n \"initializationOrder\": { \"name\": \"Ordre d'initialisation\" }\n }\n }\n },\n \"xor8Gate\": {\n \"name\": \"Porte XOR 8 entrées\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Famille logique\" },\n \"activationLogic\": { \"name\": \"Logique d'activation\" },\n \"transitionSpan\": { \"name\": \"Délai de propagation (ticks)\" },\n \"initializationOrder\": { \"name\": \"Ordre d'initialisation\" }\n }\n }\n },\n \"halfAdder\": {\n \"name\": \"Demi-additionneur\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Famille logique\" },\n \"transitionSpan\": { \"name\": \"Délai de propagation (ticks)\" },\n \"initializationOrder\": { \"name\": \"Ordre d'initialisation\" }\n }\n }\n },\n \"adder\": {\n \"name\": \"Additionneur\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Famille logique\" },\n \"transitionSpan\": { \"name\": \"Délai de propagation (ticks)\" },\n \"initializationOrder\": { \"name\": \"Ordre d'initialisation\" }\n }\n }\n },\n \"eightBitAdder\": {\n \"name\": \"Additionneur 8 bits\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Famille logique\" },\n \"transitionSpan\": { \"name\": \"Délai de propagation (ticks)\" },\n \"initializationOrder\": { \"name\": \"Ordre d'initialisation\" }\n }\n }\n },\n \"eightBitOnesComplement\": {\n \"name\": \"Complément à Un 8 bits\",\n \"config\": {\n \"fields\": {\n \"defaultLogicFamily\": { \"name\": \"Famille logique\" },\n \"transitionSpan\": { \"name\": \"Délai de propagation (ticks)\" },\n \"initializationOrder\": { \"name\": \"Ordre d'initialisation\" }\n }\n }\n },\n \"default\": {\n \"config\": {\n \"fields\": {\n \"color\": { \"name\": \"Couleur\" }\n }\n }\n }\n },\n \"picker\": {\n \"title\": \"Composants\",\n \"branchingPoint\": \"Point de jonction\"\n },\n \"config\": {\n \"title\": \"Configuration: {{name}}\"\n }\n}\n","/**\n * Internationalization entrypoint for simple-circuit-engine.\n *\n * The library does not own an i18next instance — it relies on the consumer's\n * singleton (declared as a peer dependency, same pattern as `three`). Consumers\n * initialize i18next in their app, then call {@link registerSceTranslations} to\n * merge this library's resource bundles into it under the {@link SCE_NS}\n * namespace.\n *\n * Internally, library code should reference keys as `${SCE_NS}:path.to.key` so\n * they cannot collide with consumer strings.\n *\n * @packageDocumentation\n */\nimport i18next from 'i18next';\nimport type { i18n, TOptions } from 'i18next';\n\nimport en from './locales/en.json';\nimport fr from './locales/fr.json';\n\n/** Namespace used for all simple-circuit-engine translations. */\nexport const SCE_NS = 'sce' as const;\n\nconst bundles = { en, fr } as const;\n\n/** Locales shipped with simple-circuit-engine. */\nexport type SceLocale = keyof typeof bundles;\n\n/**\n * Register simple-circuit-engine translations into the consumer's i18next\n * instance under the `sce` namespace.\n *\n * Must be called after the consumer has initialized i18next. Safe to call\n * multiple times; existing bundles in the `sce` namespace are NOT overwritten\n * so consumers can register their own overrides first.\n *\n * @param instance - The consumer's i18next singleton\n */\nexport function registerSceTranslations(instance: i18n): void {\n for (const [lng, resources] of Object.entries(bundles)) {\n instance.addResourceBundle(\n lng,\n SCE_NS,\n resources,\n /* deep */ true,\n /* overwrite */ false,\n );\n }\n}\n\n/**\n * Translate a key inside the sce namespace using the shared i18next singleton.\n *\n * Uses a direct import of the i18next singleton rather than an injected\n * instance — this avoids module-state duplication when Vite pre-bundles the\n * library's subpath exports separately. i18next is declared as a\n * peerDependency so the consumer and library always share the same module.\n *\n * Internal helper — not re-exported from src/index.ts.\n */\nexport function sceT(key: string, options?: { defaultValue?: string } & Record<string, unknown>): string {\n return i18next.t(`${SCE_NS}:${key}`, options as TOptions) as string;\n}\n","/**\n * Component Picker Widget\n * @module scene/static/tools/ComponentPickerWidget\n *\n * DOM-based overlay widget for selecting component types organized by groups.\n * Used by BuildTool in add_component mode.\n * Follows the DOM overlay pattern established by ConfigPanelManager.\n */\n\nimport type { ComponentType } from 'simple-circuit-engine/core';\nimport type { IGroupedFactoryRegistry } from '../../shared/components/GroupedFactoryRegistry';\nimport { sceT } from '../../../i18n';\n\n/**\n * Sentinel value representing the \"Branching Point\" pseudo-component entry.\n * When selected, BuildTool creates a branching point instead of a component.\n */\nexport const BRANCHING_POINT_SENTINEL = '__branching_point__' as const;\n\n/**\n * Union of actual component types and the branching point sentinel.\n */\nexport type PickerSelection = ComponentType | typeof BRANCHING_POINT_SENTINEL;\n\n/**\n * Persisted widget state (survives open/close cycles within a session).\n */\nexport interface ComponentPickerState {\n selectedGroupId: string;\n selectedItem: PickerSelection | null;\n widgetWidth: number;\n widgetHeight: number;\n}\n\n/** Group id where the Branching Point entry is prepended */\nconst BRANCHING_POINT_GROUP = 'basic';\n\n/** Default widget dimensions */\nconst DEFAULT_WIDTH = 180;\nconst DEFAULT_HEIGHT = 300;\n\n/** Viewport positioning constants (matching ConfigPanelManager) */\nconst OFFSET_X = -240;\nconst OFFSET_Y = -150;\nconst VIEWPORT_PADDING = 10;\n\n/**\n * DOM overlay widget for selecting components from grouped registry.\n *\n * Features:\n * - Group dropdown for filtering component types\n * - Scrollable item list with selection highlight\n * - Draggable header bar\n * - Resizable via CSS resize\n * - State persistence across open/close (group, selection, size)\n */\nexport class ComponentPickerWidget {\n // DOM elements\n private container: HTMLDivElement | null = null;\n private titleEl: HTMLSpanElement | null = null;\n private groupDropdown: HTMLSelectElement | null = null;\n private itemList: HTMLDivElement | null = null;\n\n // Persisted state\n private state: ComponentPickerState;\n\n // Callbacks\n private readonly onSelectionChange: (selection: PickerSelection | null) => void;\n private readonly onClose: () => void;\n\n // Event handler references for cleanup\n private escapeHandler: ((e: KeyboardEvent) => void) | null = null;\n\n // Drag state\n private dragOffset: { x: number; y: number } | null = null;\n private dragMoveHandler: ((e: MouseEvent) => void) | null = null;\n private dragEndHandler: ((e: MouseEvent) => void) | null = null;\n\n /**\n * @param registry - Grouped factory registry providing groups and component types\n * @param onSelectionChange - Called when user selects or deselects an item\n * @param onClose - Called when user closes the widget (close button or Escape)\n */\n constructor(\n private readonly registry: IGroupedFactoryRegistry,\n onSelectionChange: (selection: PickerSelection | null) => void,\n onClose: () => void\n ) {\n this.onSelectionChange = onSelectionChange;\n this.onClose = onClose;\n\n // Initialize state with first available group\n const groups = registry.getGroups();\n const firstGroup = groups[0];\n this.state = {\n selectedGroupId: firstGroup ? firstGroup.id : '',\n selectedItem: null,\n widgetWidth: DEFAULT_WIDTH,\n widgetHeight: DEFAULT_HEIGHT,\n };\n }\n\n /** Whether the widget is currently open and visible */\n get isOpen(): boolean {\n return this.container !== null;\n }\n\n /** The currently selected item (persists across open/close) */\n get currentSelection(): PickerSelection | null {\n return this.state.selectedItem;\n }\n\n /**\n * Open the widget at the given screen position.\n * If already open, repositions to the new location.\n *\n * @param screenPosition - Screen coordinates (typically from mouse event)\n */\n open(screenPosition: { x: number; y: number }): void {\n if (this.container) {\n this.positionContainer(screenPosition);\n return;\n }\n\n this.createDOM();\n this.positionContainer(screenPosition);\n this.renderItemList();\n this.registerEventListeners();\n }\n\n /**\n * Close the widget and remove DOM.\n * Preserves state (group, selection, size) for next open.\n */\n close(): void {\n if (!this.container) return;\n\n // Save current size before removing\n this.state.widgetWidth = this.container.offsetWidth;\n this.state.widgetHeight = this.container.offsetHeight;\n\n this.removeEventListeners();\n document.body.removeChild(this.container);\n this.container = null;\n this.titleEl = null;\n this.groupDropdown = null;\n this.itemList = null;\n }\n\n /**\n * Full cleanup including state reset.\n */\n dispose(): void {\n this.close();\n }\n\n /**\n * Refresh all user-visible text in place after a language change.\n * No-op if the widget is not currently open.\n * Preserves drag position, scroll, resize dimensions, and current selection.\n */\n setLanguage(_lng: string): void {\n if (!this.isOpen) return;\n\n if (this.titleEl) {\n this.titleEl.textContent = sceT('picker.title', { defaultValue: 'Components' });\n }\n\n if (this.groupDropdown) {\n const selectedValue = this.groupDropdown.value;\n for (const option of Array.from(this.groupDropdown.options)) {\n option.textContent = sceT(`components.groups.${option.value}.name`, {\n defaultValue: option.textContent ?? option.value,\n });\n }\n this.groupDropdown.value = selectedValue;\n }\n\n this.renderItemList();\n }\n\n // ========================================================================\n // DOM Creation\n // ========================================================================\n\n private createDOM(): void {\n // Container\n this.container = document.createElement('div');\n Object.assign(this.container.style, {\n position: 'absolute',\n zIndex: '1000',\n width: `${this.state.widgetWidth}px`,\n height: `${this.state.widgetHeight}px`,\n minWidth: '140px',\n minHeight: '120px',\n maxHeight: '500px',\n background: '#2a2a2a',\n color: '#eee',\n borderRadius: '6px',\n fontFamily: 'sans-serif',\n fontSize: '13px',\n boxShadow: '0 4px 16px rgba(0,0,0,0.4)',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n resize: 'both',\n });\n\n // Header bar (draggable)\n const header = document.createElement('div');\n Object.assign(header.style, {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '6px 8px',\n background: '#333',\n cursor: 'grab',\n userSelect: 'none',\n borderRadius: '6px 6px 0 0',\n flexShrink: '0',\n });\n\n const title = document.createElement('span');\n title.textContent = sceT('picker.title', { defaultValue: 'Components' });\n Object.assign(title.style, {\n fontWeight: 'bold',\n fontSize: '13px',\n });\n this.titleEl = title;\n\n const closeBtn = document.createElement('button');\n closeBtn.textContent = '\\u00D7'; // multiplication sign (x)\n Object.assign(closeBtn.style, {\n background: 'none',\n border: 'none',\n color: '#aaa',\n fontSize: '18px',\n cursor: 'pointer',\n padding: '0 4px',\n lineHeight: '1',\n });\n closeBtn.addEventListener('mouseenter', () => {\n closeBtn.style.color = '#fff';\n });\n closeBtn.addEventListener('mouseleave', () => {\n closeBtn.style.color = '#aaa';\n });\n closeBtn.addEventListener('click', (e) => {\n e.stopPropagation();\n this.onClose();\n });\n\n header.appendChild(title);\n header.appendChild(closeBtn);\n\n // Make header draggable\n header.addEventListener('mousedown', (e) => {\n if (e.target === closeBtn) return;\n this.startDrag(e);\n });\n\n // Group dropdown\n this.groupDropdown = document.createElement('select');\n Object.assign(this.groupDropdown.style, {\n margin: '6px 8px',\n padding: '4px 6px',\n background: '#444',\n color: '#eee',\n border: '1px solid #555',\n borderRadius: '4px',\n fontSize: '12px',\n flexShrink: '0',\n cursor: 'pointer',\n });\n\n let groups = this.registry.getGroups();\n // Exception rule : if groups doesn't include a basic group\n // an empty one is created to get access to branching points\n if (groups.filter((g) => g.id === BRANCHING_POINT_GROUP).length < 1) {\n groups = [\n {\n id: BRANCHING_POINT_GROUP,\n label: sceT(`components.groups.${BRANCHING_POINT_GROUP}.name`, {\n defaultValue: BRANCHING_POINT_GROUP,\n }),\n },\n ...groups,\n ];\n }\n\n for (const group of groups) {\n const option = document.createElement('option');\n option.value = group.id;\n option.textContent = sceT(`components.groups.${group.id}.name`, {\n defaultValue: group.label,\n });\n this.groupDropdown.appendChild(option);\n }\n this.groupDropdown.value = this.state.selectedGroupId;\n this.groupDropdown.addEventListener('change', () => {\n this.state.selectedGroupId = this.groupDropdown!.value;\n // Clear selection when switching groups\n if (this.state.selectedItem !== null) {\n this.state.selectedItem = null;\n this.onSelectionChange(null);\n }\n this.renderItemList();\n });\n\n // Item list container (scrollable)\n this.itemList = document.createElement('div');\n Object.assign(this.itemList.style, {\n flex: '1',\n overflowY: 'auto',\n padding: '0 4px 4px 4px',\n });\n\n // Assemble\n this.container.appendChild(header);\n this.container.appendChild(this.groupDropdown);\n this.container.appendChild(this.itemList);\n document.body.appendChild(this.container);\n }\n\n // ========================================================================\n // Item List Rendering\n // ========================================================================\n\n private renderItemList(): void {\n if (!this.itemList) return;\n this.itemList.innerHTML = '';\n\n const groupId = this.state.selectedGroupId;\n const types = this.registry.getRegisteredTypes(groupId);\n\n // Prepend Branching Point entry in the basic group\n if (groupId === BRANCHING_POINT_GROUP) {\n this.itemList.appendChild(\n this.createItemElement(\n sceT('picker.branchingPoint', { defaultValue: 'Branching Point' }),\n BRANCHING_POINT_SENTINEL\n )\n );\n }\n\n for (const type of types) {\n const label = sceT(`components.${type}.name`, { defaultValue: type });\n this.itemList.appendChild(this.createItemElement(label, type));\n }\n }\n\n private createItemElement(label: string, value: PickerSelection): HTMLDivElement {\n const item = document.createElement('div');\n item.textContent = label;\n const isSelected = this.state.selectedItem === value;\n\n Object.assign(item.style, {\n padding: '6px 8px',\n margin: '2px 0',\n borderRadius: '4px',\n cursor: 'pointer',\n background: isSelected ? '#4a6fa5' : 'transparent',\n transition: 'background 0.15s',\n });\n\n item.addEventListener('mouseenter', () => {\n if (this.state.selectedItem !== value) {\n item.style.background = '#3a3a3a';\n }\n });\n item.addEventListener('mouseleave', () => {\n item.style.background = this.state.selectedItem === value ? '#4a6fa5' : 'transparent';\n });\n item.addEventListener('click', (e) => {\n e.stopPropagation();\n this.selectItem(value);\n });\n\n return item;\n }\n\n private selectItem(value: PickerSelection): void {\n // Toggle: clicking the already-selected item deselects it\n const newValue = this.state.selectedItem === value ? null : value;\n this.state.selectedItem = newValue;\n this.renderItemList(); // Re-render to update highlight\n this.onSelectionChange(newValue);\n }\n\n // ========================================================================\n // Positioning (ConfigPanelManager pattern)\n // ========================================================================\n\n private positionContainer(screenPosition: { x: number; y: number }): void {\n if (!this.container) return;\n\n const panelWidth = this.state.widgetWidth;\n const panelHeight = this.state.widgetHeight;\n\n let left = screenPosition.x + OFFSET_X;\n let top = screenPosition.y + OFFSET_Y;\n\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n\n // If would overflow, shift\n if (left + panelWidth > viewportWidth - VIEWPORT_PADDING) {\n left = screenPosition.x - panelWidth - OFFSET_X;\n } else if (left < viewportWidth + VIEWPORT_PADDING) {\n left = screenPosition.x + OFFSET_X;\n }\n\n // Clamp left\n if (left < VIEWPORT_PADDING) {\n left = VIEWPORT_PADDING;\n }\n // Clamp top\n if (top < VIEWPORT_PADDING) {\n top = VIEWPORT_PADDING;\n } else if (top + panelHeight > viewportHeight - VIEWPORT_PADDING) {\n top = viewportHeight - panelHeight - VIEWPORT_PADDING;\n }\n\n this.container.style.left = `${left}px`;\n this.container.style.top = `${top}px`;\n }\n\n // ========================================================================\n // Dragging\n // ========================================================================\n\n private startDrag(e: MouseEvent): void {\n if (!this.container) return;\n e.preventDefault();\n\n this.dragOffset = {\n x: e.clientX - this.container.offsetLeft,\n y: e.clientY - this.container.offsetTop,\n };\n\n this.container.style.cursor = 'grabbing';\n\n this.dragMoveHandler = (ev: MouseEvent) => this.onDragMove(ev);\n this.dragEndHandler = () => this.endDrag();\n document.addEventListener('mousemove', this.dragMoveHandler);\n document.addEventListener('mouseup', this.dragEndHandler);\n }\n\n private onDragMove(e: MouseEvent): void {\n if (!this.container || !this.dragOffset) return;\n\n let left = e.clientX - this.dragOffset.x;\n let top = e.clientY - this.dragOffset.y;\n\n // Clamp to viewport\n const maxLeft = window.innerWidth - this.container.offsetWidth - VIEWPORT_PADDING;\n const maxTop = window.innerHeight - this.container.offsetHeight - VIEWPORT_PADDING;\n left = Math.max(VIEWPORT_PADDING, Math.min(left, maxLeft));\n top = Math.max(VIEWPORT_PADDING, Math.min(top, maxTop));\n\n this.container.style.left = `${left}px`;\n this.container.style.top = `${top}px`;\n }\n\n private endDrag(): void {\n if (this.container) {\n this.container.style.cursor = '';\n }\n this.dragOffset = null;\n\n if (this.dragMoveHandler) {\n document.removeEventListener('mousemove', this.dragMoveHandler);\n this.dragMoveHandler = null;\n }\n if (this.dragEndHandler) {\n document.removeEventListener('mouseup', this.dragEndHandler);\n this.dragEndHandler = null;\n }\n }\n\n // ========================================================================\n // Event Listeners\n // ========================================================================\n\n private registerEventListeners(): void {\n this.escapeHandler = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n this.onClose();\n }\n };\n document.addEventListener('keydown', this.escapeHandler);\n }\n\n private removeEventListeners(): void {\n this.endDrag();\n\n if (this.escapeHandler) {\n document.removeEventListener('keydown', this.escapeHandler);\n this.escapeHandler = null;\n }\n }\n}\n","/**\n * Build Tool Implementation\n * @module scene/static/tools/BuildTool\n *\n * Unified tool for all circuit editing operations:\n * - Wire creation between endpoints\n * - Element positioning (components, branching points, wire points)\n * - Component rotation\n * - Element deletion\n * - Branching point creation\n * - Component placement & addition (add_component mode with picker widget)\n *\n * Replaces old: PositionTool, WireTool, DeleteTool, BranchingPointTool, AddComponentTool\n */\n\nimport * as THREE from 'three';\nimport { Euler } from 'three';\nimport { Line2 } from 'three/examples/jsm/lines/Line2.js';\nimport {\n type UUID,\n type ComponentType,\n ENodeSourceType,\n ENodeType,\n ENode,\n Position,\n Rotation,\n Component,\n} from 'simple-circuit-engine/core';\n\nimport type {\n IEditingTool,\n ToolType,\n CursorType,\n HoverableType,\n MonoSelectionData,\n HoveredElement,\n} from '../../shared/types';\nimport type { IGroupedFactoryRegistry } from '../../shared/components/GroupedFactoryRegistry';\nimport type { CircuitController } from '../CircuitController';\nimport {\n gridToWorldRotation,\n nearestWorldSnapPosition,\n worldToGridPosition,\n} from '../../shared/utils/GeometryUtils';\nimport {\n ComponentPickerWidget,\n BRANCHING_POINT_SENTINEL,\n type PickerSelection,\n} from './ComponentPickerWidget';\n\n/**\n * Build tool operating modes\n *\n * State transitions:\n * idle → wire_creation (click enode)\n * idle → component_drag (pointerdown on selected element)\n * idle → wire_drag (click wire or intermediate point)\n * idle → bp_drag (double-click+hold branching point)\n * idle → add_component (double-click empty space)\n * add_component → idle (Escape, close widget)\n * {any active mode} → idle (pointerup, Escape, or operation complete)\n */\ntype BuildToolMode =\n | 'idle'\n | 'wire_creation'\n | 'wire_drag'\n | 'component_drag'\n | 'bp_drag'\n | 'add_component';\n\n/**\n * State during wire creation operation\n */\ninterface WireCreationState {\n /**\n * UUID of the source enode (pin or branching point)\n */\n sourceEnodeId: UUID;\n\n /**\n * World position of source enode (for preview line start)\n */\n sourcePosition: THREE.Vector3;\n\n /**\n * Preview wire object (Line2) rendered during creation\n * Follows cursor position until target selected\n */\n previewWire: Line2 | null;\n\n /**\n * Timestamp when operation started (for double-click disambiguation)\n */\n ts: number;\n}\n\n/**\n * State during wire intermediate point drag\n */\ninterface WireDragState {\n /**\n * UUID of wire being modified\n */\n wireId: UUID;\n\n /**\n * Index in intermediatePositions array\n * Or index where new point will be inserted\n */\n pointIndex: number;\n\n /**\n * Initial world position of drag start\n */\n initialPosition: THREE.Vector3;\n\n /**\n * Original intermediate positions (for cancel)\n * Snapshot of wire.intermediatePositions before drag\n */\n originalPositions: { x: number; y: number }[];\n\n /**\n * Target type determines behavior:\n * - 'intermediate': Dragging existing point\n * - 'new_intermediate': Creating and dragging new point\n */\n targetType: 'intermediate' | 'new_intermediate';\n}\n\n/**\n * State during component drag\n */\ninterface ComponentDragState {\n /**\n * UUID of component being dragged\n */\n componentId: UUID;\n\n /**\n * Initial world position (for cancel)\n */\n initialPosition: THREE.Vector3;\n}\n\n/**\n * State during branching point drag\n */\ninterface BPDragState {\n /**\n * UUID of branching point being dragged\n */\n enodeId: UUID;\n\n /**\n * Initial world position (for cancel)\n */\n initialPosition: THREE.Vector3;\n}\n\ninterface LastCancelledOp {\n /**\n * Type of operation that was cancelled\n */\n mode: BuildToolMode;\n /**\n * Timestamp of cancellation\n */\n ts: number;\n}\n\n/**\n * Clipboard data for copy-paste operations\n */\ninterface ClipboardData {\n /**\n * Type of component to paste\n */\n componentType: ComponentType;\n /**\n * Rotation of component to paste\n */\n rotation: Euler;\n /** Original configuration data for the component */\n config: Map<string, string>;\n /** Original pins sourceTypes */\n pinSources: Array<ENodeSourceType | undefined | null>;\n}\n\n/**\n * Returns the next sourceType in the cycle: null → Voltage → Current → null\n * @param current - Current source type\n * @returns Next source type in the cycle\n */\nfunction getNextSourceType(current: ENodeSourceType | undefined): ENodeSourceType | undefined {\n if (!current) return ENodeSourceType.Voltage;\n if (current === ENodeSourceType.Voltage) return ENodeSourceType.Current;\n return undefined; // Current → null\n}\n\n/**\n * Unified tool for building circuits\n * Implements all circuit editing functionality in a single tool\n */\nexport class BuildTool implements IEditingTool {\n readonly type: ToolType = 'build';\n\n private _controller: CircuitController;\n\n // Tool state\n private mode: BuildToolMode = 'idle';\n private lastCancelledOp: LastCancelledOp | null = null;\n private lastOperationCompletedTs: number = 0;\n\n // Mode-specific state\n private wireCreationState: WireCreationState | null = null;\n private wireDragState: WireDragState | null = null;\n private componentDragState: ComponentDragState | null = null;\n private bpDragState: BPDragState | null = null;\n\n // Clipboard for copy-paste operations\n private clipboard: ClipboardData | null = null;\n\n // Component picker widget (add_component mode)\n private pickerWidget: ComponentPickerWidget | null = null;\n\n // Ghost preview for add_component mode\n private ghostPreview: THREE.Group | null = null;\n private hasOverlap: boolean = false;\n\n // Currently selected item in the picker (persists across mode entries)\n private pickerSelection: PickerSelection | null = null;\n\n /**\n * Construct a new BuildTool instance\n * @param controller - The circuit scene controllerType instance\n */\n constructor(controller: CircuitController) {\n this._controller = controller;\n\n // Initialize component picker widget if registry supports groups\n const registry = controller.factoryRegistry;\n if ('getGroups' in registry && typeof (registry as any).getGroups === 'function') {\n this.pickerWidget = new ComponentPickerWidget(\n registry as unknown as IGroupedFactoryRegistry,\n (selection) => this.onPickerSelectionChange(selection),\n () => this.exitAddComponentMode()\n );\n }\n\n // Bind event handlers for stable references\n this.handlePointerDown = this.handlePointerDown.bind(this);\n this.handlePointerUp = this.handlePointerUp.bind(this);\n this.handleGridPositionMove = this.handleGridPositionMove.bind(this);\n this.handleKeyDown = this.handleKeyDown.bind(this);\n this.handleDblClick = this.handleDblClick.bind(this);\n }\n\n /**\n * Activate build tool and set up event listeners\n * Resets all tool state and attaches DOM event handlers\n */\n onActivate(): void {\n // Reset all state\n this.mode = 'idle';\n this.wireCreationState = null;\n this.wireDragState = null;\n this.componentDragState = null;\n this.bpDragState = null;\n this.lastCancelledOp = null;\n\n // Set up event listeners\n const container = this._controller.getContainer();\n\n container.addEventListener('pointerdown', this.handlePointerDown);\n container.addEventListener('pointerup', this.handlePointerUp);\n container.addEventListener('dblclick', this.handleDblClick);\n window.addEventListener('keydown', this.handleKeyDown);\n }\n\n /**\n * Deactivate build tool and clean up event listeners\n * Cancels any active operations and removes all event handlers\n */\n onDeactivate(): void {\n // Security : Cancel any active operations\n this.cancelOperation();\n\n // Clean up add_component mode resources\n this.disposeGhostPreview();\n this.pickerWidget?.close();\n\n const container = this._controller.getContainer();\n // Remove event listeners\n this._controller.off('gridPositionMove', this.handleGridPositionMove);\n container.removeEventListener('pointerdown', this.handlePointerDown);\n container.removeEventListener('pointerup', this.handlePointerUp);\n container.removeEventListener('dblclick', this.handleDblClick);\n window.removeEventListener('keydown', this.handleKeyDown);\n\n // Reset all state\n this.mode = 'idle';\n this.wireCreationState = null;\n this.wireDragState = null;\n this.componentDragState = null;\n this.bpDragState = null;\n this.lastCancelledOp = null;\n this.hasOverlap = false;\n\n // Safety: re-enable camera controls\n const controls = this._controller.getControls();\n if (controls) {\n controls.enablePan = true;\n }\n }\n\n /**\n * Forward a language change to the component picker.\n */\n setLanguage(lng: string): void {\n this.pickerWidget?.setLanguage(lng);\n }\n\n /**\n * Cancel current ongoing operation : can be called from outside if needed\n */\n cancelOperation(): void {\n if (this.mode === 'wire_creation') {\n this.cancelWireCreation();\n } else if (this.mode === 'wire_drag') {\n this.cancelWireDrag();\n } else if (this.mode === 'bp_drag') {\n this.cancelBPDrag();\n } else if (this.mode === 'component_drag') {\n this.cancelComponentDrag();\n } else if (this.mode === 'add_component') {\n this.exitAddComponentMode();\n }\n }\n\n /**\n * Get the current cursor type for this tool\n * Returns cursor based on current mode and hover state\n */\n getCursorType(): CursorType {\n const hoveredElement = this._controller.getHoveredElement();\n\n // During add_component mode\n if (this.mode === 'add_component') {\n if (hoveredElement && hoveredElement.type === 'component') return 'pointer';\n if (this.hasOverlap) return 'not-allowed';\n return this.pickerSelection ? 'crosshair' : 'default';\n }\n\n // During wire creation\n if (this.mode === 'wire_creation') {\n if (!this.isValidWireTarget(hoveredElement)) {\n return 'not-allowed';\n }\n return 'crosshair';\n }\n\n // During drag operations\n if (this.mode === 'component_drag' || this.mode === 'wire_drag' || this.mode === 'bp_drag') {\n return 'grabbing';\n }\n\n // Hover states (idle mode)\n if (hoveredElement) {\n // Can start wire from enode\n if (hoveredElement.type === 'enode') {\n return 'pointer';\n }\n\n // Can drag selected element\n const selection = this._controller.getSelectionManager().getSelection();\n if (selection && selection.kind === 'mono' && hoveredElement.id === selection.id) {\n return 'grab';\n }\n\n // Can interact with wire or component\n if (hoveredElement.type === 'wire' || hoveredElement.type === 'component') {\n return 'pointer';\n }\n }\n\n return 'default';\n }\n\n /**\n * Get preview objects to render in the scene\n * Returns array of preview objects currently visible\n */\n getPreviewObjects(): THREE.Object3D[] {\n const previews: THREE.Object3D[] = [];\n\n // Wire creation preview\n if (this.mode === 'wire_creation' && this.wireCreationState?.previewWire) {\n previews.push(this.wireCreationState.previewWire);\n }\n\n // Add component ghost preview\n if (this.mode === 'add_component' && this.ghostPreview) {\n previews.push(this.ghostPreview);\n }\n\n return previews;\n }\n\n // ========================================================================\n // Event Handlers\n // ========================================================================\n\n /**\n * Handle pointer down event\n * Routes to appropriate operation based on hover target and state\n */\n private handlePointerDown(event: MouseEvent): void {\n if (event.button !== 0) return; // Only handle left click\n const circuit = this._controller.getCircuit();\n if (!circuit) return;\n const hoveredElement = this._controller.getHoveredElement();\n\n if (this.mode === 'idle') {\n // Handle Ctrl+Shift+click for config panel (T011, T012)\n if ((event.ctrlKey || event.metaKey) && event.shiftKey && hoveredElement) {\n if (hoveredElement.type === 'component') {\n this.openConfigPanel(hoveredElement.id, event);\n }\n // Early exit - don't start other operations\n return;\n }\n\n // Handle Ctrl+click for sourceType or fast component config cycling\n if ((event.ctrlKey || event.metaKey) && hoveredElement) {\n if (hoveredElement.type === 'enode') {\n this.cycleEnodeSourceType(hoveredElement.id, hoveredElement.object3D);\n } else if (hoveredElement.type === 'component') {\n this._controller.cycleComponentConfig(hoveredElement.id);\n }\n // TODO: for wire maybe implement a path regularization feature later\n // Early exit - don't start wire creation\n return;\n }\n\n if (hoveredElement && hoveredElement.type === 'enode') {\n const enodeId = hoveredElement.id;\n // special priority 0 : if a wire creation was just cancelled, and we click again on the same enode within 500ms, we start dragging the branching point instead of starting a new wire creation\n const isBranchingPoint = !hoveredElement.object3D.userData.componentId;\n if (\n isBranchingPoint &&\n this.lastCancelledOp &&\n this.lastCancelledOp.mode === 'wire_creation' &&\n Date.now() - this.lastCancelledOp.ts < 500\n ) {\n this.startBPDrag(enodeId, this._controller.cursorGroundPlanePosition());\n return;\n }\n\n // Priority 1 : Check if we're hovering an enode and start wire creation\n this.startWireCreation(enodeId);\n return;\n }\n // Priority 2 : Check if we're hovering a component\n if (hoveredElement && hoveredElement.type === 'component') {\n const componentId = hoveredElement.id;\n this.startComponentDrag(componentId, this._controller.cursorGroundPlanePosition());\n return;\n }\n // Priority 3 : Check if we're hovering a wire\n if (hoveredElement && hoveredElement.type === 'wire') {\n const wireId = hoveredElement.id;\n const screenPos = new THREE.Vector2(event.clientX, event.clientY);\n const worldPosition = this._controller.cursorGroundPlanePosition();\n\n // Drag target resolution: branching point > existing intermediate > new intermediate\n const wire = circuit.getWire(wireId);\n if (!wire) return;\n\n // Priority 3-1: Check for existing intermediate point\n const nearestPoint = this._controller.wireVisualManager.findNearestIntermediatePoint(\n wireId,\n screenPos\n );\n if (nearestPoint) {\n // Start dragging existing intermediate point\n const pos = wire.intermediatePositions[nearestPoint.pointIndex];\n if (pos) {\n const worldPos = new THREE.Vector3(pos.x, 0, -pos.y);\n this.startWireDrag(wireId, 'intermediate', nearestPoint.pointIndex, worldPos);\n return;\n }\n }\n // Priority 3-2: Create new intermediate point at click position\n const insertIndex = this._controller.wireVisualManager.getInsertIndexForPosition(\n wireId,\n worldPosition\n );\n this.startWireDrag(wireId, 'new_intermediate', insertIndex, worldPosition);\n return;\n }\n } else if (this.mode === 'wire_creation') {\n if (!hoveredElement) {\n // Clicked on empty space during wire creation - cancel ?\n // TODO: isn't it the spec to create a branching point here? or handled elsewhere and this branch is unnecessary ?\n this.cancelWireCreation();\n return;\n }\n } else if (this.mode === 'add_component') {\n // In add_component mode, clicking on empty space places the selected item\n if (!hoveredElement && this.pickerSelection) {\n if (this.hasOverlap) {\n this._controller.emit('toolValidationError', {\n toolType: this.type,\n mode: 'add_component',\n errorMessage: 'Cannot place component: position occupied',\n });\n return;\n }\n this.placeSelectedItem();\n }\n return;\n }\n }\n\n /**\n * Handle pointer up event\n *\n * Ends current operation, commits final positions to the circuit model,\n * and re-enables camera controls.\n *\n * Completes current operation based on mode\n */\n private handlePointerUp(event: MouseEvent): void {\n if (event.button !== 0) return; // Only handle left click\n\n // add_component mode manages its own gridPositionMove listener lifecycle\n if (this.mode === 'add_component') return;\n\n const circuit = this._controller.getCircuit();\n if (!circuit) return;\n const hoveredElement = this._controller.getHoveredElement();\n\n if (this.mode === 'wire_creation') {\n // specific case : clicking on source enode cancels the wire creation but may lead to dragging branching point\n if (\n hoveredElement &&\n hoveredElement.type === 'enode' &&\n hoveredElement.id === this.wireCreationState?.sourceEnodeId\n ) {\n this.cancelWireCreation();\n } else {\n this.completeWireCreation(hoveredElement);\n }\n } else if (this.mode === 'wire_drag') {\n // Commit wire drag operation\n this.completeWireDrag();\n } else if (this.mode === 'bp_drag') {\n // Commit branching point drag operation\n this.completeBPDrag();\n } else if (this.mode === 'component_drag') {\n // Commit component drag operation\n this.completeComponentDrag();\n }\n\n // Finally stop listening to gridPositionMove events\n this._controller.off('gridPositionMove', this.handleGridPositionMove);\n // and unlock MapControl change of camera\n this._controller.getControls()!.enablePan = true;\n }\n\n /**\n * Handle grid position move event according to ongoing mode\n * Updates preview or drag position during active operations\n */\n private handleGridPositionMove(position: THREE.Vector3): void {\n switch (this.mode) {\n case 'wire_creation':\n this.updateWireCreation(position);\n break;\n case 'wire_drag':\n this.updateWireDrag(position);\n break;\n case 'component_drag':\n this.updateComponentDrag(position);\n break;\n case 'bp_drag':\n this.updateBPDrag(position);\n break;\n case 'add_component':\n this.updateAddComponentPreview(position);\n break;\n default:\n break;\n }\n }\n\n /**\n * Handle keyboard events\n * Supports Escape (cancel), Delete/Backspace (delete), R (rotate), Ctrl+C (copy), Ctrl+V (paste)\n */\n private handleKeyDown(event: KeyboardEvent): void {\n // cancel ongoing action on Escape\n if (event.key === 'Escape') {\n if (this.mode === 'add_component') {\n this.exitAddComponentMode();\n } else if (this.mode === 'wire_creation') {\n this.cancelWireCreation();\n } else if (this.mode === 'wire_drag') {\n this.cancelWireDrag();\n } else if (this.mode === 'bp_drag') {\n this.cancelBPDrag();\n } else if (this.mode === 'component_drag') {\n this.cancelComponentDrag();\n }\n return;\n }\n\n // Handle copy (CTRL+C) - copy selected component type and rotation\n if ((event.ctrlKey || event.metaKey) && event.key === 'c') {\n const selection = this._controller.getSelectionManager().getSelection();\n if (selection && selection.kind === 'mono' && selection.type === 'component') {\n this.copyComponent(selection.id);\n }\n return;\n }\n\n // Handle paste (CTRL+V) - paste component at hovered position\n if ((event.ctrlKey || event.metaKey) && event.key === 'v') {\n if (this.clipboard) {\n this.pasteComponent();\n }\n return;\n }\n\n // actions on selection\n const selection = this._controller.getSelectionManager().getSelection();\n if (!selection) return;\n if (selection.kind === 'multi') return; //this tool only handles mono selection for deletion\n const monoSelection = selection as MonoSelectionData;\n\n if (event.key === 'Delete' || event.key === 'Backspace') {\n // Handle deletion of components, wires, branching points\n if (monoSelection.type === 'component') {\n const componentId = monoSelection.id;\n this._controller.removeComponent(componentId);\n } else if (monoSelection.type === 'wire') {\n const wireId = monoSelection.id;\n this._controller.removeWire(wireId);\n } else if (monoSelection.type === 'enode' && monoSelection.data === 'BranchingPoint') {\n const enodeId = monoSelection.id;\n this._controller.removeBranchingPoint(enodeId);\n // TODO: may fail if the resulting merged wire is a duplicate - see how to handle this case\n }\n } else if (event.key === 'r' || event.key === 'R') {\n // Handle R key to rotate selected component\n if (monoSelection.type === 'component') {\n const componentId = monoSelection.id;\n this.rotateComponent(componentId);\n }\n }\n }\n\n /**\n * Handle double-click events\n * Routes to rotation (component) or branching point creation (wire/empty)\n */\n private handleDblClick(event: MouseEvent): void {\n if (event.button !== 0) return; // Only handle left click\n if (event.ctrlKey || event.metaKey) return; // prevent rotating while ctrl hold\n\n const hoveredElement = this._controller.getHoveredElement();\n\n // Priority 1 - Check if we're hovering a wire => split it with new branchingPoint\n if (hoveredElement && hoveredElement.type === 'wire') {\n const wireId = hoveredElement.id;\n const gridPosition = this._controller.cursorGroundPlanePosition();\n const enodeId = this.createBranchingPointOnWire(wireId, gridPosition);\n if (enodeId) {\n this._controller.getSelectionManager().selectOne('enode', enodeId, { componentId: null });\n }\n }\n // Priority 2 - Check if we're hovering a component => rotate it\n else if (hoveredElement && hoveredElement.type === 'component') {\n const componentId = hoveredElement.id;\n this.rotateComponent(componentId);\n } else if (!hoveredElement) {\n // Priority 3 - Double-click on empty space - open component picker widget\n // Suppress if a drag/wire operation just completed (avoids false dblclick after quick drag-release)\n if (Date.now() - this.lastOperationCompletedTs < 400) return;\n if (this.pickerWidget && this.mode === 'idle') {\n this.enterAddComponentMode(event);\n } else if (!this.pickerWidget) {\n // Fallback: create standalone branching point if no grouped registry\n const gridPosition = this._controller.cursorGroundPlanePosition();\n const enodeId = this.createStandaloneBranchingPoint(gridPosition);\n if (enodeId) {\n this._controller.getSelectionManager().selectOne('enode', enodeId, { componentId: null });\n }\n }\n }\n }\n\n /**\n * Operations lifecycle methods\n * (start, update, cancel, complete)\n */\n\n /**\n * Start wire creation from source enode\n * @param sourceEnodeId - Source enode ID\n */\n private startWireCreation(sourceEnodeId: UUID): void {\n const circuit = this._controller.getCircuit();\n if (!circuit) return;\n\n const sourceEnode = circuit.getENode(sourceEnodeId);\n if (!sourceEnode) return;\n\n // Get source position\n const enodeGroup = this._controller.enodeObject3Ds.get(sourceEnodeId);\n if (!enodeGroup) return;\n const sourcePosition = enodeGroup.position.clone();\n if (enodeGroup.userData.componentId) {\n // since pins (enodes of components) are children of the component object3D,\n // we need to get the world position\n enodeGroup.getWorldPosition(sourcePosition);\n }\n\n // Create preview wire\n const previewWire = this._controller.wireVisualManager.createPreviewWire(sourcePosition);\n\n // Enter wire creating state\n this.mode = 'wire_creation';\n this.wireCreationState = {\n sourceEnodeId,\n sourcePosition: sourcePosition.clone(),\n previewWire,\n ts: Date.now(),\n };\n\n this._controller.getControls()!.enablePan = false;\n this._controller.on('gridPositionMove', this.handleGridPositionMove);\n\n this._controller.emit('toolOperationStarted', {\n toolType: this.type,\n mode: this.mode,\n operationData: { sourceEnodeId },\n });\n }\n\n /**\n * Update wire creation preview with new target position\n * @param position\n * @private\n */\n private updateWireCreation(position: THREE.Vector3): void {\n this._controller.wireVisualManager.updatePreviewWire(position);\n }\n\n /**\n * Cancel wire creation and reset state\n */\n private cancelWireCreation(emit: boolean = true): void {\n if (this.mode !== 'wire_creation') return;\n this._controller.wireVisualManager.removePreviewWire();\n if (emit) {\n this._controller.emit('toolOperationCancelled', {\n toolType: this.type,\n mode: this.mode,\n });\n }\n this.lastCancelledOp = {\n mode: this.mode,\n ts: Date.now(),\n };\n // Reset state\n this.mode = 'idle';\n this.wireCreationState = null;\n }\n\n /**\n * Complete wire creation between source enode and :\n * - existing target enode if it is hovered\n * - new branching point on wire if target is a wire\n * - new branching point if target is null\n * @param hoveredElement - Currently hovered element at wire creation end\n */\n private completeWireCreation(hoveredElement: HoveredElement | null): UUID | undefined {\n if (this.mode !== 'wire_creation' || !this.wireCreationState) return;\n\n const circuit = this._controller.getCircuit();\n if (!circuit) return;\n const sourceEnodeId = this.wireCreationState.sourceEnodeId;\n\n // Saving to model, Validation checks are done in the process\n try {\n let targetEnodeId = null;\n let hasSelected = false;\n if (!hoveredElement) {\n // finishing on empty space : create end branching point\n const worldPosition = this._controller.cursorGroundPlanePosition();\n targetEnodeId = this.createStandaloneBranchingPoint(worldPosition);\n if (targetEnodeId) {\n this._controller\n .getSelectionManager()\n .selectOne('enode', targetEnodeId, { componentId: null });\n hasSelected = true;\n }\n } else if (hoveredElement.type === 'wire') {\n // this interesting case create a new branching point on the wire and connect to it\n const targetWireId = hoveredElement.id;\n const gridPosition = this._controller.cursorGroundPlanePosition();\n targetEnodeId = this.createBranchingPointOnWire(targetWireId, gridPosition);\n } else if (hoveredElement.type === 'enode') {\n targetEnodeId = hoveredElement.id;\n }\n\n if (!targetEnodeId) {\n throw new Error('Invalid target for wire creation');\n }\n\n // Create definitive wire visual\n const wire = this._controller.addWire(sourceEnodeId, targetEnodeId);\n // select the new wire if nothing was selected before\n if (!hasSelected) {\n this._controller.getSelectionManager().selectOne('wire', wire.id);\n }\n this._controller.autoAdjustCircuitGridSize();\n // Emit success event\n this._controller.emit('toolOperationCompleted', {\n toolType: this.type,\n mode: this.mode,\n operationData: { wireId: wire.id, sourceEnodeId, targetEnodeId },\n changedData: { addedWires: [wire.id] },\n });\n // Reset state (end preview)\n this.lastOperationCompletedTs = Date.now();\n this.cancelWireCreation();\n return wire.id;\n } catch (error) {\n this.cancelWireCreation();\n this._controller.emit('toolValidationError', {\n toolType: this.type,\n mode: this.mode,\n errorMessage: error instanceof Error ? error.message : 'Unknown error during wire creation',\n });\n }\n // Reset state\n this.mode = 'idle';\n this.wireCreationState = null;\n return;\n }\n\n /**\n * Start wire dragging operation\n * @param wireId - Wire being dragged\n * @param targetType - Type of drag target\n * @param pointIndex - Index of intermediate point, or -1 for new/branching\n * @param worldPosition - Initial position\n */\n private startWireDrag(\n wireId: UUID,\n targetType: 'intermediate' | 'new_intermediate',\n pointIndex: number,\n worldPosition: THREE.Vector3\n ): void {\n const circuit = this._controller.getCircuit();\n if (!circuit) return;\n\n const wire = circuit.getWire(wireId);\n if (!wire) return;\n\n // Store original positions for cancellation\n const originalPositions = wire.intermediatePositions.map((p) => ({ ...p }));\n\n // If creating new intermediate point, insert it now\n if (targetType === 'new_intermediate') {\n const insertIndex = pointIndex;\n const gridPos = worldToGridPosition(worldPosition);\n originalPositions.splice(insertIndex, 0, gridPos);\n pointIndex = insertIndex;\n }\n\n this.mode = 'wire_drag';\n this.wireDragState = {\n wireId,\n pointIndex,\n initialPosition: worldPosition.clone(),\n originalPositions,\n targetType,\n };\n\n // block MapControls panning and register for gridPositionMove events\n this._controller.getControls()!.enablePan = false;\n this._controller.on('gridPositionMove', this.handleGridPositionMove);\n\n this._controller.emit('toolOperationStarted', {\n toolType: this.type,\n mode: this.mode,\n operationData: { wireId, pointIndex, targetType },\n });\n }\n\n /**\n * Update drag target position during drag\n * @param worldPosition - Current cursor position in world space\n */\n private updateWireDrag(worldPosition: THREE.Vector3): void {\n if (this.mode !== 'wire_drag' || !this.wireDragState) return;\n\n // Update intermediate positions array\n const gridPos = worldToGridPosition(worldPosition);\n const newPositions = [...this.wireDragState.originalPositions];\n newPositions[this.wireDragState.pointIndex] = gridPos;\n\n // T063: Real-time geometry update with temporary positions\n // Use circuit's update method to set intermediate positions\n this._controller.circuitWriter.saveEditWirePositions(this.wireDragState.wireId, newPositions);\n this._controller.wireVisualManager.updateWireById(this.wireDragState.wireId);\n }\n\n /**\n * Cancel wire drag operation and revert to original positions\n */\n private cancelWireDrag(emit: boolean = true): void {\n if (this.mode !== 'wire_drag' || !this.wireDragState) return;\n\n // Revert intermediate positions\n this._controller.circuitWriter.saveEditWirePositions(\n this.wireDragState.wireId,\n this.wireDragState.originalPositions,\n true\n );\n this._controller.wireVisualManager.updateWireById(this.wireDragState.wireId);\n\n if (emit) {\n this._controller.emit('toolOperationCancelled', {\n toolType: this.type,\n mode: this.mode,\n });\n }\n this.lastCancelledOp = {\n mode: this.mode,\n ts: Date.now(),\n };\n // Reset state\n this.mode = 'idle';\n this.wireDragState = null;\n }\n\n /**\n * Commit drag operation and persist changes\n */\n private completeWireDrag(): void {\n if (this.mode !== 'wire_drag' || !this.wireDragState) return;\n\n const wireDragState = this.wireDragState;\n // Reset state\n this.mode = 'idle';\n this.wireDragState = null;\n this.lastOperationCompletedTs = Date.now();\n\n const circuit = this._controller.getCircuit();\n if (!circuit) return;\n const wireId = wireDragState.wireId;\n const wire = circuit.getWire(wireId);\n if (!wire) return;\n\n try {\n //Check for merge/delete conditions\n const finalPositions = this.checkMergeDelete(wire);\n // Persist to model via CircuitWriter\n this._controller.circuitWriter.saveEditWirePositions(\n wireDragState.wireId,\n finalPositions,\n true\n );\n\n const hoveredElement = this._controller.getHoveredElement();\n // special case 1 : if wire was dragged to enode, we need to split it and connect to it\n if (hoveredElement && hoveredElement.type === 'enode') {\n const targetEnodeId = hoveredElement.id;\n const worldPosition = this._controller.cursorGroundPlanePosition();\n const result = this._controller.splitWire(wireId, worldPosition, targetEnodeId);\n this._controller.emit('toolOperationCompleted', {\n toolType: this.type,\n mode: this.mode,\n operationData: {\n wireId: wireId,\n intermediatePositions: finalPositions,\n targetEnodeId: targetEnodeId,\n },\n changedData: {\n removedWire: wireId,\n enodeId: result.branchingPoint.id,\n addedWires: result.wires.map((w) => w.id),\n },\n });\n this._controller\n .getSelectionManager()\n .selectOne('enode', targetEnodeId, { componentId: null });\n return;\n }\n // special case 2 : if wire was dragged to ANOTHER wire, we split that wire with a branching point,\n // then split our dragged wire to connect to that branching point\n if (hoveredElement && hoveredElement.type === 'wire' && hoveredElement.id !== wireId) {\n const targetWireId = hoveredElement.id;\n const worldPosition = this._controller.cursorGroundPlanePosition();\n const targetEnodeId = this.createBranchingPointOnWire(targetWireId, worldPosition);\n const result = this._controller.splitWire(wireId, worldPosition, targetEnodeId);\n this._controller.emit('toolOperationCompleted', {\n toolType: this.type,\n mode: this.mode,\n operationData: {\n wireId: wireId,\n intermediatePositions: finalPositions,\n targetWireId: targetWireId,\n },\n changedData: {\n removedWire: wireId,\n enodeId: result.branchingPoint.id,\n addedWires: result.wires.map((w) => w.id),\n },\n });\n if (targetEnodeId) {\n this._controller\n .getSelectionManager()\n .selectOne('enode', targetEnodeId, { componentId: null });\n }\n return;\n }\n\n // Default case : Update visual\n this._controller.wireVisualManager.updateWireById(wireDragState.wireId);\n this._controller.autoAdjustCircuitGridSize();\n this._controller.emit('toolOperationCompleted', {\n toolType: this.type,\n mode: this.mode,\n operationData: {\n wireId: wireId,\n intermediatePositions: finalPositions,\n },\n changedData: {\n updatedWires: [wireId],\n },\n });\n } catch (error) {\n this._controller.emit('toolValidationError', {\n toolType: this.type,\n mode: this.mode,\n errorMessage: `Failed to commit wire drag: ${(error as Error).message}`,\n });\n this.cancelWireDrag(false);\n }\n }\n\n /**\n * Start component dragging operation\n * @param componentId - UUID of the component being dragged\n * @param worldPosition - Initial position\n */\n private startComponentDrag(componentId: UUID, worldPosition: THREE.Vector3): void {\n const circuit = this._controller.getCircuit();\n if (!circuit) return;\n\n const component = circuit.getComponent(componentId);\n if (!component) return;\n\n this.mode = 'component_drag';\n this.componentDragState = {\n componentId,\n initialPosition: worldPosition.clone(),\n };\n\n // block MapControls panning and register for gridPositionMove events\n this._controller.getControls()!.enablePan = false;\n this._controller.on('gridPositionMove', this.handleGridPositionMove);\n\n this._controller.emit('toolOperationStarted', {\n toolType: this.type,\n mode: this.mode,\n operationData: { componentId },\n });\n }\n\n /**\n * Update component visual position during drag\n * @param worldPosition - Current cursor position in world space\n */\n private updateComponentDrag(worldPosition: THREE.Vector3): void {\n if (this.mode !== 'component_drag' || !this.componentDragState) return;\n\n const object = this._controller.getObject3D('component', this.componentDragState.componentId);\n if (!object) return;\n\n const newPosition = nearestWorldSnapPosition(worldPosition);\n object.position.copy(newPosition);\n\n // moving wires connected to component in real-time during drag\n this._controller.wireVisualManager.updateWiresForComponent(this.componentDragState.componentId);\n }\n\n /**\n * Cancel component drag operation and revert to original positions\n */\n private cancelComponentDrag(emit: boolean = true): void {\n if (this.mode !== 'component_drag' || !this.componentDragState) return;\n\n // restore original component visual\n const object = this._controller.getObject3D('component', this.componentDragState.componentId);\n if (!object) return;\n\n object.position.copy(this.componentDragState.initialPosition);\n // restore wires connected to component\n this._controller.wireVisualManager.updateWiresForComponent(this.componentDragState.componentId);\n\n if (emit) {\n this._controller.emit('toolOperationCancelled', {\n toolType: this.type,\n mode: 'component_drag',\n });\n }\n this.lastCancelledOp = {\n mode: this.mode,\n ts: Date.now(),\n };\n // Reset state\n this.mode = 'idle';\n this.componentDragState = null;\n }\n\n /**\n * complete component drag operation and persist changes\n */\n private completeComponentDrag(): void {\n if (this.mode !== 'component_drag' || !this.componentDragState) return;\n\n const circuit = this._controller.getCircuit();\n if (!circuit) return;\n\n const componentId = this.componentDragState.componentId;\n const object = this._controller.getObject3D('component', componentId);\n if (!object) return;\n\n try {\n const component = this._controller.circuitWriter.saveEditComponent(componentId, object, true);\n for (const connectedWire of circuit.getWiresByComponent(componentId)) {\n this._controller.circuitWriter.saveSimplifyWirePositions(connectedWire.id);\n this._controller.wireVisualManager.updateWireById(connectedWire.id);\n }\n this._controller.autoAdjustCircuitGridSize();\n this._controller.emit('toolOperationCompleted', {\n toolType: this.type,\n mode: 'component_drag',\n operationData: {\n componentId: componentId,\n newPosition: component.position,\n },\n changedData: {},\n });\n } catch (error) {\n this._controller.emit('toolValidationError', {\n toolType: this.type,\n mode: this.mode,\n errorMessage: `Failed to commit component drag: ${(error as Error).message}`,\n });\n this.cancelComponentDrag(false);\n }\n\n // Reset state\n this.mode = 'idle';\n this.componentDragState = null;\n this.lastOperationCompletedTs = Date.now();\n }\n\n /**\n * Start branching point dragging operation\n * @param enodeId - UUID of the branching point being dragged\n * @param worldPosition - Initial position\n */\n private startBPDrag(enodeId: UUID, worldPosition: THREE.Vector3): void {\n const circuit = this._controller.getCircuit();\n if (!circuit) return;\n\n const branchingPoint = circuit.getENode(enodeId);\n if (!branchingPoint) return;\n\n this.mode = 'bp_drag';\n this.bpDragState = {\n enodeId,\n initialPosition: worldPosition.clone(),\n };\n // block MapControls panning and register for gridPositionMove events\n this._controller.getControls()!.enablePan = false;\n this._controller.on('gridPositionMove', this.handleGridPositionMove);\n\n this._controller.emit('toolOperationStarted', {\n toolType: this.type,\n mode: this.mode,\n operationData: { enodeId },\n });\n }\n\n /**\n * Update branching point position during drag\n * @param worldPosition - Current cursor position in world space\n */\n private updateBPDrag(worldPosition: THREE.Vector3): void {\n if (this.mode !== 'bp_drag' || !this.bpDragState) return;\n\n const visual = this._controller.enodeObject3Ds.get(this.bpDragState.enodeId);\n if (!visual) return;\n\n visual.position.copy(nearestWorldSnapPosition(worldPosition));\n const enode = this._controller.circuitWriter.saveEditBranchingPoint(visual);\n\n // Update all wires connected to this branching point\n for (const connectedWireId of enode.wires) {\n this._controller.wireVisualManager.updateWireById(connectedWireId);\n }\n }\n\n /**\n * Cancel branching point drag operation and revert to original positions\n */\n private cancelBPDrag(emit: boolean = true): void {\n if (this.mode !== 'bp_drag' || !this.bpDragState) return;\n\n const initialPosition = this.bpDragState.initialPosition;\n // Update bp visual\n const visual = this._controller.enodeObject3Ds.get(this.bpDragState.enodeId);\n if (!visual) return;\n visual.position.copy(initialPosition);\n\n const enode = this._controller.circuitWriter.saveEditBranchingPoint(visual);\n\n // restore all wires connected to this branching point\n for (const connectedWireId of enode.wires) {\n this._controller.wireVisualManager.updateWireById(connectedWireId);\n }\n\n if (emit) {\n this._controller.emit('toolOperationCancelled', {\n toolType: this.type,\n mode: 'bp_drag',\n });\n }\n this.lastCancelledOp = {\n mode: this.mode,\n ts: Date.now(),\n };\n // Reset state\n this.mode = 'idle';\n this.bpDragState = null;\n }\n\n /**\n * Commit branching point drag operation and persist changes\n */\n private completeBPDrag(): void {\n if (this.mode !== 'bp_drag' || !this.bpDragState) return;\n\n const circuit = this._controller.getCircuit();\n if (!circuit) return;\n\n try {\n const branchingPoint = circuit.getENode(this.bpDragState.enodeId);\n if (!branchingPoint) {\n throw new Error(`Branching point ${this.bpDragState.enodeId} not found`);\n }\n // Branching point drag position is already updated, but it's a good place to simplify wire path if necessary\n for (const connectedWireId of branchingPoint.wires) {\n this._controller.circuitWriter.saveSimplifyWirePositions(connectedWireId);\n this._controller.wireVisualManager.updateWireById(connectedWireId);\n }\n this._controller.autoAdjustCircuitGridSize();\n this._controller.emit('toolOperationCompleted', {\n toolType: this.type,\n mode: 'bp_drag',\n operationData: {\n branchingPointId: this.bpDragState.enodeId,\n newPosition: branchingPoint.position,\n },\n changedData: {},\n });\n } catch (error) {\n this._controller.emit('toolValidationError', {\n toolType: this.type,\n mode: this.mode,\n errorMessage: `Failed to commit branching point drag: ${(error as Error).message}`,\n });\n this.cancelBPDrag(false);\n return;\n }\n // Reset state\n this.mode = 'idle';\n this.bpDragState = null;\n this.lastOperationCompletedTs = Date.now();\n }\n\n /**\n * private helpers\n */\n\n /**\n * Create a standalone branching point at empty grid position (T048)\n * @param worldPosition - 3D position in world space\n */\n private createStandaloneBranchingPoint(worldPosition: THREE.Vector3): UUID | undefined {\n const circuit = this._controller.getCircuit();\n if (!circuit) return;\n try {\n // Create branching point in circuit model (no sourceType initially)\n const branchingPoint = this._controller.addBranchingPoint(worldPosition);\n this._controller.emit('toolOperationCompleted', {\n toolType: this.type,\n mode: 'bp_creation',\n operationData: {\n worldPosition,\n },\n changedData: {\n enodeId: branchingPoint.id,\n },\n });\n return branchingPoint.id;\n } catch (error) {\n this._controller.emit('toolValidationError', {\n toolType: this.type,\n mode: 'bp_creation',\n errorMessage: `Failed to create branching point: ${(error as Error).message}`,\n });\n }\n return;\n }\n\n /**\n * Create a branching point on an existing wire, splitting it (T044)\n * @param wireId - Wire to split\n * @param worldPosition - 3D position in world space\n */\n private createBranchingPointOnWire(wireId: UUID, worldPosition: THREE.Vector3): UUID | undefined {\n const circuit = this._controller.getCircuit();\n if (!circuit) return;\n\n try {\n const result = this._controller.splitWire(wireId, worldPosition);\n // Emit success event\n this._controller.emit('toolOperationCompleted', {\n toolType: this.type,\n mode: 'bp_creation',\n operationData: {\n wireId,\n worldPosition,\n },\n changedData: {\n removedWire: wireId,\n enodeId: result.branchingPoint.id,\n addedWires: result.wires.map((w) => w.id),\n },\n });\n return result.branchingPoint.id;\n } catch (error) {\n this._controller.emit('toolValidationError', {\n toolType: this.type,\n mode: 'bp_creation',\n errorMessage: `Failed to create branching point: ${(error as Error).message}`,\n });\n return;\n }\n }\n\n /**\n * Check if hoveredElement is a valid wire target during wire creation\n * @param hoveredElement - Current hovered element or null\n * @returns True if target is valid for wire endpoint\n */\n private isValidWireTarget(hoveredElement: { type: HoverableType; id: UUID } | null): boolean {\n if (!this.wireCreationState) return false;\n\n if (!hoveredElement) return true; // Empty space is valid (creates BP)\n\n // Enode is valid unless it's the source\n if (hoveredElement.type === 'enode') {\n return hoveredElement.id !== this.wireCreationState.sourceEnodeId;\n }\n\n // Wire is valid (creates BP on wire)\n if (hoveredElement.type === 'wire') {\n return true;\n }\n\n // Component is not a valid target\n return false;\n }\n\n /**\n * Rotate a component 90° clockwise\n *\n * Updates both the circuit model and visual representation.\n * Emits componentRotated event to notify listeners.\n * Only works on selected components (not wires or enodes).\n */\n private rotateComponent(componentId: UUID): void {\n const object = this._controller.getObject3D('component', componentId);\n if (!object) {\n return;\n }\n const currentAngle = object.rotation.y;\n const newAngle = (currentAngle - Math.PI / 2) % (Math.PI * 2);\n object.rotation.set(0, newAngle, 0);\n\n try {\n const component = this._controller.circuitWriter.saveEditComponent(componentId, object);\n this._controller.wireVisualManager.updateWiresForComponent(component.id);\n this._controller.emit('toolOperationCompleted', {\n toolType: this.type,\n mode: 'component_rotate',\n operationData: {\n componentId: componentId,\n newPosition: component.position,\n },\n changedData: {},\n });\n } catch (error) {\n this._controller.emit('toolValidationError', {\n toolType: this.type,\n mode: this.mode,\n errorMessage: `Failed to commit component rotate: ${(error as Error).message}`,\n });\n this.cancelComponentDrag(false);\n }\n }\n\n /**\n * Check if wire intermediate point should be merged or deleted\n * Returns updated positions array after merge/delete check\n * @param wire - Wire being modified\n * @returns Final intermediate positions array\n */\n private checkMergeDelete(wire: any): { x: number; y: number }[] {\n if (!this.wireDragState) return wire.intermediatePositions;\n\n const positions = [...wire.intermediatePositions];\n // handle no intermediate positions\n if (positions.length === 0) return positions;\n const draggedIndex = this.wireDragState.pointIndex;\n const draggedPos = positions[draggedIndex];\n\n // Check if dragged point is very close to wire endpoints or other intermediate points\n const circuit = this._controller.getCircuit();\n if (!circuit) return positions;\n\n const node1 = circuit.getENode(wire.node1);\n const node2 = circuit.getENode(wire.node2);\n if (!node1 || !node2) return positions;\n\n const endpoint1 = node1.getPosition(circuit);\n const endpoint2 = node2.getPosition(circuit);\n\n const threshold = 0.5; // Grid units\n\n // Check if close to endpoint1\n const distToEndpoint1 = Math.sqrt(\n Math.pow(draggedPos.x - endpoint1.x, 2) + Math.pow(draggedPos.y - endpoint1.y, 2)\n );\n if (distToEndpoint1 < threshold) {\n // Remove this point\n positions.splice(draggedIndex, 1);\n return positions;\n }\n\n // Check if close to endpoint2\n const distToEndpoint2 = Math.sqrt(\n Math.pow(draggedPos.x - endpoint2.x, 2) + Math.pow(draggedPos.y - endpoint2.y, 2)\n );\n if (distToEndpoint2 < threshold) {\n // Remove this point\n positions.splice(draggedIndex, 1);\n return positions;\n }\n\n // Check if close to other intermediate points\n for (let i = 0; i < positions.length; i++) {\n if (i === draggedIndex) continue;\n\n const otherPos = positions[i];\n const dist = Math.sqrt(\n Math.pow(draggedPos.x - otherPos.x, 2) + Math.pow(draggedPos.y - otherPos.y, 2)\n );\n\n if (dist < threshold) {\n // Merge: remove the dragged point\n positions.splice(draggedIndex, 1);\n return positions;\n }\n }\n\n return positions;\n }\n\n /**\n * Copy component type and rotation to clipboard\n * @param componentId - UUID of the component to copy\n */\n private copyComponent(componentId: UUID): void {\n const circuit = this._controller.getCircuit();\n if (!circuit) return;\n\n const component = circuit.getComponent(componentId);\n if (!component) return;\n\n const sources = component.pins.map((pinId) => {\n const enode = circuit.getENode(pinId);\n return enode ? enode.source : null;\n });\n\n this.clipboard = {\n componentType: component.type,\n rotation: gridToWorldRotation(component.rotation),\n pinSources: sources,\n config: new Map(component.config), // Deep copy of config\n };\n }\n\n /**\n * Paste component from clipboard at hovered position\n * Creates a new component with the type and rotation from clipboard\n */\n private pasteComponent(): void {\n if (!this.clipboard) return;\n\n const circuit = this._controller.getCircuit();\n if (!circuit) return;\n\n // Get cursor position\n const worldPosition = this._controller.cursorGroundPlanePosition();\n\n this._controller.addComponent(\n this.clipboard.componentType,\n worldPosition,\n this.clipboard.rotation,\n this.clipboard.config,\n this.clipboard.pinSources\n );\n\n this._controller.autoAdjustCircuitGridSize();\n }\n\n /**\n * Cycles the sourceType of an enode: null → Voltage → Current → null\n * Updates both model and visual immediately.\n * @param enodeId - UUID of the enode to cycle\n * @param hitbox - hitbox of the enode being clicked\n */\n private cycleEnodeSourceType(enodeId: UUID, hitbox: THREE.Object3D): void {\n if (hitbox.userData.lockedSourceType) return; // do not update locked source types\n if (!hitbox.parent) return;\n\n const nextSourceType = getNextSourceType(hitbox.parent.userData.sourceType);\n this._controller.updateEnodeSourceType(enodeId, nextSourceType || null);\n }\n\n /**\n * Open config panel for component (T014)\n * Converts component world position to screen coordinates and opens panel\n * @param componentId - UUID of the component to configure\n * @param event - Mouse event for screen position\n */\n private openConfigPanel(componentId: UUID, event: MouseEvent): void {\n const screenPosition = { x: event.clientX, y: event.clientY };\n this._controller.openConfigPanel(componentId, screenPosition);\n }\n\n // ========================================================================\n // Add Component Mode\n // ========================================================================\n\n /**\n * Enter add_component mode: open the picker widget and listen for cursor movement\n * @param event - Mouse event (used for widget positioning)\n */\n private enterAddComponentMode(event: MouseEvent): void {\n if (!this.pickerWidget) return;\n\n this.mode = 'add_component';\n const screenPos = { x: event.clientX, y: event.clientY };\n this.pickerWidget.open(screenPos);\n\n // Listen for cursor movement for ghost preview\n this._controller.on('gridPositionMove', this.handleGridPositionMove);\n\n // If there was a previous selection, recreate the ghost\n if (this.pickerWidget.currentSelection) {\n this.pickerSelection = this.pickerWidget.currentSelection;\n this.createGhostPreview(this.pickerSelection);\n }\n\n this._controller.emit('toolOperationStarted', {\n toolType: this.type,\n mode: 'add_component',\n operationData: {},\n });\n }\n\n /**\n * Exit add_component mode: close widget, dispose ghost, return to idle\n */\n private exitAddComponentMode(): void {\n if (this.mode !== 'add_component') return;\n\n this.disposeGhostPreview();\n this.pickerWidget?.close();\n this._controller.off('gridPositionMove', this.handleGridPositionMove);\n this.hasOverlap = false;\n\n this._controller.emit('toolOperationCancelled', {\n toolType: this.type,\n mode: 'add_component',\n });\n\n this.mode = 'idle';\n }\n\n /**\n * Called when user selects or deselects an item in the picker widget\n */\n private onPickerSelectionChange(selection: PickerSelection | null): void {\n this.pickerSelection = selection;\n this.disposeGhostPreview();\n if (selection) {\n this.createGhostPreview(selection);\n }\n }\n\n /**\n * Place the currently selected item (component or branching point) at cursor position\n */\n private placeSelectedItem(): void {\n if (!this.pickerSelection) return;\n\n const worldPosition = this._controller.cursorGroundPlanePosition();\n\n if (this.pickerSelection === BRANCHING_POINT_SENTINEL) {\n const enodeId = this.createStandaloneBranchingPoint(worldPosition);\n if (enodeId) {\n this._controller.getSelectionManager().selectOne('enode', enodeId, { componentId: null });\n }\n // Recreate ghost for next placement\n this.disposeGhostPreview();\n this.createGhostPreview(this.pickerSelection);\n return;\n }\n\n try {\n const component = this._controller.addComponent(this.pickerSelection, worldPosition, null);\n this._controller.autoAdjustCircuitGridSize();\n this._controller.emit('toolOperationCompleted', {\n toolType: this.type,\n mode: 'add_component',\n operationData: {\n componentId: component.id,\n componentType: this.pickerSelection,\n position: worldPosition.clone(),\n },\n changedData: { addedComponents: [component.id] },\n });\n // Recreate ghost for next placement\n this.disposeGhostPreview();\n this.createGhostPreview(this.pickerSelection);\n } catch (error) {\n this._controller.emit('toolValidationError', {\n toolType: this.type,\n mode: 'add_component',\n errorMessage: `Failed to place component: ${(error as Error).message}`,\n });\n }\n }\n\n /**\n * Update ghost preview position and overlap check during add_component mode\n */\n private updateAddComponentPreview(worldPosition: THREE.Vector3): void {\n if (!this.ghostPreview) return;\n\n const snappedPosition = new THREE.Vector3(\n Math.round(worldPosition.x),\n 0,\n Math.round(worldPosition.z)\n );\n this.ghostPreview.position.copy(snappedPosition);\n\n // Check for overlap\n const previousOverlap = this.hasOverlap;\n this.hasOverlap = this.checkGhostOverlap();\n\n if (this.hasOverlap && !previousOverlap) {\n this.applyInvalidEffect(this.ghostPreview);\n } else if (!this.hasOverlap && previousOverlap) {\n this.removeInvalidEffect(this.ghostPreview);\n }\n }\n\n // ========================================================================\n // Ghost Preview\n // ========================================================================\n\n /**\n * Create ghost preview for the selected item\n * @param selection - Component type or branching point sentinel\n */\n private createGhostPreview(selection: PickerSelection): void {\n this.disposeGhostPreview();\n\n try {\n let visual: THREE.Object3D;\n\n if (selection === BRANCHING_POINT_SENTINEL) {\n // Create a branching point preview using the factory\n const tempEnode = new ENode(\n ENodeType.BranchingPoint,\n undefined,\n undefined,\n new Position(0, 0)\n );\n visual = this._controller.branchingPointVisualFactory.createVisual(tempEnode);\n } else {\n const factory = this._controller.factoryRegistry.get(selection);\n const tempComponent = new Component(\n selection,\n new Position(0, 0),\n new Rotation(0),\n [] // Empty pins array for preview\n );\n visual = factory.createVisual(tempComponent, this._controller.visualContext);\n visual.rotation.set(0, factory.defaultRotation(), 0);\n }\n\n if (!(visual instanceof THREE.Group)) {\n console.warn(`Factory returned non-Group object for ${selection}`);\n return;\n }\n\n // Mark as preview\n visual.userData.preview = true;\n visual.traverse((child) => {\n child.userData.preview = true;\n });\n\n this.ghostPreview = visual;\n this.applyGhostEffect(this.ghostPreview);\n\n // Position at current cursor\n const cursorPos = this._controller.cursorGroundPlanePosition();\n this.ghostPreview.position.set(Math.round(cursorPos.x), 0, Math.round(cursorPos.z));\n\n this._controller.getScene().add(this.ghostPreview);\n } catch (error) {\n console.warn(`Failed to create ghost preview for ${selection}:`, error);\n this.ghostPreview = null;\n }\n }\n\n /**\n * Dispose ghost preview and cleanup resources\n */\n private disposeGhostPreview(): void {\n if (!this.ghostPreview) return;\n\n this._controller.getScene().remove(this.ghostPreview);\n this.ghostPreview.traverse((child) => {\n if (child instanceof THREE.Mesh) {\n if (child.geometry) child.geometry.dispose();\n if (Array.isArray(child.material)) {\n child.material.forEach((mat) => mat.dispose());\n } else if (child.material) {\n child.material.dispose();\n }\n }\n });\n this.ghostPreview = null;\n this.hasOverlap = false;\n }\n\n /**\n * Apply semi-transparent ghost effect to preview object\n */\n private applyGhostEffect(object: THREE.Object3D): void {\n object.traverse((child) => {\n if (child instanceof THREE.Mesh) {\n if (Array.isArray(child.material)) {\n child.material = child.material.map((mat: THREE.Material) => mat.clone());\n child.material.forEach((mat: THREE.Material) => {\n mat.transparent = true;\n mat.opacity = 0.5;\n });\n } else {\n child.material = child.material.clone();\n child.material.transparent = true;\n child.material.opacity = 0.5;\n }\n }\n });\n }\n\n /**\n * Check if ghost preview overlaps with existing components\n */\n private checkGhostOverlap(): boolean {\n if (!this.ghostPreview) return false;\n\n // Branching points don't need overlap detection\n if (this.pickerSelection === BRANCHING_POINT_SENTINEL) return false;\n\n const previewBox = new THREE.Box3().setFromObject(this.ghostPreview);\n const componentObjects = this._controller.componentObject3Ds;\n\n for (const [_id, otherGroup] of componentObjects) {\n // to make this rule not too strict we signal overlap only if ghost box contains the center of other component box\n if (previewBox.containsPoint(otherGroup.position)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Apply red emissive to indicate invalid placement\n */\n private applyInvalidEffect(object: THREE.Object3D): void {\n object.traverse((child) => {\n if (child instanceof THREE.Mesh && child.material) {\n if (Array.isArray(child.material)) {\n child.material.forEach((mat: THREE.Material) => {\n if (mat instanceof THREE.MeshStandardMaterial) {\n mat.emissive.setHex(0xff0000);\n mat.emissiveIntensity = 0.5;\n }\n });\n } else if (child.material instanceof THREE.MeshStandardMaterial) {\n child.material.emissive.setHex(0xff0000);\n child.material.emissiveIntensity = 0.5;\n }\n }\n });\n }\n\n /**\n * Remove red emissive invalid placement effect\n */\n private removeInvalidEffect(object: THREE.Object3D): void {\n object.traverse((child) => {\n if (child instanceof THREE.Mesh && child.material) {\n if (Array.isArray(child.material)) {\n child.material.forEach((mat: THREE.Material) => {\n if (mat instanceof THREE.MeshStandardMaterial) {\n mat.emissive.setHex(0x000000);\n mat.emissiveIntensity = 0;\n }\n });\n } else if (child.material instanceof THREE.MeshStandardMaterial) {\n child.material.emissive.setHex(0x000000);\n child.material.emissiveIntensity = 0;\n }\n }\n });\n }\n}\n","/**\n * MultiSelectTool - Enables multi-element selection and bulk operations\n * @module scene/static/tools/MultiSelectTool\n */\n\nimport * as THREE from 'three';\nimport type { UUID, ComponentType, ENodeSourceType } from 'simple-circuit-engine/core';\nimport { Position, Rotation } from 'simple-circuit-engine/core';\n\nimport type { IEditingTool, CursorType } from '../../shared/types';\nimport type { CircuitController } from '../CircuitController';\n\nimport {\n gridToWorldPosition,\n gridToWorldRotation,\n isPointInScreenRect,\n nearestWorldSnapPosition,\n worldToGridPosition,\n} from '../../shared/utils/GeometryUtils';\n\n/**\n * Operating modes for the MultiSelectTool\n */\nexport type MultiSelectToolMode = 'idle' | 'selecting' | 'dragging';\n\n/**\n * State during rectangle selection operation (T010)\n */\nexport interface SelectionRectState {\n /** Starting mouse position in screen coordinates */\n startScreen: { x: number; y: number };\n /** Current mouse position in screen coordinates */\n currentScreen: { x: number; y: number };\n /** DOM element for visual rectangle overlay */\n overlayElement: HTMLDivElement;\n /** Whether Shift key is held (additive selection mode) */\n shiftHeld: boolean;\n /** Elements currently previewed as \"will be selected\" */\n previewedElements: Set<UUID>;\n}\n\n/**\n * State during bulk move operation (T023)\n */\nexport interface BulkDragState {\n /** Starting cursor position in world coordinates */\n dragStartWorld: THREE.Vector3;\n /** Snapshot of initial positions for all selected elements */\n initialPositions: Map<UUID, THREE.Vector3>;\n /** Wire IDs that need geometry updates during drag */\n affectedWireIds: Set<UUID>;\n /** Initial intermediate positions for selected wires (wireId -> array of positions) */\n initialWireIntermediatePositions: Map<UUID, THREE.Vector3[]>;\n}\n\n/** Minimum selection rectangle size in pixels to distinguish from click */\nconst MIN_SELECTION_RECT_SIZE = 5;\n\n// =============================================================================\n// Clipboard Interfaces (T038)\n// =============================================================================\n\n/**\n * Complete clipboard data structure for copy/paste\n * TODO : copy and paste edited enodes sourceType and components config\n */\nexport interface ClipboardData {\n /** Center of selection bounding box in grid coordinates */\n anchor: { x: number; y: number };\n /** Copied component definitions */\n components: ClipboardComponent[];\n /** Copied branching point definitions */\n branchingPoints: ClipboardBranchingPoint[];\n /** Copied wire definitions (only wires with both endpoints in selection) */\n wires: ClipboardWire[];\n}\n\n/**\n * Component data within clipboard\n */\nexport interface ClipboardComponent {\n /** Component type identifier */\n type: string;\n /** Position relative to clipboard anchor */\n relativePosition: { x: number; y: number };\n /** Rotation angle in degrees */\n rotation: number;\n /** Original element ID for wire remapping during paste */\n originalId: UUID;\n /** Original configuration data for the component */\n config: Map<string, string>;\n /** Original pins sourceTypes */\n pinSources: Array<ENodeSourceType | undefined | null>;\n}\n\n/**\n * Branching point data within clipboard\n */\nexport interface ClipboardBranchingPoint {\n /** Position relative to clipboard anchor */\n relativePosition: { x: number; y: number };\n /** Original element ID for wire remapping during paste */\n originalId: UUID;\n /** Source type of the branching point */\n source?: ENodeSourceType | undefined;\n}\n\n/**\n * Wire data within clipboard\n */\nexport interface ClipboardWire {\n /** Original ID of first endpoint (component pin or branching point) */\n node1OriginalId: UUID;\n /** Original ID of second endpoint */\n node2OriginalId: UUID;\n /** Intermediate positions relative to clipboard anchor */\n relativeIntermediatePositions: Array<{ x: number; y: number }>;\n}\n\n/**\n * MultiSelectTool implementation\n *\n * Provides functionality for:\n * - Rectangle selection of multiple elements\n * - Bulk move operations\n * - Bulk delete operations\n * - Copy/paste and cut/paste operations\n */\nexport class MultiSelectTool implements IEditingTool {\n readonly type = 'multiSelect' as const;\n\n private mode: MultiSelectToolMode = 'idle';\n private readonly controller: CircuitController;\n\n // Selection rectangle state\n private selectionRectState: SelectionRectState | null = null;\n\n // Bulk drag state (Phase 4)\n private bulkDragState: BulkDragState | null = null;\n\n // Clipboard state (Phase 6)\n private clipboardData: ClipboardData | null = null;\n // Maps pin IDs to their parent component IDs for wire reconstruction during paste\n private clipboardPinToComponent: Map<UUID, UUID> = new Map();\n // Maps pin IDs to their index within their parent component\n private clipboardPinIndices: Map<UUID, number> = new Map();\n\n // Bound event handlers for stable references\n private handlePointerDown: (event: PointerEvent) => void;\n private handlePointerMove: (event: PointerEvent) => void;\n private handlePointerUp: (event: PointerEvent) => void;\n private handleKeyDown: (event: KeyboardEvent) => void;\n private handleGridPositionMove: (position: THREE.Vector3) => void;\n\n constructor(controller: CircuitController) {\n this.controller = controller;\n\n // Bind event handlers\n this.handlePointerDown = this._handlePointerDown.bind(this);\n this.handlePointerMove = this._handlePointerMove.bind(this);\n this.handlePointerUp = this._handlePointerUp.bind(this);\n this.handleKeyDown = this._handleKeyDown.bind(this);\n this.handleGridPositionMove = this._handleGridPositionMove.bind(this);\n }\n\n /**\n * Get the current operating mode\n */\n getMode(): MultiSelectToolMode {\n return this.mode;\n }\n\n /**\n * Called when tool becomes active\n */\n onActivate(): void {\n this.mode = 'idle';\n this.selectionRectState = null;\n this.bulkDragState = null;\n\n // Register event listeners\n const container = this.controller.getContainer();\n container.addEventListener('pointerdown', this.handlePointerDown);\n container.addEventListener('pointermove', this.handlePointerMove);\n container.addEventListener('pointerup', this.handlePointerUp);\n window.addEventListener('keydown', this.handleKeyDown);\n }\n\n /**\n * Called when tool is deactivated\n */\n onDeactivate(): void {\n // Cancel any ongoing operation\n this.cancelOperation();\n\n // Unregister event listeners\n const container = this.controller.getContainer();\n container.removeEventListener('pointerdown', this.handlePointerDown);\n container.removeEventListener('pointermove', this.handlePointerMove);\n container.removeEventListener('pointerup', this.handlePointerUp);\n window.removeEventListener('keydown', this.handleKeyDown);\n this.controller.off('gridPositionMove', this.handleGridPositionMove);\n\n this.mode = 'idle';\n this.selectionRectState = null;\n this.bulkDragState = null;\n }\n\n /**\n * Cancel any in-progress operation\n */\n cancelOperation(): void {\n if (this.mode === 'selecting') {\n this._cancelSelectionRect();\n } else if (this.mode === 'dragging') {\n this._cancelBulkDrag();\n }\n }\n\n /**\n * Get the current cursor type for this tool (T031)\n */\n getCursorType(): CursorType {\n const hoveredElement = this.controller.getHoveredElement();\n const selectionManager = this.controller.getSelectionManager();\n\n switch (this.mode) {\n case 'selecting':\n return 'crosshair';\n case 'dragging':\n return 'grabbing';\n case 'idle':\n default:\n // T031: Check if hovering over a selected element (for drag cursor)\n if (hoveredElement) {\n const isSelected = selectionManager.isSelected(hoveredElement.type, hoveredElement.id);\n if (isSelected) {\n return 'grab';\n }\n // Hovering over an element that could be selected\n return 'pointer';\n }\n return 'default';\n }\n }\n\n /**\n * Get preview objects to render in the scene\n */\n getPreviewObjects(): THREE.Object3D[] {\n // Selection rectangle is rendered via CSS overlay, not THREE.js\n return [];\n }\n\n // ==========================================================================\n // Event Handlers\n // ==========================================================================\n\n /**\n * Handle pointer down event (T011, T019, T020, T021, T024)\n * - Empty space: start rectangle selection\n * - Element click: select that element (clear others unless Shift held)\n * - Selected element: prepare for bulk drag (Phase 4)\n */\n private _handlePointerDown(event: PointerEvent): void {\n if (event.button !== 0) return; // Only left click\n\n const hoveredElement = this.controller.getHoveredElement();\n const selectionManager = this.controller.getSelectionManager();\n const shiftHeld = event.shiftKey;\n\n if (this.mode === 'idle') {\n // Case 1: Clicking on an element\n if (hoveredElement) {\n const isSelected = selectionManager.isSelected(hoveredElement.type, hoveredElement.id);\n\n // T020: Shift-click adds to selection\n if (shiftHeld) {\n if (hoveredElement.type === 'wire') return; // Wires cannot be individually added/removed from selection\n if (!isSelected) {\n selectionManager.addToSelection(hoveredElement.type, hoveredElement.id);\n // TODO: also add wires connected to this element (need some refactoring on SelectionManager)\n } else {\n // If already selected toggle => remove from the selection with deselecting cascade\n selectionManager.removeFromSelection(hoveredElement.type, hoveredElement.id);\n if (hoveredElement.type === 'component') {\n const circuitComponent = this.controller\n .getCircuit()!\n .getComponent(hoveredElement.id);\n if (circuitComponent) {\n // Also remove its pins and connected wires from selection\n for (const pinId of circuitComponent.pins) {\n selectionManager.removeFromSelection('enode', pinId);\n const enode = this.controller.getCircuit()!.getENode(pinId);\n if (enode) {\n for (const wireId of enode.wires) {\n selectionManager.removeFromSelection('wire', wireId);\n }\n }\n }\n }\n }\n // enode without componentId means branching point\n else if (\n hoveredElement.type === 'enode' &&\n !hoveredElement.object3D.userData.componentId\n ) {\n const enode = this.controller.getCircuit()!.getENode(hoveredElement.id);\n if (enode) {\n for (const wireId of enode.wires) {\n selectionManager.removeFromSelection('wire', wireId);\n }\n }\n }\n }\n return;\n }\n\n // T019: Single click selects element (clears previous)\n if (\n !isSelected &&\n !(hoveredElement.type === 'enode' && !!hoveredElement.object3D.userData.componentId)\n ) {\n selectionManager.selectOne(hoveredElement.type, hoveredElement.id);\n return;\n }\n\n // T024: Clicking on already selected element - start bulk drag\n const worldPosition = this.controller.cursorGroundPlanePosition();\n this._startBulkDrag(worldPosition);\n return;\n }\n\n // Case 2: T021 - Click on empty space clears selection (if no drag started)\n // Case 3: T011 - Start rectangle selection on empty space\n const containerRect = this.controller.getContainer().getBoundingClientRect();\n const screenX = event.clientX - containerRect.left;\n const screenY = event.clientY - containerRect.top;\n\n this._startSelectionRect(screenX, screenY, shiftHeld);\n }\n }\n\n /**\n * Handle pointer move event (T013)\n * Updates rectangle dimensions during selection\n */\n private _handlePointerMove(event: PointerEvent): void {\n if (this.mode !== 'selecting' || !this.selectionRectState) return;\n\n const containerRect = this.controller.getContainer().getBoundingClientRect();\n const screenX = event.clientX - containerRect.left;\n const screenY = event.clientY - containerRect.top;\n\n // Update current screen position\n this.selectionRectState.currentScreen = { x: screenX, y: screenY };\n\n // Update CSS overlay position and size (T013)\n this._updateSelectionRectOverlay();\n\n // Update preview highlighting (T015)\n this._updatePreviewHighlighting();\n }\n\n /**\n * Handle pointer up event (T016, T021, T029)\n * Commits selection or bulk drag, or clears if it was just a click\n */\n private _handlePointerUp(event: PointerEvent): void {\n if (event.button !== 0) return; // Only left click\n\n if (this.mode === 'selecting' && this.selectionRectState) {\n const { startScreen, currentScreen, shiftHeld } = this.selectionRectState;\n\n // Calculate rectangle size\n const width = Math.abs(currentScreen.x - startScreen.x);\n const height = Math.abs(currentScreen.y - startScreen.y);\n\n // If rectangle is too small, treat as click (T021 - clear selection)\n if (width < MIN_SELECTION_RECT_SIZE && height < MIN_SELECTION_RECT_SIZE) {\n // T021: Click on empty space clears selection\n if (!shiftHeld) {\n this.controller.getSelectionManager().deselect();\n }\n this._cancelSelectionRect();\n return;\n }\n\n // T016: Commit selection\n this._commitSelectionRect();\n } else if (this.mode === 'dragging' && this.bulkDragState) {\n // T029: Commit bulk drag\n this._commitBulkDrag();\n }\n }\n\n /**\n * Handle grid position move event (T027)\n * Updates element positions during bulk drag\n */\n private _handleGridPositionMove(position: THREE.Vector3): void {\n if (this.mode === 'dragging' && this.bulkDragState) {\n this._updateBulkDrag(position);\n }\n }\n\n /**\n * Handle keyboard events (T017, T030, T034, T043, T048, T053)\n * - Escape: cancel current operation\n * - Delete/Backspace: delete selection\n * - Ctrl+C/Cmd+C: copy selection\n * - Ctrl+V/Cmd+V: paste clipboard\n * - Ctrl+X/Cmd+X: cut selection\n */\n private _handleKeyDown(event: KeyboardEvent): void {\n // T017, T030: Escape cancels rectangle selection or bulk drag\n if (event.key === 'Escape') {\n if (this.mode === 'selecting') {\n this._cancelSelectionRect();\n return;\n } else if (this.mode === 'dragging') {\n this._cancelBulkDrag();\n return;\n }\n }\n\n // T034: Delete/Backspace triggers bulk delete\n if (event.key === 'Delete' || event.key === 'Backspace') {\n // Only delete if we have a selection and we're idle (not during drag)\n if (this.mode === 'idle') {\n this.deleteSelection();\n }\n return;\n }\n\n // Detect Ctrl (Windows/Linux) or Cmd (Mac)\n const ctrlOrCmd = event.ctrlKey || event.metaKey;\n\n if (!ctrlOrCmd) return;\n\n // T043: Ctrl+C / Cmd+C - Copy selection\n if (event.key === 'c' || event.key === 'C') {\n if (this.mode === 'idle') {\n this.copySelection();\n }\n return;\n }\n\n // T048: Ctrl+V / Cmd+V - Paste clipboard\n if (event.key === 'v' || event.key === 'V') {\n if (this.mode === 'idle') {\n this.pasteAtCursor();\n }\n return;\n }\n\n // T053: Ctrl+X / Cmd+X - Cut selection\n if (event.key === 'x' || event.key === 'X') {\n if (this.mode === 'idle') {\n this.cutSelection();\n }\n return;\n }\n }\n\n // ==========================================================================\n // Selection Rectangle Operations (T012, T014, T015, T016, T018, T022)\n // ==========================================================================\n\n /**\n * Start rectangle selection (T012)\n */\n private _startSelectionRect(screenX: number, screenY: number, shiftHeld: boolean): void {\n // Create CSS overlay element\n const overlayElement = document.createElement('div');\n overlayElement.style.cssText = `\n position: absolute;\n border: 2px dashed #4a90d9;\n background: rgba(74, 144, 217, 0.1);\n pointer-events: none;\n z-index: 1000;\n `;\n this.controller.getContainer().appendChild(overlayElement);\n\n // Initialize state\n this.selectionRectState = {\n startScreen: { x: screenX, y: screenY },\n currentScreen: { x: screenX, y: screenY },\n overlayElement,\n shiftHeld,\n previewedElements: new Set(),\n };\n\n this.mode = 'selecting';\n\n // Lock camera controls\n const controls = this.controller.getControls();\n if (controls) {\n controls.enablePan = false;\n }\n\n // T022: Emit event\n this.controller.emit('toolOperationStarted', {\n toolType: this.type,\n mode: 'selecting',\n operationData: { startScreen: { x: screenX, y: screenY } },\n });\n }\n\n /**\n * Update CSS overlay dimensions and position (T013)\n */\n private _updateSelectionRectOverlay(): void {\n if (!this.selectionRectState) return;\n\n const { startScreen, currentScreen, overlayElement } = this.selectionRectState;\n\n const left = Math.min(startScreen.x, currentScreen.x);\n const top = Math.min(startScreen.y, currentScreen.y);\n const width = Math.abs(currentScreen.x - startScreen.x);\n const height = Math.abs(currentScreen.y - startScreen.y);\n\n overlayElement.style.left = `${left}px`;\n overlayElement.style.top = `${top}px`;\n overlayElement.style.width = `${width}px`;\n overlayElement.style.height = `${height}px`;\n }\n\n /**\n * Update preview highlighting for elements inside rectangle (T015)\n */\n private _updatePreviewHighlighting(): void {\n if (!this.selectionRectState) return;\n\n const elementsInRect = this._getElementsInSelectionRect();\n const newPreviewSet = new Set<UUID>();\n\n // Collect all element IDs\n for (const id of elementsInRect.components) {\n newPreviewSet.add(id);\n }\n for (const id of elementsInRect.enodes) {\n newPreviewSet.add(id);\n }\n for (const id of elementsInRect.wires) {\n newPreviewSet.add(id);\n }\n\n // Update preview state\n this.selectionRectState.previewedElements = newPreviewSet;\n\n // TODO: Visual preview highlighting could be implemented here\n // by applying temporary visual state to elements\n }\n\n /**\n * Get elements inside the current selection rectangle (T014)\n * Components/BPs: selected if center is inside rectangle\n * Wires: selected only if BOTH endpoints are selected\n */\n private _getElementsInSelectionRect(): {\n components: UUID[];\n enodes: UUID[];\n wires: UUID[];\n } {\n const result = {\n components: [] as UUID[],\n enodes: [] as UUID[],\n wires: [] as UUID[],\n };\n\n if (!this.selectionRectState) return result;\n\n const circuit = this.controller.getCircuit();\n if (!circuit) return result;\n\n const camera = this.controller.getCamera();\n const container = this.controller.getContainer();\n const width = container.clientWidth;\n const height = container.clientHeight;\n\n // Calculate screen rect bounds\n const { startScreen, currentScreen } = this.selectionRectState;\n const screenRect = {\n minX: Math.min(startScreen.x, currentScreen.x),\n minY: Math.min(startScreen.y, currentScreen.y),\n maxX: Math.max(startScreen.x, currentScreen.x),\n maxY: Math.max(startScreen.y, currentScreen.y),\n };\n\n // Set of selected enode IDs\n const selectedEnodeIds = new Set<UUID>();\n // Map of wire and number of their enodes selected\n const wireEnodeSelectionCount = new Map<UUID, number>();\n\n // Check components (T014)\n const componentObject3Ds = this.controller.componentObject3Ds;\n for (const [componentId, object3D] of componentObject3Ds) {\n const worldPos = new THREE.Vector3();\n object3D.getWorldPosition(worldPos);\n\n if (isPointInScreenRect(worldPos, camera, width, height, screenRect)) {\n result.components.push(componentId);\n\n // Mark component pins as selected for wire check\n const component = circuit.getComponent(componentId);\n if (component) {\n for (const pinId of component.pins) {\n selectedEnodeIds.add(pinId);\n const enode = circuit.getENode(pinId);\n if (!enode) continue;\n for (const wireId of enode.wires) {\n const currentCount = wireEnodeSelectionCount.get(wireId) || 0;\n wireEnodeSelectionCount.set(wireId, currentCount + 1);\n }\n }\n }\n }\n }\n\n // Check enodes (branching points only - pins are covered by components)\n const enodeObject3Ds = this.controller.enodeObject3Ds;\n for (const [enodeId, object3D] of enodeObject3Ds) {\n // Only select standalone branching points\n if (object3D.userData.componentId) continue; // Skip pins\n\n // Skip if already marked (pin of selected component)\n if (selectedEnodeIds.has(enodeId)) continue;\n\n const worldPos = new THREE.Vector3();\n object3D.getWorldPosition(worldPos);\n\n if (isPointInScreenRect(worldPos, camera, width, height, screenRect)) {\n result.enodes.push(enodeId);\n selectedEnodeIds.add(enodeId);\n const enode = circuit.getENode(enodeId);\n if (!enode) continue;\n for (const wireId of enode.wires) {\n const currentCount = wireEnodeSelectionCount.get(wireId) || 0;\n wireEnodeSelectionCount.set(wireId, currentCount + 1);\n }\n }\n }\n\n for (const [wireId, count] of wireEnodeSelectionCount) {\n // If both endpoints are selected, include the wire (T014)\n if (count >= 2) {\n result.wires.push(wireId);\n }\n }\n\n return result;\n }\n\n /**\n * Commit the rectangle selection (T016, T018, T022)\n */\n private _commitSelectionRect(): void {\n if (!this.selectionRectState) return;\n\n const elementsInRect = this._getElementsInSelectionRect();\n const selectionManager = this.controller.getSelectionManager();\n\n // Build selection maps\n const components = new Map<UUID, string | null>();\n const enodes = new Map<UUID, string | null>();\n const wires = new Map<UUID, string | null>();\n\n for (const id of elementsInRect.components) {\n components.set(id, null);\n }\n for (const id of elementsInRect.enodes) {\n enodes.set(id, null);\n }\n for (const id of elementsInRect.wires) {\n wires.set(id, null);\n }\n\n // T018: Additive selection mode\n if (this.selectionRectState.shiftHeld) {\n // Add new elements to existing selection\n const existing = selectionManager.getSelectedIds();\n for (const id of existing.components) {\n components.set(id, null);\n }\n for (const id of existing.enodes) {\n enodes.set(id, null);\n }\n for (const id of existing.wires) {\n wires.set(id, null);\n }\n }\n\n // Apply selection\n selectionManager.selectMultiple(components, enodes, wires);\n\n // T022: Emit completion event\n const totalCount = components.size + enodes.size + wires.size;\n this.controller.emit('toolOperationCompleted', {\n toolType: this.type,\n mode: 'selecting',\n operationData: {\n selectedCount: totalCount,\n additive: this.selectionRectState.shiftHeld,\n },\n changedData: {},\n });\n\n // Cleanup\n this._cleanupSelectionRect();\n this.mode = 'idle';\n\n // Unlock camera controls\n const controls = this.controller.getControls();\n if (controls) {\n controls.enablePan = true;\n }\n }\n\n /**\n * Cancel rectangle selection (T017)\n */\n private _cancelSelectionRect(): void {\n if (!this.selectionRectState) return;\n\n // Emit cancellation event\n this.controller.emit('toolOperationCancelled', {\n toolType: this.type,\n mode: 'selecting',\n });\n\n // Cleanup\n this._cleanupSelectionRect();\n this.mode = 'idle';\n\n // Unlock camera controls\n const controls = this.controller.getControls();\n if (controls) {\n controls.enablePan = true;\n }\n }\n\n /**\n * Clean up selection rectangle overlay\n */\n private _cleanupSelectionRect(): void {\n if (this.selectionRectState?.overlayElement) {\n this.selectionRectState.overlayElement.remove();\n }\n this.selectionRectState = null;\n }\n\n // ==========================================================================\n // Bulk Drag Operations (T024-T032) - Phase 4\n // ==========================================================================\n\n /**\n * Start bulk drag operation (T024, T025, T026, T032)\n */\n private _startBulkDrag(worldPosition: THREE.Vector3): void {\n const circuit = this.controller.getCircuit();\n if (!circuit) return;\n\n const selectionManager = this.controller.getSelectionManager();\n const selectedIds = selectionManager.getSelectedIds();\n\n // T025: Capture initial positions for all selected elements\n const initialPositions = new Map<UUID, THREE.Vector3>();\n\n // Capture component positions\n for (const componentId of selectedIds.components) {\n const object3D = this.controller.componentObject3Ds.get(componentId);\n if (object3D) {\n initialPositions.set(componentId, object3D.position.clone());\n }\n }\n\n // Capture branching point positions\n for (const enodeId of selectedIds.enodes) {\n const object3D = this.controller.enodeObject3Ds.get(enodeId);\n if (object3D && !object3D.userData.componentId) {\n // Only branching points\n initialPositions.set(enodeId, object3D.position.clone());\n }\n }\n\n // T026: Collect affected wires (selected wires + boundary wires)\n const affectedWireIds = new Set<UUID>();\n\n // Add selected wires\n for (const wireId of selectedIds.wires) {\n affectedWireIds.add(wireId);\n }\n\n // Add boundary wires (wires connected to selected components/enodes but not fully selected)\n for (const componentId of selectedIds.components) {\n const component = circuit.getComponent(componentId);\n if (component) {\n for (const pinId of component.pins) {\n const enode = circuit.getENode(pinId);\n if (enode) {\n for (const wireId of enode.wires) {\n affectedWireIds.add(wireId);\n }\n }\n }\n }\n }\n\n for (const enodeId of selectedIds.enodes) {\n const enode = circuit.getENode(enodeId);\n if (enode) {\n for (const wireId of enode.wires) {\n affectedWireIds.add(wireId);\n }\n }\n }\n\n // Capture initial intermediate positions for selected wires\n const initialWireIntermediatePositions = new Map<UUID, THREE.Vector3[]>();\n for (const wireId of selectedIds.wires) {\n const wire = circuit.getWire(wireId);\n if (wire && wire.intermediatePositions.length > 0) {\n // Clone all intermediate positions\n const positions = wire.intermediatePositions.map((pos) => gridToWorldPosition(pos));\n initialWireIntermediatePositions.set(wireId, positions);\n }\n }\n\n // Initialize drag state\n this.bulkDragState = {\n dragStartWorld: worldPosition.clone(),\n initialPositions,\n affectedWireIds,\n initialWireIntermediatePositions,\n };\n\n this.mode = 'dragging';\n\n // Lock camera controls\n const controls = this.controller.getControls();\n if (controls) {\n controls.enablePan = false;\n }\n\n // Register for grid position move events\n this.controller.on('gridPositionMove', this.handleGridPositionMove);\n\n // T032: Emit event\n this.controller.emit('toolOperationStarted', {\n toolType: this.type,\n mode: 'dragging',\n operationData: { elementCount: initialPositions.size },\n });\n }\n\n /**\n * Update bulk drag - apply delta to all selected elements (T027, T028)\n */\n private _updateBulkDrag(worldPosition: THREE.Vector3): void {\n if (!this.bulkDragState) return;\n\n const { dragStartWorld, initialPositions, affectedWireIds, initialWireIntermediatePositions } =\n this.bulkDragState;\n\n // we retrieve unbound world position to avoid snapping during drag since Multi Select tool allows to expand grid\n worldPosition = this.controller.cursorGroundPlanePosition();\n\n // Calculate delta\n const delta = new THREE.Vector3().subVectors(worldPosition, dragStartWorld);\n\n // T027: Apply delta to all selected elements\n for (const [elementId, initialPos] of initialPositions) {\n const newPosition = new THREE.Vector3().addVectors(initialPos, delta);\n const snappedPosition = nearestWorldSnapPosition(newPosition);\n\n // Update component visual\n const componentObject3D = this.controller.componentObject3Ds.get(elementId);\n if (componentObject3D) {\n componentObject3D.position.copy(snappedPosition);\n\n // Update component in circuit model\n this.controller.circuitWriter.saveEditComponent(elementId, componentObject3D);\n continue;\n }\n\n // Update branching point visual\n const enodeObject3D = this.controller.enodeObject3Ds.get(elementId);\n if (enodeObject3D) {\n enodeObject3D.position.copy(snappedPosition);\n\n // Update branching point in circuit model\n this.controller.circuitWriter.saveEditBranchingPoint(enodeObject3D);\n }\n }\n\n // Apply delta to intermediate positions of selected wires\n\n for (const [wireId, initialIntermediatePositions] of initialWireIntermediatePositions) {\n // Apply delta to each intermediate position\n const updatedPositions = initialIntermediatePositions.map((pos) => {\n const newPos = new THREE.Vector3().addVectors(pos, delta);\n return worldToGridPosition(newPos);\n });\n\n // Update wire intermediate positions in circuit model\n this.controller.circuitWriter.saveEditWirePositions(wireId, updatedPositions, false);\n }\n\n // T028: Update wire geometry for all affected wires\n const wireVisualManager = this.controller.wireVisualManager;\n for (const wireId of affectedWireIds) {\n wireVisualManager.updateWireById(wireId);\n }\n }\n\n /**\n * Commit bulk drag operation (T029, T032)\n */\n private _commitBulkDrag(): void {\n if (!this.bulkDragState) return;\n\n const circuit = this.controller.getCircuit();\n if (!circuit) return;\n\n const { dragStartWorld, initialPositions } = this.bulkDragState;\n const currentPosition = this.controller.cursorGroundPlanePosition();\n\n // Calculate final delta\n const delta = new THREE.Vector3().subVectors(currentPosition, dragStartWorld);\n const gridDelta = worldToGridPosition(delta);\n\n // T032: Emit completion event\n this.controller.emit('toolOperationCompleted', {\n toolType: this.type,\n mode: 'dragging',\n operationData: {\n elementCount: initialPositions.size,\n delta: { x: gridDelta.x, y: gridDelta.y },\n },\n changedData: {},\n });\n\n // launch autoAdjust of the grid after bulk move\n this.controller.autoAdjustCircuitGridSize();\n\n // Cleanup\n this.mode = 'idle';\n this.bulkDragState = null;\n\n // Unregister grid position move listener\n this.controller.off('gridPositionMove', this.handleGridPositionMove);\n\n // Unlock camera controls\n const controls = this.controller.getControls();\n if (controls) {\n controls.enablePan = true;\n }\n }\n\n /**\n * Cancel bulk drag operation - revert all elements to initial positions (T030)\n */\n private _cancelBulkDrag(): void {\n if (!this.bulkDragState) return;\n\n const { initialPositions, affectedWireIds, initialWireIntermediatePositions } =\n this.bulkDragState;\n\n // Revert all elements to initial positions\n for (const [elementId, initialPos] of initialPositions) {\n const componentObject3D = this.controller.componentObject3Ds.get(elementId);\n if (componentObject3D) {\n componentObject3D.position.copy(initialPos);\n this.controller.circuitWriter.saveEditComponent(elementId, componentObject3D);\n continue;\n }\n\n const enodeObject3D = this.controller.enodeObject3Ds.get(elementId);\n if (enodeObject3D) {\n enodeObject3D.position.copy(initialPos);\n this.controller.circuitWriter.saveEditBranchingPoint(enodeObject3D);\n }\n }\n\n // Revert intermediate positions of selected wires\n for (const [wireId, initialIntermediatePositions] of initialWireIntermediatePositions) {\n const positions = initialIntermediatePositions.map((pos) => worldToGridPosition(pos));\n this.controller.circuitWriter.saveEditWirePositions(wireId, positions, false);\n }\n\n // Update all affected wires\n const wireVisualManager = this.controller.wireVisualManager;\n for (const wireId of affectedWireIds) {\n wireVisualManager.updateWireById(wireId);\n }\n\n // Emit cancellation event\n this.controller.emit('toolOperationCancelled', {\n toolType: this.type,\n mode: 'dragging',\n });\n\n // Cleanup\n this.mode = 'idle';\n this.bulkDragState = null;\n\n // Unregister grid position move listener\n this.controller.off('gridPositionMove', this.handleGridPositionMove);\n\n // Unlock camera controls\n const controls = this.controller.getControls();\n if (controls) {\n controls.enablePan = true;\n }\n }\n\n // ==========================================================================\n // Copy/Paste Operations (T039-T051) - Phase 6\n // ==========================================================================\n\n /**\n * Check if clipboard has content (T050)\n */\n hasClipboardContent(): boolean {\n return this.clipboardData !== null;\n }\n\n /**\n * Copy current selection to clipboard (T039, T040, T041, T042, T051)\n * @returns true if copy succeeded (non-empty selection)\n */\n copySelection(): boolean {\n const circuit = this.controller.getCircuit();\n if (!circuit) return false;\n\n const selectionManager = this.controller.getSelectionManager();\n const selectedIds = selectionManager.getSelectedIds();\n\n // Empty selection - no-op\n if (\n selectedIds.components.length === 0 &&\n selectedIds.enodes.length === 0 &&\n selectedIds.wires.length === 0\n ) {\n return false;\n }\n\n // T040: Calculate anchor (center of selection bounding box)\n const bounds = {\n minX: Infinity,\n maxX: -Infinity,\n minY: Infinity,\n maxY: -Infinity,\n };\n\n // Gather component positions\n for (const componentId of selectedIds.components) {\n const component = circuit.getComponent(componentId);\n if (component) {\n const pos = component.position;\n bounds.minX = Math.min(bounds.minX, pos.x);\n bounds.maxX = Math.max(bounds.maxX, pos.x);\n bounds.minY = Math.min(bounds.minY, pos.y);\n bounds.maxY = Math.max(bounds.maxY, pos.y);\n }\n }\n\n // Gather branching point positions\n for (const enodeId of selectedIds.enodes) {\n const enode = circuit.getENode(enodeId);\n if (enode && enode.position) {\n const pos = enode.position;\n bounds.minX = Math.min(bounds.minX, pos.x);\n bounds.maxX = Math.max(bounds.maxX, pos.x);\n bounds.minY = Math.min(bounds.minY, pos.y);\n bounds.maxY = Math.max(bounds.maxY, pos.y);\n }\n }\n\n const anchor = {\n x: (bounds.minX + bounds.maxX) / 2,\n y: (bounds.minY + bounds.maxY) / 2,\n };\n\n // T041: Serialize components with relative positions\n const clipboardComponents: ClipboardComponent[] = [];\n for (const componentId of selectedIds.components) {\n const component = circuit.getComponent(componentId);\n if (component) {\n const pos = component.position;\n const sources = component.pins.map((pinId) => {\n const enode = circuit.getENode(pinId);\n return enode ? enode.source : null;\n });\n clipboardComponents.push({\n type: component.type,\n relativePosition: {\n x: pos.x - anchor.x,\n y: pos.y - anchor.y,\n },\n rotation: component.rotation.angle,\n originalId: componentId,\n pinSources: sources,\n config: new Map(component.config), // Deep copy of config\n });\n }\n }\n\n // T041: Serialize branching points with relative positions\n const clipboardBranchingPoints: ClipboardBranchingPoint[] = [];\n for (const enodeId of selectedIds.enodes) {\n const enode = circuit.getENode(enodeId);\n if (enode && enode.position) {\n const pos = enode.position;\n clipboardBranchingPoints.push({\n relativePosition: {\n x: pos.x - anchor.x,\n y: pos.y - anchor.y,\n },\n originalId: enodeId,\n source: enode.source,\n });\n }\n }\n\n // T042: Serialize wires (only wires with both endpoints in selection)\n // Create set of all selected endpoint IDs (component pins + branching points)\n const selectedEndpointIds = new Set<UUID>();\n\n // Also build pin-to-component mapping and pin indices for paste operation\n this.clipboardPinToComponent.clear();\n this.clipboardPinIndices.clear();\n\n // Add component pins\n for (const componentId of selectedIds.components) {\n const component = circuit.getComponent(componentId);\n if (component) {\n for (let i = 0; i < component.pins.length; i++) {\n const pinId = component.pins[i];\n if (pinId) {\n selectedEndpointIds.add(pinId);\n this.clipboardPinToComponent.set(pinId, componentId);\n this.clipboardPinIndices.set(pinId, i);\n }\n }\n }\n }\n\n // Add branching points\n for (const enodeId of selectedIds.enodes) {\n selectedEndpointIds.add(enodeId);\n }\n\n const clipboardWires: ClipboardWire[] = [];\n for (const wireId of selectedIds.wires) {\n const wire = circuit.getWire(wireId);\n if (!wire) continue;\n\n // Only include wire if both endpoints are in selection\n if (selectedEndpointIds.has(wire.node1) && selectedEndpointIds.has(wire.node2)) {\n // Calculate relative intermediate positions\n const relativeIntermediatePositions = wire.intermediatePositions.map((pos) => ({\n x: pos.x - anchor.x,\n y: pos.y - anchor.y,\n }));\n\n clipboardWires.push({\n node1OriginalId: wire.node1,\n node2OriginalId: wire.node2,\n relativeIntermediatePositions,\n });\n }\n }\n\n // Store clipboard data\n this.clipboardData = {\n anchor,\n components: clipboardComponents,\n branchingPoints: clipboardBranchingPoints,\n wires: clipboardWires,\n };\n\n // T051: Emit copy completed event\n this.controller.emit('toolOperationCompleted', {\n toolType: this.type,\n mode: 'copy',\n operationData: {\n componentCount: clipboardComponents.length,\n branchingPointCount: clipboardBranchingPoints.length,\n wireCount: clipboardWires.length,\n },\n changedData: {},\n });\n\n return true;\n }\n\n /**\n * Paste clipboard content at cursor position (T044, T045, T046, T047, T049, T051)\n * @returns true if paste succeeded (non-empty clipboard)\n */\n pasteAtCursor(): boolean {\n if (!this.clipboardData) return false;\n\n const circuit = this.controller.getCircuit();\n if (!circuit) return false;\n\n const cursorPosition = this.controller.cursorGroundPlanePosition();\n const gridCursor = worldToGridPosition(cursorPosition);\n\n // Map from original IDs to newly created element IDs for wire remapping (T047)\n const idRemap = new Map<UUID, UUID>();\n\n // T045: Create components from clipboard\n const createdComponentIds: UUID[] = [];\n for (const clipComponent of this.clipboardData.components) {\n const newGridPos = new Position(\n Math.round(gridCursor.x + clipComponent.relativePosition.x),\n Math.round(gridCursor.y + clipComponent.relativePosition.y)\n );\n\n const worldPos = gridToWorldPosition(newGridPos);\n const rotation = gridToWorldRotation(new Rotation(clipComponent.rotation));\n\n try {\n const newComponent = this.controller.addComponent(\n clipComponent.type as ComponentType,\n worldPos,\n rotation,\n clipComponent.config,\n clipComponent.pinSources\n );\n\n createdComponentIds.push(newComponent.id);\n\n // Map original component ID → new component ID\n idRemap.set(clipComponent.originalId, newComponent.id);\n\n // Map original pin IDs → new pin IDs using stored pin indices\n // This works even after cut (when original component no longer exists)\n for (const [originalPinId, originalComponentId] of this.clipboardPinToComponent) {\n if (originalComponentId === clipComponent.originalId) {\n const pinIndex = this.clipboardPinIndices.get(originalPinId);\n if (pinIndex !== undefined && pinIndex < newComponent.pins.length) {\n const newPinId = newComponent.pins[pinIndex];\n if (newPinId) {\n idRemap.set(originalPinId, newPinId);\n }\n }\n }\n }\n } catch (error) {\n console.error('Failed to paste component:', error);\n }\n }\n\n // T046: Create branching points from clipboard\n const createdBranchingPointIds: UUID[] = [];\n for (const clipBP of this.clipboardData.branchingPoints) {\n const newGridPos = new Position(\n Math.round(gridCursor.x + clipBP.relativePosition.x),\n Math.round(gridCursor.y + clipBP.relativePosition.y)\n );\n\n const worldPos = gridToWorldPosition(newGridPos);\n\n try {\n const newEnode = this.controller.addBranchingPoint(worldPos, clipBP.source);\n createdBranchingPointIds.push(newEnode.id);\n\n // Map original BP ID → new BP ID\n idRemap.set(clipBP.originalId, newEnode.id);\n } catch (error) {\n console.error('Failed to paste branching point:', error);\n }\n }\n\n // T047: Create wires with ID remapping\n const createdWireIds: UUID[] = [];\n for (const clipWire of this.clipboardData.wires) {\n const newNode1 = idRemap.get(clipWire.node1OriginalId);\n const newNode2 = idRemap.get(clipWire.node2OriginalId);\n\n // Only create wire if both endpoints were successfully created\n if (newNode1 && newNode2) {\n try {\n const newWire = this.controller.addWire(newNode1, newNode2);\n createdWireIds.push(newWire.id);\n\n // Update intermediate positions if any\n if (clipWire.relativeIntermediatePositions.length > 0) {\n const absolutePositions = clipWire.relativeIntermediatePositions.map((relPos) => ({\n x: Math.round(gridCursor.x + relPos.x),\n y: Math.round(gridCursor.y + relPos.y),\n }));\n\n this.controller.circuitWriter.saveEditWirePositions(\n newWire.id,\n absolutePositions,\n true\n );\n this.controller.wireVisualManager.updateWireById(newWire.id);\n }\n } catch (error) {\n console.error('Failed to paste wire:', error);\n }\n }\n }\n\n // T049: Select pasted elements\n const selectionManager = this.controller.getSelectionManager();\n const componentsMap = new Map<UUID, string | null>();\n const enodesMap = new Map<UUID, string | null>();\n const wiresMap = new Map<UUID, string | null>();\n\n for (const id of createdComponentIds) {\n componentsMap.set(id, null);\n }\n for (const id of createdBranchingPointIds) {\n enodesMap.set(id, null);\n }\n for (const id of createdWireIds) {\n wiresMap.set(id, null);\n }\n\n selectionManager.selectMultiple(componentsMap, enodesMap, wiresMap);\n\n // T051: Emit paste completed event\n this.controller.emit('toolOperationCompleted', {\n toolType: this.type,\n mode: 'paste',\n operationData: {\n componentCount: createdComponentIds.length,\n branchingPointCount: createdBranchingPointIds.length,\n wireCount: createdWireIds.length,\n position: { x: gridCursor.x, y: gridCursor.y },\n },\n changedData: {},\n });\n\n // launch autoAdjust of the grid after bulk paste\n this.controller.autoAdjustCircuitGridSize();\n\n return true;\n }\n\n // ==========================================================================\n // Cut/Paste Operations (T052-T053) - Phase 7\n // ==========================================================================\n\n /**\n * Cut current selection (copy + delete) (T052)\n * @returns true if cut succeeded\n */\n cutSelection(): boolean {\n // Copy first\n const copySuccess = this.copySelection();\n if (!copySuccess) return false;\n\n // Then delete\n this.deleteSelection();\n return true;\n }\n\n // ==========================================================================\n // Bulk Delete Operations (T033-T037) - Phase 5\n // ==========================================================================\n\n /**\n * Delete all selected elements (T033, T034, T035, T036, T037)\n *\n * Deletion order per research.md:\n * 1. Selected wires\n * 2. Selected components (cascades to connected wires)\n * 3. Selected branching points\n */\n deleteSelection(): boolean {\n const selectionManager = this.controller.getSelectionManager();\n const selectedIds = selectionManager.getSelectedIds();\n\n const totalCount =\n selectedIds.components.length + selectedIds.enodes.length + selectedIds.wires.length;\n\n // No selection to delete\n if (totalCount === 0) {\n return false;\n }\n\n // T033: Delete in order: wires → components → branching points\n\n // 1. Delete selected wires\n for (const wireId of selectedIds.wires) {\n this.controller.removeWire(wireId);\n }\n\n // 2. Delete selected components (T035: cascades to connected wires - orphaned cleanup)\n for (const componentId of selectedIds.components) {\n this.controller.removeComponent(componentId);\n }\n\n // 3. Delete selected branching points\n for (const enodeId of selectedIds.enodes) {\n this.controller.removeBranchingPoint(enodeId);\n }\n\n // T037: Emit bulk delete event\n this.controller.emit('toolOperationCompleted', {\n toolType: this.type,\n mode: 'bulk_delete',\n operationData: {\n componentCount: selectedIds.components.length,\n branchingPointCount: selectedIds.enodes.length,\n wireCount: selectedIds.wires.length,\n },\n changedData: {},\n });\n\n // launch autoAdjust of the grid after bulk delete\n this.controller.autoAdjustCircuitGridSize();\n\n // T036: Clear selection after delete\n selectionManager.deselect();\n\n return true;\n }\n}\n","/**\n * Selection Manager\n * @module scene/shared/SelectionManager\n *\n * Manages component selection state in the circuit scene.\n * However controller handles applying or not visuals for selection, based on current toolState.\n * Follows a similar pattern as HoverManager for consistency.\n */\n\nimport type { UUID } from 'simple-circuit-engine/core';\nimport { ENodeType } from 'simple-circuit-engine/core';\nimport type { HoverableType, SelectionData, MultiSelectionData } from './types';\n\n/**\n * Callback invoked when selection changes\n *\n * @param newSelection - The new selection, or null if deselected\n * @param previousSelection - The previous selection, or null if none was selected\n */\nexport type SelectionCallback = (\n newSelection: SelectionData | null,\n previousSelection: SelectionData | null\n) => void;\n\n/**\n * Manages selected components, enodes or wires for the circuit scene.\n * Multi-selection is planned (notably with the use of a flexible SelectionData) but still to implement.\n *\n * Key current responsibilities:\n * - Track -currently single object- selection state\n * - Notify listeners of selection changes (observer pattern)\n *\n * @example\n * ```typescript\n * const selectionManager = new SelectionManager();\n *\n *\n * // Select a single object\n * selectionManager.selectOne('component', 'component-uuid-1234');\n * selectionManager.selectOne('enode', 'enode-uuid-1234');\n * selectionManager.selectOne('wire', 'wire-uuid-1234');\n *\n * // Deselect\n * selectionManager.deselect();\n * ```\n */\nexport class SelectionManager {\n /** Current selection */\n private selection: SelectionData | null = null;\n\n /** Timestamp when selection occurred (for double-click detection) */\n private selectedAt: number | null = null;\n\n /** Registered selection change callbacks */\n private callbacks: Set<SelectionCallback> = new Set();\n\n /**\n * Create a new SelectionManager\n */\n constructor() {}\n\n /**\n * Get the current selection\n *\n * @returns The SelectionData, or null if nothing is selected\n */\n getSelection(): SelectionData | null {\n return this.selection;\n }\n\n /**\n * Get the timestamp when selection occurred\n *\n * @returns Timestamp in milliseconds, or null if nothing is selected\n */\n getSelectedAt(): number | null {\n return this.selectedAt;\n }\n\n /**\n * Check if a specific object is selected\n *\n * @param type - The type of hoverable object\n * @param objectId - The object ID to check\n * @returns true if the object is currently selected\n */\n isSelected(type: HoverableType, objectId: UUID): boolean {\n if (!this.selection) {\n return false;\n }\n if (this.selection.kind === 'mono') {\n return this.selection.type === type && this.selection.id === objectId;\n }\n if (this.selection.kind === 'multi') {\n if (type === 'component') {\n return this.selection.components?.has(objectId) ?? false;\n }\n if (type === 'enode') {\n return this.selection.enodes?.has(objectId) ?? false;\n }\n if (type === 'wire') {\n return this.selection.wires?.has(objectId) ?? false;\n }\n }\n return false;\n }\n\n /**\n * Check if anything is selected\n *\n * @returns true if one object is currently selected or several objects are currently selected\n */\n hasSelection(): boolean {\n return this.selection !== null;\n }\n\n private _selectionsEqual(a: SelectionData | null, b: SelectionData | null): boolean {\n if (a === b) {\n return true;\n }\n if (a === null || b === null) {\n return false;\n }\n if (a.kind !== b.kind) {\n return false;\n }\n if (a.kind === 'mono' && b.kind === 'mono') {\n return a.id === b.id;\n }\n if (a.kind === 'multi' && b.kind === 'multi') {\n const aComponents = a.components ?? new Map<UUID, string | null>();\n const bComponents = b.components ?? new Map<UUID, string | null>();\n const aEnodes = a.enodes ?? new Map<UUID, string | null>();\n const bEnodes = b.enodes ?? new Map<UUID, string | null>();\n const aWires = a.wires ?? new Map<UUID, string | null>();\n const bWires = b.wires ?? new Map<UUID, string | null>();\n\n if (\n aComponents.size !== bComponents.size ||\n aEnodes.size !== bEnodes.size ||\n aWires.size !== bWires.size\n ) {\n return false;\n }\n\n for (const id of aComponents.keys()) {\n if (!bComponents.has(id)) {\n return false;\n }\n }\n for (const id of aEnodes.keys()) {\n if (!bEnodes.has(id)) {\n return false;\n }\n }\n for (const id of aWires.keys()) {\n if (!bWires.has(id)) {\n return false;\n }\n }\n }\n return true;\n }\n\n /**\n * Select one object\n *\n * If another object was previously selected or a multi selection existed, they will be deselected first.\n *\n * @param type - The type of hoverable object to select\n * @param objectId - The object ID to select\n * @param userData - Optional userData of the 3D object being selected\n */\n selectOne(type: HoverableType, objectId: UUID, userData?: object | undefined): void {\n const previousSelection = this.selection;\n const newSelection: SelectionData = { kind: 'mono', type: type, id: objectId, data: null };\n\n // No change if selections are equal\n if (this._selectionsEqual(newSelection, previousSelection)) {\n return;\n }\n\n if (type === 'enode' && !!userData) {\n // @ts-ignore\n newSelection.data = !userData['componentId'] ? ENodeType.BranchingPoint : ENodeType.Pin;\n }\n\n // Update state\n this.selection = newSelection;\n this.selectedAt = Date.now();\n\n // Notify callbacks\n this.notifyCallbacks(newSelection, previousSelection);\n }\n\n /**\n * Deselect the current selection\n */\n deselect(): void {\n const previousSelection = this.selection;\n // Nothing to deselect\n if (!previousSelection) {\n return;\n }\n // Clear state\n this.selection = null;\n this.selectedAt = null;\n\n // Notify callbacks\n this.notifyCallbacks(null, previousSelection);\n }\n\n /**\n * Select multiple elements at once, replacing any existing selection\n *\n * Creates a MultiSelectionData with the provided element maps.\n * Empty maps are allowed; passing all empty maps clears the selection.\n *\n * @param components - Map of component IDs to optional metadata\n * @param enodes - Map of enode IDs to optional metadata\n * @param wires - Map of wire IDs to optional metadata\n */\n selectMultiple(\n components?: Map<UUID, string | null>,\n enodes?: Map<UUID, string | null>,\n wires?: Map<UUID, string | null>\n ): void {\n const previousSelection = this.selection;\n\n // Check if all maps are empty - treat as deselect\n const totalCount = (components?.size ?? 0) + (enodes?.size ?? 0) + (wires?.size ?? 0);\n\n if (totalCount === 0) {\n this.deselect();\n return;\n }\n\n const newSelection: MultiSelectionData = {\n kind: 'multi',\n ...(components && components.size > 0 ? { components } : {}),\n ...(enodes && enodes.size > 0 ? { enodes } : {}),\n ...(wires && wires.size > 0 ? { wires } : {}),\n };\n\n // No change if selections are equal\n if (this._selectionsEqual(newSelection, previousSelection)) {\n return;\n }\n\n // Update state\n this.selection = newSelection;\n this.selectedAt = Date.now();\n\n // Notify callbacks\n this.notifyCallbacks(newSelection, previousSelection);\n }\n\n /**\n * Add a single element to the current selection\n *\n * If current selection is null, creates a mono selection.\n * If current selection is mono, converts to multi and adds element.\n * If current selection is multi, adds element to appropriate map.\n *\n * No-op if element is already selected.\n *\n * @param type - Type of element to add\n * @param objectId - UUID of element to add\n * @param userData - Optional metadata for the element\n */\n addToSelection(type: HoverableType, objectId: UUID, userData?: object): void {\n // If already selected, no-op\n if (this.isSelected(type, objectId)) {\n return;\n }\n\n const previousSelection = this.selection;\n\n // If no selection, create mono selection\n if (!previousSelection) {\n this.selectOne(type, objectId, userData);\n return;\n }\n\n // If mono selection, convert to multi\n if (previousSelection.kind === 'mono') {\n const components = new Map<UUID, string | null>();\n const enodes = new Map<UUID, string | null>();\n const wires = new Map<UUID, string | null>();\n\n // Add existing selection\n if (previousSelection.type === 'component') {\n components.set(previousSelection.id, previousSelection.data ?? null);\n } else if (previousSelection.type === 'enode') {\n enodes.set(previousSelection.id, previousSelection.data ?? null);\n } else if (previousSelection.type === 'wire') {\n wires.set(previousSelection.id, previousSelection.data ?? null);\n }\n\n // Add new element\n if (type === 'component') {\n components.set(objectId, null);\n } else if (type === 'enode') {\n enodes.set(objectId, null);\n } else if (type === 'wire') {\n wires.set(objectId, null);\n }\n\n this.selectMultiple(components, enodes, wires);\n return;\n }\n\n // If multi selection, add to appropriate map\n if (previousSelection.kind === 'multi') {\n const components = new Map(previousSelection.components ?? new Map());\n const enodes = new Map(previousSelection.enodes ?? new Map());\n const wires = new Map(previousSelection.wires ?? new Map());\n\n if (type === 'component') {\n components.set(objectId, null);\n } else if (type === 'enode') {\n enodes.set(objectId, null);\n } else if (type === 'wire') {\n wires.set(objectId, null);\n }\n\n this.selectMultiple(components, enodes, wires);\n }\n }\n\n /**\n * Remove a single element from the current selection\n *\n * If element is in a mono selection, clears the selection.\n * If element is in a multi selection, removes from appropriate map.\n * If multi selection becomes single element, converts to mono.\n *\n * No-op if element is not selected.\n *\n * @param type - Type of element to remove\n * @param objectId - UUID of element to remove\n */\n removeFromSelection(type: HoverableType, objectId: UUID): void {\n // If not selected, no-op\n if (!this.isSelected(type, objectId)) {\n return;\n }\n\n const previousSelection = this.selection;\n\n if (!previousSelection) {\n return;\n }\n\n // If mono selection, deselect\n if (previousSelection.kind === 'mono') {\n this.deselect();\n return;\n }\n\n // If multi selection, remove from appropriate map\n if (previousSelection.kind === 'multi') {\n const components = new Map(previousSelection.components ?? new Map());\n const enodes = new Map(previousSelection.enodes ?? new Map());\n const wires = new Map(previousSelection.wires ?? new Map());\n\n if (type === 'component') {\n components.delete(objectId);\n } else if (type === 'enode') {\n enodes.delete(objectId);\n } else if (type === 'wire') {\n wires.delete(objectId);\n }\n\n const totalCount = components.size + enodes.size + wires.size;\n\n // If only one element left, convert to mono\n if (totalCount === 1) {\n if (components.size === 1) {\n for (const [id, data] of components.entries()) {\n this.selectOne('component', id, data ? { data } : undefined);\n return;\n }\n } else if (enodes.size === 1) {\n for (const [id, data] of enodes.entries()) {\n this.selectOne('enode', id, data ? { data } : undefined);\n return;\n }\n } else if (wires.size === 1) {\n for (const [id, data] of wires.entries()) {\n this.selectOne('wire', id, data ? { data } : undefined);\n return;\n }\n }\n return;\n }\n\n // Otherwise, update multi selection\n this.selectMultiple(components, enodes, wires);\n }\n }\n\n /**\n * Get the total count of selected elements across all types\n *\n * @returns Number of selected elements (0 if no selection)\n */\n getSelectionCount(): number {\n if (!this.selection) {\n return 0;\n }\n\n if (this.selection.kind === 'mono') {\n return 1;\n }\n\n if (this.selection.kind === 'multi') {\n return (\n (this.selection.components?.size ?? 0) +\n (this.selection.enodes?.size ?? 0) +\n (this.selection.wires?.size ?? 0)\n );\n }\n\n return 0;\n }\n\n /**\n * Get all selected element IDs grouped by type\n *\n * Returns empty arrays if no selection.\n * For mono selection, returns single-element array in appropriate category.\n *\n * @returns Object with arrays of selected IDs by type\n */\n getSelectedIds(): {\n components: UUID[];\n enodes: UUID[];\n wires: UUID[];\n } {\n if (!this.selection) {\n return { components: [], enodes: [], wires: [] };\n }\n\n if (this.selection.kind === 'mono') {\n if (this.selection.type === 'component') {\n return { components: [this.selection.id], enodes: [], wires: [] };\n }\n if (this.selection.type === 'enode') {\n return { components: [], enodes: [this.selection.id], wires: [] };\n }\n if (this.selection.type === 'wire') {\n return { components: [], enodes: [], wires: [this.selection.id] };\n }\n }\n\n if (this.selection.kind === 'multi') {\n return {\n components: Array.from(this.selection.components?.keys() ?? []),\n enodes: Array.from(this.selection.enodes?.keys() ?? []),\n wires: Array.from(this.selection.wires?.keys() ?? []),\n };\n }\n\n return { components: [], enodes: [], wires: [] };\n }\n\n /**\n * Register a callback for selection changes\n *\n * @param callback - Function to call when selection changes\n * @returns Unsubscribe function\n */\n onSelectionChange(callback: SelectionCallback): () => void {\n this.callbacks.add(callback);\n\n return () => {\n this.callbacks.delete(callback);\n };\n }\n\n /**\n * Notify all registered callbacks of selection change\n *\n * @param newSelection - New selection\n * @param previousSelection - Previous selection\n */\n private notifyCallbacks(\n newSelection: SelectionData | null,\n previousSelection: SelectionData | null\n ): void {\n for (const callback of this.callbacks) {\n try {\n callback(newSelection, previousSelection);\n } catch (error) {\n console.error('Selection callback error:', error);\n }\n }\n }\n\n /**\n * Clean up resources\n */\n dispose(): void {\n this.selection = null;\n this.selectedAt = null;\n this.callbacks.clear();\n }\n}\n","/**\n * CircuitWriter that centralizes all write operations on core circuit objects\n * @module scene/static/CircuitWriter\n */\nimport { Euler, Object3D, Vector3 } from 'three';\nimport type { ENodeSourceType, UUID, ENode, Wire, Component } from 'simple-circuit-engine/core';\nimport { Position, ComponentType, CameraOptions, Position3D } from 'simple-circuit-engine/core';\n\nimport type { CircuitController } from './CircuitController';\nimport type { ControllerEventMap } from '../shared/types';\nimport {\n computeDivisionsForSize,\n worldToGridPosition,\n worldToGridRotation,\n} from '../shared/utils/GeometryUtils';\n\n/**\n * Manages editing operations of 3D models from the circuit scene into the core circuit model.\n */\nexport class CircuitWriter {\n private _controller: CircuitController;\n\n constructor(controller: CircuitController) {\n this._controller = controller;\n }\n\n /**\n * add branching point to the core circuit model and emits the appropriate event.\n * @param position - the world position to add the branching point at\n * @param sourceType - optional source type for the branching point\n * @throws Error\n * @return The circuit enode\n */\n saveAddBranchingPoint(position: Vector3, sourceType?: ENodeSourceType | undefined) {\n const circuit = this._controller.getCircuit();\n try {\n if (!circuit) {\n throw new Error('No circuit available in the scene controller.');\n }\n\n const modelPosition = worldToGridPosition(position);\n const circuitEnode = circuit.addBranchingPoint(modelPosition, sourceType);\n\n const event: ControllerEventMap['circuitElementAction'] = {\n type: 'enode',\n action: 'add',\n id: circuitEnode.id,\n error: null,\n data: {\n position: modelPosition,\n sourceType: sourceType,\n },\n };\n this._controller.emit('circuitElementAction', event);\n return circuitEnode;\n } catch (error) {\n const event: ControllerEventMap['circuitElementAction'] = {\n type: 'enode',\n action: 'add',\n id: undefined,\n error: error as Error,\n data: null,\n };\n this._controller.emit('circuitElementAction', event);\n throw new Error((error as Error).message);\n }\n }\n\n /**\n * edit branching point to the core circuit model and emits the appropriate event.\n * @param branchingPoint - the branching point Object3D\n * @param emit - should the event be emitted if commit OK (error event will always be)\n * @throws Error\n * @return The circuit enode\n */\n saveEditBranchingPoint(branchingPoint: Object3D, emit: boolean = false) {\n const circuit = this._controller.getCircuit();\n try {\n if (!circuit) {\n throw new Error('No circuit available in the scene controller.');\n }\n const circuitEnode = circuit.getENode(branchingPoint.userData.enodeId);\n if (!circuitEnode) {\n throw new Error(\n `No enode with id ${branchingPoint.userData.enodeId} found in the circuit.`\n );\n }\n\n const modelPosition = worldToGridPosition(branchingPoint.position);\n const sourceType = branchingPoint.userData.sourceType as ENodeSourceType | undefined;\n circuitEnode.setPosition(modelPosition);\n circuitEnode.setSourceType(sourceType);\n\n if (emit) {\n this._controller.emit('circuitElementAction', {\n type: 'enode',\n action: 'edit',\n id: circuitEnode.id,\n error: null,\n data: {\n position: modelPosition,\n sourceType: sourceType,\n },\n });\n }\n return circuitEnode;\n } catch (error) {\n const event: ControllerEventMap['circuitElementAction'] = {\n type: 'enode',\n action: 'edit',\n id: branchingPoint.userData.id,\n error: error as Error,\n data: null,\n };\n this._controller.emit('circuitElementAction', event);\n throw new Error((error as Error).message);\n }\n }\n\n /**\n * delete branching point in the core circuit model and emits the appropriate event.\n * @param enodeId - the branching point id\n * @throws Error\n * @return The circuit enode\n */\n saveDeleteBranchingPoint(enodeId: UUID): {\n deletedWires?: UUID[] | undefined;\n mergedWires?: UUID[] | undefined;\n newWire?: Wire | undefined;\n } {\n const circuit = this._controller.getCircuit();\n try {\n if (!circuit) {\n throw new Error('No circuit available in the scene controller.');\n }\n const result = circuit.removeBranchingPoint(enodeId);\n\n const event: ControllerEventMap['circuitElementAction'] = {\n type: 'enode',\n action: 'delete',\n id: enodeId,\n error: null,\n data: {\n ...result,\n },\n };\n this._controller.emit('circuitElementAction', event);\n return result;\n } catch (error) {\n const event: ControllerEventMap['circuitElementAction'] = {\n type: 'enode',\n action: 'delete',\n id: enodeId,\n error: error as Error,\n data: null,\n };\n this._controller.emit('circuitElementAction', event);\n throw new Error((error as Error).message);\n }\n }\n\n /**\n * add wire to the core circuit model and emits the appropriate event.\n * @param sourceEnodeId - the source enode ID\n * @param targetEnodeId - the target enode ID\n * @throws Error\n * @return The circuit wire\n */\n saveAddWire(sourceEnodeId: UUID, targetEnodeId: UUID) {\n const circuit = this._controller.getCircuit();\n try {\n if (!circuit) {\n throw new Error('No circuit available in the scene controller.');\n }\n const addResult = circuit.addWire(sourceEnodeId, targetEnodeId);\n if (addResult instanceof Error) {\n throw new Error(addResult.message);\n }\n const wire = addResult as Wire;\n const event: ControllerEventMap['circuitElementAction'] = {\n type: 'wire',\n action: 'add',\n id: wire.id,\n error: null,\n data: {\n node1: wire.node1,\n node2: wire.node2,\n },\n };\n this._controller.emit('circuitElementAction', event);\n return wire;\n } catch (error) {\n const event: ControllerEventMap['circuitElementAction'] = {\n type: 'wire',\n action: 'add',\n id: undefined,\n error: error as Error,\n data: null,\n };\n this._controller.emit('circuitElementAction', event);\n throw new Error((error as Error).message);\n }\n }\n\n /**\n * Save wire split operation to circuit model. Either :\n * - at a position, inserting a new branching point and two new wires replacing the deleted ones\n * - at an existing target enode, replacing the wire by two new wires connected to this enode\n * @param wireId - Wire to split\n * @param worldPosition - world Position for the new branching point : no effect if targetEnodeId provided\n * @param targetEnodeId - if provided, the existing enode to split the wire at\n * @returns Object containing the new branching point and two wires\n */\n saveSplitWire(\n wireId: UUID,\n worldPosition: Vector3,\n targetEnodeId: UUID | null = null\n ): { branchingPoint: ENode; wires: Wire[] } {\n const circuit = this._controller.getCircuit();\n if (!circuit) {\n throw new Error('No circuit available in the scene controller.');\n }\n // Convert world position to grid position\n const gridPosition = worldToGridPosition(worldPosition);\n const result = circuit.splitWire(wireId, gridPosition, targetEnodeId);\n\n this._controller.emit('circuitElementAction', {\n type: 'wire',\n action: 'delete',\n id: wireId,\n });\n if (!targetEnodeId) {\n this._controller.emit('circuitElementAction', {\n type: 'enode',\n action: 'add',\n id: result.branchingPoint.id,\n });\n }\n for (const wire of result.wires) {\n this._controller.emit('circuitElementAction', {\n type: 'wire',\n action: 'add',\n id: wire.id,\n });\n }\n return result;\n }\n\n /**\n * delete wire to the core circuit model and emits the appropriate event.\n * @param wireId - the wire ID\n * @throws Error\n * @return The circuit wire\n */\n saveDeleteWire(wireId: UUID): void {\n const circuit = this._controller.getCircuit();\n try {\n if (!circuit) {\n throw new Error('No circuit available in the scene controller.');\n }\n circuit.removeWire(wireId);\n const event: ControllerEventMap['circuitElementAction'] = {\n type: 'wire',\n action: 'delete',\n id: wireId,\n error: null,\n data: null,\n };\n this._controller.emit('circuitElementAction', event);\n return;\n } catch (error) {\n const event: ControllerEventMap['circuitElementAction'] = {\n type: 'wire',\n action: 'delete',\n id: wireId,\n error: error as Error,\n data: null,\n };\n this._controller.emit('circuitElementAction', event);\n }\n }\n\n /**\n * Used to commit wire edits (intermediate positions) to the core circuit model and emits the appropriate event.\n * If emit positions are also simplified before committing.\n * @param wireId - the wire ID\n * @param positions\n * @param emit\n * @return The circuit wire\n */\n saveEditWirePositions(\n wireId: UUID,\n positions: Array<{ x: number; y: number }>,\n emit: boolean = false\n ): Wire | undefined {\n const circuit = this._controller.getCircuit();\n if (!circuit) return;\n const wire = circuit.getWire(wireId);\n if (!wire) return;\n\n const positionObjects = positions.map((p) => new Position(p.x, p.y));\n circuit.updateWireIntermediatePositions(wireId, positionObjects);\n\n if (emit) {\n circuit.simplifyWireIntermediatePositions(wireId);\n this._controller.emit('circuitElementAction', {\n type: 'wire',\n action: 'edit',\n id: wireId,\n error: null,\n data: {\n intermediatePositions: [...wire.intermediatePositions],\n },\n });\n }\n return wire;\n }\n\n /**\n * Used to simplify wire path in the core circuit model and emits the appropriate event.\n * Notably used when end enodes move is committed.\n * @param wireId - the wire ID\n * @return The circuit wire\n */\n saveSimplifyWirePositions(wireId: UUID): Wire | undefined {\n const circuit = this._controller.getCircuit();\n if (!circuit) return;\n const wire = circuit.getWire(wireId);\n if (!wire) return;\n\n circuit.simplifyWireIntermediatePositions(wireId);\n this._controller.emit('circuitElementAction', {\n type: 'wire',\n action: 'edit',\n id: wireId,\n error: null,\n data: {\n intermediatePositions: [...wire.intermediatePositions],\n },\n });\n return wire;\n }\n\n /**\n * Save ENode sourceType update (T011)\n * @param enodeId - ENode to update\n * @param sourceType - New sourceType value\n */\n saveEditENodeSourceType(enodeId: UUID, sourceType: ENodeSourceType | null): void {\n const circuit = this._controller.getCircuit();\n if (!circuit) {\n throw new Error('No circuit available in the scene controller.');\n }\n\n circuit.updateENodeSourceType(enodeId, sourceType);\n\n this._controller.emit('circuitElementAction', {\n type: 'enode',\n action: 'edit',\n id: enodeId,\n error: null,\n data: {\n sourceType: sourceType,\n },\n });\n }\n\n /**\n * Add a component to the circuit model and emit the appropriate event\n * handles conversion of world position and rotation to circuit model values\n * @param type - Component type to add\n * @param position - world Position\n * @param rotation - world Rotation\n * @param config - optional initial component configuration\n * @param pinSources - optional array of ENode source types for each component pin\n * @returns The created Component\n * @throws Error if circuit is not available or component creation fails\n */\n saveAddComponent(\n type: ComponentType,\n position: Vector3,\n rotation: Euler,\n config?: Map<string, string> | undefined,\n pinSources?: Array<ENodeSourceType | undefined | null> | undefined\n ): Component {\n const circuit = this._controller.getCircuit();\n try {\n if (!circuit) {\n throw new Error('No circuit available in the scene controller.');\n }\n // convert 3D world position to 2D grid position with Grid snapping\n const modelPosition = worldToGridPosition(position);\n const modelRotation = worldToGridRotation(rotation);\n const component = circuit.addComponent(type, modelPosition, modelRotation, config);\n\n if (pinSources) {\n for (const pinIdx in component.pins) {\n if (!pinSources[pinIdx]) continue;\n const customSource = pinSources[pinIdx];\n\n const cmpPinId = component.pins[pinIdx];\n if (!cmpPinId) continue;\n const pin = circuit.getENode(cmpPinId);\n if (!pin) continue;\n pin.setSourceType(customSource);\n }\n }\n\n const event: ControllerEventMap['circuitElementAction'] = {\n type: 'component',\n action: 'add',\n id: component.id,\n error: null,\n data: {\n componentId: component.id,\n componentType: type,\n position: modelPosition,\n rotation: modelRotation,\n config: config,\n pinSources: pinSources,\n },\n };\n this._controller.emit('circuitElementAction', event);\n return component;\n } catch (error) {\n const event: ControllerEventMap['circuitElementAction'] = {\n type: 'component',\n action: 'add',\n id: undefined,\n error: error as Error,\n data: null,\n };\n this._controller.emit('circuitElementAction', event);\n throw new Error((error as Error).message);\n }\n }\n\n /**\n * Save edits made to a component in the circuit model and emit the appropriate event\n * @param componentId\n * @param visual\n * @param emit - should the event be emitted if commit OK (error event will always be)\n */\n saveEditComponent(componentId: UUID, visual: Object3D, emit: boolean = false): Component {\n // Logic to save the current state of the scene component into the core model\n const circuit = this._controller.getCircuit();\n if (!circuit) {\n throw new Error('No circuit available in the scene controller.');\n }\n const component = circuit.getComponent(componentId);\n if (!component) {\n throw new Error(`Component with ID ${componentId} not found in the circuit.`);\n }\n\n const modelPosition = worldToGridPosition(visual.position);\n const modelRotation = worldToGridRotation(visual.rotation);\n component.setRotation(modelRotation);\n component.setPosition(modelPosition);\n\n if (emit) {\n this._controller.emit('circuitElementAction', {\n type: 'component',\n action: 'edit',\n id: componentId,\n error: null,\n data: {\n position: modelPosition,\n rotation: modelRotation,\n },\n });\n }\n\n return component;\n }\n\n /**\n * Save edits made to a component configuration in the circuit model and emit the appropriate event\n * @param componentId\n * @param parameters - updated configuration parameters\n */\n saveEditComponentConfig(componentId: UUID, parameters: Map<string, string>): Component {\n // Logic to save the current state of the scene component into the core model\n const circuit = this._controller.getCircuit();\n if (!circuit) {\n throw new Error('No circuit available in the scene controller.');\n }\n const component = circuit.getComponent(componentId);\n if (!component) {\n throw new Error(`Component with ID ${componentId} not found in the circuit.`);\n }\n\n component.config = new Map([...component.config, ...parameters]);\n circuit.resolveTransitionSpan(component);\n this._controller.emit('circuitElementAction', {\n type: 'component',\n action: 'edit',\n id: componentId,\n error: null,\n data: {\n config: component.config,\n },\n });\n return component;\n }\n\n /**\n * cycle component config and update visuals if necessary\n * have effect only on components that supports fast config cycle (used to invert logic or initial state of switches)\n * if component supports it, update the supported config item to the next value in the cycle\n * @param componentId\n * @returns object with hasChanged boolean and updated component\n * @throws Error if circuit is not available or component not found\n */\n cycleComponentConfig(componentId: UUID): { hasChanged: boolean; component: Component } {\n // Logic to save the current state of the scene component into the core model\n const circuit = this._controller.getCircuit();\n if (!circuit) {\n throw new Error('No circuit available in the scene controller.');\n }\n const component = circuit.getComponent(componentId);\n if (!component) {\n throw new Error(`Component with ID ${componentId} not found in the circuit.`);\n }\n const config = component.config;\n switch (component.type) {\n case ComponentType.Switch:\n config.set('initialState', config.get('initialState') === 'open' ? 'closed' : 'open');\n this.saveEditComponentConfig(component.id, config);\n return { hasChanged: true, component: component };\n case ComponentType.DoubleThrowSwitch:\n config.set('initialState', config.get('initialState') === 'input1' ? 'input2' : 'input1');\n this.saveEditComponentConfig(component.id, config);\n return { hasChanged: true, component: component };\n case ComponentType.Clock:\n config.set('startHigh', config.get('startHigh') == 'true' ? 'false' : 'true');\n this.saveEditComponentConfig(component.id, config);\n return { hasChanged: true, component: component };\n default:\n if (!config.has('activationLogic')) {\n return { hasChanged: false, component: component };\n }\n config.set(\n 'activationLogic',\n config.get('activationLogic') === 'positive' ? 'negative' : 'positive'\n );\n circuit.resolveTransitionSpan(component);\n this.saveEditComponentConfig(component.id, config);\n return { hasChanged: true, component: component };\n }\n }\n\n /**\n * Delete a component from the circuit model and emit the appropriate event\n * @param componentId - UUID of the component to delete\n * @returns Information about removed wires and enodes\n * @throws Error if circuit is not available or component not found\n */\n saveDeleteComponent(componentId: UUID): {\n deletedWires: UUID[];\n deletedENodes: UUID[];\n } {\n const circuit = this._controller.getCircuit();\n try {\n if (!circuit) {\n throw new Error('No circuit available in the scene controller.');\n }\n\n const component = circuit.getComponent(componentId);\n if (!component) {\n throw new Error(`Component with ID ${componentId} not found in the circuit.`);\n }\n\n // Remove component from circuit (this will also remove connected wires)\n const result: { deletedWires: UUID[]; deletedENodes: UUID[] } =\n circuit.removeComponent(componentId);\n\n const event: ControllerEventMap['circuitElementAction'] = {\n type: 'component',\n action: 'delete',\n id: componentId,\n error: null,\n data: { ...result },\n };\n this._controller.emit('circuitElementAction', event);\n\n return result;\n } catch (error) {\n const event: ControllerEventMap['circuitElementAction'] = {\n type: 'component',\n action: 'delete',\n id: componentId,\n error: error as Error,\n data: null,\n };\n this._controller.emit('circuitElementAction', event);\n throw new Error((error as Error).message);\n }\n }\n\n /**\n * Automatically adjust the circuit size and divisions based on positions of all core circuit elements.\n * @return if the size/division has been updated or not\n */\n saveAutoAdjustCircuitSize(): boolean {\n const circuit = this._controller.getCircuit();\n if (!circuit) {\n throw new Error('No circuit available in the scene controller.');\n }\n\n const newSize = Math.max(10, circuit.getEnclosingSize(1));\n if (circuit.metadata.size === newSize) {\n return false; // no change\n }\n\n const divisions = computeDivisionsForSize(newSize);\n\n circuit.metadata.size = newSize;\n circuit.metadata.divisions = divisions;\n\n this._controller.emit('circuitMetadataEdition', {\n circuitName: circuit.name,\n data: {\n size: newSize,\n divisions: divisions,\n },\n });\n return true;\n }\n\n /**\n * Save current camera parameters, position and target into circuit metadata\n */\n saveCameraOptions(): void {\n const circuit = this._controller.getCircuit();\n if (!circuit) {\n throw new Error('No circuit available in the scene controller.');\n }\n const camera = this._controller.getCamera();\n if (!camera) {\n throw new Error('No camera available in the scene controller.');\n }\n const controls = this._controller.getControls();\n if (!controls) {\n throw new Error('No controls available in the scene controller.');\n }\n\n const options = new CameraOptions(\n new Position3D(camera.position.x, camera.position.y, camera.position.z),\n new Position3D(controls.target.x, controls.target.y, controls.target.z),\n camera.fov,\n camera.near,\n camera.far\n );\n circuit.metadata.cameraOptions = options;\n\n this._controller.emit('circuitMetadataEdition', {\n circuitName: circuit.name,\n data: {\n cameraOptions: options,\n },\n });\n return;\n }\n}\n","/**\n * Camera Utilities\n * @module scene/shared/utils/CameraUtils\n *\n * Helper functions for camera setup and management\n */\n\nimport * as THREE from 'three';\nimport { CameraOptions } from 'simple-circuit-engine/core';\n\n/**\n * Create a perspective camera with default or custom parameters\n *\n * @param options -\n * @param aspect - Camera aspect ratio (width / height)\n * @returns Configured PerspectiveCamera\n */\nexport function createPerspectiveCamera(\n aspect: number = 1,\n options: CameraOptions = new CameraOptions()\n): THREE.PerspectiveCamera {\n const camera = new THREE.PerspectiveCamera(options.fov, aspect, options.near, options.far);\n // Default camera position for circuit viewing\n const camPos = options.position;\n camera.position.set(camPos.x, camPos.y, camPos.z);\n const camTarget = options.lookAtPosition;\n camera.lookAt(camTarget.x, camTarget.y, camTarget.z);\n return camera;\n}\n\n/**\n * update camera position and target from options\n *\n * @param camera - Camera to configure\n * @param options\n */\nexport function updateCamera(camera: THREE.PerspectiveCamera, options: CameraOptions): void {\n camera.fov = options.fov;\n camera.near = options.near;\n camera.far = options.far;\n\n const camPos = options.position;\n camera.position.set(camPos.x, camPos.y, camPos.z);\n // NB : if controls are used, they may override the lookAt\n // then you need to update their controls separately\n const camTarget = options.lookAtPosition;\n camera.lookAt(camTarget.x, camTarget.y, camTarget.z);\n camera.updateProjectionMatrix();\n}\n","/**\n * Lighting Utilities\n * @module scene/shared/utils/LightingUtils\n *\n * Helper functions for setting up scene lighting\n */\n\nimport * as THREE from 'three';\n\n/**\n * Create an ambient light with default intensity\n *\n * @param color - Light color (hex)\n * @param intensity - Light intensity (0-1)\n * @returns AmbientLight\n */\nexport function createAmbientLight(\n color: number = 0xffffff,\n intensity: number = 0.6\n): THREE.AmbientLight {\n return new THREE.AmbientLight(color, intensity);\n}\n\n/**\n * Create a directional light with default positioning\n *\n * @param color - Light color (hex)\n * @param intensity - Light intensity\n * @param position - Light position\n * @returns DirectionalLight\n */\nexport function createDirectionalLight(\n color: number = 0xffffff,\n intensity: number = 0.8,\n position: THREE.Vector3 = new THREE.Vector3(10, 20, 10)\n): THREE.DirectionalLight {\n const light = new THREE.DirectionalLight(color, intensity);\n light.position.copy(position);\n return light;\n}\n\n/**\n * Setup standard scene lighting (ambient + directional)\n *\n * @param scene - Scene to add lights to\n * @returns Array of created lights\n */\nexport function setupSceneLights(scene: THREE.Scene): THREE.Light[] {\n const lights: THREE.Light[] = [];\n\n // Ambient light for base illumination\n const ambient = createAmbientLight(0xffffff, 0.6);\n scene.add(ambient);\n lights.push(ambient);\n\n // Main directional light from above\n const main = createDirectionalLight(0xffffff, 0.8, new THREE.Vector3(10, 20, 10));\n scene.add(main);\n lights.push(main);\n\n // Fill light from opposite side\n const fill = createDirectionalLight(0xffffff, 0.3, new THREE.Vector3(-10, 15, -10));\n scene.add(fill);\n lights.push(fill);\n\n return lights;\n}\n","/**\n * Three.js Layer Constants for Hitbox Organization\n * @module scene/shared/utils/LayerConstants\n *\n * Defines layer assignments for priority-based raycasting and rendering.\n */\n\n/**\n * Three.js layer assignments for hitbox meshes and rendering\n *\n * Layers enable priority-based raycasting by querying layers sequentially.\n * Lower layer numbers = higher priority for hover detection.\n *\n * @remarks\n * - Layer 0 is reserved for default visual rendering\n * - Hitbox layers (1-3) are invisible but raycastable\n * - Camera.layers should include only layer 0 for rendering\n * - Raycaster.layers is set per-query based on priority\n *\n * @example\n * ```typescript\n * import { HitboxLayers } from './LayerConstants';\n *\n * // Assign hitbox to component layer\n * hitboxMesh.layers.set(HitboxLayers.COMPONENT);\n *\n * // Raycast only enode hitboxes\n * raycaster.layers.set(HitboxLayers.ENODE);\n * ```\n */\nexport const HitboxLayers = {\n /** Default layer for visual rendering (do not use for hitboxes) */\n DEFAULT: 0,\n /** Enode hitboxes - highest hover priority */\n ENODE: 1,\n /** Component hitboxes - medium hover priority */\n COMPONENT: 2,\n /** Wire hitboxes - lowest hover priority */\n WIRE: 3,\n} as const;\n\nexport type HitboxLayerValue = (typeof HitboxLayers)[keyof typeof HitboxLayers];\n","/**\n * HoverManager Implementation\n * @module scene/shared/HoverManager\n *\n * Handles priority-based hover detection using Three.js Raycaster and Layers.\n * Implements priority: enode > component > wire\n */\n\nimport * as THREE from 'three';\nimport type { HoveredElement, HitboxUserData } from './types';\nimport { HitboxLayers } from './utils/LayerConstants';\n\n/**\n * Callback type for hover state changes\n */\nexport type HoverCallback = (\n element: HoveredElement | null,\n previousElement: HoveredElement | null\n) => void;\n\n/**\n * HoverManager Class\n *\n * Manages hover detection using Three.js Raycaster against hitbox layers.\n * Implements priority-based detection: enode > component > wire.\n */\nexport class HoverManager {\n private scene: THREE.Scene;\n private camera: THREE.Camera;\n private raycaster: THREE.Raycaster;\n private readonly groundPlane: THREE.Plane;\n private readonly groundPlanePosition = new THREE.Vector3();\n private currentlyHovered: HoveredElement | null = null;\n private callbacks: Set<HoverCallback> = new Set();\n private enabled: boolean = true;\n private initialized: boolean = false;\n private lastMouseX: number = 0;\n private lastMouseY: number = 0;\n private lastUpdateTime: number = 0;\n private throttleMs: number = 8; // ~120fps max update rate (performance optimization)\n\n /**\n * Create a new HoverManager\n *\n * @param scene - Three.js scene containing hitbox meshes\n * @param camera - Three.js camera for raycasting\n */\n constructor(scene: THREE.Scene, camera: THREE.Camera) {\n this.scene = scene;\n this.camera = camera;\n this.raycaster = new THREE.Raycaster();\n // Set up raycaster with Line2 threshold (in screen pixels)\n this.raycaster.params.Line2 = { threshold: 10 }; // 10px hover zone around line\n\n this.groundPlane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); // XZ plane at y=0\n }\n\n /**\n * Get the last computed ground plane position under the mouse\n *\n * @returns THREE.Vector3 position on ground plane\n */\n getGroundPlanePosition(): THREE.Vector3 {\n return this.groundPlanePosition;\n }\n\n /**\n * Update hover state based on normalized mouse coordinates\n * Also update the VERY IMPORTANT ground plane position used for CircuitManager cursorGroundPlanePosition\n *\n * Performs priority-based raycasting against hitbox layers.\n * If hover state changes, triggers onHoverChange callback.\n *\n * @param normalizedX - Mouse X in normalized HTML container coordinates [-1, 1]\n * @param normalizedY - Mouse Y in normalized HTML container coordinates [-1, 1]\n */\n updateFromMouse(normalizedX: number, normalizedY: number): void {\n if (!this.enabled) {\n return;\n }\n\n // Performance optimization: Throttle updates to max ~120fps (T033)\n const now = performance.now();\n if (now - this.lastUpdateTime < this.throttleMs) {\n return;\n }\n this.lastUpdateTime = now;\n\n // Store mouse position for refresh()\n this.lastMouseX = normalizedX;\n this.lastMouseY = normalizedY;\n\n // Setup raycaster\n this.raycaster.setFromCamera(new THREE.Vector2(normalizedX, normalizedY), this.camera);\n\n // VERY IMPORTANT: Update ground plane position so that controller cursorGroundPlanePosition queries work correctly\n this.raycaster.ray.intersectPlane(this.groundPlane, this.groundPlanePosition);\n\n // Try to find a hit in priority order: ENODE > COMPONENT > WIRE\n let hitElement: HoveredElement | null = null;\n\n // Priority 1: Check ENODE layer\n hitElement = this._raycastLayer(HitboxLayers.ENODE, 'enode', 'enodeHitbox');\n // disambiguation for very closed components (transistors)\n // if enode is a component pin, we check if the mouse is actually over the component hitbox too\n // if they differ we prioritize the component hitbox\n if (hitElement && hitElement.object3D.userData.componentId) {\n const componentHit = this._raycastLayer(\n HitboxLayers.COMPONENT,\n 'component',\n 'componentHitbox'\n );\n if (componentHit && componentHit.id !== hitElement.object3D.userData.componentId) {\n hitElement = componentHit;\n }\n }\n\n // Priority 2: Check COMPONENT layer\n if (!hitElement) {\n hitElement = this._raycastLayer(HitboxLayers.COMPONENT, 'component', 'componentHitbox');\n }\n\n // Priority 3: Check WIRE layer if no component hit\n if (!hitElement) {\n hitElement = this._raycastLayer(HitboxLayers.WIRE, 'wire', 'wire');\n }\n // Compare with current state and trigger callbacks if changed\n this._updateHoverState(hitElement);\n }\n\n /**\n * Force update hover state at current mouse position\n *\n * Useful after camera changes or scene updates to refresh hover state\n * without requiring a new mouse event.\n */\n refresh(): void {\n if (this.enabled) {\n this.updateFromMouse(this.lastMouseX, this.lastMouseY);\n }\n }\n\n /**\n * Clear current hover state\n *\n * Triggers unhover callback if an element was hovered.\n * Call this when mouse leaves the container.\n */\n clear(): void {\n this._updateHoverState(null);\n }\n\n /**\n * Get the currently hovered element\n *\n * @returns HoveredElement if something is hovered, null otherwise\n */\n getHoveredElement(): HoveredElement | null {\n return this.currentlyHovered;\n }\n\n /**\n * Register callback for hover state changes\n *\n * Callback is invoked when:\n * - Hover starts (element becomes non-null)\n * - Hover changes to different element\n * - Hover ends (element becomes null)\n *\n * @param callback - Function to call on hover state change\n */\n onHoverChange(callback: HoverCallback): void {\n this.callbacks.add(callback);\n }\n\n /**\n * Remove previously registered hover change callback\n *\n * @param callback - Same function reference passed to onHoverChange\n */\n offHoverChange(callback: HoverCallback): void {\n this.callbacks.delete(callback);\n }\n\n /**\n * Enable or disable hover detection\n *\n * When disabled, updateFromMouse() becomes a no-op.\n * Useful for temporarily disabling hover during drag operations.\n *\n * @param enabled - Whether to enable hover detection\n */\n setEnabled(enabled: boolean): void {\n this.enabled = enabled;\n if (!enabled) {\n // Clear hover state when disabling\n this.clear();\n }\n }\n\n /**\n * Check if hover detection is enabled\n *\n * @returns true if enabled\n */\n isEnabled(): boolean {\n return this.enabled;\n }\n\n /**\n * Set initialization state (to prevent double init)\n * @param initialized\n */\n setInitialized(initialized: boolean): void {\n this.initialized = initialized;\n }\n\n /**\n * Check if HoverManager is initialized (to prevent double init)\n */\n isInitialized(): boolean {\n return this.initialized;\n }\n\n /**\n * Clean up resources\n *\n * Removes all callbacks and clears state.\n * Call when disposing the scene controllerType.\n */\n dispose(): void {\n this.clear();\n this.callbacks.clear();\n this.initialized = false;\n }\n\n // ==========================================\n // Private Helper Methods\n // ==========================================\n\n /**\n * Perform raycasting on a specific layer and extract hit information\n *\n * @param layer - Hitbox layer number to raycast against\n * @param hoverableType - Type for HoveredElement\n * @param objectType - CircuitSceneObjectType for event payload\n * @returns HoveredElement if hit found, null otherwise\n */\n private _raycastLayer(\n layer: number,\n hoverableType: 'enode' | 'component' | 'wire',\n objectType: 'enodeHitbox' | 'componentHitbox' | 'wire'\n ): HoveredElement | null {\n // Configure raycaster to only check the specified layer\n this.raycaster.layers.set(layer);\n\n // Perform raycast (recursive to check all children)\n const intersections = this.raycaster.intersectObjects(this.scene.children, true);\n\n // Find first valid hit with proper userData\n for (const intersection of intersections) {\n const obj = intersection.object;\n const userData = obj.userData as HitboxUserData;\n\n // Validate userData has correct type\n if (userData && userData.type === objectType) {\n if (userData.hasOwnProperty('preview')) {\n continue; // Ignore preview hitboxes\n }\n // Extract element ID based on hitbox type\n let elementId: string;\n if (userData.type === 'enodeHitbox') {\n // For enode hitboxes, use componentId (the component that owns the pin)\n elementId = userData.enodeId;\n } else if (userData.type === 'componentHitbox') {\n elementId = userData.componentId;\n } else if (userData.type === 'wire') {\n elementId = userData.wireId;\n } else {\n continue;\n }\n\n return {\n id: elementId,\n type: hoverableType,\n objectType: objectType,\n object3D: obj,\n };\n }\n }\n\n return null;\n }\n\n /**\n * Update hover state and trigger callbacks if changed\n *\n * Compares new hit with currentlyHovered and only triggers callbacks on change.\n *\n * @param newHit - New hover element or null\n */\n private _updateHoverState(newHit: HoveredElement | null): void {\n // Check if state has changed\n const hasChanged = !this._isSameHover(this.currentlyHovered, newHit);\n\n if (hasChanged) {\n const previousHit = this.currentlyHovered;\n this.currentlyHovered = newHit;\n\n // Trigger all registered callbacks\n for (const callback of this.callbacks) {\n callback(newHit, previousHit);\n }\n }\n }\n\n /**\n * Compare two hover elements for equality\n *\n * @param a - First element\n * @param b - Second element\n * @returns true if both represent the same hover state\n */\n private _isSameHover(a: HoveredElement | null, b: HoveredElement | null): boolean {\n // Both null = same\n if (a === null && b === null) {\n return true;\n }\n\n // One null, one not = different\n if (a === null || b === null) {\n return false;\n }\n\n // Compare id and type\n return a.id === b.id && a.type === b.type;\n }\n}\n","/**\n * Branching Point Visual Factory\n * @module scene/shared/components/BranchingPointVisualFactory\n *\n * Creates cone-shaped visuals for branching point enodes with:\n * - SourceType-based color coding (white/red/blue)\n * - Hover/selection feedback via brightness shift\n * - Hitbox for raycasting on ENODE layer\n */\n\nimport * as THREE from 'three';\nimport { type ENodeSourceType, type ENode, ENodeType } from 'simple-circuit-engine/core';\nimport { HitboxLayers } from './utils/LayerConstants';\n\n/**\n * Factory for creating branching point visuals.\n *\n * Branching points are rendered as cones with colors indicating their sourceType:\n * - White (0xffffff): No source (null)\n * - Red (0xff0000): Voltage source\n * - Blue (0x0000ff): Current source\n *\n * Hover and selection states use brightness shift of the base color.\n */\nexport class BranchingPointVisualFactory {\n // Color constants\n private static readonly COLORS = {\n null: 0xffffff, // white - no source\n Voltage: 0xff0000, // red - voltage source\n Current: 0x0000ff, // blue - current source\n };\n\n private static readonly DEFAULT_HOVER_COLOR = 0x4488ff; // Yellow for hitbox hover feedback\n private static readonly HOVER_EMISSIVE = 0x4488ff; // Slight brightening on hover\n private static readonly SELECTED_EMISSIVE = 0xff8800; // More brightening on selection\n\n // Cone geometry dimensions\n private static readonly CONE_RADIUS = 0.3;\n private static readonly CONE_HEIGHT = 0.6;\n private static readonly CONE_SEGMENTS = 16;\n\n // Hitbox square (larger than visual for easier clicking)\n private static readonly HITBOX_SQUARE = 1;\n\n /**\n * Create visual representation for a branching point.\n * @param enode - The branching point ENode\n * @returns THREE.Group containing cone mesh and hitbox\n */\n createVisual(enode: ENode): THREE.Group {\n const group = new THREE.Group();\n group.name = ENodeType.BranchingPoint;\n // Set userData for raycasting identification\n group.userData = {\n type: 'enodeGroup',\n componentId: null,\n enodeId: enode.id,\n label: ENodeType.BranchingPoint,\n };\n\n // Hitbox (box, raycastable)\n const hitboxGeom = new THREE.BoxGeometry(\n BranchingPointVisualFactory.HITBOX_SQUARE,\n BranchingPointVisualFactory.HITBOX_SQUARE,\n BranchingPointVisualFactory.HITBOX_SQUARE\n );\n const hitbox = new THREE.Mesh(\n hitboxGeom,\n new THREE.MeshStandardMaterial({\n color: BranchingPointVisualFactory.DEFAULT_HOVER_COLOR,\n transparent: true,\n opacity: 0,\n })\n );\n hitbox.userData = {\n type: 'enodeHitbox',\n componentId: null,\n enodeId: enode.id,\n label: ENodeType.BranchingPoint,\n };\n hitbox.layers.set(HitboxLayers.ENODE);\n group.add(hitbox);\n\n // Create cone geometry\n const coneGeometry = new THREE.ConeGeometry(\n BranchingPointVisualFactory.CONE_RADIUS,\n BranchingPointVisualFactory.CONE_HEIGHT,\n BranchingPointVisualFactory.CONE_SEGMENTS\n );\n // Get color based on sourceType\n const color = this.getColorForSourceType(enode.source);\n // Create material with base color\n const coneMaterial = new THREE.MeshStandardMaterial({\n color,\n emissive: 0x000000,\n metalness: 0.3,\n roughness: 0.7,\n });\n\n // Create visual mesh\n const visual = new THREE.Mesh(coneGeometry, coneMaterial);\n visual.userData = {\n type: 'enode',\n componentId: null,\n enodeId: enode.id,\n label: ENodeType.BranchingPoint,\n };\n visual.position.set(0, 0.1, 0); // increase the height a little\n group.add(visual);\n\n return group;\n }\n\n /**\n * Update branching point object3D's visual to reflect sourceType change.\n * @param object3D - The branching point basis object3D (group)\n * @param sourceType - New source type\n */\n updateSourceType(object3D: THREE.Object3D, sourceType: ENodeSourceType | null): void {\n object3D.userData.sourceType = sourceType;\n const visual = object3D.children.find((child) => child.userData.type === 'enode') as\n | THREE.Mesh\n | undefined;\n\n if (visual && visual.material instanceof THREE.MeshStandardMaterial) {\n const newColor = this.getColorForSourceType(sourceType);\n visual.material.color.setHex(newColor);\n }\n }\n\n protected colorForElectricalState(state: 'current' | 'voltage' | 'vc' | 'idle'): number {\n switch (state) {\n case 'voltage':\n return 0xff0000; // Red\n case 'current':\n return 0x0000ff; // Blue\n case 'vc':\n return 0xcc00cc; // Magenta\n case 'idle':\n default:\n return 0x000000;\n }\n }\n\n /**\n * Apply hover object3D feedback.\n * @param object3D - The branching point basis object3D (group)\n */\n applyHover(object3D: THREE.Object3D): void {\n if (object3D.userData.isSelected) {\n // object is selected; skip hover visual\n return;\n }\n\n const visual = object3D.children.find((child) => child.userData.type === 'enode') as\n | THREE.Mesh\n | undefined;\n\n if (visual && visual.material instanceof THREE.MeshStandardMaterial) {\n visual.material.emissive.setHex(BranchingPointVisualFactory.HOVER_EMISSIVE);\n }\n }\n\n /**\n * Remove hover object3D feedback.\n * @param object3D - The branching point basis object3D (group)\n */\n removeHover(object3D: THREE.Object3D): void {\n if (object3D.userData.isSelected) {\n // object is selected; skip hover visual\n return;\n }\n\n let fallbackEmissive = 0x000000;\n if (object3D.userData.electricalState) {\n fallbackEmissive = this.colorForElectricalState(object3D.userData.electricalState);\n }\n\n const visual = object3D.children.find((child) => child.userData.type === 'enode') as\n | THREE.Mesh\n | undefined;\n\n if (visual && visual.material instanceof THREE.MeshStandardMaterial) {\n visual.material.emissive.setHex(fallbackEmissive);\n }\n }\n\n /**\n * Apply selection object3D feedback.\n * @param object3D - The branching point basis object3D (group)\n */\n applySelection(object3D: THREE.Object3D): void {\n const visual = object3D.children.find((child) => child.userData.type === 'enode') as\n | THREE.Mesh\n | undefined;\n\n if (visual && visual.material instanceof THREE.MeshStandardMaterial) {\n visual.material.emissive.setHex(BranchingPointVisualFactory.SELECTED_EMISSIVE);\n }\n object3D.userData.isSelected = true;\n }\n\n /**\n * Remove selection object3D feedback.\n * @param object3D - The branching point basis object3D (group)\n */\n removeSelection(object3D: THREE.Object3D): void {\n const visual = object3D.children.find((child) => child.userData.type === 'enode') as\n | THREE.Mesh\n | undefined;\n\n if (visual && visual.material instanceof THREE.MeshStandardMaterial) {\n visual.material.emissive.setHex(0x000000);\n }\n object3D.userData.isSelected = false;\n }\n\n /**\n * Get the base color for a sourceType.\n * @param sourceType - Source type (null, 'Voltage', or 'Current')\n * @returns Color hex value\n */\n private getColorForSourceType(sourceType: ENodeSourceType | null | undefined): number {\n if (!sourceType) {\n return BranchingPointVisualFactory.COLORS.null;\n }\n return BranchingPointVisualFactory.COLORS[sourceType];\n }\n}\n","/**\n * Material Utilities\n * @module scene/shared/utils/MaterialUtils\n *\n * Helper functions for creating and managing Three.js materials\n */\n\nimport * as THREE from 'three';\nimport { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js';\n\n/**\n * Create a standard material with common defaults\n *\n * @param color - Base color (hex)\n * @param options - Additional material options\n * @returns MeshStandardMaterial\n */\nexport function createStandardMaterial(\n color: number,\n options: {\n emissive?: number;\n emissiveIntensity?: number;\n metalness?: number;\n roughness?: number;\n transparent?: boolean;\n opacity?: number;\n } = {}\n): THREE.MeshStandardMaterial {\n return new THREE.MeshStandardMaterial({\n color,\n emissive: options.emissive ?? 0x000000,\n emissiveIntensity: options.emissiveIntensity ?? 0,\n metalness: options.metalness ?? 0.3,\n roughness: options.roughness ?? 0.7,\n transparent: options.transparent ?? false,\n opacity: options.opacity ?? 1,\n });\n}\n\n/**\n * Create a material for wire lines\n *\n * @param color - Line color (hex)\n * @param linewidth - Line width (note: may not work on all platforms)\n * @returns LineBasicMaterial\n */\nexport function createLineMaterial(\n color: number = 0xffffff,\n linewidth: number = 1\n): THREE.LineBasicMaterial {\n return new THREE.LineBasicMaterial({\n color,\n linewidth,\n });\n}\n\n/**\n * Create a LineMaterial for Line2 rendering with consistent line width\n *\n * Note: Resolution must be set after creation using material.resolution.set(width, height)\n *\n * @param color - Line color (hex, default: 0xffffff/white)\n * @param linewidth - Line width in pixels (default: 2)\n * @returns LineMaterial for Line2 objects\n *\n * @example\n * ```typescript\n * const material = createLine2Material(0xffffff, 2);\n * material.resolution.set(window.innerWidth, window.innerHeight);\n * ```\n */\nexport function createLine2Material(color: number = 0xffffff, linewidth: number = 2): LineMaterial {\n return new LineMaterial({\n color,\n linewidth,\n });\n}\n\n/**\n * Update material state for component state changes\n *\n * @param material - Material to update\n * @param state - Component state data\n */\nexport function updateMaterialState(\n material: THREE.MeshStandardMaterial,\n state: {\n isActive?: boolean;\n isHighlighted?: boolean;\n customColor?: number;\n emissiveIntensity?: number;\n }\n): void {\n if (state.customColor !== undefined) {\n material.color.setHex(state.customColor);\n }\n\n if (state.isHighlighted) {\n material.emissive.setHex(0x00ff00);\n material.emissiveIntensity = 0.3;\n } else if (state.isActive) {\n material.emissive.setHex(material.color.getHex());\n material.emissiveIntensity = state.emissiveIntensity ?? 0.5;\n } else {\n material.emissive.setHex(0x000000);\n material.emissiveIntensity = 0;\n }\n}\n\n/**\n * Create a semi-transparent preview material\n *\n * @param baseColor - Base color\n * @param opacity - Transparency level (0-1)\n * @returns MeshStandardMaterial configured for preview\n */\nexport function createPreviewMaterial(\n baseColor: number,\n opacity: number = 0.5\n): THREE.MeshStandardMaterial {\n return new THREE.MeshStandardMaterial({\n color: baseColor,\n transparent: true,\n opacity,\n emissive: baseColor,\n emissiveIntensity: 0.2,\n });\n}\n\n/**\n * Create an error state material (for validation feedback)\n *\n * @returns MeshStandardMaterial in error state (red tint)\n */\nexport function createErrorMaterial(): THREE.MeshStandardMaterial {\n return new THREE.MeshStandardMaterial({\n color: 0xff0000,\n transparent: true,\n opacity: 0.6,\n emissive: 0xff0000,\n emissiveIntensity: 0.5,\n });\n}\n","/**\n * Wire Visual Manager\n * @module scene/shared/WireVisualManager\n *\n * Manages wire visual rendering with:\n * - Pin-accurate endpoints (derived from component visuals)\n * - Multi-segment rendering via intermediate positions\n * - Dynamic updates during component movement\n */\n\nimport * as THREE from 'three';\nimport { Line2 } from 'three/examples/jsm/lines/Line2.js';\nimport { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js';\nimport { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js';\nimport type { UUID, Wire, Circuit } from 'simple-circuit-engine/core';\nimport { ENodeType } from 'simple-circuit-engine/core';\n\nimport { createLine2Material } from './utils/MaterialUtils';\nimport type { WireMaterialState } from './types';\nimport { HitboxLayers } from './utils/LayerConstants';\nimport { gridToWorldPosition } from './utils/GeometryUtils';\n\n/**\n * Wire path representation for rendering\n */\nexport interface WirePath {\n /** Wire ID */\n wireId: UUID;\n\n /** Ordered points in world space (Three.js coordinates) */\n points: THREE.Vector3[];\n}\n\n/**\n * Delegation of CircuitController which handles wire visual rendering with proper pin targeting and dynamic updates.\n *\n * Key responsibilities:\n * - Create wire visuals with endpoints at actual pin positions\n * - Support multi-segment wires via intermediatePositions\n * - Update wires dynamically when components move/rotate\n * - Update wire materials for hover/selection states\n * - may add and remove wire and branching point object3Ds in the scene directly\n *\n * @example\n * ```typescript\n * const wireManager = new WireVisualManager();\n *\n * // Create wire visual\n * const line = wireManager.createOrUpdateWire(wire, circuit, scene, componentGroups);\n *\n * // Update wires when component moves\n * wireManager.updateWiresForComponent(componentId, circuit, componentGroups);\n * ```\n */\nexport class WireVisualManager {\n private containerWidth: number = 500;\n private containerHeight: number = 500;\n\n private _scene: THREE.Scene | null = null;\n private _camera: THREE.Camera | null = null;\n private _componentObject3Ds: Map<UUID, THREE.Object3D>;\n private _wireObject3Ds: Map<UUID, Line2>;\n private _container: HTMLElement | null = null;\n private _circuit: Circuit | null = null;\n\n //private _controller: AbstractCircuitController;\n /** Shared LineMaterials for all wires (memory efficient, consistent styling) */\n private wireMaterials: Map<WireMaterialState, LineMaterial> = new Map();\n /** Preview wire for wire creation mode */\n private previewWire: Line2 | null = null;\n\n constructor(componentObject3Ds: Map<UUID, THREE.Object3D>, wireObject3Ds: Map<UUID, Line2>) {\n this._componentObject3Ds = componentObject3Ds;\n this._wireObject3Ds = wireObject3Ds;\n // Create shared LineMaterial with default white color and 2px width\n this.wireMaterials = new Map([\n ['idle', createLine2Material(0xffffff, 2)],\n ['hovered', createLine2Material(0x40dfff, 4)],\n ['selected', createLine2Material(0xffaa00, 3)],\n ['voltage', createLine2Material(0xff0000, 3)], // Red for voltage only\n ['current', createLine2Material(0x0000ff, 3)], // Blue for current only\n ['vc', createLine2Material(0xcc00cc, 4)], // Magenta for both voltage and current\n ]);\n }\n\n setContainer(container: HTMLElement | null): void {\n this._container = container;\n }\n\n setSceneAndCamera(scene: THREE.Scene, camera: THREE.Camera): void {\n this._scene = scene;\n this._camera = camera;\n }\n\n setCircuit(circuit: Circuit | null): void {\n this._circuit = circuit;\n }\n\n /**\n * Set the resolution for LineMaterial rendering\n *\n * MUST be called after initialization and on window/container resize\n * for Line2 to render correctly.\n *\n * @param width - Viewport width in pixels\n * @param height - Viewport height in pixels\n *\n * @example\n * ```typescript\n * wireManager.setResolution(window.innerWidth, window.innerHeight);\n *\n * window.addEventListener('resize', () => {\n * wireManager.setResolution(window.innerWidth, window.innerHeight);\n * });\n * ```\n */\n setResolution(width: number, height: number): void {\n this.containerWidth = width;\n this.containerHeight = height;\n for (const material of this.wireMaterials.values()) {\n material.resolution.set(width, height);\n }\n }\n\n /**\n * Get the Line2 object for a wire\n *\n * @param wireId - Wire ID\n * @returns Line2 or undefined if not found or disposed\n */\n getWireLine(wireId: UUID): Line2 | undefined {\n return this._wireObject3Ds?.get(wireId);\n }\n\n /**\n * Check if a wire visual exists\n *\n * @param wireId - Wire ID\n * @returns true if wire visual exists, false if not found or disposed\n */\n hasWire(wireId: UUID): boolean {\n return this._wireObject3Ds?.has(wireId) ?? false;\n }\n\n /**\n * Get all wire IDs managed by this controllerType\n *\n * @returns Array of wire UUIDs, empty if disposed\n */\n getWireIds(): UUID[] {\n if (!this._wireObject3Ds) {\n return [];\n }\n return Array.from(this._wireObject3Ds.keys());\n }\n\n /**\n * Create or update the visual for a wire using model wire positions\n *\n * @param wire - Wire to render\n * @returns The created/updated Line2 object\n */\n createOrUpdateWire(wire: Wire): Line2 {\n if (!this._scene) {\n throw new Error('WireVisualManager scene is not set');\n }\n\n const wirePath = this.computeWirePath(wire);\n\n let line = this._wireObject3Ds.get(wire.id);\n\n if (line) {\n // Update existing line geometry\n const geometry = new LineGeometry();\n geometry.setFromPoints(wirePath.points);\n line.geometry.dispose();\n line.geometry = geometry;\n } else {\n // Create new Line2\n const geometry = new LineGeometry();\n geometry.setFromPoints(wirePath.points);\n line = new Line2(geometry, this.wireMaterials.get('idle'));\n line.userData = {\n type: 'wire',\n wireId: wire.id,\n electricalState: 'idle',\n };\n // Enable wire hitbox layer\n line.layers.enable(HitboxLayers.WIRE);\n this._wireObject3Ds.set(wire.id, line);\n // Adding to scene is directly done here\n this._scene.add(line);\n }\n return line;\n }\n\n /**\n * Update a specific wire's geometry\n *\n * @param wireId - Wire ID to update\n */\n updateWireById(wireId: UUID): void {\n const circuit = this._circuit!; // TODO handle null circuit\n\n const wire = circuit.getWire(wireId);\n if (wire) {\n this.createOrUpdateWire(wire);\n }\n }\n\n /**\n * Update all wires connected to a component\n *\n * Called when a component is moved or rotated to update wire endpoints.\n *\n * @param componentId - Component that moved\n */\n updateWiresForComponent(componentId: UUID): void {\n const circuit = this._circuit!; // TODO handle null circuit\n\n const component = circuit.getComponent(componentId);\n if (!component) return;\n\n // Find all wires connected to this component's pins\n const wireIdsToUpdate = new Set<UUID>();\n\n for (const pinId of component.pins) {\n const enode = circuit.getENode(pinId);\n if (enode) {\n for (const wireId of enode.wires) {\n wireIdsToUpdate.add(wireId);\n }\n }\n }\n\n // Update each wire\n for (const wireId of wireIdsToUpdate) {\n this.updateWireById(wireId);\n }\n }\n\n /**\n * visual hover and selection effects\n */\n\n /**\n * Apply hovered visual effect to a wire\n * @param wireId\n */\n applyHoveredVisual(wireId: UUID): void {\n const line = this._wireObject3Ds.get(wireId);\n if (!line) return;\n if (line.material === this.wireMaterials.get('selected')) return;\n\n line.material = this.wireMaterials.get('hovered')!;\n }\n\n removeHoveredVisual(wireId: UUID): void {\n const line = this._wireObject3Ds.get(wireId);\n if (!line) return;\n if (line.material !== this.wireMaterials.get('hovered')) return;\n\n line.material = this.wireMaterials.get(line.userData.electricalState || 'idle')!;\n }\n\n applySelectedVisual(wireId: UUID): void {\n const line = this._wireObject3Ds.get(wireId);\n if (!line) return;\n line.material = this.wireMaterials.get('selected')!;\n }\n\n removeSelectedVisual(wireId: UUID): void {\n const line = this._wireObject3Ds.get(wireId);\n if (!line) return;\n if (line.material !== this.wireMaterials.get('selected')) return;\n\n line.material = this.wireMaterials.get(line.userData.electricalState || 'idle')!;\n }\n\n /**\n * Apply electrical state material to a wire\n * Used during simulation to visualize voltage/current flow\n *\n * @param wireId - Wire ID to update\n * @param state - Material state: 'current', 'voltage', 'vc' (voltage and current) or 'idle'\n */\n applyElectricalState(wireId: UUID, state: 'current' | 'voltage' | 'vc' | 'idle'): void {\n const line = this._wireObject3Ds.get(wireId);\n if (!line) return;\n\n // set fallback visual state\n line.userData.electricalState = state;\n // Don't override selected/hovered states\n const currentMaterial = line.material;\n if (currentMaterial === this.wireMaterials.get('selected')) return;\n if (currentMaterial === this.wireMaterials.get('hovered')) return;\n\n line.material = this.wireMaterials.get(state)!;\n }\n\n /**\n * removal and dispose\n */\n\n /**\n * Remove a wire visual from the scene\n *\n * @param wireId - Wire ID to remove\n */\n removeWire(wireId: UUID): void {\n if (!this._scene) {\n throw new Error('WireVisualManager scene is not set');\n }\n\n const wireLines = this._wireObject3Ds;\n const scene = this._scene;\n\n const line = wireLines.get(wireId);\n if (line) {\n scene.remove(line);\n line.geometry.dispose();\n // Do NOT dispose material - it's shared across all wires\n wireLines.delete(wireId);\n }\n }\n\n /**\n * Clean up all managed wire visuals\n */\n dispose(): void {\n // Guard against multiple dispose calls\n if (!this._wireObject3Ds) {\n return;\n }\n\n const wireLines = this._wireObject3Ds;\n const scene = this._scene;\n for (const [_wireId, line] of wireLines) {\n if (scene) scene.remove(line);\n line.geometry.dispose();\n // Individual wire materials are NOT disposed here - only geometries\n }\n wireLines.clear();\n\n // Remove preview wire if it exists\n this.removePreviewWire();\n\n // Dispose shared material once during full cleanup\n for (const material of this.wireMaterials.values()) {\n material.dispose();\n }\n this.wireMaterials.clear();\n // dereference all\n this._scene = null;\n this._camera = null;\n this._container = null;\n this._circuit = null;\n // @ts-ignore\n this._componentObject3Ds = null;\n // @ts-ignore\n this._wireObject3Ds = null;\n }\n\n /**\n * Preview wire helpers\n */\n\n /**\n * Create a preview wire for wire creation mode.\n * @param startPosition - World position of wire start\n * @returns Line2 object for preview\n */\n createPreviewWire(startPosition: THREE.Vector3): Line2 {\n if (!this._scene) {\n throw new Error('WireVisualManager scene is not set');\n }\n\n // Remove any existing preview\n this.removePreviewWire();\n\n // Create preview line geometry with two points (start and end at same position initially)\n const geometry = new LineGeometry();\n geometry.setFromPoints([\n startPosition.clone(),\n startPosition.clone(),\n //startPosition.clone().add(new THREE.Vector3(5, 5, 5))\n ]);\n\n // Use a slightly different material for preview (dashed or lower opacity)\n const material = createLine2Material(0xcccccc, 3);\n material.opacity = 0.7;\n material.dashed = true;\n material.dashSize = 1;\n material.gapSize = 0.3;\n material.transparent = false;\n\n const previewLine = new Line2(geometry, material);\n previewLine.renderOrder = 100; // Render on top\n\n this.previewWire = previewLine;\n this.previewWire.userData = {\n startPosition: startPosition.clone(),\n };\n\n this._scene.add(previewLine);\n\n return previewLine;\n }\n\n /**\n * Update preview wire endpoint.\n * @param endPosition - World position of wire end\n */\n updatePreviewWire(endPosition: THREE.Vector3): void {\n if (!this.previewWire) {\n return;\n }\n\n const geometry = this.previewWire.geometry as LineGeometry;\n\n const startPosition = this.previewWire.userData.startPosition;\n\n geometry.setFromPoints([startPosition.clone(), endPosition.clone()]);\n }\n\n /**\n * Remove preview wire from scene.\n */\n removePreviewWire(): void {\n if (this._scene && this.previewWire) {\n this._scene.remove(this.previewWire);\n this.previewWire.geometry.dispose();\n (this.previewWire.material as LineMaterial).dispose();\n this.previewWire = null;\n }\n }\n\n /**\n * Intermediate points and position helpers\n */\n\n /**\n * Get pin world position by traversing component group\n *\n * @param enodeId - The pin's ENode ID\n * @param componentGroup - The component's Three.js group\n * @returns World position of the pin, or null if not found\n */\n getPinWorldPositionFromGroup(\n enodeId: UUID,\n componentGroup: THREE.Object3D\n ): THREE.Vector3 | null {\n const target = new THREE.Vector3();\n let found = false;\n\n componentGroup.traverse((child) => {\n if (found) return;\n\n // Look for enode visual or enodeGroup with matching ID\n if (\n child.userData.enodeId === enodeId ||\n (child.userData.type === 'enodeGroup' && child.userData.enodeId === enodeId)\n ) {\n child.getWorldPosition(target);\n found = true;\n }\n });\n\n return found ? target : null;\n }\n\n /**\n * Get insertion index for a new intermediate point on a wire (T058)\n * @param wireId - Wire ID\n * @param worldPosition - Position where user clicked\n * @returns Index where new point should be inserted\n */\n getInsertIndexForPosition(wireId: UUID, worldPosition: THREE.Vector3): number {\n const circuit = this._circuit;\n if (!circuit) return 0;\n\n const wire = circuit.getWire(wireId);\n if (!wire) return 0;\n\n // Get wire path\n const wirePath = this.computeWirePath(wire);\n const points = wirePath.points;\n\n // Find the segment closest to the click position\n let minDistance = Infinity;\n let insertIndex = 0;\n\n for (let i = 0; i < points.length - 1; i++) {\n const segmentStart = points[i];\n const segmentEnd = points[i + 1];\n\n if (!segmentStart || !segmentEnd) continue;\n\n // Project click position onto segment\n const segmentDir = new THREE.Vector3().subVectors(segmentEnd, segmentStart);\n const segmentLength = segmentDir.length();\n\n if (segmentLength === 0) continue;\n\n segmentDir.normalize();\n const toClick = new THREE.Vector3().subVectors(worldPosition, segmentStart);\n const projection = toClick.dot(segmentDir);\n const clampedProjection = Math.max(0, Math.min(segmentLength, projection));\n\n const closestPoint = segmentStart.clone().addScaledVector(segmentDir, clampedProjection);\n const distance = worldPosition.distanceTo(closestPoint);\n\n if (distance < minDistance) {\n minDistance = distance;\n insertIndex = i;\n }\n }\n\n return insertIndex;\n }\n\n /**\n * Find nearest intermediate point on a wire within proximity threshold (T057)\n * @param wireId - Wire ID to search\n * @param clientPos - Client position (event.clientX/Y) to test - will be converted to container-relative\n * @param thresholdPx - Proximity threshold in pixels (default: 10)\n * @returns Object with pointIndex and distance, or null if none found\n */\n findNearestIntermediatePoint(\n wireId: UUID,\n clientPos: THREE.Vector2,\n thresholdPx: number = 10\n ): { pointIndex: number; distance: number } | null {\n const circuit = this._circuit;\n if (!circuit) return null;\n\n const wire = circuit.getWire(wireId);\n if (!wire) return null;\n\n // Convert client coordinates to container-relative coordinates\n const containerRelativePos = this.clientToContainerCoords(clientPos);\n\n let nearestIndex = -1;\n let nearestDistance = Infinity;\n\n // Check each intermediate position\n for (let i = 0; i < wire.intermediatePositions.length; i++) {\n const pos = wire.intermediatePositions[i];\n if (!pos) continue;\n\n const worldPos = gridToWorldPosition(pos);\n const pointScreenPos = this.worldToScreen(worldPos);\n const distance = this.screenDistance(containerRelativePos, pointScreenPos);\n\n if (distance < thresholdPx && distance < nearestDistance) {\n nearestIndex = i;\n nearestDistance = distance;\n }\n }\n\n if (nearestIndex >= 0) {\n return { pointIndex: nearestIndex, distance: nearestDistance };\n }\n\n return null;\n }\n\n /**\n * Compute the full path for a wire including intermediate positions\n *\n * @param wire - Wire to compute path for\n * @returns WirePath with array of Vector3 points from start to end\n */\n computeWirePath(wire: Wire): WirePath {\n const circuit = this._circuit!; // TODO handle null circuit\n const componentObject3Ds = this._componentObject3Ds;\n\n const node1 = circuit.getENode(wire.node1);\n const node2 = circuit.getENode(wire.node2);\n\n if (!node1 || !node2) {\n throw new Error(`Wire ${wire.id} has invalid node references`);\n }\n\n // Get start position\n const startPos = this._getENodeWorldPosition(\n node1.id,\n node1.type,\n node1.component,\n circuit,\n componentObject3Ds\n );\n\n // Get end position\n const endPos = this._getENodeWorldPosition(\n node2.id,\n node2.type,\n node2.component,\n circuit,\n componentObject3Ds\n );\n\n // Build full path: start -> intermediate positions -> end\n const points: THREE.Vector3[] = [startPos];\n\n // Add intermediate positions (convert from grid to world coordinates)\n for (const pos of wire.intermediatePositions) {\n points.push(gridToWorldPosition(pos));\n }\n\n points.push(endPos);\n\n return { wireId: wire.id, points };\n }\n\n /**\n * private helpers\n */\n\n /**\n * Get the world position of an ENode (pin or branching point)\n *\n * For pins: Traverses component group to find pin visual and gets world position\n * For branching points: Converts grid position to world coordinates\n *\n * @param enodeId - The ENode ID\n * @param enodeType - Type of the ENode (Pin or BranchingPoint)\n * @param componentId - Parent component ID (for pins)\n * @param circuit - Circuit for position lookup\n * @param componentGroups - Map of component ID to Three.js objects\n * @returns World position as Vector3\n */\n private _getENodeWorldPosition(\n enodeId: UUID,\n enodeType: ENodeType,\n componentId: UUID | undefined,\n circuit: Circuit,\n componentGroups: Map<UUID, THREE.Object3D>\n ): THREE.Vector3 {\n if (enodeType === ENodeType.Pin && componentId) {\n const componentGroup = componentGroups.get(componentId);\n if (componentGroup) {\n const pinPosition = this.getPinWorldPositionFromGroup(enodeId, componentGroup);\n if (pinPosition) {\n return pinPosition;\n }\n }\n // Fallback to component center if pin not found in visual hierarchy\n // TODO: handles regularly when branching points are renderered as scene objects\n const enode = circuit.getENode(enodeId);\n if (enode) {\n return gridToWorldPosition(enode.getPosition(circuit));\n }\n }\n\n // Branching point or fallback: use ENode.getPosition()\n const enode = circuit.getENode(enodeId);\n if (!enode) {\n throw new Error(`ENode ${enodeId} not found`);\n }\n return gridToWorldPosition(enode.getPosition(circuit));\n }\n\n /**\n * Convert 3D world position to 2D screen position (T056)\n * @param worldPosition - World position as Vector3\n * @returns Screen position as Vector2\n */\n private worldToScreen(worldPosition: THREE.Vector3): THREE.Vector2 {\n if (!this._camera) {\n throw new Error('WireVisualManager camera is not set');\n }\n\n const camera = this._camera;\n\n const vector = worldPosition.clone();\n vector.project(camera);\n\n const widthHalf = this.containerWidth / 2;\n const heightHalf = this.containerHeight / 2;\n\n return new THREE.Vector2(\n vector.x * widthHalf + widthHalf,\n -(vector.y * heightHalf) + heightHalf\n );\n }\n\n /**\n * Calculate distance between two 2D screen positions (T056)\n * @param screenPos1 - First screen position\n * @param screenPos2 - Second screen position\n * @returns Distance in pixels\n */\n private screenDistance(screenPos1: THREE.Vector2, screenPos2: THREE.Vector2): number {\n return screenPos1.distanceTo(screenPos2);\n }\n\n /**\n * Convert client coordinates (event.clientX/Y) to container-relative coordinates\n * @param clientPos - Position in client/viewport coordinates\n * @returns Position relative to the container's top-left corner\n */\n private clientToContainerCoords(clientPos: THREE.Vector2): THREE.Vector2 {\n if (!this._container) {\n throw new Error('WireVisualManager container is not set');\n }\n const container = this._container;\n const rect = container.getBoundingClientRect();\n\n return new THREE.Vector2(clientPos.x - rect.left, clientPos.y - rect.top);\n }\n}\n","/**\n * Utility functions to provide default options for the 3D circuit scene controllers and engine\n * @module scene/shared/utils/Options\n */\n\nimport type { ControllerOptions, EngineOptions, MapControlsOptions } from '../types';\n\n/**\n * returns default complete mapControlsOptions or an autocompleted partial mapControlOptions\n * @param options\n */\nexport function mapControlsOptions(\n options: MapControlsOptions | undefined = undefined\n): MapControlsOptions {\n const defaultOptions: MapControlsOptions = {\n enablePan: true,\n screenSpacePanning: true,\n enableZoom: true,\n enableRotate: true,\n enableDamping: true,\n dampingFactor: 0.5,\n minDistance: 1,\n maxDistance: 200,\n panSpeed: 1.0,\n zoomSpeed: 2.0,\n rotateSpeed: 1.0,\n };\n\n if (!options) return defaultOptions;\n return { ...defaultOptions, ...options };\n}\n\n/**\n * returns default complete controllerOptions or an autocompleted partial controllerOptions\n * @param options\n */\nexport function controllerOptions(\n options: ControllerOptions | undefined = undefined\n): ControllerOptions {\n const defaultOptions: ControllerOptions = {\n backgroundColor: 0x222230,\n colorCenterLine: 0xddddaa,\n colorGrid: 0x777777,\n defaultTool: 'build',\n mapControls: mapControlsOptions(),\n simulationSpeed: 3,\n simulationAutoPlay: false,\n };\n\n if (!options) return defaultOptions;\n options.mapControls = mapControlsOptions(options.mapControls);\n return { ...defaultOptions, ...options };\n}\n\n/**\n * returns default complete engineOptions or and autoCompleted partial engineOptions\n * @param options\n */\nexport function engineOptions(options: EngineOptions | undefined = undefined): EngineOptions {\n const defaultOptions: EngineOptions = {\n initialMode: 'edit',\n controllerOptions: controllerOptions(),\n runnerOptions: { enableHistory: false, historyLimit: 1 },\n };\n\n if (!options) return defaultOptions;\n options.controllerOptions = controllerOptions(options.controllerOptions);\n return { ...defaultOptions, ...options };\n}\n","/**\n * Controls utilities\n * @module scene/shared/utils/ControlsUtils\n */\n\nimport * as THREE from 'three';\nimport { MapControls } from 'three/addons/controls/MapControls.js';\nimport type { MapControlsOptions } from '../types';\nimport { mapControlsOptions } from './Options';\n\nexport function createMapControls(\n camera: THREE.PerspectiveCamera,\n container: HTMLElement,\n options: MapControlsOptions\n): MapControls {\n const controls = new MapControls(camera, container);\n options = mapControlsOptions(options);\n\n controls.enablePan = options.enablePan!;\n controls.screenSpacePanning = options.screenSpacePanning!;\n controls.enableZoom = options.enableZoom!;\n controls.enableRotate = options.enableRotate!;\n controls.enableDamping = options.enableDamping!;\n controls.dampingFactor = options.dampingFactor!;\n controls.minDistance = options.minDistance!;\n controls.maxDistance = options.maxDistance!;\n controls.panSpeed = options.panSpeed!;\n controls.zoomSpeed = options.zoomSpeed!;\n controls.rotateSpeed = options.rotateSpeed!;\n\n return controls;\n}\n","/**\n * Abstract Circuit Controller\n * @module scene/shared/AbstractCircuitController\n *\n * Base class for circuit visualization controllers.\n * Provides common Three.js scene management, camera controls, and hover detection.\n */\n\nimport * as THREE from 'three';\nimport { MapControls } from 'three/addons/controls/MapControls.js';\nimport { type UUID, type Circuit } from 'simple-circuit-engine/core';\nimport { EventEmitter } from './EventEmitter';\nimport type { IFactoryRegistry } from './components/ComponentVisualFactory';\nimport type {\n ControllerEventMap,\n ControllerOptions,\n HoveredElement,\n HoverableType,\n HitboxUserData,\n WireHitboxUserData,\n ComponentHitboxUserData,\n EnodeHitboxUserData,\n SharedResources,\n VisualContext,\n} from './types';\nimport { createPerspectiveCamera, updateCamera } from './utils/CameraUtils';\nimport { setupSceneLights } from './utils/LightingUtils';\nimport { HoverManager } from './HoverManager';\nimport type { Line2 } from 'three/examples/jsm/lines/Line2.js';\nimport { BranchingPointVisualFactory } from './BranchingPointVisualFactory';\nimport { WireVisualManager } from './WireVisualManager';\nimport { createGridHelper } from './utils/GeometryUtils';\nimport { controllerOptions } from './utils/Options';\nimport { createMapControls } from './utils/ControlsUtils';\n\n/**\n * Abstract base class for circuit controllers.\n *\n * Provides common functionality for both static editing (CircuitController)\n * and live simulation (CircuitRunnerController) modes:\n * - Three.js scene, camera, and MapControls management\n * - Container lifecycle (initialize/dispose)\n * - Hover detection via HoverManager\n * - Visual object tracking maps\n *\n * Subclasses must implement:\n * - Circuit/CircuitRunner specific logic\n * - Visual creation and update methods\n * - Mode-specific features (editing tools or simulation interpolation)\n */\nexport abstract class AbstractCircuitController extends EventEmitter<ControllerEventMap> {\n // Container and Three.js core objects\n protected _container: HTMLElement | null = null;\n protected _scene: THREE.Scene | null = null;\n protected _grid: THREE.GridHelper | null = null;\n protected _camera: THREE.PerspectiveCamera | null = null;\n protected _mapControls: MapControls | null = null;\n\n // circuit being visualized\n protected _circuit: Circuit | null = null;\n\n // Wire visuals\n public readonly wireVisualManager: WireVisualManager;\n\n // State flags\n protected _initialized: boolean = false;\n protected _options: ControllerOptions | null;\n\n protected _active: boolean = false;\n protected _disposed: boolean = false;\n protected _gridHalfSize: number = 20;\n\n // Scene visual object factories - Wire visuals managed separately\n public readonly factoryRegistry: IFactoryRegistry;\n public readonly branchingPointVisualFactory: BranchingPointVisualFactory;\n\n // Scene objects tracking\n protected _componentObject3Ds: Map<UUID, THREE.Object3D> = new Map();\n protected _enodeObject3Ds: Map<UUID, THREE.Object3D> = new Map();\n protected _wireObject3Ds: Map<UUID, Line2> = new Map();\n\n // Hover system\n protected _hoverManager: HoverManager | null = null;\n protected _mouseMoveHandler: ((event: MouseEvent) => void) | null = null;\n protected _mouseLeaveHandler: ((event: MouseEvent) => void) | null = null;\n protected _mapControlsChangeHandler: (() => void) | null = null;\n\n // Shared resources injection\n protected _sharedResources: SharedResources | null = null;\n protected _useSharedResources: boolean = false;\n\n /**\n * Create a new circuit controller\n *\n * @param factoryRegistry - Component visual factory registry\n * @param sharedResources - Optional shared resources for facade pattern (CircuitEngine)\n * @throws {TypeError} If factoryRegistry is null/undefined\n */\n constructor(factoryRegistry: IFactoryRegistry, sharedResources?: SharedResources) {\n super();\n if (!factoryRegistry) {\n throw new TypeError('FactoryRegistry is required');\n }\n this._options = controllerOptions();\n\n // If shared resources provided, store them for use in initialize()\n if (sharedResources) {\n this._sharedResources = sharedResources;\n this._useSharedResources = true;\n this.factoryRegistry = sharedResources.factoryRegistry;\n this.branchingPointVisualFactory = sharedResources.branchingPointVisualFactory;\n this.wireVisualManager = sharedResources.wireVisualManager;\n } else {\n this._useSharedResources = false;\n this.factoryRegistry = factoryRegistry;\n this.branchingPointVisualFactory = new BranchingPointVisualFactory();\n this.wireVisualManager = new WireVisualManager(this._componentObject3Ds, this._wireObject3Ds);\n }\n }\n\n get componentObject3Ds(): Map<UUID, THREE.Object3D> {\n return this._componentObject3Ds;\n }\n\n get enodeObject3Ds(): Map<UUID, THREE.Object3D> {\n return this._enodeObject3Ds;\n }\n\n get wireObject3Ds(): Map<UUID, Line2> {\n return this._wireObject3Ds;\n }\n\n // @Memoize(...{\n // expiring: undefined,\n // hashFunction: (this._circuit) => this._circuit,\n // tags: undefined\n // })\n get visualContext(): VisualContext {\n return {\n getENode: (id: UUID) => this._circuit?.getENode(id),\n };\n }\n\n protected get grid(): THREE.GridHelper | null {\n if (this._useSharedResources) return this._sharedResources?.grid || null;\n return this._grid;\n }\n\n protected set grid(grid: THREE.GridHelper) {\n if (this._useSharedResources) {\n if (this._sharedResources) {\n this._sharedResources.grid = grid;\n }\n } else {\n this._grid = grid;\n }\n }\n\n // ==========================================\n // Initialization and Lifecycle\n // ==========================================\n\n /**\n * Initialize the controller with a DOM container.\n * Creates scene, camera, lights, MapControls, and HoverManager.\n *\n * When sharedResources were provided in constructor, uses those instead\n * of creating new resources. This enables the CircuitEngine facade pattern.\n *\n * @param container - HTMLElement to attach scene to\n * @param options - Optional configuration\n * @throws {TypeError} If container is not a valid HTMLElement\n * @throws {Error} If already initialized\n */\n initialize(container: HTMLElement, options?: ControllerOptions): void {\n if (this._initialized) {\n return; // Already initialized\n }\n options = controllerOptions(options);\n this._options = options;\n\n if (!container || !(container instanceof HTMLElement)) {\n const error = new TypeError('Container must be a valid HTMLElement');\n this.emitError(error);\n throw error;\n }\n\n try {\n this._container = container;\n\n if (this._useSharedResources && this._sharedResources) {\n // Use injected shared resources\n this._componentObject3Ds = this._sharedResources.componentObject3Ds;\n this._enodeObject3Ds = this._sharedResources.enodeObject3Ds;\n this._wireObject3Ds = this._sharedResources.wireObject3Ds;\n\n this._scene = this._sharedResources.scene;\n this._camera = this._sharedResources.camera;\n this._mapControls = this._sharedResources.mapControls;\n this._hoverManager = this._sharedResources.hoverManager;\n // Note: factoryRegistry, branchingPointVisualFactory and WireVisualManager are already set from constructor\n\n // Initialize WireVisualManager resolution (Line2 rendering)\n this.wireVisualManager.setResolution(\n this._container!.clientWidth,\n this._container!.clientHeight\n );\n\n // Setup hover change callback for this controller\n if (!this._hoverManager.isInitialized()) {\n this._initializeHoverManager();\n }\n // Setup mouse event callbacks\n this._setupMouseCallbacks();\n } else {\n // Create own resources (standalone mode)\n // Create scene\n this._scene = new THREE.Scene();\n this._scene.background = new THREE.Color(options.backgroundColor);\n // Add default sized grid\n this._grid = createGridHelper(10, 10, options.colorCenterLine!, options.colorGrid!);\n this._scene.add(this._grid);\n\n setupSceneLights(this._scene);\n\n // Create camera\n const aspect = container.clientWidth / container.clientHeight || 1;\n this._camera = createPerspectiveCamera(aspect);\n this._camera.layers.set(0); // main visual layer\n this._camera.layers.enable(1); // enode hover layer\n this._camera.layers.enable(2); // component hover layer\n // Initialize MapControls\n this._mapControls = createMapControls(this._camera, this._container, options.mapControls!);\n\n // Initialize WireVisualManager\n this.wireVisualManager.setContainer(this._container!);\n this.wireVisualManager.setResolution(\n this._container!.clientWidth,\n this._container!.clientHeight\n );\n this.wireVisualManager.setSceneAndCamera(this._scene, this._camera);\n\n // Create HoverManager instance\n this._hoverManager = new HoverManager(this._scene, this._camera);\n // Initialize HoverManager\n this._initializeHoverManager();\n // Setup mouse event callbacks\n this._setupMouseCallbacks();\n // in standalone mode set active\n this._active = true;\n }\n\n // Allow subclasses to perform additional initialization\n this.onInitialize(options);\n\n this._initialized = true;\n\n // Emit ready event\n this.emitReady();\n } catch (error) {\n const err = error as Error;\n this.emitError(err);\n throw error;\n }\n }\n\n /**\n * Hook for subclasses to perform additional initialization.\n * Called after base initialization but before emitting 'ready'.\n *\n * @param options - Controller options passed to initialize()\n */\n protected abstract onInitialize(options?: ControllerOptions): void;\n\n /**\n * Emit the ready event with controller-specific data.\n */\n protected abstract emitReady(): void;\n\n /**\n * Emit an error event.\n */\n protected emitError(error: Error): void {\n (this as EventEmitter<ControllerEventMap>).emit('error', {\n message: error.message,\n error,\n });\n }\n\n /**\n * Check that controller is initialized and not disposed.\n * @throws {Error} If not initialized or already disposed\n */\n protected _checkInitialized(): void {\n if (!this._initialized) {\n throw new Error('Controller not initialized. Call initialize() first.');\n }\n if (this._disposed) {\n throw new Error('Controller has been disposed');\n }\n }\n\n /**\n * Clean up all WebGL resources.\n * Disposes geometries, materials, controls, and clears event listeners.\n *\n * When using shared resources, does not dispose scene, camera, controls, or hover manager\n * as those are owned by the CircuitEngine facade.\n */\n dispose(): void {\n this._checkInitialized();\n\n try {\n // Allow subclasses to clean up first\n this.onDispose();\n\n // Remove DOM event listeners (always owned by this controller)\n if (this._container) {\n if (this._mouseMoveHandler) {\n this._container.removeEventListener('mousemove', this._mouseMoveHandler);\n this._mouseMoveHandler = null;\n }\n if (this._mouseLeaveHandler) {\n this._container.removeEventListener('mouseleave', this._mouseLeaveHandler);\n this._mouseLeaveHandler = null;\n }\n }\n\n // Only dispose resources we own (not shared ones)\n if (!this._useSharedResources) {\n // Dispose HoverManager\n if (this._hoverManager) {\n this._hoverManager.dispose();\n this._hoverManager = null;\n }\n\n // Remove all visuals\n this._removeAllVisuals();\n // clear grid\n if (this.grid) {\n this._scene!.remove(this.grid);\n this.grid.geometry.dispose();\n this.grid.dispose();\n this._grid = null;\n }\n\n // Clear tracking maps\n this._componentObject3Ds.clear();\n this._enodeObject3Ds.clear();\n this._wireObject3Ds.clear();\n\n // dispose own wireVisualManager\n this.wireVisualManager.dispose();\n\n // Dispose MapControls\n if (this._mapControls) {\n if (this._mapControlsChangeHandler) {\n this._mapControls.removeEventListener('change', this._mapControlsChangeHandler);\n this._mapControlsChangeHandler = null;\n }\n this._mapControls.dispose();\n this._mapControls = null;\n }\n } else {\n // When using shared resources, just clear our references\n // The CircuitEngine will handle actual disposal\n this._hoverManager = null;\n this._scene = null;\n this._camera = null;\n this._mapControls = null;\n this._grid = null;\n // Note: visual maps are shared, so we don't clear them\n }\n\n // Clear event listeners\n this.removeAllListeners();\n\n this._disposed = true;\n this._initialized = false;\n } catch (error) {\n const err = error as Error;\n this.emitError(err);\n throw error;\n }\n }\n\n /**\n * Hook for subclasses to perform cleanup before base dispose.\n */\n protected abstract onDispose(): void;\n\n /**\n * Remove all visual objects from scene.\n * Subclasses must implement to handle their specific wire types.\n */\n protected abstract _removeAllVisuals(): void;\n\n public setActive(active: boolean): void {\n this._active = active;\n this.onSetActive(active);\n }\n\n protected abstract onSetActive(active: boolean): void;\n\n /**\n * Set the current circuit to visualize or null to clear the scene\n * @param circuit\n */\n abstract setCircuit(circuit: Circuit | null): void;\n\n /**\n * Get the current circuit being visualized\n */\n getCircuit(): Circuit | null {\n return this._circuit;\n }\n\n /**\n * Refresh any user-visible strings in owned widgets after the consumer has\n * switched i18next to a new language. Default is a no-op for controllers\n * that own no translated widgets.\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n setLanguage(_lng: string): void {}\n\n protected abstract onSetCircuit(): void;\n\n /**\n * Loads a new circuit to visualize or null for clearing the scene\n * @param circuit\n */\n protected _setCircuit(circuit: Circuit | null): void {\n this._checkInitialized();\n if (circuit === this._circuit) return; // TODO : implement hash and equals methods in circuit to perform value equality check\n\n if (!!this._circuit) {\n // Clear all existing visuals\n this._removeAllVisuals();\n const oldCircuitName =\n this._circuit.metadata && this._circuit.metadata.options\n ? this._circuit.metadata.options.name\n : 'Unnamed Circuit';\n this._circuit = null;\n this.wireVisualManager.setCircuit(null);\n this.emit('circuitCleared', { name: oldCircuitName });\n }\n // clear grid in standalone mode\n if (!this._useSharedResources && this._grid) {\n this._grid.geometry.dispose();\n this._grid.dispose();\n this._scene!.remove(this._grid);\n this._grid = null;\n }\n\n if (circuit !== null) {\n const nameOrDefault =\n circuit.metadata && circuit.metadata.options\n ? circuit.metadata.options.name\n : 'Unnamed Circuit';\n const options = this._options || controllerOptions();\n // Perform full update with new circuit\n this._circuit = circuit;\n this._scene!.name = nameOrDefault;\n this.wireVisualManager.setCircuit(circuit);\n this._gridHalfSize = Math.ceil(circuit.metadata.size / 2);\n // in standalone mode update grid, camera and controls according to circuit metadata\n if (!this._useSharedResources) {\n this._grid = createGridHelper(\n circuit.metadata.size,\n circuit.metadata.divisions,\n options.colorCenterLine!,\n options.colorGrid!\n );\n this._scene!.add(this._grid);\n\n if (this._camera) {\n updateCamera(this._camera, circuit.metadata.cameraOptions);\n }\n\n if (this._mapControls) {\n const controls = this._mapControls;\n const target = circuit.metadata.cameraOptions.lookAtPosition;\n controls.target.set(target.x, target.y, target.z);\n }\n }\n this.onSetCircuit();\n this.emit('circuitLoaded', { name: nameOrDefault });\n }\n }\n\n /**\n * get the object3D (Group for components and enodes, Line2 for wires) by hoverable type and id\n * @param type\n * @param id\n */\n getObject3D(type: HoverableType, id: UUID): THREE.Object3D | undefined {\n switch (type) {\n case 'component':\n return this._componentObject3Ds.get(id);\n case 'enode':\n return this._enodeObject3Ds.get(id);\n case 'wire':\n return this._wireObject3Ds.get(id);\n default:\n return undefined;\n }\n }\n\n /**\n * Get the MapControls instance for direct manipulation.\n */\n getControls(): MapControls | null {\n return this._mapControls;\n }\n\n // ==========================================\n // Hover System\n // ==========================================\n\n /**\n * Get the current cursor position on the ground plane (y=0) in world coordinates\n * The position is clamped within the circuit grid boundaries but not snapped to grid\n * @param bound - Whether to constrain position within grid boundaries, default false\n */\n cursorGroundPlanePosition(bound: boolean = false): THREE.Vector3 {\n const vector = this._hoverManager!.getGroundPlanePosition().clone();\n if (bound) {\n vector.set(\n Math.min(Math.max(vector.x, -this._gridHalfSize), this._gridHalfSize),\n 0,\n Math.min(Math.max(vector.z, -this._gridHalfSize), this._gridHalfSize)\n );\n }\n return vector;\n }\n\n /**\n * Initialize HoverManager for hover detection\n *\n * @private\n */\n private _initializeHoverManager(): void {\n if (!this._hoverManager) {\n throw new Error('HoverManager must be constructed before initialization');\n }\n if (!this._container) {\n throw new Error('Container must be defined to initialize HoverManager');\n }\n\n // Track previous hover state for unhover events\n // let previousElement: {\n // objectId: UUID;\n // objectType: any;\n // userData: HitboxUserData;\n // } | null = null;\n\n const unhoverPreviousElement = (element: HoveredElement) => {\n if (element.objectType === 'enodeHitbox') {\n const userData = element.object3D.userData as EnodeHitboxUserData;\n const enodeId = userData.enodeId;\n if (!enodeId) {\n console.warn('Failed to apply unhover effect (missing enodeId)');\n return;\n }\n const enodeGroup = this._enodeObject3Ds.get(enodeId);\n if (!enodeGroup) {\n console.warn('Failed to apply unhover effect (enodeGroup not found)');\n return;\n }\n try {\n // Use BranchingPointVisualFactory for branching points (T024)\n if (!userData.componentId) {\n this.branchingPointVisualFactory.removeHover(enodeGroup);\n } else {\n this.factoryRegistry.getFallbackFactory().removePinHover(enodeGroup);\n }\n } catch (error) {\n console.warn('Failed to apply unhover effect:', error);\n }\n return;\n } else if (element.objectType === 'componentHitbox') {\n const userData = element.object3D.userData as ComponentHitboxUserData;\n const componentId = userData.componentId;\n if (!componentId) {\n console.warn('Failed to apply unhover effect (missing componentId)');\n return;\n }\n const componentGroup = this._componentObject3Ds.get(componentId);\n if (!componentGroup) {\n console.warn('Failed to apply unhover effect (componentGroup not found)');\n return;\n }\n try {\n const factory = this.factoryRegistry.get(userData.componentType);\n factory.removeHover(componentGroup);\n } catch (error) {\n console.warn('Failed to remove hover effect:', error);\n }\n return;\n } else if (element.objectType === 'wire') {\n const userData = element.object3D.userData as WireHitboxUserData;\n const wireId = userData.wireId;\n if (!wireId) {\n console.warn('Failed to apply unhover effect (missing wireId)');\n return;\n }\n const wire = this._wireObject3Ds.get(wireId);\n if (!wire) {\n console.warn('Failed to apply unhover effect (wire not found)');\n return;\n }\n this.wireVisualManager.removeHoveredVisual(wireId);\n }\n };\n\n const hoverElement = (element: HoveredElement) => {\n if (element.objectType === 'enodeHitbox') {\n const userData = element.object3D.userData as EnodeHitboxUserData;\n const enodeId = userData.enodeId;\n if (!enodeId) {\n console.warn('Failed to apply hover effect (missing enodeId)');\n return;\n }\n const enodeGroup = this._enodeObject3Ds.get(enodeId);\n if (!enodeGroup) {\n console.warn('Failed to apply hover effect (enodeGroup not found)');\n return;\n }\n try {\n // Use BranchingPointVisualFactory for branching points (T024)\n if (!userData.componentId) {\n this.branchingPointVisualFactory.applyHover(enodeGroup);\n } else {\n this.factoryRegistry.getFallbackFactory().applyPinHover(enodeGroup);\n }\n } catch (error) {\n console.warn('Failed to apply hover effect:', error);\n }\n return;\n } else if (element.objectType === 'componentHitbox') {\n const userData = element.object3D.userData as ComponentHitboxUserData;\n const componentId = userData.componentId;\n if (!componentId) {\n console.warn('Failed to apply hover effect (missing componentId)');\n return;\n }\n const componentGroup = this._componentObject3Ds.get(componentId);\n if (!componentGroup) {\n console.warn('Failed to apply hover effect (componentGroup not found)');\n return;\n }\n try {\n const factory = this.factoryRegistry.get(userData.componentType);\n factory.applyHover(componentGroup);\n } catch (error) {\n console.warn('Failed to apply hover effect:', error);\n }\n return;\n } else if (element.objectType === 'wire') {\n const userData = element.object3D.userData as WireHitboxUserData;\n const wireId = userData.wireId;\n if (!wireId) {\n console.warn('Failed to apply hover effect (missing wireId)');\n return;\n }\n const wire = this._wireObject3Ds.get(wireId);\n if (!wire) {\n console.warn('Failed to apply hover effect (wire not found)');\n return;\n }\n this.wireVisualManager.applyHoveredVisual(wireId);\n }\n };\n\n // Register callback to emit hover/unhover events\n this._hoverManager.onHoverChange((element, previousElement) => {\n // Emit unhover for previous element if it exists\n if (previousElement && (!element || element.id !== previousElement.id)) {\n unhoverPreviousElement(previousElement);\n this.emit('unhover', {\n objectId: previousElement.id,\n objectType: previousElement.objectType,\n userData: previousElement.object3D.userData as HitboxUserData,\n });\n previousElement = null;\n }\n\n // Emit hover for new element\n if (element) {\n hoverElement(element);\n this.emit('hover', {\n objectId: element.id,\n objectType: element.objectType,\n userData: element.object3D.userData as HitboxUserData,\n });\n }\n });\n\n // Setup MapControls 'change' listener to refresh hover on camera movement\n if (this._mapControls) {\n this._mapControlsChangeHandler = () => {\n if (this._hoverManager) {\n this._hoverManager.refresh();\n }\n };\n this._mapControls.addEventListener('change', this._mapControlsChangeHandler);\n }\n this._hoverManager.setInitialized(true);\n }\n\n protected _setupMouseCallbacks(): void {\n if (!this._hoverManager) {\n throw new Error('HoverManager must be constructed before setting up mouse callbacks');\n }\n if (!this._container) {\n throw new Error('Container must be defined to setup mouse callbacks');\n }\n\n // Setup mousemove event listener : must always be active so that current world position can be queried\n this._mouseMoveHandler = (event: MouseEvent) => {\n if (!this._active || !this._container || !this._hoverManager) return;\n const rect = this._container.getBoundingClientRect();\n const x = ((event.clientX - rect.left) / rect.width) * 2 - 1;\n const y = -((event.clientY - rect.top) / rect.height) * 2 + 1;\n const oldPosition = this.cursorGroundPlanePosition();\n this._hoverManager.updateFromMouse(x, y);\n const newPosition = this.cursorGroundPlanePosition();\n if (!newPosition.equals(oldPosition)) {\n // this important event will be used by tools such as BuildTool to update preview positions\n this.emit('gridPositionMove', newPosition);\n }\n };\n this._container.addEventListener('mousemove', this._mouseMoveHandler);\n\n // Setup mouseleave event listener\n this._mouseLeaveHandler = (_event: MouseEvent) => {\n if (this._hoverManager) {\n this._hoverManager.clear();\n }\n };\n this._container.addEventListener('mouseleave', this._mouseLeaveHandler);\n }\n\n /**\n * Get the currently hovered element.\n */\n getHoveredElement(): HoveredElement | null {\n return this._hoverManager?.getHoveredElement() ?? null;\n }\n\n /**\n * Enable or disable hover detection.\n */\n setHoverEnabled(enabled: boolean): void {\n this._hoverManager?.setEnabled(enabled);\n }\n\n /**\n * Check if hover detection is enabled.\n */\n isHoverEnabled(): boolean {\n return this._hoverManager?.isEnabled() ?? false;\n }\n\n // ==========================================\n // Getters\n // ==========================================\n\n /**\n * Get the Three.js scene for rendering.\n * @throws {Error} If not initialized\n */\n getScene(): THREE.Scene {\n this._checkInitialized();\n return this._scene!;\n }\n\n /**\n * Get the Three.js camera for rendering.\n * @throws {Error} If not initialized\n */\n getCamera(): THREE.PerspectiveCamera {\n this._checkInitialized();\n return this._camera!;\n }\n\n /**\n * Get the HTML container element.\n * @throws {Error} If not initialized\n */\n getContainer(): HTMLElement {\n this._checkInitialized();\n if (!this._container) {\n throw new Error('Container not initialized');\n }\n return this._container;\n }\n\n /**\n * Check if controller is initialized.\n */\n get isInitialized(): boolean {\n return this._initialized;\n }\n\n /**\n * Check if controller is disposed.\n */\n get isDisposed(): boolean {\n return this._disposed;\n }\n\n // ==========================================\n // Container Resize\n // ==========================================\n\n /**\n * event handler when the container size changes\n * Can override the container boundingClientRect size by providing width and height\n * - Update camera projection matrix\n * - Update viewport size for Line2 material resolution\n * Should be called when the container size changes (e.g., window resize)\n *\n * @param width - New width (optional, uses container size if not provided)\n * @param height - New height (optional, uses container size if not provided)\n */\n onContainerResize(width?: number, height?: number): void {\n if (!this._container) return;\n\n if (width === undefined || height === undefined) {\n const rect = this._container.getBoundingClientRect();\n width = rect.width;\n height = rect.height;\n }\n\n if (this._camera) {\n this._camera.aspect = width / height;\n this._camera.updateProjectionMatrix();\n }\n this.wireVisualManager.setResolution(width, height);\n\n // Allow subclasses to handle resize\n this.onResize(width, height);\n }\n\n /**\n * Hook for subclasses to handle container resize.\n */\n protected onResize(_width: number, _height: number): void {\n // Default: no-op, subclasses can override\n }\n}\n","/**\n * Config Panel Manager\n * @module scene/static/ConfigPanelManager\n *\n * Manages the lifecycle of the lil-gui configuration panel for component editing.\n */\n\nimport GUI from 'lil-gui';\nimport type { UUID, Component } from 'simple-circuit-engine/core';\nimport type { IFactoryRegistry } from '../../shared/components/ComponentVisualFactory';\nimport type { IComponentVisualFactory } from '../../shared/components/ComponentVisualFactory';\nimport type { ConfigFormDefinition, ConfigFieldDefinition } from '../../shared/types';\nimport { sceT } from '../../../i18n';\n\n/**\n * Manages lil-gui configuration panel for component config editing\n *\n * Responsibilities:\n * - Panel lifecycle (open, close, dispose)\n * - DOM container creation and positioning\n * - lil-gui initialization and form building\n * - Click-outside and Escape key handling\n * - onChange event wiring to update component config\n * - Event emission for config changes\n */\nexport class ConfigPanelWidget {\n private factoryRegistry: IFactoryRegistry;\n private readonly editComponentConfig: (componentId: UUID, newConfig: Map<string, string>) => void;\n\n // Panel state\n private _isOpen: boolean = false;\n private _currentComponentId: UUID | null = null;\n private _currentComponent: Component | null = null;\n private _currentFactory: IComponentVisualFactory | null = null;\n private gui: GUI | null = null;\n private container: HTMLDivElement | null = null;\n private formDataObject: Record<string, any> = {};\n\n // Event handlers (stored for removal)\n private clickOutsideHandler: ((event: MouseEvent) => void) | null = null;\n private escapeHandler: ((event: KeyboardEvent) => void) | null = null;\n\n /**\n * Create a new ConfigPanelManager\n *\n * @param factoryRegistry - Factory registry for getting component factories\n * @param editComponentConfig - Function to edit component config\n * @param _camera - Three.js camera (reserved for future use)\n * @param _domElement - Container DOM element (reserved for future use)\n */\n constructor(\n factoryRegistry: IFactoryRegistry,\n editComponentConfig: (componentId: UUID, newConfig: Map<string, string>) => void,\n _camera: unknown,\n _domElement: unknown\n ) {\n this.factoryRegistry = factoryRegistry;\n this.editComponentConfig = editComponentConfig;\n }\n\n /**\n * Check if panel is currently open\n */\n get isOpen(): boolean {\n return this._isOpen;\n }\n\n /**\n * Get the ID of the component currently being edited\n */\n get currentComponentId(): UUID | null {\n return this._currentComponentId;\n }\n\n /**\n * Open the config panel for a component\n *\n * @param component - component to edit\n * @param screenPosition - Screen coordinates for panel positioning\n * @returns true if panel opened, false if component has no config\n */\n open(component: Component, screenPosition: { x: number; y: number }): boolean {\n // Close existing panel if open\n if (this._isOpen) {\n this.close();\n }\n\n const factory = this.factoryRegistry.get(component.type);\n const formDef = factory.getConfigFormDefinition(component.config);\n\n if (!formDef || formDef.fields.length === 0) {\n return false; // No configurable options\n }\n\n // Create container\n this.container = document.createElement('div');\n this.container.style.position = 'absolute';\n this.container.style.zIndex = '1000';\n document.body.appendChild(this.container);\n\n // Position container (will be implemented in T007)\n this.positionContainer(screenPosition);\n\n this._currentComponent = component;\n this._currentFactory = factory;\n\n // Create lil-gui (will be implemented in T008)\n this.buildGui(formDef, component, factory);\n\n // Setup event listeners (will be implemented in T009, T010)\n this.setupEventListeners();\n\n this._isOpen = true;\n this._currentComponentId = component.id;\n\n return true;\n }\n\n /**\n * Close the config panel if open\n */\n close(): void {\n if (!this._isOpen) {\n return;\n }\n\n // Destroy lil-gui\n if (this.gui) {\n this.gui.destroy();\n this.gui = null;\n }\n\n // Remove container from DOM\n if (this.container) {\n document.body.removeChild(this.container);\n this.container = null;\n }\n\n // Remove event listeners\n this.removeEventListeners();\n\n this._isOpen = false;\n this._currentComponentId = null;\n this._currentComponent = null;\n this._currentFactory = null;\n this.formDataObject = {};\n }\n\n /**\n * Dispose of all resources\n */\n dispose(): void {\n this.close();\n }\n\n /**\n * Position the container adjacent to component (right side preferred)\n */\n private positionContainer(screenPosition: { x: number; y: number }): void {\n if (!this.container) return;\n\n const PANEL_WIDTH = 300; // Approximate lil-gui width\n const OFFSET_X = 20; // Spacing from component\n const VIEWPORT_PADDING = 10; // Min distance from viewport edge\n\n // Calculate preferred position (right side)\n let left = screenPosition.x + OFFSET_X;\n let top = screenPosition.y;\n\n // Check viewport overflow and adjust\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n\n // If panel would overflow right edge, position to left of component\n if (left + PANEL_WIDTH > viewportWidth - VIEWPORT_PADDING) {\n left = screenPosition.x - PANEL_WIDTH - OFFSET_X;\n }\n\n // If still overflows left edge, clamp to viewport\n if (left < VIEWPORT_PADDING) {\n left = VIEWPORT_PADDING;\n }\n\n // Clamp top to viewport bounds\n if (top < VIEWPORT_PADDING) {\n top = VIEWPORT_PADDING;\n } else if (top > viewportHeight - VIEWPORT_PADDING) {\n top = viewportHeight - VIEWPORT_PADDING - 100; // Approximate panel height\n }\n\n this.container.style.left = `${left}px`;\n this.container.style.top = `${top}px`;\n }\n\n /**\n * Build lil-gui from form definition\n */\n private buildGui(formDef: ConfigFormDefinition, component: any, factory: any): void {\n if (!this.container) return;\n\n // Create lil-gui instance\n this.gui = new GUI({ container: this.container, width: 280 });\n const typeName = sceT(`components.${component.type}.name`, { defaultValue: component.type });\n this.gui.title(sceT('config.title', { name: typeName, defaultValue: `Config: ${typeName}` }));\n\n // Map core config to form data\n const formData = factory.mapCoreConfigToForm(component.config);\n\n // Convert Map to plain object for lil-gui\n this.formDataObject = {};\n for (const [key, value] of formData.entries()) {\n this.formDataObject[key] = value;\n }\n\n // Build controls for each field\n for (const field of formDef.fields) {\n let controller: ReturnType<GUI['add']> | null = null;\n\n switch (field.type) {\n case 'boolean':\n controller = this.gui\n .add(this.formDataObject, field.key)\n .name(this._resolveFieldLabel(component.type, field))\n .onChange((value: any) => this.onValueChange(field.key, value, component, factory));\n break;\n\n case 'dropdown':\n if (field.options) {\n controller = this.gui\n .add(this.formDataObject, field.key, field.options)\n .name(this._resolveFieldLabel(component.type, field))\n .onChange((value: any) => this.onValueChange(field.key, value, component, factory));\n }\n break;\n\n case 'number':\n controller = this.gui\n .add(this.formDataObject, field.key)\n .name(this._resolveFieldLabel(component.type, field))\n .onChange((value: any) => this.onValueChange(field.key, value, component, factory));\n if (field.min !== undefined) controller.min(field.min);\n if (field.max !== undefined) controller.max(field.max);\n if (field.step !== undefined) controller.step(field.step);\n break;\n\n case 'text':\n controller = this.gui\n .add(this.formDataObject, field.key)\n .name(this._resolveFieldLabel(component.type, field))\n .onChange((value: any) => this.onValueChange(field.key, value, component, factory));\n break;\n\n case 'color':\n controller = this.gui\n .addColor(this.formDataObject, field.key)\n .name(this._resolveFieldLabel(component.type, field))\n .onChange((value: any) => this.onValueChange(field.key, value, component, factory));\n break;\n }\n\n if (controller && field.disabled) {\n controller.disable(true);\n }\n }\n }\n\n /**\n * Handle value change in the config form\n * Maps form data back to core config and updates the component.\n * When defaultLogicFamily or activationLogic changes, rebuilds the GUI to reflect\n * updated disabled states and computed transitionSpan values.\n *\n * @param changedKey - The key of the field that changed\n * @param _value - The new value\n * @param component - Component being edited\n * @param factory - Visual factory for the component\n */\n private onValueChange(changedKey: string, _value: any, component: any, factory: any): void {\n // Convert formDataObject back to Map for mapping\n const formDataMap = new Map<string, any>();\n for (const [key, value] of Object.entries(this.formDataObject)) {\n formDataMap.set(key, value);\n }\n // Map form data back to core config format\n const coreConfig = factory.mapFormToCoreConfig(formDataMap) as Map<string, string>;\n // call editComponentConfig to update the component\n if (this._currentComponentId) {\n this.editComponentConfig(this._currentComponentId, coreConfig);\n }\n\n // Rebuild GUI when defaultLogicFamily or activationLogic changes to update disabled states\n if (changedKey === 'defaultLogicFamily' || changedKey === 'activationLogic') {\n this.rebuildGui(component, factory);\n }\n }\n\n /**\n * Resolve a form field label via i18n, falling back to a humanised form\n * of the field key when no translation is available (e.g. \"transitionSpan\" →\n * \"Transition Span\"). The fallback covers two cases: (a) the consumer never\n * called registerSceTranslations, (b) the key is not yet in the locale file.\n */\n private _resolveFieldLabel(componentType: string, field: ConfigFieldDefinition): string {\n const fallback = field.key\n .replace(/([A-Z])/g, ' $1')\n .replace(/^./, (c) => c.toUpperCase())\n .trim();\n return sceT(`components.${componentType}.config.fields.${field.key}.name`, {\n defaultValue: fallback,\n });\n }\n\n /**\n * Refresh the panel to display strings in the current language.\n * No-op if the panel is not open.\n */\n setLanguage(_lng: string): void {\n if (!this._isOpen || !this._currentComponent || !this._currentFactory) return;\n this.rebuildGui(this._currentComponent, this._currentFactory);\n }\n\n /**\n * Rebuild the GUI in place, re-reading the updated component config.\n * Used after interdependent field changes (defaultLogicFamily, activationLogic).\n */\n private rebuildGui(component: any, factory: any): void {\n if (!this.gui || !this.container) return;\n\n // Destroy existing GUI\n this.gui.destroy();\n this.gui = null;\n\n // Re-read updated config from the component (already updated by editComponentConfig)\n const formDef = factory.getConfigFormDefinition(component.config);\n if (!formDef) return;\n\n this.buildGui(formDef, component, factory);\n }\n\n /**\n * Setup event listeners for dismissing the panel\n */\n private setupEventListeners(): void {\n // Click-outside detection\n this.clickOutsideHandler = (event: MouseEvent) => {\n if (!this.container) return;\n\n const target = event.target as Node;\n // Check if click is outside the panel container\n if (!this.container.contains(target)) {\n this.close();\n }\n };\n\n // Add with a small delay to avoid immediate closure from the click that opened it\n setTimeout(() => {\n if (this.clickOutsideHandler) {\n document.addEventListener('pointerdown', this.clickOutsideHandler);\n }\n }, 100);\n\n // Escape key handling\n this.escapeHandler = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n this.close();\n }\n };\n document.addEventListener('keydown', this.escapeHandler);\n }\n\n /**\n * Remove event listeners\n */\n private removeEventListeners(): void {\n if (this.clickOutsideHandler) {\n document.removeEventListener('pointerdown', this.clickOutsideHandler);\n this.clickOutsideHandler = null;\n }\n if (this.escapeHandler) {\n document.removeEventListener('keydown', this.escapeHandler);\n this.escapeHandler = null;\n }\n }\n}\n","/**\n * Pin Tooltip Widget\n * @module scene/static/PinTooltipWidget\n *\n * Shows a clickable HTML tooltip near the cursor when hovering a component pin in edit mode.\n * Content: \"{pinLabel} ({componentName})\" — clicking triggers a componentHelpRequested callback.\n */\n\nimport { ComponentType } from 'simple-circuit-engine/core';\nimport { sceT } from '../../i18n';\n\n/**\n * Manages a transient HTML tooltip displayed when hovering a component pin.\n *\n * Lifecycle: created by CircuitController on init, disposed on deactivation/disposal.\n * Positioning: fixed, follows the raw client mouse coordinates.\n */\nexport class PinTooltipWidget {\n private _element: HTMLDivElement | null = null;\n private _currentComponentType: ComponentType | null = null;\n private _clickHandler: ((e: MouseEvent) => void) | null = null;\n private readonly _onHelpRequested: (componentType: ComponentType) => void;\n\n constructor(onHelpRequested: (componentType: ComponentType) => void) {\n this._onHelpRequested = onHelpRequested;\n }\n\n /**\n * Show tooltip at the given client coordinates for the specified pin.\n * If already showing for the same component type, only repositions.\n */\n show(pinLabel: string, componentType: ComponentType, clientX: number, clientY: number): void {\n if (this._element && this._currentComponentType === componentType) {\n this._position(clientX, clientY);\n return;\n }\n this.hide();\n this._currentComponentType = componentType;\n\n const componentName = sceT(`components.${componentType}.name`, { defaultValue: componentType });\n\n this._element = document.createElement('div');\n this._element.textContent = `${pinLabel} (${componentName})`;\n Object.assign(this._element.style, {\n position: 'fixed',\n zIndex: '2000',\n background: 'rgba(30, 30, 30, 0.92)',\n color: '#fff',\n padding: '4px 10px',\n borderRadius: '4px',\n fontSize: '12px',\n fontFamily: 'sans-serif',\n pointerEvents: 'auto',\n cursor: 'pointer',\n userSelect: 'none',\n boxShadow: '0 2px 6px rgba(0,0,0,0.4)',\n whiteSpace: 'nowrap',\n border: '1px solid rgba(255,255,255,0.15)',\n });\n\n this._clickHandler = (e: MouseEvent) => {\n e.stopPropagation();\n if (this._currentComponentType !== null) {\n this._onHelpRequested(this._currentComponentType);\n }\n };\n this._element.addEventListener('click', this._clickHandler);\n\n document.body.appendChild(this._element);\n this._position(clientX, clientY);\n }\n\n /** Hide and remove the tooltip element */\n hide(): void {\n if (!this._element) return;\n if (this._clickHandler) {\n this._element.removeEventListener('click', this._clickHandler);\n this._clickHandler = null;\n }\n document.body.removeChild(this._element);\n this._element = null;\n this._currentComponentType = null;\n }\n\n /** Update cursor position without recreating the tooltip */\n updatePosition(clientX: number, clientY: number): void {\n if (this._element) {\n this._position(clientX, clientY);\n }\n }\n\n get isVisible(): boolean {\n return this._element !== null;\n }\n\n /**\n * Signal a language change — hides any open tooltip so the next hover\n * recreates it with updated translations.\n */\n setLanguage(_lng: string): void {\n this.hide();\n }\n\n dispose(): void {\n this.hide();\n }\n\n private _position(clientX: number, clientY: number): void {\n if (!this._element) return;\n const OFFSET_X = 12;\n const OFFSET_Y = -28;\n let left = clientX + OFFSET_X;\n let top = clientY + OFFSET_Y;\n\n const tooltipWidth = this._element.offsetWidth || 150;\n const tooltipHeight = this._element.offsetHeight || 28;\n const vw = window.innerWidth;\n\n if (left + tooltipWidth > vw - 8) left = vw - tooltipWidth - 8;\n if (left < 8) left = 8;\n if (top < 8) top = clientY + tooltipHeight + 4;\n\n this._element.style.left = `${left}px`;\n this._element.style.top = `${top}px`;\n }\n}\n","/**\n * Static Circuit Controller for editing circuits\n * @module scene/static/CircuitController\n *\n * Manages static circuit THREE.js scene with support for editing tools.\n */\n\nimport * as THREE from 'three';\nimport type { Euler } from 'three';\nimport type {\n Component,\n Wire,\n ENode,\n UUID,\n ComponentType,\n ENodeSourceType,\n Circuit,\n} from 'simple-circuit-engine/core';\nimport { ENodeType } from 'simple-circuit-engine/core';\nimport type { IFactoryRegistry } from '../shared/components/ComponentVisualFactory';\nimport type {\n ToolType,\n SelectionData,\n SharedResources,\n ControllerOptions,\n IEditingTool,\n} from '../shared/types';\nimport {\n createGridHelper,\n gridToWorldPosition,\n gridToWorldRotation,\n} from '../shared/utils/GeometryUtils';\nimport { BuildTool } from './tools/BuildTool';\nimport { MultiSelectTool } from './tools/MultiSelectTool';\nimport { SelectionManager } from '../shared/SelectionManager';\nimport { CircuitWriter } from './CircuitWriter';\nimport { AbstractCircuitController } from '../shared/AbstractCircuitController';\nimport { ConfigPanelWidget } from './tools/ConfigPanelWidget';\nimport { PinTooltipWidget } from './PinTooltipWidget';\nimport { controllerOptions } from '../shared/utils/Options';\n\n/**\n * Static Circuit Controller Implementation\n *\n * Manager providing a bidirectional interface between a Circuit and a Three.js scene/camera ready to be rendered.\n * Supports view manipulation and editing via integrated tool system.\n * Provides event hooks for error handling and state changes.\n */\nexport class CircuitController extends AbstractCircuitController {\n // flags\n private _editMode: boolean = false;\n // Circuit writer\n public readonly circuitWriter: CircuitWriter;\n // Selection manager\n private _selectionManager: SelectionManager | null = null;\n // Config panel manager\n private _configPanelManager: ConfigPanelWidget | null = null;\n // Pin tooltip widget\n private _pinTooltipWidget: PinTooltipWidget | null = null;\n private _lastClientX: number = 0;\n private _lastClientY: number = 0;\n private _tooltipMouseMoveHandler: ((e: MouseEvent) => void) | null = null;\n // Circuit RepositoryTool system\n private _tools: Map<ToolType, IEditingTool> = new Map();\n private _activeTool: ToolType | null = null;\n\n /**\n * Constructor and initialization\n */\n\n /**\n * Create a new Static Circuit Controller\n *\n * @param factoryRegistry - Component visual factory registry\n * @param sharedResources - Optional shared resources for facade pattern (CircuitEngine)\n * @throws {TypeError} factoryRegistry is null/undefined\n */\n constructor(factoryRegistry: IFactoryRegistry, sharedResources?: SharedResources) {\n super(factoryRegistry, sharedResources);\n\n this.circuitWriter = new CircuitWriter(this);\n\n this.handlePointerDown = this.handlePointerDown.bind(this);\n this.onContainerResize = this.onContainerResize.bind(this);\n }\n\n override setLanguage(lng: string): void {\n this._pinTooltipWidget?.setLanguage(lng);\n this._configPanelManager?.setLanguage(lng);\n (this._tools.get('build') as BuildTool | undefined)?.setLanguage(lng);\n }\n\n /**\n * Specific Initialization logic, performed after AbstractCircuitController initialization\n * @private\n */\n protected onInitialize(_options?: ControllerOptions) {\n // Initialize tools\n this._initializeTools();\n // Initialize Selection Manager\n this._initializeSelectionManager();\n // Initialize Config Panel Manager\n this._initializeConfigPanelManager();\n // Initialize Pin Tooltip\n this._initializePinTooltip();\n\n this._initialized = true; // flag must be set before calling setActiveTool\n // standalone mode -> Controller active\n if (!this._sharedResources) {\n this.setActive(true);\n }\n }\n\n protected emitReady() {\n this.emit('ready', { controllerType: 'static' });\n }\n\n /**\n * Enable or disable edit mode (FR-006, FR-027)\n * When disabled, deactivates any active tool and resets tool state\n *\n * @param enabled - True to enable edit mode, false to disable\n */\n setEditMode(enabled: boolean): void {\n this._checkInitialized();\n\n if (this._editMode === enabled) return; // No changge\n this._editMode = enabled;\n\n if (!enabled) {\n // Disable edit mode - deactivate active tool if any\n if (this._activeTool !== null) {\n const previousTool = this._activeTool;\n const tool = this._tools.get(previousTool);\n\n if (tool) {\n tool.onDeactivate();\n }\n this._activeTool = null;\n // Emit toolDeactivated event\n this.emit('toolDeactivated', { toolType: previousTool });\n }\n }\n }\n\n /**\n * specific disposal prepended at the beginning of dispose process\n */\n protected onDispose(): void {\n this.setEditMode(false); // Ensure edit mode and all tools are disabled\n\n // dispose ConfigPanelManager\n if (this._configPanelManager) {\n this._configPanelManager.dispose();\n this._configPanelManager = null;\n }\n // dispose SelectionManager\n if (this._selectionManager) {\n this._selectionManager.dispose();\n this._selectionManager = null;\n }\n // dispose PinTooltipWidget\n this._disposePinTooltip();\n // dispose own wireVisualManager\n this.wireVisualManager.dispose();\n }\n\n onSetActive(active: boolean): void {\n if (!active) {\n // Deactivate edit mode (which deactivates active tool and emits toolDeactivated)\n this.setEditMode(false);\n this._selectionManager?.deselect();\n this._pinTooltipWidget?.hide();\n } else {\n if (this._initialized && this._options && this._options.defaultTool) {\n this.setEditMode(true);\n this.setActiveTool(this._options.defaultTool);\n }\n }\n }\n\n setCircuit(circuit: Circuit | null): void {\n this._setCircuit(circuit);\n }\n\n /**\n * specific logic when to render a new set circuit\n * @protected\n */\n protected onSetCircuit() {\n this._fullUpdate();\n }\n\n /**\n * Get the SelectionManager instance for direct manipulation\n * @returns SelectionManager\n */\n getSelectionManager(): SelectionManager {\n this._checkInitialized();\n if (!this._selectionManager) {\n throw new Error('SelectionManager not initialized');\n }\n return this._selectionManager!;\n }\n\n private handlePointerDown(event: MouseEvent): void {\n // discard if right click or middle click\n if (event.button !== 0) {\n return;\n }\n\n // common behavior regardless of the tool: select on pointer down\n if (this._hoverManager?.getHoveredElement()) {\n // always: emit position event when hovered element\n const element = this._hoverManager.getHoveredElement()!;\n const alreadySelected = this._selectionManager!.isSelected(element.type, element.id);\n if (!alreadySelected) {\n this._selectionManager?.selectOne(element.type, element.id, element.object3D.userData);\n this.emit('select', this._selectionManager!.getSelection()!);\n }\n } else {\n // always: emit deselect event when not hovered element\n const hasSelection = this._selectionManager?.hasSelection();\n if (hasSelection) {\n const selection = this._selectionManager!.getSelection()!;\n this._selectionManager?.deselect();\n this.emit('deselect', selection);\n }\n }\n }\n\n private _applySelectionVisual(selection: SelectionData, selected: boolean): void {\n let components: Map<UUID, string | null> | null = null;\n let enodes: Map<UUID, string | null> | null = null;\n let wires: Map<UUID, string | null> | null = null;\n if (selection.kind === 'mono') {\n switch (selection.type) {\n case 'component':\n components = new Map<UUID, string | null>();\n components.set(selection.id, selection.data ?? null);\n break;\n case 'enode':\n enodes = new Map<UUID, string | null>();\n enodes.set(selection.id, selection.data ?? null);\n break;\n case 'wire':\n wires = new Map<UUID, string | null>();\n wires.set(selection.id, selection.data ?? null);\n break;\n default:\n break;\n }\n } else {\n components = selection.components || null;\n enodes = selection.enodes || null;\n wires = selection.wires || null;\n }\n\n if (components) {\n for (const [id, _data] of components) {\n const object3D = this._componentObject3Ds.get(id);\n if (!object3D) {\n continue;\n }\n try {\n const componentType = object3D.userData.componentType as ComponentType;\n const factory = this.factoryRegistry.get(componentType);\n if (selected) {\n factory.applySelection(object3D);\n } else {\n factory.removeSelection(object3D);\n }\n } catch (error) {\n console.warn(\n `Failed to ${selected ? 'apply' : 'remove'} component selection visual:`,\n error\n );\n }\n }\n }\n if (enodes) {\n for (const [id, _data] of enodes) {\n const object3D = this._enodeObject3Ds.get(id);\n if (!object3D) {\n continue;\n }\n if (!!object3D.userData.componentId) continue; // pins cannot be selected individually\n if (selected) {\n this.branchingPointVisualFactory.applySelection(object3D);\n } else {\n this.branchingPointVisualFactory.removeSelection(object3D);\n }\n }\n }\n if (wires) {\n for (const [id, _data] of wires) {\n if (selected) {\n this.wireVisualManager.applySelectedVisual(id);\n } else {\n this.wireVisualManager.removeSelectedVisual(id);\n }\n }\n }\n }\n\n private _initializeSelectionManager(): void {\n if (!this._scene || !this._camera || !this._container) {\n throw new Error('Scene, camera, and container must be initialized before SelectionManager');\n }\n\n // Create SelectionManager instance\n this._selectionManager = new SelectionManager();\n\n // Register callback to handle selection visual changes (T025)\n this._selectionManager.onSelectionChange((newSelection, previousSelection) => {\n // Remove selection visual from previous selection\n if (previousSelection) {\n this._applySelectionVisual(previousSelection, false);\n }\n // Apply selection visual to new selection\n if (newSelection) {\n this._applySelectionVisual(newSelection, true);\n }\n\n // Emit selectionChange event (T026)\n this.emit('selectionChange', {\n newSelection: previousSelection,\n previousSelection: newSelection,\n });\n });\n\n this._container.addEventListener('pointerdown', this.handlePointerDown);\n }\n\n /**\n * Initialize ConfigPanelManager (T013)\n * @private\n */\n private _initializeConfigPanelManager(): void {\n if (!this._scene || !this._camera || !this._container) {\n throw new Error('Scene, camera and container must be initialized before ConfigPanelManager');\n }\n\n // Create ConfigPanelManager instance\n this._configPanelManager = new ConfigPanelWidget(\n this.factoryRegistry,\n this.editComponentConfig.bind(this),\n this._camera,\n this._container\n );\n }\n\n private _initializePinTooltip(): void {\n this._pinTooltipWidget = new PinTooltipWidget((componentType) => {\n this.emit('componentHelpRequested', { componentType });\n });\n\n this._tooltipMouseMoveHandler = (e: MouseEvent) => {\n this._lastClientX = e.clientX;\n this._lastClientY = e.clientY;\n this._pinTooltipWidget?.updatePosition(e.clientX, e.clientY);\n };\n this._container!.addEventListener('mousemove', this._tooltipMouseMoveHandler);\n\n this.on('hover', (payload) => {\n if (\n payload.objectType === 'enodeHitbox' &&\n payload.userData?.type === 'enodeHitbox' &&\n payload.userData.componentId !== null\n ) {\n const { label, componentType } = payload.userData;\n if (label && componentType) {\n this._pinTooltipWidget?.show(label, componentType, this._lastClientX, this._lastClientY);\n return;\n }\n }\n this._pinTooltipWidget?.hide();\n });\n\n this.on('unhover', () => this._pinTooltipWidget?.hide());\n }\n\n private _disposePinTooltip(): void {\n if (this._tooltipMouseMoveHandler && this._container) {\n this._container.removeEventListener('mousemove', this._tooltipMouseMoveHandler);\n this._tooltipMouseMoveHandler = null;\n }\n if (this._pinTooltipWidget) {\n this._pinTooltipWidget.dispose();\n this._pinTooltipWidget = null;\n }\n }\n\n /**\n * Open the config panel for a component (T014)\n *\n * @param componentId - UUID of the component to edit\n * @param screenPosition - Screen coordinates for panel positioning\n * @returns true if panel opened, false if component has no config\n */\n openConfigPanel(componentId: UUID, screenPosition: { x: number; y: number }): boolean {\n this._checkInitialized();\n if (!this._configPanelManager) {\n throw new Error('ConfigPanelManager not initialized');\n }\n if (!this._circuit) {\n return false;\n }\n const component = this._circuit.getComponent(componentId);\n if (!component) {\n console.warn(`ConfigPanelManager.open: Component ${componentId} not found`);\n return false;\n }\n return this._configPanelManager.open(component, screenPosition);\n }\n\n /**\n * Tool System Methods\n */\n\n /**\n * Convenience method that toggle a tool on if it is off (possibly deactivating previous tool), or off if it is on\n * @param toolType\n */\n toggleTool(toolType: ToolType): void {\n const previousTool = this._activeTool;\n\n if (previousTool !== null) {\n this.deactivateTool(toolType);\n }\n if (previousTool === toolType) {\n return;\n }\n this.setActiveTool(toolType);\n }\n\n deactivateTool(toolType: ToolType): void {\n if (this._activeTool === null) {\n return;\n }\n if (this._activeTool !== toolType) {\n return; // only deactivate if the specified tool is active\n }\n\n const previousTool = this._activeTool;\n const tool = this._tools.get(previousTool);\n\n if (tool) {\n tool.onDeactivate();\n }\n\n this._activeTool = null;\n // Emit toolDeactivated event\n this.emit('toolDeactivated', { toolType: previousTool });\n }\n\n /**\n * Get the currently active tool (FR-028)\n *\n * @returns Current tool type or null if no tool is active\n */\n getActiveTool(): ToolType | null {\n return this._activeTool;\n }\n\n /**\n * Set the active editing tool (FR-026, FR-028, FR-034)\n * Only one tool can be active at a time\n * Switching tools will deactivate the previous tool\n *\n * @param toolType - Type of tool to activate\n * @throws {Error} If edit mode is not enabled\n */\n setActiveTool(toolType: ToolType): void {\n this._checkInitialized();\n\n if (!this._editMode) {\n throw new Error('Edit mode must be enabled to activate tools');\n }\n // Check if tool is already active\n if (this._activeTool === toolType) {\n return;\n } else if (this._activeTool !== null) {\n // Deactivate previous tool\n this.deactivateTool(this._activeTool);\n }\n\n // Activate new tool\n this._activeTool = toolType;\n const tool = this._tools.get(toolType);\n\n if (tool) {\n tool.onActivate();\n\n // Emit toolActivated event\n this.emit('toolActivated', { toolType });\n\n // Emit cursorChangeRequested event\n const cursorType = tool.getCursorType();\n this.emit('cursorChangeRequested', { cursorType });\n }\n }\n\n /**\n * Initialize editing tools\n * @private\n */\n private _initializeTools(): void {\n // Create tool instances\n this._tools.set('build', new BuildTool(this));\n this._tools.set('multiSelect', new MultiSelectTool(this));\n }\n\n /**\n * recreate all visuals based on circuit data\n * Should be called on an already cleared scene\n * @private\n */\n private _fullUpdate(): void {\n this._checkInitialized();\n\n if (!this._circuit) return;\n\n // Create visuals for all circuit elements\n const components = this._circuit.getAllComponents();\n const wires = this._circuit.getAllWires();\n const enodes = this._circuit.getAllENodes();\n\n for (const component of components) {\n this._createComponentObject3D(component);\n }\n for (const enode of enodes) {\n this._createEnodeObject3D(enode);\n }\n for (const wire of wires) {\n this._createWireObject3D(wire);\n }\n }\n\n private _createComponentObject3D(component: Component): void {\n try {\n const factory = this.factoryRegistry.get(component.type);\n // Support both function-based (legacy) and class-based (new) factories\n const mesh = factory.createVisual(component, this.visualContext);\n\n // Position mesh at component location (2D circuit -> 3D world)\n mesh.position.copy(gridToWorldPosition(component.position));\n mesh.rotation.copy(gridToWorldRotation(component.rotation));\n\n // Store component metadata\n mesh.userData.componentId = component.id;\n mesh.userData.componentType = component.type;\n\n this._scene!.add(mesh);\n this._indexComponentObject3D(component.id, mesh);\n\n // For edited pin enodes, update source type visual (component visual factory creates them only in their default mode)\n for (const pinId of component.pins) {\n const enode = this._circuit!.getENode(pinId);\n if (!enode || !enode.source) continue;\n const pinGroup = this._enodeObject3Ds.get(enode.id);\n if (!pinGroup) continue;\n this.factoryRegistry.getFallbackFactory().updatePinSourceType(pinGroup, enode.source);\n }\n } catch (error) {\n const err = error as Error;\n console.warn(`Failed to create mesh for component ${component.id}:`, err.message);\n this.emit('error', { message: `Component rendering failed: ${err.message}`, error: err });\n }\n }\n\n /**\n * Index component mesh and its pins meshes for interaction (hover, selection)\n * @param componentId\n * @param object3D\n * @private\n */\n private _indexComponentObject3D(componentId: string, object3D: THREE.Object3D): void {\n this._componentObject3Ds.set(componentId, object3D);\n object3D.traverse((obj) => {\n if (obj.userData && obj.userData.type === 'enodeGroup') {\n const enodeId = obj.userData.enodeId;\n if (enodeId) {\n this._enodeObject3Ds.set(enodeId, obj as THREE.Group);\n }\n }\n });\n }\n\n private _removeComponentObject3D(id: string): void {\n const group = this._componentObject3Ds.get(id);\n if (!group) {\n return;\n }\n\n this._scene!.remove(group);\n // Parcours complet pour disposer toutes les géométries / matériaux des enfants\n group.traverse((obj) => {\n if (obj.userData && obj.userData.type === 'enodeGroup') {\n this._removeEnodeObject3D(obj.userData.enodeId);\n } else if (obj instanceof THREE.Mesh) {\n if (obj.geometry) {\n obj.geometry.dispose();\n }\n if (obj.material) {\n if (Array.isArray(obj.material)) {\n obj.material.forEach((mat) => mat.dispose());\n } else {\n obj.material.dispose();\n }\n }\n }\n });\n this._componentObject3Ds.delete(id);\n }\n\n /**\n * Create enode (branching point ONLY) visual object and add to scene\n * pin enodes are created and attache to their components by createComponentObject3D()\n *\n * @param enode\n * @private\n */\n private _createEnodeObject3D(enode: ENode): void {\n // Skip pin enodes - they're visualized as part of their components\n if (enode.type === ENodeType.Pin) return;\n\n // Use BranchingPointVisualFactory to create the visual\n const group = this.branchingPointVisualFactory.createVisual(enode);\n\n // Use getPosition() to properly handle position retrieval\n group.position.copy(gridToWorldPosition(enode.getPosition(this._circuit!)));\n\n this._scene!.add(group);\n this._enodeObject3Ds.set(enode.id, group);\n }\n\n private _removeEnodeObject3D(id: string): void {\n const group = this._enodeObject3Ds.get(id);\n if (!group) return;\n group?.traverse((obj) => {\n if (obj instanceof THREE.Mesh) {\n if (obj.geometry) {\n obj.geometry.dispose();\n }\n if (obj.material) {\n if (Array.isArray(obj.material)) {\n obj.material.forEach((mat) => mat.dispose());\n } else {\n obj.material.dispose();\n }\n }\n }\n });\n this._scene!.remove(group);\n this._enodeObject3Ds.delete(id);\n }\n\n addBranchingPoint(worldPosition: THREE.Vector3, sourceType?: ENodeSourceType | undefined): ENode {\n const branchingPoint = this.circuitWriter.saveAddBranchingPoint(worldPosition, sourceType);\n // Create and add bp visual to scene\n this._createEnodeObject3D(branchingPoint);\n return branchingPoint;\n }\n\n /**\n * Split wire either :\n * - at a position, inserting a new branching point and two new wires replacing the deleted ones\n * - at an existing target enode, replacing the wire by two new wires connected to this enode\n * @param wireId\n * @param worldPosition - world Position for the new branching point : no effect if targetEnodeId provided\n * @param targetEnodeId - if provided, the existing enode to split the wire at\n */\n splitWire(\n wireId: UUID,\n worldPosition: THREE.Vector3,\n targetEnodeId: UUID | null = null\n ): { branchingPoint: ENode; wires: Wire[] } {\n // 1: Call CircuitWriter to split the wire and create branching point\n const result = this.circuitWriter.saveSplitWire(wireId, worldPosition, targetEnodeId);\n // 2: Remove old wire visual from scene\n this.wireVisualManager.removeWire(wireId);\n // 3: add new Branching point visual to the scene (only if not targetEnodeId)\n if (!targetEnodeId) {\n this._createEnodeObject3D(result.branchingPoint);\n }\n\n // 4: Add new wire visuals to scene\n for (const wire of result.wires) {\n this.wireVisualManager.createOrUpdateWire(wire);\n }\n\n return result;\n }\n\n /**\n * Remove branching point enode visual and update the circuit and visuals\n * @param enodeId\n */\n removeBranchingPoint(enodeId: UUID) {\n const result = this.circuitWriter.saveDeleteBranchingPoint(enodeId);\n if (!result) return;\n this._removeEnodeObject3D(enodeId);\n this._enodeObject3Ds.delete(enodeId);\n if (result.deletedWires) {\n for (const wireId of result.deletedWires) {\n this._removeWireObject3D(wireId);\n }\n this.autoAdjustCircuitGridSize();\n }\n if (result.mergedWires) {\n for (const wireId of result.mergedWires) {\n this._removeWireObject3D(wireId);\n }\n }\n if (result.newWire) {\n this._createWireObject3D(result.newWire);\n this.autoAdjustCircuitGridSize();\n }\n }\n\n private _createWireObject3D(wire: Wire): void {\n if (!this._scene || !this._circuit) {\n console.warn(`Cannot create wire ${wire.id}: scene or circuit not initialized`);\n return;\n }\n try {\n // Use WireVisualManager to create wire with pin-accurate endpoints\n this.wireVisualManager.createOrUpdateWire(wire);\n } catch (error) {\n const err = error as Error;\n console.warn(`Failed to create Line2 for wire ${wire.id}:`, err.message);\n }\n }\n\n /**\n * add a wire between two enodes : update the circuit and add visuals\n * @param sourceEnodeId\n * @param targetEnodeId\n */\n addWire(sourceEnodeId: UUID, targetEnodeId: UUID): Wire {\n const wire = this.circuitWriter.saveAddWire(sourceEnodeId, targetEnodeId);\n this.wireVisualManager.createOrUpdateWire(wire);\n return wire;\n }\n\n /**\n * Add a component to the circuit and scene\n *\n * @param type - Component type to add\n * @param worldPosition - Position in 3D world coordinates (x, z)\n * @param rotation - 3D world rotation, if left null the default componentType rotation is applied\n * @param config - Optional configuration map for the component\n * @param pinSources - Optional array of source types for the component pins\n * @returns The created Component\n */\n addComponent(\n type: ComponentType,\n worldPosition: THREE.Vector3,\n rotation: Euler | null,\n config?: Map<string, string> | undefined,\n pinSources?: Array<ENodeSourceType | undefined | null> | undefined\n ): Component {\n if (!rotation) {\n const factory = this.factoryRegistry.get(type);\n const defaultRotation = factory ? factory.defaultRotation() : 0;\n rotation = new THREE.Euler(0, defaultRotation, 0);\n }\n\n // Create component in circuit model\n const component = this.circuitWriter.saveAddComponent(\n type,\n worldPosition,\n rotation,\n config,\n pinSources\n );\n // Create and add visual to scene\n this._createComponentObject3D(component);\n return component;\n }\n\n /**\n * edit component config and update visuals if necessary\n *\n * @param componentId\n * @param newConfig - map of parameters to be merged into the current config\n */\n editComponentConfig(componentId: UUID, newConfig: Map<string, string>) {\n const component = this.circuitWriter.saveEditComponentConfig(componentId, newConfig);\n if (!component) return;\n\n const object3D = this._componentObject3Ds.get(componentId);\n if (!object3D) return;\n // Update visuals if component hasChanged\n const factory = this.factoryRegistry.get(component.type);\n factory.updateFromConfiguration(object3D, component.config);\n // if config change, update wires connected to component\n // TODO optimize to do it only if necessary (size, ...)\n this.wireVisualManager.updateWiresForComponent(component.id);\n return;\n }\n\n /**\n * cycle component config and update visuals if necessary\n * have effect only on components that supports fast config cycle (used to invert logic or initial state of switches)\n * else use editComponentConfig\n *\n * @returns if the component has changed config\n * @param componentId\n */\n cycleComponentConfig(componentId: UUID): boolean {\n const result = this.circuitWriter.cycleComponentConfig(componentId);\n if (!result.hasChanged) {\n return false;\n }\n const object3D = this._componentObject3Ds.get(componentId);\n if (!object3D) return false;\n // Update visuals if component hasChanged\n const factory = this.factoryRegistry.get(result.component.type);\n factory.updateFromConfiguration(object3D, result.component.config);\n return true;\n }\n\n /**\n * Remove a component from the circuit and scene\n *\n * @param componentId - UUID of the component to remove\n */\n removeComponent(componentId: UUID): void {\n // Remove from circuit model (also removes connected wires)\n const result = this.circuitWriter.saveDeleteComponent(componentId);\n // Remove visuals for wires that were connected to the component\n for (const wireId of result.deletedWires) {\n this._removeWireObject3D(wireId);\n }\n // Remove component visual\n this._removeComponentObject3D(componentId);\n this.autoAdjustCircuitGridSize();\n }\n\n /**\n * Update an enode based to a new source type.\n * @param enodeId - UUID of the enode\n * @param sourceType - New source type (null for no source)\n */\n updateEnodeSourceType(enodeId: UUID, sourceType: ENodeSourceType | null): void {\n const object3D = this._enodeObject3Ds.get(enodeId);\n if (!object3D) return;\n if (object3D.userData.lockedSourceType) return; // do not update locked source types\n\n this.circuitWriter.saveEditENodeSourceType(enodeId, sourceType);\n\n if (object3D.userData.componentId) {\n this.factoryRegistry.getFallbackFactory().updatePinSourceType(object3D, sourceType ?? null);\n } else {\n this.branchingPointVisualFactory.updateSourceType(object3D, sourceType ?? null);\n }\n }\n\n /**\n * Remove wire visual and update the circuit\n * @param wireId\n */\n removeWire(wireId: UUID) {\n this.circuitWriter.saveDeleteWire(wireId);\n this._removeWireObject3D(wireId);\n this.autoAdjustCircuitGridSize();\n }\n\n /**\n * Automatically adjust the circuit grid size and divisions based on positions of all core circuit elements.\n */\n autoAdjustCircuitGridSize() {\n this._checkInitialized();\n if (!this._circuit) return;\n if (this.circuitWriter.saveAutoAdjustCircuitSize()) {\n // Update halfSize\n this._gridHalfSize = Math.ceil(this._circuit.metadata.size / 2);\n // Update grid helper\n if (this.grid) {\n this._scene!.remove(this.grid);\n this.grid.geometry.dispose();\n }\n const options = this._options || controllerOptions();\n this.grid = createGridHelper(\n this._circuit.metadata.size,\n this._circuit.metadata.divisions,\n options.colorCenterLine!,\n options.colorGrid!\n );\n this._scene!.add(this.grid);\n }\n }\n\n /**\n * Hook called before exporting the circuit visualization.\n * Saves world informations such as camera position, in the circuit metadata.\n */\n public beforeExport(): void {\n if (!this._circuit || !this._camera || !this._mapControls) return;\n try {\n this.circuitWriter.saveCameraOptions();\n } catch (error) {\n console.warn(error);\n }\n }\n\n private _removeWireObject3D(id: string): void {\n if (this._wireObject3Ds.has(id)) {\n // Use WireVisualManager to remove wire (handles all disposal and delete from map)\n this.wireVisualManager.removeWire(id);\n }\n }\n\n protected _removeAllVisuals(): void {\n // Remove all wire meshes\n for (const id of Array.from(this._wireObject3Ds.keys())) {\n this._removeWireObject3D(id);\n }\n // Remove all enode meshes\n for (const id of Array.from(this._enodeObject3Ds.keys())) {\n this._removeEnodeObject3D(id);\n }\n // Remove all component meshes\n for (const id of Array.from(this._componentObject3Ds.keys())) {\n this._removeComponentObject3D(id);\n }\n // remove grid\n if (this.grid) {\n this._scene!.remove(this.grid);\n this.grid.dispose();\n this._grid = null;\n }\n }\n}\n","/**\n * Simulation Circuit Controller\n * @module scene/simulation/CircuitRunnercontroller\n *\n * Controls live circuit simulation with real-time state updates and animated current flow.\n * Provides smooth interpolation between discrete simulation ticks for fluid animation.\n */\n\nimport * as THREE from 'three';\nimport {\n type Component,\n type Wire,\n type ENode,\n type Circuit,\n type IUserCommand,\n TRANSITION_DEFAULTS,\n SIMULATION_SPEED,\n} from 'simple-circuit-engine/core';\nimport {\n ENodeType,\n ComponentType,\n CircuitRunner,\n BehaviorRegistry,\n} from 'simple-circuit-engine/core';\nimport type { IFactoryRegistry } from '../shared/components/ComponentVisualFactory';\nimport type {\n AnimationContext,\n SharedResources,\n HoveredElement,\n ControllerOptions,\n} from '../shared/types';\nimport { AbstractCircuitController } from '../shared/AbstractCircuitController';\nimport { gridToWorldPosition, gridToWorldRotation } from '../shared/utils/GeometryUtils';\n\n/**\n * Simulation Circuit Runner Controller Implementation\n *\n * Manages Three.js scene for live circuit simulation visualization.\n * Provides smooth interpolation between simulation ticks for 60fps rendering.\n * Animates current flow through wires and component state changes.\n */\nexport class CircuitRunnerController extends AbstractCircuitController {\n private _runner: CircuitRunner | null = null;\n private _behaviorRegistry: BehaviorRegistry;\n\n // Animation context shared with visual factories\n private _animationContext: AnimationContext | null = null;\n\n // Playback control state\n private _autoPlay = false;\n private _isPlaying: boolean = false;\n private _tickIntervalMs: number = SIMULATION_SPEED.DEFAULT_INTERVAL_MS;\n private _simulationLoopId: number | null = null;\n private _clickHandler: ((event: MouseEvent) => void) | null = null;\n\n /**\n * Create a new Simulation Circuit Controller\n *\n * @param factoryRegistry - Component visual factory registry\n * @param behaviorRegistry - Component behavior registry\n * @param sharedResources - Optional shared resources for facade pattern (CircuitEngine)\n * @throws {TypeError} If factoryRegistry is null/undefined\n */\n constructor(\n factoryRegistry: IFactoryRegistry,\n behaviorRegistry: BehaviorRegistry,\n sharedResources?: SharedResources\n ) {\n super(factoryRegistry, sharedResources);\n if (!behaviorRegistry) {\n throw new TypeError('BehaviorRegistry is required');\n }\n this._behaviorRegistry = behaviorRegistry;\n }\n\n /**\n * Check if simulation is currently playing (auto-advancing ticks)\n * Returns false if paused or no circuit loaded\n */\n get isPlaying(): boolean {\n return this._isPlaying;\n }\n\n /**\n * Get current tick interval in milliseconds\n * Default is 500ms (2 ticks per second)\n */\n get tickInterval(): number {\n return this._tickIntervalMs;\n }\n\n /**\n * Set tick interval in milliseconds (50-2000ms)\n * If simulation is playing, restarts the interval with new value\n *\n * @param value - Interval in milliseconds, must be between 50-2000ms\n * @throws {RangeError} If value is outside valid range\n */\n set tickInterval(value: number) {\n if (value < 50 || value > 2000) {\n throw new RangeError('Tick interval must be between 50 and 2000ms');\n }\n this._tickIntervalMs = value;\n\n // If playing, restart interval with new value\n if (this._isPlaying) {\n this.pause();\n this.play();\n }\n }\n\n /**\n * Get current simulation speed in ticks per second.\n * Range: 1-20 TPS\n */\n get simulationSpeed(): number {\n return Math.round(1000 / this._tickIntervalMs);\n }\n\n /**\n * Set simulation speed in ticks per second.\n * Value is clamped to range 1-20 TPS.\n * If simulation is playing, restarts the interval with new value.\n * Emits 'simulationSpeedChanged' event when speed changes.\n *\n * @param tps - Ticks per second (1-20)\n */\n set simulationSpeed(tps: number) {\n const previousSpeed = this.simulationSpeed;\n const clampedTps = Math.max(SIMULATION_SPEED.MIN_TPS, Math.min(SIMULATION_SPEED.MAX_TPS, tps));\n\n // Skip if no change\n if (clampedTps === previousSpeed) {\n return;\n }\n\n // Convert TPS to interval and apply\n this._tickIntervalMs = Math.round(1000 / clampedTps);\n\n // Update animation timescales for in-flight animations (reads old tps from context)\n this._updateAnimationTimescales(clampedTps);\n\n // Update context with new tps AFTER timescale adjustment\n if (this._animationContext) {\n this._animationContext.ticksPerSecond = clampedTps;\n }\n\n // If playing, restart interval with new value\n if (this._isPlaying) {\n this.pause();\n this.play();\n }\n\n // Emit speed changed event\n this.emit('simulationSpeedChanged', {\n previousSpeed,\n newSpeed: clampedTps,\n });\n }\n\n /**\n * Minimum allowed simulation speed in ticks per second.\n */\n get minSimulationSpeed(): number {\n return SIMULATION_SPEED.MIN_TPS;\n }\n\n /**\n * Maximum allowed simulation speed in ticks per second.\n */\n get maxSimulationSpeed(): number {\n return SIMULATION_SPEED.MAX_TPS;\n }\n\n /**\n * Compute the number of ticks required for a transition given its duration in milliseconds.\n * Formula: ceil(transitionUserSpanMs × simulationSpeed / 1000), minimum 1.\n *\n * @param transitionUserSpanMs - Transition duration in milliseconds\n * @returns Number of ticks for the transition (minimum 1)\n */\n computeTickCount(transitionUserSpanMs: number): number {\n const tickCount = Math.ceil((transitionUserSpanMs * this.simulationSpeed) / 1000);\n return Math.max(1, tickCount);\n }\n\n /**\n * Get the transition duration from component config for user-driven transitions.\n * @param config - Component config map\n * @returns Transition duration in milliseconds (defaults to TRANSITION_USER_SPAN_MS)\n */\n private _getTransitionUserSpan(config: Map<string, string>): number {\n const value = parseInt(config.get('transitionUserSpan') || '', 10);\n if (isNaN(value) || value < 0) {\n return TRANSITION_DEFAULTS.TRANSITION_USER_SPAN_MS;\n }\n return value;\n }\n\n /**\n * Get current simulation tick number\n * Returns 0 if no circuit runner is loaded\n */\n get currentTick(): number {\n return this._runner?.getCurrentTick() ?? 0;\n }\n\n /**\n * Specific Initialization logic, performed after AbstractCircuitController initialization\n * @private\n *\n * @param options - Controller options passed to initialize()\n */\n protected onInitialize(options?: ControllerOptions) {\n if (options) {\n if (options.simulationSpeed) this.simulationSpeed = options.simulationSpeed;\n if (typeof options.simulationAutoPlay == 'boolean')\n this._autoPlay = options.simulationAutoPlay;\n }\n // Register click handler for component (switches) interaction\n this._clickHandler = this._handleClick.bind(this);\n this._container!.addEventListener('click', this._clickHandler);\n\n // standalone mode -> Controller active\n if (!this._sharedResources) {\n this.setActive(true);\n }\n }\n\n protected emitReady() {\n this.emit('ready', { controllerType: 'simulation' });\n }\n\n /**\n * specific disposal prepended at the beginning of dispose process\n */\n protected onDispose(): void {\n // Stop simulation loop if running\n if (this._isPlaying) {\n this.pause();\n }\n\n // Remove click event listener if registered\n if (this._clickHandler && this._container) {\n this._container.removeEventListener('click', this._clickHandler);\n this._clickHandler = null;\n }\n\n // Clear runner reference\n this._runner = null;\n }\n\n onSetActive(active: boolean): void {\n if (!active) {\n this.stop();\n this._runner = null;\n this._removeSimulationStateVisuals();\n this.factoryRegistry.setAnimationContext(null);\n this._animationContext = null;\n } else {\n if (!this._circuit) return;\n // recreate runner for the current circuit (which can have been modified in edit mode while this controller was inactive)\n this._runner = new CircuitRunner(this._circuit, this._behaviorRegistry);\n // Create and fan out animation context before visual update\n this._animationContext = {\n ticksPerSecond: this.simulationSpeed,\n simulationStatus: 'initial',\n };\n this.factoryRegistry.setAnimationContext(this._animationContext);\n // update graphics\n this._fullUpdate();\n // if autoplay launch !\n if (this._autoPlay) this.play();\n }\n }\n\n setCircuit(circuit: Circuit | null): void {\n this._checkInitialized();\n if (circuit === this._circuit) return;\n\n // Stop current simulation if playing\n if (this._isPlaying) {\n this.stop();\n }\n this._runner = null;\n\n // When using shared resources, skip visual management (edit controller handles it)\n // Just update circuit reference and create runner\n if (this._useSharedResources) {\n this._circuit = circuit;\n this.wireVisualManager.setCircuit(circuit);\n if (circuit) {\n this._gridHalfSize = Math.ceil(circuit.metadata.size / 2);\n if (!this._active) return; // nothing more to do if not active\n // if active launch the thing\n this._runner = new CircuitRunner(circuit, this._behaviorRegistry);\n // Create/refresh animation context before visual update\n this._animationContext = {\n ticksPerSecond: this.simulationSpeed,\n simulationStatus: 'initial',\n };\n this.factoryRegistry.setAnimationContext(this._animationContext);\n // update graphics\n this._fullUpdate();\n // if autoplay launch !\n if (this._autoPlay) this.play();\n }\n return;\n }\n\n // Standalone mode: full visual management\n this._setCircuit(circuit);\n }\n\n /**\n * specific logic when to render a new set circuit\n * @protected\n */\n protected onSetCircuit() {\n if (!this._circuit) return;\n this._runner = new CircuitRunner(this._circuit, this._behaviorRegistry);\n if (!this._useSharedResources) {\n // if standalone mode, activate immediately\n this.setActive(true);\n }\n }\n\n /**\n * Play automatic simulation playback\n * Simulation will advance at the configured tick interval until paused\n *\n * Requires a circuit runner to be loaded via setCircuitRunner()\n * Emits 'simulationPlayed' event on play\n * Emits 'simulationTick' event on each tick\n */\n play(): void {\n if (!this._runner) {\n console.warn('Cannot play: no circuit runner loaded');\n return;\n }\n\n if (this._isPlaying) {\n return; // Already playing\n }\n this._isPlaying = true;\n if (this._animationContext) {\n this._animationContext.simulationStatus = 'playing';\n }\n // Kick all factories so animations start/resume now that status is 'playing'\n this._visualUpdateFromSimulationState();\n this.emit('simulationPlayed', { tick: this._runner.getCurrentTick() });\n\n // Start interval loop\n this._simulationLoopId = window.setInterval(() => {\n this._executeTick();\n }, this._tickIntervalMs);\n }\n\n /**\n * Pause automatic simulation playback\n * Safe to call even if already paused or no circuit loaded\n *\n * Emits 'simulationPaused' event\n */\n pause(): void {\n if (!this._isPlaying) {\n return; // Already paused\n }\n\n this._isPlaying = false;\n if (this._animationContext) {\n this._animationContext.simulationStatus = 'paused';\n }\n\n // Clear interval\n if (this._simulationLoopId !== null) {\n window.clearInterval(this._simulationLoopId);\n this._simulationLoopId = null;\n }\n\n this.emit('simulationPaused', { tick: this._runner?.getCurrentTick() ?? 0 });\n }\n\n /**\n * Execute a single simulation tick\n * Simulation remains paused after step, useful for debugging\n *\n * If currently playing, pauses first then steps\n * Requires a circuit runner to be loaded via setCircuitRunner()\n * Emits 'simulationStepped' event with tick result\n */\n step(): void {\n if (!this._runner) {\n console.warn('Cannot step: no circuit runner loaded');\n return;\n }\n\n // If playing, pause first\n if (this._isPlaying) {\n this.pause();\n }\n\n // Execute one tick\n const result = this._executeTick();\n\n this.emit('simulationStepped', { tick: this._runner.getCurrentTick(), result });\n }\n\n /**\n * Stop the simulation, reset visual to initial state\n * Simulation remains paused after step, useful for debugging\n *\n * If currently playing, pauses first then steps\n * Requires a circuit runner to be loaded via setCircuitRunner()\n * Emits 'simulationStopped' event with tick result (0)\n */\n stop(): void {\n if (!this._runner) {\n console.warn('Cannot step: no circuit runner loaded');\n return;\n }\n // If playing, pause first\n if (this._isPlaying) {\n this.pause();\n }\n if (this._animationContext) {\n this._animationContext.simulationStatus = 'initial';\n }\n this._runner.reset();\n // Update visuals to initial state\n this._visualUpdateFromSimulationState();\n const result = this._runner.getCurrentTick();\n\n this.emit('simulationStopped', { tick: result });\n }\n\n /**\n * Execute one simulation tick and update visuals\n * @private\n */\n private _executeTick(): unknown {\n if (!this._runner) {\n return null;\n }\n\n // Execute simulation tick\n const result = this._runner.tick();\n\n // Get dirty elements for optimized updates\n const dirty = this._runner.dirtyTracker.getDirtyElements();\n\n // Emit tick event\n this.emit('simulationTick', { tick: this._runner.getCurrentTick(), dirty });\n\n // Update visuals for changed elements\n this._updateDirtyComponents(dirty);\n this._updateDirtyWires(dirty);\n this._updateDirtyEnodes(dirty);\n\n return result;\n }\n\n /**\n * Update component animations for dirty components\n * @private\n */\n private _updateDirtyComponents(dirty: { components: ReadonlySet<string> }): void {\n if (!this._runner) return;\n\n // Update each dirty component's visual animation\n for (const componentId of dirty.components) {\n const object3D = this._componentObject3Ds.get(componentId);\n if (!object3D) continue;\n\n // Get component and its current state\n const component = this._circuit?.getComponent(componentId);\n if (!component) continue;\n\n const state = this._runner.getComponentState(componentId);\n if (!state) continue;\n\n // Get factory and update animation\n const factory = this.factoryRegistry.get(component.type);\n factory.updateAnimation(object3D, state);\n }\n }\n\n /**\n * Update all active AnimationMixers.\n * Called per frame from the render loop via CircuitEngine.update(delta).\n *\n * @param delta - Time in seconds since last frame\n */\n updateAnimations(delta: number): void {\n if (!this._animationContext || this._animationContext.simulationStatus !== 'playing') return;\n\n for (const object3D of this._componentObject3Ds.values()) {\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n if (!mixer) continue;\n mixer.update(delta);\n }\n }\n\n /**\n * Recompute animation timescales when simulation speed changes mid-animation.\n * Updates ticksPerSecond on userData and adjusts active action timeScales.\n */\n private _updateAnimationTimescales(newTps: number): void {\n const previousTps = this._animationContext?.ticksPerSecond ?? newTps;\n\n for (const object3D of this._componentObject3Ds.values()) {\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n if (!mixer) continue;\n\n // Adjust timeScale of active action to match new speed\n const action = object3D.userData.currentAction as THREE.AnimationAction | undefined;\n if (!action) continue;\n\n // Scale ratio: new speed / old speed\n action.timeScale = newTps / previousTps;\n }\n }\n\n /**\n * Update wire visual state based on electrical state\n * @private\n */\n private _updateDirtyWires(dirty: { wires: ReadonlySet<string> }): void {\n if (!this._runner) return;\n\n // Update each dirty wire's material state\n for (const wireId of dirty.wires) {\n // Get wire electrical state from runner\n const wireState = this._runner.getWireState(wireId);\n if (!wireState) continue;\n\n // Determine material state based on electrical state\n // either idle, voltage, current or vc (voltage and current)\n let materialState: 'current' | 'voltage' | 'vc' | 'idle';\n if (wireState.hasCurrent && wireState.hasVoltage) {\n materialState = 'vc';\n } else if (wireState.hasVoltage) {\n materialState = 'voltage';\n } else if (wireState.hasCurrent) {\n materialState = 'current';\n } else {\n materialState = 'idle';\n }\n\n // Apply material state via WireVisualManager\n this.wireVisualManager.applyElectricalState(wireId, materialState);\n }\n }\n\n /**\n * Update enode visual state based on electrical state\n * Applies emissive glow to pins and branching points\n * @private\n */\n private _updateDirtyEnodes(dirty: { enodes: ReadonlySet<string> }): void {\n if (!this._runner) return;\n\n // Update each dirty enode's emissive state\n for (const enodeId of dirty.enodes) {\n // Get enode electrical state from runner\n const enodeState = this._runner.getEnodeState(enodeId);\n if (!enodeState) continue;\n\n // Get enode visual object (could be pin group or branching point)\n const enodeObject = this._enodeObject3Ds.get(enodeId);\n if (!enodeObject) continue;\n\n enodeObject.userData;\n\n // Determine emissive color based on electrical state\n // Priority: current (blue) > voltage (red) > none\n let emissiveColor: number;\n if (enodeState.hasCurrent && enodeState.hasVoltage) {\n enodeObject.userData.electricalState = 'vc';\n emissiveColor = 0xcc00cc; // Magenta for voltage and current (current circulating)\n } else if (enodeState.hasCurrent) {\n enodeObject.userData.electricalState = 'current';\n emissiveColor = 0x0000ff; // Blue for current\n } else if (enodeState.hasVoltage) {\n enodeObject.userData.electricalState = 'voltage';\n emissiveColor = 0xff0000; // Red for voltage only\n } else {\n enodeObject.userData.electricalState = 'idle';\n }\n\n // Apply emissive color to all meshes in the enode group\n enodeObject.traverse((obj) => {\n if (obj instanceof THREE.Mesh && obj.material) {\n const material = obj.material as THREE.MeshStandardMaterial;\n if (material.emissive) {\n material.emissive.setHex(emissiveColor);\n material.emissiveIntensity = emissiveColor === 0x000000 ? 0 : 1;\n }\n }\n });\n }\n }\n\n /**\n * rollback wires/enodes/components visuals to edition state (no simulation state)\n */\n _removeSimulationStateVisuals(): void {\n for (const wireId of this._wireObject3Ds.keys()) {\n this.wireVisualManager.applyElectricalState(wireId, 'idle');\n }\n for (const enodeId of this._enodeObject3Ds.keys()) {\n const enodeObject = this._enodeObject3Ds.get(enodeId);\n if (!enodeObject) continue;\n enodeObject.userData.electricalState = 'idle';\n enodeObject.traverse((obj) => {\n if (obj instanceof THREE.Mesh && obj.material) {\n const material = obj.material as THREE.MeshStandardMaterial;\n if (material.emissive) {\n material.emissive.setHex(0x000000);\n material.emissiveIntensity = 0;\n }\n }\n });\n }\n for (const componentId of this._componentObject3Ds.keys()) {\n const componentObject = this._componentObject3Ds.get(componentId);\n if (!componentObject) continue;\n // Get component and its current state\n const component = this._circuit?.getComponent(componentId);\n if (!component) continue;\n const factory = this.factoryRegistry.get(component.type);\n factory.updateAnimation(componentObject, null);\n }\n }\n\n /**\n * Handle click events for component interaction\n * @param event\n * @private\n */\n private _handleClick(event: MouseEvent): void {\n if (!this._active) return;\n // Only handle left clicks\n if (event.button !== 0) return;\n const hoveredElement = this.getHoveredElement();\n if (!hoveredElement) return;\n if (event.metaKey && event.ctrlKey) {\n this._handleCtrlClick(hoveredElement);\n } else {\n this._handleRegularClick(hoveredElement);\n }\n }\n\n /**\n * Handle regular click events : emit user commands to the runner\n * @param clickedElement\n * @private\n */\n private _handleRegularClick(clickedElement: HoveredElement) {\n if (!this._runner) return; // only process if we have a runner\n if (clickedElement.type === 'wire') return;\n let componentGroup = null;\n if (clickedElement.type === 'component') {\n componentGroup = clickedElement.object3D.parent;\n } else if (clickedElement.type === 'enode') {\n // for pin enodes, get parent component\n const enode = this._circuit?.getENode(clickedElement.id);\n if (!enode) return;\n if (!enode.component) return;\n componentGroup = this._componentObject3Ds.get(enode.component);\n }\n if (!componentGroup) return;\n const componentType = componentGroup.userData.componentType;\n const componentId = componentGroup.userData.componentId;\n switch (componentType) {\n case ComponentType.DoubleThrowSwitch:\n case ComponentType.Switch: {\n // Get component to read its config\n const component = this._circuit?.getComponent(componentId);\n if (!component) return;\n\n // Compute tickCount from transitionUserSpan and simulationSpeed\n const transitionUserSpan = this._getTransitionUserSpan(component.config);\n const tickCount = this.computeTickCount(transitionUserSpan);\n\n const command: IUserCommand = {\n type: 'toggle_switch',\n targetId: componentId,\n scheduledAtTick: this._runner.getCurrentTick(),\n parameters: new Map<string, string>([['tickCount', String(tickCount)]]),\n };\n // Submit command to runner and emit event\n this._runner.submitCommand(command);\n this.emit('simulationUserCommand', command);\n return;\n }\n default:\n return;\n }\n }\n\n private _handleCtrlClick(_clickedElement: HoveredElement) {\n // TODO: implement ctrl+click handling\n console.warn('TODO: implement ctrl+click handling');\n }\n\n /**\n * recreate all visuals based on circuit data\n * Should be called on an already cleared scene\n *\n * When using shared resources (CircuitEngine facade), skips visual creation\n * if visuals already exist in the shared maps (created by edit controller).\n * @private\n */\n private _fullUpdate(): void {\n this._checkInitialized();\n\n if (!this._circuit) return;\n\n // When using shared resources and visuals already exist, skip creation\n // The edit controller has already created all visuals\n\n if (!this._useSharedResources) {\n // Create visuals for all circuit elements\n const components = this._circuit.getAllComponents();\n const wires = this._circuit.getAllWires();\n const enodes = this._circuit.getAllENodes();\n\n for (const component of components) {\n this._createComponentObject3D(component);\n }\n for (const enode of enodes) {\n this._createEnodeObject3D(enode);\n }\n for (const wire of wires) {\n this._createWireObject3D(wire);\n }\n }\n\n // Always update simulation visual state (colors, animations) from simulation state\n this._visualUpdateFromSimulationState();\n }\n\n /**\n * Consider all elements as dirty to update all visual state according to simulation state\n * @private\n */\n private _visualUpdateFromSimulationState(): void {\n if (!this._circuit || !this._runner) return;\n\n const components = this._circuit.getAllComponents();\n const wires = this._circuit.getAllWires();\n const enodes = this._circuit.getAllENodes();\n\n this._updateDirtyComponents({ components: new Set(components.map((c) => c.id)) });\n this._updateDirtyEnodes({ enodes: new Set(enodes.map((e) => e.id)) });\n this._updateDirtyWires({ wires: new Set(wires.map((w) => w.id)) });\n }\n\n private _createComponentObject3D(component: Component): void {\n try {\n const factory = this.factoryRegistry.get(component.type);\n // Support both function-based (legacy) and class-based (new) factories\n const mesh = factory.createVisual(component, this.visualContext);\n\n // Position mesh at component location (2D circuit -> 3D world)\n mesh.position.copy(gridToWorldPosition(component.position));\n mesh.rotation.copy(gridToWorldRotation(component.rotation));\n\n // Store component metadata\n mesh.userData.componentId = component.id;\n mesh.userData.componentType = component.type;\n\n this._scene!.add(mesh);\n this._indexComponentObject3D(component.id, mesh);\n\n // For edited pin enodes, update source type visual (component visual factory creates them only in their default mode)\n for (const pinId of component.pins) {\n const enode = this._circuit!.getENode(pinId);\n if (!enode || !enode.source) continue;\n const pinGroup = this._enodeObject3Ds.get(enode.id);\n if (!pinGroup) continue;\n this.factoryRegistry.getFallbackFactory().updatePinSourceType(pinGroup, enode.source);\n }\n } catch (error) {\n const err = error as Error;\n console.warn(`Failed to create mesh for component ${component.id}:`, err.message);\n this.emit('error', { message: `Component rendering failed: ${err.message}`, error: err });\n }\n }\n\n /**\n * Index component mesh and its pins meshes for interaction (hover, selection)\n * @param componentId\n * @param object3D\n * @private\n */\n private _indexComponentObject3D(componentId: string, object3D: THREE.Object3D): void {\n this._componentObject3Ds.set(componentId, object3D);\n object3D.traverse((obj) => {\n if (obj.userData && obj.userData.type === 'enodeGroup') {\n const enodeId = obj.userData.enodeId;\n if (enodeId) {\n this._enodeObject3Ds.set(enodeId, obj as THREE.Group);\n }\n }\n });\n }\n\n /**\n * Create enode (branching point ONLY) visual object and add to scene\n * pin enodes are created and attache to their components by createComponentObject3D()\n *\n * @param enode\n * @private\n */\n private _createEnodeObject3D(enode: ENode): void {\n // Skip pin enodes - they're visualized as part of their components\n if (enode.type === ENodeType.Pin) return;\n\n // Use BranchingPointVisualFactory to create the visual\n const group = this.branchingPointVisualFactory.createVisual(enode);\n\n // Use getPosition() to properly handle position retrieval\n group.position.copy(gridToWorldPosition(enode.getPosition(this._circuit!)));\n\n this._scene!.add(group);\n this._enodeObject3Ds.set(enode.id, group);\n }\n\n private _createWireObject3D(wire: Wire): void {\n if (!this._scene || !this._circuit) {\n console.warn(`Cannot create wire ${wire.id}: scene or circuit not initialized`);\n return;\n }\n try {\n // Use WireVisualManager to create wire with pin-accurate endpoints\n this.wireVisualManager.createOrUpdateWire(wire);\n } catch (error) {\n const err = error as Error;\n console.warn(`Failed to create Line2 for wire ${wire.id}:`, err.message);\n }\n }\n\n private _removeComponentObject3D(id: string): void {\n const group = this._componentObject3Ds.get(id);\n if (!group) {\n return;\n }\n\n // Clean up AnimationMixer if present\n const mixer = group.userData.mixer as THREE.AnimationMixer | undefined;\n if (mixer) {\n mixer.stopAllAction();\n mixer.uncacheRoot(group);\n }\n\n this._scene!.remove(group);\n // Parcours complet pour disposer toutes les géométries / matériaux des enfants\n group.traverse((obj) => {\n if (obj.userData && obj.userData.type === 'enodeGroup') {\n this._removeEnodeObject3D(obj.userData.enodeId);\n } else if (obj instanceof THREE.Mesh) {\n if (obj.geometry) {\n obj.geometry.dispose();\n }\n if (obj.material) {\n if (Array.isArray(obj.material)) {\n obj.material.forEach((mat) => mat.dispose());\n } else {\n obj.material.dispose();\n }\n }\n }\n });\n this._componentObject3Ds.delete(id);\n }\n\n private _removeEnodeObject3D(id: string): void {\n const group = this._enodeObject3Ds.get(id);\n if (!group) return;\n\n group?.traverse((obj) => {\n if (obj instanceof THREE.Mesh) {\n if (obj.geometry) {\n obj.geometry.dispose();\n }\n if (obj.material) {\n if (Array.isArray(obj.material)) {\n obj.material.forEach((mat) => mat.dispose());\n } else {\n obj.material.dispose();\n }\n }\n }\n });\n this._scene!.remove(group);\n this._enodeObject3Ds.delete(id);\n }\n\n private _removeWireObject3D(id: string): void {\n if (this._wireObject3Ds.has(id)) {\n // Use WireVisualManager to remove wire (handles all disposal and delete from map)\n this.wireVisualManager.removeWire(id);\n }\n }\n\n protected _removeAllVisuals(): void {\n // Remove all wire meshes\n for (const id of Array.from(this._wireObject3Ds.keys())) {\n this._removeWireObject3D(id);\n }\n // Remove all enode meshes\n for (const id of Array.from(this._enodeObject3Ds.keys())) {\n this._removeEnodeObject3D(id);\n }\n // Remove all component meshes\n for (const id of Array.from(this._componentObject3Ds.keys())) {\n this._removeComponentObject3D(id);\n }\n }\n}\n","/**\n * CircuitEngine - Unified Facade for Circuit Editing and Simulation\n * @module scene/CircuitEngine\n *\n * Provides a unified API for both static circuit editing and live simulation,\n * managing mode transitions and resource sharing between controllers.\n */\n\nimport * as THREE from 'three';\nimport { MapControls } from 'three/addons/controls/MapControls.js';\nimport type { Line2 } from 'three/examples/jsm/lines/Line2.js';\nimport { EventEmitter } from './shared/EventEmitter';\n\nimport type { Circuit, BehaviorRegistry, UUID } from 'simple-circuit-engine/core';\n\nimport { CircuitController } from './static/CircuitController';\nimport { CircuitRunnerController } from './simulation/CircuitRunnerController';\nimport { HoverManager } from './shared/HoverManager';\nimport { WireVisualManager } from './shared/WireVisualManager';\nimport { BranchingPointVisualFactory } from './shared/BranchingPointVisualFactory';\nimport { createPerspectiveCamera, updateCamera } from './shared/utils/CameraUtils';\nimport { setupSceneLights } from './shared/utils/LightingUtils';\nimport type { IFactoryRegistry } from './shared/components/ComponentVisualFactory';\nimport type {\n EngineMode,\n SharedResources,\n CircuitEngineEventMap,\n EngineOptions,\n ToolType,\n} from './shared/types';\nimport { createGridHelper } from './shared/utils/GeometryUtils';\nimport { engineOptions } from './shared/utils/Options';\nimport { createMapControls } from './shared/utils/ControlsUtils';\n\n/**\n * CircuitEngine - Unified Facade for Circuit Editing and Simulation\n *\n * Manages two internal controllers:\n * - CircuitController: Static editing with tools, selection, and manipulation\n * - CircuitRunnerController: Live simulation with playback and animation\n *\n * Both controllers share the same Three.js scene, camera, and visual objects,\n * enabling seamless transitions between edit and simulation modes.\n *\n * @example\n * ```typescript\n * const engine = new CircuitEngine(factoryRegistry, behaviorRegistry);\n * engine.initialize(container);\n * engine.setCircuit(circuit);\n *\n * // Edit mode (default)\n * engine.setEditModeEnabled(true);\n * engine.setActiveTool('build');\n *\n * // Switch to simulation\n * engine.setMode('simulation');\n * engine.play();\n *\n * // Switch back to edit\n * engine.setMode('edit');\n * ```\n */\nexport class CircuitEngine extends EventEmitter<CircuitEngineEventMap> {\n // Dependencies\n private readonly _factoryRegistry: IFactoryRegistry;\n private readonly _behaviorRegistry: BehaviorRegistry;\n\n // Shared resources\n private _sharedResources: SharedResources | null = null;\n private _container: HTMLElement | null = null;\n\n // Controllers\n private _editController: CircuitController | null = null;\n private _simulationController: CircuitRunnerController | null = null;\n\n // State\n private _mode: EngineMode = 'edit';\n private _initialized: boolean = false;\n private _options: EngineOptions | null = null;\n private _disposed: boolean = false;\n\n // Event forwarding cleanup functions\n private _editControllerCleanup: (() => void) | null = null;\n private _simulationControllerCleanup: (() => void) | null = null;\n\n /**\n * Create a new CircuitEngine instance.\n *\n * @param factoryRegistry - Registry of component visual factories\n * @param behaviorRegistry - Registry of component simulation behaviors\n * @throws {TypeError} If factoryRegistry or behaviorRegistry is null/undefined\n */\n constructor(factoryRegistry: IFactoryRegistry, behaviorRegistry: BehaviorRegistry) {\n super();\n\n if (!factoryRegistry) {\n throw new TypeError('FactoryRegistry is required');\n }\n if (!behaviorRegistry) {\n throw new TypeError('BehaviorRegistry is required');\n }\n\n this._factoryRegistry = factoryRegistry;\n this._behaviorRegistry = behaviorRegistry;\n }\n\n // ============================================================================\n // Lifecycle\n // ============================================================================\n\n /**\n * Check if engine is initialized\n */\n get isInitialized(): boolean {\n return this._initialized;\n }\n\n /**\n * Check if engine is disposed\n */\n get isDisposed(): boolean {\n return this._disposed;\n }\n\n /**\n * Initialize the engine with a DOM container.\n * Creates shared Three.js resources and both controllers.\n *\n * @param container - HTMLElement to mount the scene\n * @param options - Configuration options\n * @throws {TypeError} If container is not a valid HTMLElement\n * @throws {Error} If already initialized\n */\n initialize(container: HTMLElement, options?: EngineOptions): void {\n if (this._initialized) {\n throw new Error('CircuitEngine is already initialized');\n }\n if (this._disposed) {\n throw new Error('CircuitEngine has been disposed');\n }\n if (!container || !(container instanceof HTMLElement)) {\n throw new TypeError('Container must be a valid HTMLElement');\n }\n\n options = engineOptions(options);\n this._options = options;\n\n this._container = container;\n\n // Create shared resources\n this._sharedResources = this._createSharedResources(container, options);\n\n // Create controllers with shared resources\n this._editController = new CircuitController(this._factoryRegistry, this._sharedResources);\n this._simulationController = new CircuitRunnerController(\n this._factoryRegistry,\n this._behaviorRegistry,\n this._sharedResources\n );\n // Initialize both controllers\n this._editController.initialize(container, options.controllerOptions);\n this._simulationController.initialize(container, options.controllerOptions);\n\n // Setup event forwarding from both controllers\n this._setupEventForwarding();\n\n // Set initial mode\n this._mode = options?.initialMode ?? 'edit';\n if (this._mode === 'edit') {\n this._editController.setActive(true);\n } else {\n this._simulationController.setActive(true);\n }\n\n this._initialized = true;\n\n // Emit ready event\n this.emit('ready', { controllerType: 'engine' });\n const startupMode = this._mode as EngineMode;\n this.emit('modeChanged', { mode: startupMode });\n }\n\n /**\n * Create shared resources for both controllers.\n */\n private _createSharedResources(container: HTMLElement, options: EngineOptions): SharedResources {\n const controllerOptions = options.controllerOptions!;\n // Create scene\n const scene = new THREE.Scene();\n scene.background = new THREE.Color(controllerOptions.backgroundColor);\n // Add default sized grid\n const grid = createGridHelper(\n 10,\n 10,\n controllerOptions.colorCenterLine!,\n controllerOptions.colorGrid!\n );\n scene.add(grid);\n setupSceneLights(scene);\n\n // Create camera\n const aspect = container.clientWidth / container.clientHeight || 1;\n const camera = createPerspectiveCamera(aspect);\n camera.layers.set(0); // main visual layer\n camera.layers.enable(1); // enode hover layer\n camera.layers.enable(2); // component hover layer\n\n // Create MapControls\n const mapControls = createMapControls(camera, container, controllerOptions.mapControls!);\n\n // Create managers\n const hoverManager = new HoverManager(scene, camera);\n const branchingPointVisualFactory = new BranchingPointVisualFactory();\n\n // Create shared visual maps\n const componentObject3Ds = new Map<UUID, THREE.Object3D>();\n const enodeObject3Ds = new Map<UUID, THREE.Object3D>();\n const wireObject3Ds = new Map<UUID, Line2>();\n\n const wireVisualManager = new WireVisualManager(componentObject3Ds, wireObject3Ds);\n wireVisualManager.setContainer(container);\n wireVisualManager.setResolution(container.clientWidth, container.clientHeight);\n wireVisualManager.setSceneAndCamera(scene, camera);\n\n return {\n scene,\n camera,\n mapControls,\n grid: grid,\n factoryRegistry: this._factoryRegistry,\n branchingPointVisualFactory,\n wireVisualManager,\n hoverManager,\n componentObject3Ds,\n enodeObject3Ds,\n wireObject3Ds,\n };\n }\n\n /**\n * Setup event forwarding from both controllers to engine.\n */\n private _setupEventForwarding(): void {\n if (this._editController) {\n this._editControllerCleanup = this._editController.onAny((event, payload) => {\n // Forward all events from edit controller\n this.emit(event as keyof CircuitEngineEventMap, payload as any);\n });\n }\n\n if (this._simulationController) {\n this._simulationControllerCleanup = this._simulationController.onAny((event, payload) => {\n // Forward all events from simulation controller\n this.emit(event as keyof CircuitEngineEventMap, payload as any);\n });\n }\n }\n\n /**\n * Teardown event forwarding cleanup.\n */\n private _teardownEventForwarding(): void {\n if (this._editControllerCleanup) {\n this._editControllerCleanup();\n this._editControllerCleanup = null;\n }\n if (this._simulationControllerCleanup) {\n this._simulationControllerCleanup();\n this._simulationControllerCleanup = null;\n }\n }\n\n /**\n * Dispose all resources and clean up.\n *\n * @throws {Error} If not initialized or already disposed\n */\n dispose(): void {\n this._checkInitialized();\n\n // Stop simulation if running\n if (this._mode === 'simulation' && this._simulationController?.isPlaying) {\n this._simulationController.pause();\n }\n\n // Teardown event forwarding\n this._teardownEventForwarding();\n\n // Dispose controllers (they won't dispose shared resources)\n if (this._editController) {\n this._editController.dispose();\n this._editController = null;\n }\n if (this._simulationController) {\n this._simulationController.dispose();\n this._simulationController = null;\n }\n\n // Dispose shared resources (we own them)\n if (this._sharedResources) {\n this._sharedResources.hoverManager.dispose();\n this._sharedResources.wireVisualManager.dispose();\n this._sharedResources.mapControls.dispose();\n\n // Clear visual maps\n this._sharedResources.componentObject3Ds.clear();\n this._sharedResources.enodeObject3Ds.clear();\n this._sharedResources.wireObject3Ds.clear();\n\n this._sharedResources = null;\n }\n\n // Clear runner\n //this._runner = null;\n\n // Clear event listeners\n this.removeAllListeners();\n\n this._disposed = true;\n this._initialized = false;\n }\n\n // ============================================================================\n // Mode Management\n // ============================================================================\n\n /**\n * Current operating mode\n */\n get mode(): EngineMode {\n return this._mode;\n }\n\n /**\n * Refresh all scene widgets to display strings in the given language.\n *\n * Does NOT change the consumer's i18next instance — the caller must have\n * already called `i18next.changeLanguage(lng)` before invoking this. This\n * method only signals the scene to re-read translations for currently-open\n * widgets (pin tooltip, component picker, config panel).\n *\n * Safe to call at any point in the engine lifecycle after `initialize()`.\n *\n * @param lng - Target language code (e.g., 'en', 'fr')\n */\n setLanguage(lng: string): void {\n console.log(lng);\n this._checkInitialized();\n this._editController?.setLanguage(lng);\n this._simulationController?.setLanguage(lng);\n }\n\n /**\n * Switch between edit and simulation modes.\n *\n * @param mode - Target mode to switch to\n * @throws {Error} If not initialized\n * @throws {Error} If switching to simulation without a circuit loaded\n */\n setMode(mode: EngineMode): void {\n this._checkInitialized();\n\n // Early return if same mode\n if (this._mode === mode) {\n return;\n }\n\n const previousMode = this._mode;\n\n if (mode === 'simulation') {\n this._transitionToSimulation();\n } else {\n this._transitionToEdit();\n }\n\n this._mode = mode;\n\n // Emit modeChanged event\n this.emit('modeChanged', { mode, previousMode });\n }\n\n /**\n * Transition from edit mode to simulation mode.\n */\n private _transitionToSimulation(): void {\n // Validate circuit is loaded\n const circuit = this._editController?.getCircuit();\n if (!circuit) {\n throw new Error('Cannot switch to simulation mode: no circuit loaded');\n }\n\n // Set edit controller inactive\n if (this._editController) {\n this._editController.setActive(false);\n }\n\n // Set simulation controller active\n if (this._simulationController) {\n this._simulationController.setActive(true);\n }\n }\n\n /**\n * Transition from simulation mode to edit mode.\n */\n private _transitionToEdit(): void {\n // Set simulation controller inactive\n if (this._simulationController) {\n this._simulationController.setActive(false);\n }\n\n // Set edit controller active\n // Note: The edit controller maintains its circuit and visuals\n if (this._editController) {\n this._editController.setActive(true);\n }\n }\n\n // ============================================================================\n // Circuit Management\n // ============================================================================\n\n /**\n * Load a circuit for editing/simulation.\n *\n * @param circuit - Circuit to load, or null to clear\n * @throws {Error} If not initialized\n */\n setCircuit(circuit: Circuit | null): void {\n this._checkInitialized();\n const options = this._options || engineOptions();\n\n if (this._sharedResources?.grid) {\n this._sharedResources?.grid?.dispose();\n this._sharedResources?.scene.remove(this._sharedResources?.grid);\n }\n\n // Load circuit via edit controller\n if (this._editController) {\n this._editController.setCircuit(circuit);\n this._simulationController?.setCircuit(circuit);\n }\n\n const gridSize = circuit ? circuit.metadata.size : 10;\n const gridDivisions = circuit ? circuit.metadata.divisions : 10;\n this._sharedResources!.grid = createGridHelper(\n gridSize,\n gridDivisions,\n options.controllerOptions!.colorCenterLine!,\n options.controllerOptions!.colorGrid!\n );\n this._sharedResources?.scene.add(this._sharedResources!.grid);\n\n if (circuit && this._sharedResources?.camera) {\n updateCamera(this._sharedResources?.camera, circuit.metadata.cameraOptions);\n }\n if (circuit && this._sharedResources?.mapControls) {\n const controls = this._sharedResources?.mapControls;\n const target = circuit.metadata.cameraOptions.lookAtPosition;\n controls.target.set(target.x, target.y, target.z);\n }\n }\n\n /**\n * Get the currently loaded circuit\n */\n getCircuit(): Circuit | null {\n this._checkInitialized();\n return this._editController?.getCircuit() ?? null;\n }\n\n // ============================================================================\n // Controllers Access\n // ============================================================================\n\n /**\n * Get the edit controller for advanced operations.\n *\n * @throws {Error} If not initialized\n */\n getEditController(): CircuitController {\n this._checkInitialized();\n return this._editController!;\n }\n\n /**\n * Get the simulation controller for advanced operations.\n *\n * @throws {Error} If not initialized\n */\n getSimulationController(): CircuitRunnerController {\n this._checkInitialized();\n return this._simulationController!;\n }\n\n // ============================================================================\n // Edit Mode Operations\n // ============================================================================\n\n /**\n * Activate an editing tool.\n *\n * @param toolType - Tool to activate\n * @throws {Error} If not in edit mode\n */\n setActiveTool(toolType: ToolType): void {\n this._checkEditMode();\n this._editController!.setActiveTool(toolType);\n }\n\n /**\n * Get the currently active tool\n *\n * @throws {Error} If not in edit mode\n */\n getActiveTool(): ToolType | null {\n this._checkEditMode();\n return this._editController!.getActiveTool();\n }\n\n /**\n * Enable or disable edit mode (tool system).\n *\n * @param enabled - True to enable, false to disable\n * @throws {Error} If not in edit mode\n */\n setEditModeEnabled(enabled: boolean): void {\n this._checkEditMode();\n this._editController!.setEditMode(enabled);\n }\n\n // ============================================================================\n // Simulation Mode Operations\n // ============================================================================\n\n /**\n * Start automatic simulation playback.\n *\n * @throws {Error} If not in simulation mode\n */\n play(): void {\n this._checkSimulationMode();\n this._simulationController!.play();\n }\n\n /**\n * Pause automatic simulation playback.\n *\n * @throws {Error} If not in simulation mode\n */\n pause(): void {\n this._checkSimulationMode();\n this._simulationController!.pause();\n }\n\n /**\n * Execute a single simulation tick.\n *\n * @throws {Error} If not in simulation mode\n */\n step(): void {\n this._checkSimulationMode();\n this._simulationController!.step();\n }\n\n /**\n * Stop simulation and reset to initial state.\n *\n * @throws {Error} If not in simulation mode\n */\n stop(): void {\n this._checkSimulationMode();\n this._simulationController!.stop();\n }\n\n /**\n * Check if simulation is currently playing\n */\n get isPlaying(): boolean {\n if (this._mode !== 'simulation') {\n return false;\n }\n return this._simulationController?.isPlaying ?? false;\n }\n\n /**\n * Get current simulation tick\n */\n get currentTick(): number {\n if (this._mode !== 'simulation') {\n return 0;\n }\n return this._simulationController?.currentTick ?? 0;\n }\n\n /**\n * Tick interval in milliseconds\n */\n get tickInterval(): number {\n return this._simulationController?.tickInterval ?? 500;\n }\n\n set tickInterval(value: number) {\n if (this._simulationController) {\n this._simulationController.tickInterval = value;\n }\n }\n\n /**\n * Simulation speed in ticks per second.\n * Range: 1-20 TPS. Works in both edit and simulation modes.\n */\n get simulationSpeed(): number {\n return this._simulationController?.simulationSpeed ?? 2;\n }\n\n set simulationSpeed(tps: number) {\n if (this._simulationController) {\n this._simulationController.simulationSpeed = tps;\n }\n }\n\n /**\n * Minimum allowed simulation speed in ticks per second.\n */\n get minSimulationSpeed(): number {\n return this._simulationController?.minSimulationSpeed ?? 1;\n }\n\n /**\n * Maximum allowed simulation speed in ticks per second.\n */\n get maxSimulationSpeed(): number {\n return this._simulationController?.maxSimulationSpeed ?? 20;\n }\n\n // ============================================================================\n // Per-frame Update\n // ============================================================================\n\n /**\n * Update active animations. Call once per frame from the render loop.\n * No-op if not initialized, disposed, or not in simulation mode.\n *\n * @param delta - Time in seconds since last frame (from THREE.Clock.getDelta())\n */\n update(delta: number): void {\n if (!this._initialized || this._disposed || this._mode !== 'simulation') return;\n this._simulationController!.updateAnimations(delta);\n }\n\n // ============================================================================\n // Three.js Access\n // ============================================================================\n\n /**\n * Get the Three.js scene for external rendering.\n *\n * @throws {Error} If not initialized\n */\n getScene(): THREE.Scene {\n this._checkInitialized();\n return this._sharedResources!.scene;\n }\n\n /**\n * Get the camera for external rendering.\n *\n * @throws {Error} If not initialized\n */\n getCamera(): THREE.PerspectiveCamera {\n this._checkInitialized();\n return this._sharedResources!.camera;\n }\n\n /**\n * Get the MapControls for external manipulation.\n *\n * @throws {Error} If not initialized\n */\n getControls(): MapControls {\n this._checkInitialized();\n return this._sharedResources!.mapControls;\n }\n\n /**\n * Hook called before exporting the circuit visualization.\n * Saves world informations such as camera position, in the circuit metadata.\n */\n public beforeExport(): void {\n if (!this._editController) return;\n this._editController.beforeExport();\n }\n\n /**\n * Handle container resize.\n *\n * @param width - New width (optional, uses container if omitted)\n * @param height - New height (optional, uses container if omitted)\n */\n onContainerResize(width?: number, height?: number): void {\n this._checkInitialized();\n\n const w = width ?? this._container!.clientWidth;\n const h = height ?? this._container!.clientHeight;\n\n // Update camera\n const camera = this._sharedResources!.camera;\n camera.aspect = w / h;\n camera.updateProjectionMatrix();\n\n // Update wire visual manager resolution\n this._sharedResources!.wireVisualManager.setResolution(w, h);\n\n // Delegate to active controller for any additional resize handling\n if (this._mode === 'edit' && this._editController) {\n this._editController.onContainerResize(w, h);\n } else if (this._mode === 'simulation' && this._simulationController) {\n this._simulationController.onContainerResize(w, h);\n }\n }\n\n // ============================================================================\n // Internal Helpers\n // ============================================================================\n\n /**\n * Check that controller is initialized and not disposed.\n *\n * @throws {Error} If not initialized or disposed\n */\n private _checkInitialized(): void {\n if (this._disposed) {\n throw new Error('CircuitEngine has been disposed');\n }\n if (!this._initialized) {\n throw new Error('CircuitEngine is not initialized');\n }\n }\n\n /**\n * Check that controller is in edit mode.\n *\n * @throws {Error} If not in edit mode\n */\n private _checkEditMode(): void {\n this._checkInitialized();\n if (this._mode !== 'edit') {\n throw new Error('Operation not available: not in edit mode');\n }\n }\n\n /**\n * Check that engine is in simulation mode.\n *\n * @throws {Error} If not in simulation mode\n */\n private _checkSimulationMode(): void {\n this._checkInitialized();\n if (this._mode !== 'simulation') {\n throw new Error('Operation not available: not in simulation mode');\n }\n }\n}\n","/**\n * Component Material Types\n * @module scene/shared/components/types\n *\n * Enums and shared material dictionary for the component visual factory system.\n * Materials are singleton instances shared across all factory instances.\n */\n\nimport * as THREE from 'three';\n\n// ---------------------------------------------------------------------------\n// Enums\n// ---------------------------------------------------------------------------\n\n/** Material category — identifies a family of related shared materials */\nexport const enum CmpMatCategory {\n WHITE = 'WHITE',\n SHINY_SILVER = 'SHINY_SILVER',\n GLASS = 'GLASS',\n DARK_GRAY = 'DARK_GRAY',\n}\n\n/** Material variant — visual state within a category */\nexport const enum CmpMatVariant {\n NORMAL = 'NORMAL',\n HOVERED = 'HOVERED',\n SELECTED = 'SELECTED',\n}\n\n/**\n * Material ownership type — stored on `material.userData.matType`.\n * Numeric for fast comparison; determines how hover/select/animation interact.\n *\n * - **SHARED** — from CMP_MATERIALS dict; hover/select may swap to a variant\n * - **FACTORY** — per-visual factory material; skip hover/select swap\n * - **PRIVATE** — per-instance material (e.g. LED configurable color); skip hover/select swap\n * - **ANIMATION_CLONE** — cloned from SHARED for animation; skip hover/select swap\n */\nexport const enum CmpMatType {\n SHARED = 0,\n FACTORY = 1,\n PRIVATE = 2,\n ANIMATION_CLONE = 3,\n}\n\n// ---------------------------------------------------------------------------\n// Shared material dictionary\n// ---------------------------------------------------------------------------\n\n/** Default hover glow color (light blue) */\nconst HOVER_COLOR = 0x4488ff;\n/** Default selection glow color (orange) */\nconst SELECTION_COLOR = 0xff8800;\n\n/**\n * Shared material dictionary: CmpMatCategory → CmpMatVariant → MeshLambertMaterial.\n *\n * Every material carries `userData.matType` and `userData.matCat` (its category)\n * (`CmpMatType.SHARED`) so hover/select logic can identify and swap them.\n */\nexport const CMP_MATERIALS: Readonly<\n Record<CmpMatCategory, Record<CmpMatVariant, THREE.MeshLambertMaterial>>\n> = {\n [CmpMatCategory.WHITE]: {\n [CmpMatVariant.NORMAL]: new THREE.MeshLambertMaterial({\n color: 0xffffff,\n userData: { matType: CmpMatType.SHARED, matCat: CmpMatCategory.WHITE },\n }),\n [CmpMatVariant.HOVERED]: new THREE.MeshLambertMaterial({\n color: 0xffffff,\n emissive: HOVER_COLOR,\n emissiveIntensity: 0.6,\n userData: { matType: CmpMatType.SHARED, matCat: CmpMatCategory.WHITE },\n }),\n [CmpMatVariant.SELECTED]: new THREE.MeshLambertMaterial({\n color: 0xffffff,\n emissive: SELECTION_COLOR,\n emissiveIntensity: 0.8,\n userData: { matType: CmpMatType.SHARED, matCat: CmpMatCategory.WHITE },\n }),\n },\n [CmpMatCategory.SHINY_SILVER]: {\n [CmpMatVariant.NORMAL]: new THREE.MeshLambertMaterial({\n color: 0xc0c0c0,\n emissive: 0xffffff,\n emissiveIntensity: 0.7,\n userData: { matType: CmpMatType.SHARED, matCat: CmpMatCategory.SHINY_SILVER },\n }),\n [CmpMatVariant.HOVERED]: new THREE.MeshLambertMaterial({\n color: 0xc0c0c0,\n emissive: HOVER_COLOR,\n emissiveIntensity: 0.8,\n userData: { matType: CmpMatType.SHARED, matCat: CmpMatCategory.SHINY_SILVER },\n }),\n [CmpMatVariant.SELECTED]: new THREE.MeshLambertMaterial({\n color: 0xc0c0c0,\n emissive: SELECTION_COLOR,\n emissiveIntensity: 0.9,\n userData: { matType: CmpMatType.SHARED, matCat: CmpMatCategory.SHINY_SILVER },\n }),\n },\n [CmpMatCategory.GLASS]: {\n [CmpMatVariant.NORMAL]: new THREE.MeshLambertMaterial({\n color: 0xffffff,\n transparent: true,\n opacity: 0.75,\n userData: { matType: CmpMatType.SHARED, matCat: CmpMatCategory.GLASS },\n }),\n [CmpMatVariant.HOVERED]: new THREE.MeshLambertMaterial({\n color: 0xffffff,\n transparent: true,\n opacity: 0.8,\n emissive: HOVER_COLOR,\n emissiveIntensity: 0.6,\n userData: { matType: CmpMatType.SHARED, matCat: CmpMatCategory.GLASS },\n }),\n [CmpMatVariant.SELECTED]: new THREE.MeshLambertMaterial({\n color: 0xffffff,\n transparent: true,\n opacity: 0.9,\n emissive: SELECTION_COLOR,\n emissiveIntensity: 0.8,\n userData: { matType: CmpMatType.SHARED, matCat: CmpMatCategory.GLASS },\n }),\n },\n [CmpMatCategory.DARK_GRAY]: {\n [CmpMatVariant.NORMAL]: new THREE.MeshLambertMaterial({\n color: 0x2b2b2b,\n userData: { matType: CmpMatType.SHARED, matCat: CmpMatCategory.DARK_GRAY },\n }),\n [CmpMatVariant.HOVERED]: new THREE.MeshLambertMaterial({\n color: 0x666666,\n emissive: HOVER_COLOR,\n emissiveIntensity: 0.6,\n userData: { matType: CmpMatType.SHARED, matCat: CmpMatCategory.DARK_GRAY },\n }),\n [CmpMatVariant.SELECTED]: new THREE.MeshLambertMaterial({\n color: 0x999999,\n emissive: SELECTION_COLOR,\n emissiveIntensity: 0.8,\n userData: { matType: CmpMatType.SHARED, matCat: CmpMatCategory.DARK_GRAY },\n }),\n },\n};\n","/**\n * Factory Registry Implementation\n * @module scene/shared/FactoryRegistry\n *\n * Manages registration and retrieval of component visual factories\n * with fallback support for unknown component types.\n */\n\nimport type { ComponentType } from 'simple-circuit-engine/core';\nimport type { AnimationContext } from '../types';\nimport type { IComponentVisualFactory, IFactoryRegistry } from './ComponentVisualFactory';\n\n/**\n * Registry mapping ComponentType to ComponentVisualFactory\n *\n * Provides type-safe registration and retrieval with automatic fallback.\n * Supports both function-based (legacy) and class-based (new) factories.\n * Thread-safe for read operations (get/has), write operations should be\n * performed during initialization.\n *\n * @example\n * ```typescript\n * const registry = new FactoryRegistry(new DefaultVisualFactory());\n * registry.register(ComponentType.Battery, new BatteryVisualFactory());\n * registry.register(ComponentType.LED, new SmallLEDVisualFactory());\n *\n * const factory = registry.get(ComponentType.Battery); // Returns BatteryVisualFactory\n * const unknown = registry.get(ComponentType.Unknown); // Returns fallback\n * const mesh = factory.createVisual(component);\n * ```\n */\nexport class FactoryRegistry implements IFactoryRegistry {\n private factories: Map<ComponentType, IComponentVisualFactory> = new Map();\n private fallbackFactory: IComponentVisualFactory;\n\n /**\n * Create a new factory registry\n *\n * @param fallbackFactory - Factory to use for unregistered component types\n * @throws {TypeError} If fallbackFactory is null or undefined\n */\n constructor(fallbackFactory: IComponentVisualFactory) {\n if (!fallbackFactory) {\n throw new TypeError('FactoryRegistry requires a valid fallback factory');\n }\n this.fallbackFactory = fallbackFactory;\n }\n\n /**\n * Register a visual factory for a specific component type\n *\n * @param type - Component type identifier\n * @param factory - Factory (class instance or function) to create visuals for this type\n * @throws {TypeError} If type is empty/whitespace or factory is null/undefined\n * @returns This FactoryRegistry instance (for chaining)\n */\n register(type: ComponentType, factory: IComponentVisualFactory): FactoryRegistry {\n if (typeof type !== 'string' || type.trim() === '') {\n throw new TypeError('Component type must be a non-empty string');\n }\n if (!factory) {\n throw new TypeError(`Factory cannot be null or undefined for type: ${type}`);\n }\n // Accept both class instances (object with createVisual method) and functions\n if (typeof factory !== 'object' || typeof (factory as any).createVisual !== 'function') {\n throw new TypeError(`Factory must be a an object with createVisual method for type: ${type}`);\n }\n this.factories.set(type, factory);\n return this;\n }\n\n /**\n * Retrieve the factory for a component type\n *\n * @param type - Component type identifier\n * @returns Factory (fallback factory if type not registered)\n *\n * @remarks\n * This method NEVER returns null/undefined. If the type is not registered,\n * the fallback factory provided in the constructor is returned.\n */\n get(type: ComponentType): IComponentVisualFactory {\n return this.factories.get(type) ?? this.fallbackFactory;\n }\n\n /**\n * Check if a factory is registered for a component type\n *\n * @param type - Component type identifier\n * @returns true if explicitly registered, false if would use fallback\n */\n has(type: ComponentType): boolean {\n return this.factories.has(type);\n }\n\n /**\n * Get the fallback factory used for unregistered types\n */\n getFallbackFactory(): IComponentVisualFactory {\n return this.fallbackFactory;\n }\n\n /**\n * Unregister a factory for a component type\n *\n * @param type - Component type identifier\n * @returns true if factory was registered and removed, false otherwise\n *\n * @remarks\n * After unregistering, get() will return the fallback factory for this type.\n */\n unregister(type: ComponentType): boolean {\n return this.factories.delete(type);\n }\n\n /**\n * Get all registered component types\n *\n * @returns Array of ComponentType values that have registered factories\n */\n getRegisteredTypes(): ComponentType[] {\n return Array.from(this.factories.keys());\n }\n\n /**\n * Fan out animation context to all registered factories and the fallback.\n */\n setAnimationContext(ctx: AnimationContext | null): void {\n for (const factory of this.factories.values()) {\n factory.setAnimationContext(ctx);\n }\n this.fallbackFactory.setAnimationContext(ctx);\n }\n}\n","/**\n * Component Visual Factory System\n * @module scene/shared/ComponentVisualFactory\n *\n * Provides factory pattern for creating Three.js visuals from Circuit components.\n * Supports dynamic registration and fallback for unknown component types.\n */\n\nimport {\n type Component,\n type ComponentType,\n type ComponentState,\n ENode,\n} from 'simple-circuit-engine/core';\nimport { ENodeSourceType } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport { HitboxLayers } from '../utils/LayerConstants';\n\nimport type { AnimationContext, ConfigFormDefinition, VisualContext } from '../types';\nimport type { Direction2D } from '../utils/GeometryUtils';\nimport { MeshLambertMaterial } from 'three';\nimport { CMP_MATERIALS, CmpMatCategory, CmpMatType, CmpMatVariant } from './types';\n\n/**\n * Interface for component visual factories\n *\n * Implementations provide methods for:\n * - Creating the 3D visual representation\n * - Applying/removing hover effects\n * - Applying/removing selection effects\n * - Updating animation based on simulation state\n *\n * @example\n * ```typescript\n * class MyComponentFactory extends ComponentVisualFactoryBase {\n * createVisual(component: Component, context?: VisualContext): THREE.Object3D {\n * const group = new THREE.Group();\n * // ... create visual elements\n * group.userData.componentId = component.id;\n * group.userData.componentType = component.type;\n * return group;\n * }\n * }\n * ```\n */\nexport interface IComponentVisualFactory {\n /**\n * @returns nominal rotation along the Y axis when component added (angle in radian)\n */\n defaultRotation(): number;\n\n /**\n * Create the Three.js visual representation for a component\n *\n * @param component - The circuit component to visualize\n * @param context - Optional visual context providing access to ENode data\n * @returns THREE.Object3D (typically a Group) containing the visual\n *\n * @remarks\n * Implementations MUST:\n * - Set `object.userData.componentId = component.id`\n * - Set `object.userData.componentType = component.type`\n * - Create component hitbox on HitboxLayers.COMPONENT layer\n * - Create pin groups with enodes on HitboxLayers.ENODE layer\n * - Return objects positioned at origin (scene controllerType handles placement)\n */\n createVisual(component: Component, context: VisualContext): THREE.Object3D;\n\n /**\n * Update visual based on component configuration\n *\n * @param object3D - The Object3D created by createVisual()\n * @param config - The core component configuration Map\n *\n */\n updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void;\n\n /**\n * Apply hover visual effect to a component's Object3D\n *\n * @param object3D - The Object3D created by createVisual()\n *\n * @remarks\n * - Should store original material state in userData for restoration\n * - Default implementation: emissive glow effect (light blue, 0.5 intensity)\n * - Called by scene controllerType when component is hovered\n * - Should be idempotent (safe to call multiple times)\n */\n applyHover(object3D: THREE.Object3D): void;\n\n /**\n * Remove hover visual effect from a component's Object3D\n *\n * @param object3D - The Object3D created by createVisual()\n *\n * @remarks\n * - Should restore original material state from userData\n * - Called by scene controllerType when hover ends\n * - Should be safe to call even if not currently hovered\n */\n removeHover(object3D: THREE.Object3D): void;\n\n /**\n * Apply selection visual effect to a component's Object3D\n *\n * @param object3D - The Object3D created by createVisual()\n *\n * @remarks\n * - Currently a placeholder (no-op) for future implementation\n */\n applySelection(object3D: THREE.Object3D): void;\n\n /**\n * Remove selection visual effect from a component's Object3D\n *\n * @param object3D - The Object3D created by createVisual()\n *\n * @remarks\n * - Currently a placeholder (no-op) for future implementation\n */\n removeSelection(object3D: THREE.Object3D): void;\n\n /**\n * Update pin source type visual (optional method)\n *\n * @param pinGroup - The THREE.Group containing the pin visual\n * @param sourceType - The new source type (null for no source)\n *\n * @remarks\n * - Optional method for component factories that support pin source type visualization\n * - Changes pin color based on source type (bronze/red/blue)\n * - Default implementation in ComponentVisualFactoryBase\n */\n updatePinSourceType(pinGroup: THREE.Object3D, sourceType: ENodeSourceType | null): void;\n\n /**\n * Apply hover effect on a pin\n * @param pinGroup\n */\n applyPinHover(pinGroup: THREE.Object3D): void;\n\n /**\n * Remove hover effect on a pin\n */\n removePinHover(pinGroup: THREE.Object3D): void;\n\n /**\n * Update animation state based on simulation data\n *\n * @param object3D - The Object3D created by createVisual()\n * @param state - The component's current simulation state or null to reset to edition mode\n *\n * @remarks\n * - Called by CircuitRunnerController during simulation\n * - Animation visual updates have priority over hover effects\n * - Default implementation: no-op (static components)\n * - Subclasses override for component-specific animation\n * (e.g., LED glow, switch contactor rotation)\n */\n updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void;\n\n /**\n * Get the config form definition for this component type\n *\n * @param config - Optional current component config to compute field states (e.g., disabled)\n * @returns Form definition with field specifications, or null if no config\n *\n * @remarks\n * Defines the UI controls for editing component configuration.\n * Return null for components with no configurable options.\n * When config is provided, implementations may use it to set field.disabled\n * based on interdependencies (e.g., transitionSpan disabled when defaultLogicFamily != Sandbox).\n */\n getConfigFormDefinition(config?: Map<string, string>): ConfigFormDefinition | null;\n\n /**\n * Map core component config (string values) to form data (typed values)\n *\n * @param config - Core config from Component.config\n * @returns Form data with appropriate types for UI controls\n *\n * @remarks\n * Called when config panel opens to initialize form controls.\n * Converts core string values to UI-appropriate types (e.g., \"open\" → true).\n */\n mapCoreConfigToForm(config: Map<string, string>): Map<string, any>;\n\n /**\n * Map form data (typed values) back to core config (string values)\n *\n * @param formData - Current form values from UI\n * @returns Core config ready for Component.setAllParameters()\n *\n * @remarks\n * Called on each value change to update Component.config.\n * Converts UI values back to core string format (e.g., true → \"open\").\n */\n mapFormToCoreConfig(formData: Map<string, any>): Map<string, string>;\n\n /**\n * Set the shared animation context for simulation-aware factories.\n * Called by the registry fan-out when entering/leaving simulation.\n *\n * @param ctx - Animation context, or null when leaving simulation\n */\n setAnimationContext(ctx: AnimationContext | null): void;\n}\n\n/**\n * Abstract base class for component visual factories\n *\n * Provides default implementations for:\n * - Hover effect (emissive glow)\n * - Selection effect (placeholder/no-op)\n * - Animation update (placeholder/no-op)\n * - Pin group creation (shared helper)\n * - Component hitbox creation (shared helper)\n *\n * Subclasses must implement:\n * - createVisual() - component-specific visual creation\n *\n * Subclasses may override:\n * - applyHover() / removeHover() - custom hover effect\n * - applySelection() / removeSelection() - custom selection effect (future)\n * - updateAnimation() - component-specific animation\n *\n * @example\n * ```typescript\n * export class BatteryVisualFactory extends ComponentVisualFactoryBase {\n * createVisual(component: Component, context?: VisualContext): THREE.Object3D {\n * const group = new THREE.Group();\n * // ... create battery-specific visual\n * return group;\n * }\n * // Inherits default hover, selection, and animation\n * }\n * ```\n */\nexport abstract class ComponentVisualFactoryBase implements IComponentVisualFactory {\n /** Shared animation context injected by the registry during simulation */\n protected _animationContext: AnimationContext | null = null;\n\n /** ComponentType handled by this factory, set in each subclass constructor */\n protected _componentType: ComponentType | null = null;\n\n /** Default hover glow color (light blue) */\n protected static readonly DEFAULT_HOVER_COLOR = 0x4488ff;\n\n /** Default hover emissive intensity */\n protected static readonly DEFAULT_HOVER_INTENSITY = 0.6;\n\n protected getMat(\n category: CmpMatCategory,\n variant: CmpMatVariant = CmpMatVariant.NORMAL\n ): MeshLambertMaterial {\n const matCat = CMP_MATERIALS[category];\n if (!matCat) {\n return CMP_MATERIALS[CmpMatCategory.WHITE][CmpMatVariant.NORMAL];\n }\n return matCat[variant] || CMP_MATERIALS[CmpMatCategory.WHITE][CmpMatVariant.NORMAL];\n }\n\n defaultRotation() {\n return 0;\n }\n\n /**\n * Create the Three.js visual representation for a component\n * Must be implemented by subclasses\n */\n abstract createVisual(component: Component, context: VisualContext): THREE.Object3D;\n\n /**\n * By default no visual configuration-based updates is needed\n */\n updateFromConfiguration(_object3D: THREE.Object3D, _config: Map<string, string>) {\n // Default: no-op\n }\n\n /**\n * Apply hover visual effect using emissive glow\n *\n * Default implementation traverses all meshes and applies\n * an emissive blue glow effect, storing original values in userData.\n *\n * Note: If component is selected, selection visual takes precedence\n * and hover visual is not applied (but isHovered flag is still set).\n */\n applyHover(object3D: THREE.Object3D): void {\n if (object3D.userData.isSelected) {\n // Component is selected; skip hover visual\n return;\n }\n\n object3D.traverse((child) => {\n if (child.userData.type === 'enodeHitbox' || child.userData.type === 'enode') {\n return;\n }\n if (child.userData.type === 'enodeGroup') {\n this.applyPinHover(child);\n return;\n }\n if (!(child instanceof THREE.Mesh)) return;\n\n const material = child.material;\n if (material.visible === false || material.userData?.matType !== CmpMatType.SHARED) return;\n\n const matCategory = CMP_MATERIALS[material.userData.matCat as CmpMatCategory];\n if (!matCategory) return;\n child.material = matCategory[CmpMatVariant.HOVERED];\n });\n }\n\n /**\n * Remove hover visual effect, restoring original materials\n */\n removeHover(object3D: THREE.Object3D): void {\n if (object3D.userData.isSelected) {\n // Component is selected; skip unhover visual\n return;\n }\n object3D.traverse((child) => {\n if (child.userData.type === 'enodeHitbox' || child.userData.type === 'enode') {\n return;\n }\n if (child.userData.type === 'enodeGroup') {\n this.removePinHover(child);\n return;\n }\n if (!(child instanceof THREE.Mesh)) return;\n\n const material = child.material;\n if (material.visible === false || material.userData?.matType !== CmpMatType.SHARED) return;\n\n const matCategory = CMP_MATERIALS[material.userData.matCat as CmpMatCategory];\n if (!matCategory) return;\n child.material = matCategory[CmpMatVariant.NORMAL];\n });\n }\n\n /**\n * Apply selection visual effect using emissive orange glow\n *\n * Selection takes precedence over hover effect.\n * Stores original material state in userData for restoration.\n *\n * @param object3D - The component's root Three.js object\n */\n applySelection(object3D: THREE.Object3D): void {\n object3D.traverse((child) => {\n if (child.userData.type === 'enodeHitbox' || child.userData.type === 'enode') {\n return;\n }\n if (child.userData.type === 'enodeGroup') {\n this.removePinHover(child);\n return;\n }\n if (!(child instanceof THREE.Mesh)) return;\n\n const material = child.material;\n if (material.visible === false || material.userData?.matType !== CmpMatType.SHARED) return;\n\n const matCategory = CMP_MATERIALS[material.userData.matCat as CmpMatCategory];\n if (!matCategory) return;\n child.material = matCategory[CmpMatVariant.SELECTED];\n });\n\n object3D.userData.isSelected = true;\n }\n\n /**\n * Remove selection visual effect, restoring original or hover state\n *\n * If component was hovered before selection, restores hover visual.\n * Otherwise, restores original material state.\n *\n * @param object3D - The component's root Three.js object\n */\n removeSelection(object3D: THREE.Object3D): void {\n object3D.traverse((child) => {\n if (child.userData.type === 'enodeHitbox' || child.userData.type === 'enode') {\n return;\n }\n if (child.userData.type === 'enodeGroup') {\n this.removePinHover(child);\n return;\n }\n if (!(child instanceof THREE.Mesh)) return;\n\n const material = child.material;\n if (material.visible === false || material.userData?.matType !== CmpMatType.SHARED) return;\n\n const matCategory = CMP_MATERIALS[material.userData.matCat as CmpMatCategory];\n if (!matCategory) return;\n child.material = matCategory[CmpMatVariant.NORMAL];\n });\n object3D.userData.isSelected = false;\n }\n\n /**\n * @private\n */\n private pointPinGroupToward(group: THREE.Group, direction: Direction2D) {\n switch (direction) {\n case 'right':\n group.rotateZ(-Math.PI / 2);\n group.rotateY(Math.PI);\n break;\n case 'bottom':\n group.rotateX(Math.PI / 2);\n break;\n case 'left':\n group.rotateZ(Math.PI / 2);\n group.rotateY(Math.PI);\n break;\n case 'top':\n group.rotateX(-Math.PI / 2);\n break;\n }\n }\n\n /**\n * Create a pin group with hitbox and visual sphere\n *\n * Creates a THREE.Group containing:\n * - Hemisphere hitbox (on ENODE layer for raycasting)\n * - Hemisphere visual (blue sphere)\n * - Hover callback in userData\n *\n * @param node - ENode to create as visual pin\n * @param pointsTo - rotate pin to make it point the wanted direction\n * @param visualRotation - if set rotate the visual of the pin to adjust display without affecting hitbox\n * @returns THREE.Group configured as pin group\n */\n protected createPinGroup(\n node: ENode,\n pointsTo: Direction2D = 'right',\n visualRotation: THREE.Euler | null = null\n ): THREE.Group {\n if (!node.component) {\n throw new Error('This method only manage components eNodes (pins)');\n }\n\n // pins with this subtype will be considered locked (not possible to change their source type)\n const lockedSubtypes = ['mainVcc', 'vcc', 'mainGnd', 'gnd', 'logicOutput'];\n // pins with this subtype will be represented smaller (not intended to be wired on anything so they're just a reminder for the user)\n const smallSubtypes = ['vcc', 'gnd'];\n\n const userInfos = {\n componentId: node.component,\n enodeId: node.id,\n label: node.pinLabel,\n subtype: node.subtype,\n lockedSourceType: lockedSubtypes.includes(node.subtype),\n };\n\n const pinGroup = new THREE.Group();\n pinGroup.userData = { ...userInfos, type: 'enodeGroup', sourceType: node.source || null };\n // default radius\n let hitboxRadius = 0.9;\n let visualRadius = 0.3;\n // since those pins won't be used for wiring they can be smaller\n if (smallSubtypes.includes(node.subtype)) {\n hitboxRadius = 0.25;\n visualRadius = 0.2;\n }\n\n // Hitbox (hemisphere, raycastable)\n const hitboxGeom = new THREE.SphereGeometry(\n hitboxRadius,\n 16,\n 8,\n 0,\n Math.PI * 2,\n 0,\n Math.PI / 2\n );\n const hitbox = new THREE.Mesh(\n hitboxGeom,\n new THREE.MeshBasicMaterial({\n color: ComponentVisualFactoryBase.DEFAULT_HOVER_COLOR,\n transparent: true,\n opacity: 0,\n })\n );\n hitbox.userData = { ...userInfos, type: 'enodeHitbox', componentType: this._componentType };\n hitbox.layers.set(HitboxLayers.ENODE);\n pinGroup.add(hitbox);\n\n // Visual sphere\n const visual = new THREE.Mesh(\n new THREE.SphereGeometry(visualRadius, 16, 8, 0, Math.PI * 2, 0, Math.PI / 2),\n new THREE.MeshLambertMaterial({\n color: this.pinColorForSourceType(node.source || null),\n emissive: this.pinColorForSourceType(node.source || null),\n emissiveIntensity: 0,\n })\n );\n visual.userData = {\n ...userInfos,\n type: 'enode',\n sourceType: node.source || null,\n radius: visualRadius,\n };\n pinGroup.add(visual);\n if (!!visualRotation) {\n visual.setRotationFromEuler(visualRotation);\n }\n\n this.pointPinGroupToward(pinGroup, pointsTo);\n return pinGroup;\n }\n\n /**\n * Utility to create semi spheres visually closing pins\n * @param pinGroup\n * @param material\n * @protected\n */\n protected createPinCounterpart(\n pinGroup: THREE.Group,\n material: THREE.MeshLambertMaterial\n ): THREE.Mesh | null {\n const pinVisual = this.findPinVisualFromGroup(pinGroup);\n if (!pinVisual) {\n return null;\n }\n\n const visual = new THREE.Mesh(\n new THREE.SphereGeometry(pinVisual.userData.radius, 16, 8, 0, Math.PI * 2, 0, Math.PI / 2),\n material\n );\n visual.userData = {\n type: 'component',\n componentId: pinVisual.userData.componentId,\n part: 'pinCounterpart',\n };\n visual.position.copy(pinGroup.position);\n\n const pinVisualRotation = pinVisual.rotation.clone();\n\n visual.rotation.copy(\n new THREE.Euler(\n -pinGroup.rotation.x,\n -pinGroup.rotation.y,\n -pinGroup.rotation.z,\n pinGroup.rotation.order\n )\n );\n visual.rotateX(-pinVisualRotation.x);\n visual.rotateY(-pinVisualRotation.y);\n visual.rotateZ(pinVisualRotation.z);\n\n return visual;\n }\n\n /**\n * Find pin group by label within a component Object3D\n * @param object3D\n * @param label\n */\n findPinGroup(object3D: THREE.Object3D, label: string): THREE.Group | null {\n let pinGroup: THREE.Group | null = null;\n object3D.traverse((child) => {\n if (\n child.userData.type === 'enodeGroup' &&\n child.userData.label === label &&\n child instanceof THREE.Group\n ) {\n pinGroup = child;\n }\n });\n return pinGroup;\n }\n\n /**\n * fin pin inner visual from within its pin group\n * @param pinGroup\n */\n findPinVisualFromGroup(pinGroup: THREE.Group): THREE.Mesh | null {\n let pinVisual: THREE.Mesh | null;\n pinGroup.traverse((child) => {\n if (child.userData.type === 'enode' && child instanceof THREE.Mesh) {\n pinVisual = child;\n }\n });\n // @ts-ignore\n return pinVisual;\n }\n\n /**\n * Find pin group visual by label within a component Object3D\n * @param object3D\n * @param label\n */\n findPinVisual(object3D: THREE.Object3D, label: string): THREE.Mesh | null {\n let pinGroup: THREE.Group | null = this.findPinGroup(object3D, label);\n if (!pinGroup) {\n return null;\n }\n return this.findPinVisualFromGroup(pinGroup);\n }\n\n /**\n * Create component hitbox mesh\n *\n * Helper to create standard component hitbox with proper userData and layer.\n *\n * @param componentId - UUID of the component\n * @param groupId - ID of the parent THREE.Group\n * @param width - Hitbox width\n * @param height - Hitbox height\n * @param depth - Hitbox depth\n * @returns THREE.Mesh configured as component hitbox\n */\n protected createComponentHitbox(\n componentId: string,\n groupId: number,\n width: number,\n height: number,\n depth: number\n ): THREE.Mesh {\n const geometry = new THREE.BoxGeometry(width, height, depth);\n const material = new THREE.MeshBasicMaterial({\n color: 0xffff00,\n transparent: true,\n opacity: 0.2,\n visible: false,\n });\n const hitbox = new THREE.Mesh(geometry, material);\n hitbox.userData = {\n type: 'componentHitbox',\n componentId: componentId,\n groupId: groupId,\n };\n hitbox.layers.set(HitboxLayers.COMPONENT);\n return hitbox;\n }\n\n protected findHitbox(object3D: THREE.Object3D): THREE.Mesh | null {\n let hitbox: THREE.Mesh | null = null;\n object3D.traverse((child) => {\n if (child.userData.type === 'componentHitbox' && child instanceof THREE.Mesh) {\n hitbox = child;\n }\n });\n return hitbox;\n }\n\n protected pinColorForSourceType(sourceType: ENodeSourceType | null): number {\n if (!sourceType) {\n return 0xd4894c; // Copper for no source\n }\n if (sourceType === ENodeSourceType.Voltage) {\n return 0xff0000; // Red for voltage\n } else if (sourceType === ENodeSourceType.Current) {\n return 0x0000ff; // Blue for current\n }\n return 0xd4894c; // Copper by default\n }\n\n protected pinColorForElectricalState(state: 'current' | 'voltage' | 'vc' | 'idle'): number {\n switch (state) {\n case 'voltage':\n return 0xff0000; // Red\n case 'current':\n return 0x0000ff; // Blue\n case 'vc':\n return 0xcc00cc; // Magenta\n case 'idle':\n default:\n return 0x000000;\n }\n }\n\n /**\n * Updates the visual color of a component pin based on its sourceType type.\n *\n * This method changes the pin's material color to reflect the sourceType type:\n * - null/undefined: copper (default pin color)\n * - Voltage: red\n * - Current: blue\n *\n * @param pinGroup - The THREE.Group containing the pin visual (created by createPinGroup)\n * @param sourceType - The new sourceType (null for no sourceType)\n *\n * @remarks\n * - Searches for the child mesh with userData.type === 'enode'\n * - Updates both color and emissive properties for visual consistency\n * - If sourceType is null/undefined, restores default copper pin color\n * - Color scheme matches BranchingPointVisualFactory for consistency\n */\n updatePinSourceType(pinGroup: THREE.Object3D, sourceType: ENodeSourceType | null): void {\n if (!!pinGroup.userData.lockedSourceType) return; // Pin is locked to a sourceType type, do not change color\n pinGroup.userData.sourceType = sourceType;\n\n const visual = pinGroup.children.find((child) => child.userData.type === 'enode') as\n | THREE.Mesh\n | undefined;\n\n if (!visual) {\n return;\n }\n visual.userData.sourceType = sourceType;\n const color = this.pinColorForSourceType(sourceType);\n const material = visual.material as THREE.MeshLambertMaterial;\n material.color.setHex(color);\n material.emissive.setHex(color);\n }\n\n /**\n * Apply hover visual effect on this pin\n */\n applyPinHover(pinGroup: THREE.Object3D): void {\n if (pinGroup.userData.isHovered) {\n return; // Already hovered\n }\n pinGroup.userData.isHovered = true;\n\n pinGroup.traverse((child) => {\n if (child instanceof THREE.Mesh) {\n const material = child.material as THREE.MeshLambertMaterial;\n\n if (child.userData.type === 'enodeHitbox') {\n material.opacity = 0.3;\n } else if (child.userData.type === 'enode') {\n material.color.setHex(0x00ff00);\n // Apply hover effect\n material.emissiveIntensity = 0.9;\n }\n }\n });\n }\n\n /**\n * remove hover visual effect on this pin\n */\n removePinHover(pinGroup: THREE.Object3D): void {\n if (!pinGroup.userData.isHovered) {\n return; // Already hovered\n }\n pinGroup.userData.isHovered = false;\n\n const source: ENodeSourceType | null = pinGroup.userData.sourceType || null;\n\n pinGroup.traverse((child) => {\n if (child instanceof THREE.Mesh) {\n const material = child.material as THREE.MeshLambertMaterial;\n\n if (child.userData.type === 'enodeHitbox') {\n material.opacity = 0;\n } else if (child.userData.type === 'enode') {\n material.color.setHex(this.pinColorForSourceType(source));\n material.emissiveIntensity = source ? 1 : 0;\n if (!source && pinGroup.userData.electricalState) {\n const emissiveColor = this.pinColorForElectricalState(\n pinGroup.userData.electricalState\n );\n material.emissive.setHex(emissiveColor);\n material.emissiveIntensity = emissiveColor === 0x000000 ? 0 : 1;\n }\n }\n }\n });\n }\n\n /**\n * Update animation state based on simulation data (placeholder)\n *\n * Default implementation is a no-op for static components.\n * Override in subclasses that have animation (LED, Switch).\n */\n updateAnimation(_object3D: THREE.Object3D, _state: ComponentState | null): void {\n // Default: no-op for static components\n // Subclasses override for component-specific animation\n }\n\n /**\n * Get config form definition (default: null - no config)\n *\n * Override in subclasses that have configurable options.\n */\n getConfigFormDefinition(_config?: Map<string, string>): ConfigFormDefinition | null {\n return null;\n }\n\n /**\n * Map core config to form data (default: identity mapping)\n *\n * Override in subclasses that need type conversions.\n */\n mapCoreConfigToForm(config: Map<string, string>): Map<string, any> {\n return new Map(config);\n }\n\n /**\n * Map form data to core config (default: convert all to strings)\n *\n * Override in subclasses that need type conversions.\n */\n mapFormToCoreConfig(formData: Map<string, any>): Map<string, string> {\n const config = new Map<string, string>();\n for (const [key, value] of formData.entries()) {\n config.set(key, String(value));\n }\n return config;\n }\n\n /**\n * Set the shared animation context for simulation-aware factories.\n */\n setAnimationContext(ctx: AnimationContext | null): void {\n this._animationContext = ctx;\n }\n}\n\n/**\n * Registry interface for managing component visual factories\n *\n * Provides type-safe registration and retrieval of component factories.\n * Falls back to a default factory for unregistered component types.\n *\n * @remarks\n * Updated to support both function-based and class-based factories.\n * Prefer using IComponentVisualFactory class instances.\n *\n * @example\n * ```typescript\n * const registry = new FactoryRegistry(new DefaultVisualFactory());\n * registry.register(ComponentType.Battery, new BatteryVisualFactory());\n * registry.register(ComponentType.LED, new SmallLEDVisualFactory());\n *\n * const factory = registry.get(ComponentType.Battery);\n * const mesh = factory.createVisual(batteryComponent);\n * ```\n */\nexport interface IFactoryRegistry {\n /**\n * Retrieve the factory for a component type\n *\n * @param type - Component type identifier\n * @returns Factory (fallback factory if type not registered)\n *\n * @remarks\n * This method NEVER returns null/undefined. If the type is not registered,\n * the fallback factory provided in the constructor is returned.\n */\n get(type: ComponentType): IComponentVisualFactory;\n\n /**\n * Check if a factory is registered for a component type\n *\n * @param type - Component type identifier\n * @returns true if explicitly registered, false if would use fallback\n */\n has(type: ComponentType): boolean;\n\n /**\n * Get the fallback factory used for unregistered component types\n */\n getFallbackFactory(): IComponentVisualFactory;\n\n /**\n * Unregister a factory for a component type\n *\n * @param type - Component type identifier\n * @returns true if factory was registered and removed, false otherwise\n *\n * @remarks\n * After unregistering, get() will return the fallback factory for this type.\n */\n unregister(type: ComponentType): boolean;\n\n /**\n * Get all registered component types\n *\n * @returns Array of ComponentType values that have registered factories\n */\n getRegisteredTypes(): ComponentType[];\n\n /**\n * Fan out animation context to all registered factories and the fallback.\n *\n * @param ctx - Animation context, or null when leaving simulation\n */\n setAnimationContext(ctx: AnimationContext | null): void;\n}\n","/**\n * Color utilities\n * @module scene/shared/utils/ColorUtils\n */\n\n/**\n * Standard color presets for hybrid color controls\n */\nexport const COLOR_PRESETS: Readonly<Record<string, string>> = {\n red: '#ff0000',\n green: '#00ff00',\n blue: '#0000ff',\n yellow: '#ffff00',\n orange: '#ff8800',\n purple: '#8800ff',\n cyan: '#00ffff',\n magenta: '#ff00ff',\n white: '#ffffff',\n black: '#000000',\n};\n\n/**\n * Check if a color value is a hex string\n * @param value - Color value to check\n * @returns true if value matches #RRGGBB format\n */\nexport function isHexColor(value: string): boolean {\n return /^#[0-9A-Fa-f]{6}$/.test(value);\n}\n\n/**\n * Convert a hex color to preset name if it matches, otherwise return hex\n * @param hex - Hex color string (e.g., \"#ff0000\")\n * @returns Preset name if match found, otherwise the hex string\n */\nexport function hexToPresetOrHex(hex: string): string {\n const lowerHex = hex.toLowerCase();\n for (const [name, presetHex] of Object.entries(COLOR_PRESETS)) {\n if (presetHex.toLowerCase() === lowerHex) {\n return name;\n }\n }\n return hex;\n}\n\n/**\n * Convert a preset name or hex to hex value\n * @param value - Preset name (e.g., \"red\") or hex string (e.g., \"#ff0000\")\n * @returns Hex color string\n */\nexport function presetOrHexToHex(value: string): string {\n if (isHexColor(value)) {\n return value;\n }\n return COLOR_PRESETS[value] ?? '#ffffff';\n}\n","import { ComponentVisualFactoryBase } from './ComponentVisualFactory';\nimport type { Component } from 'simple-circuit-engine/core';\nimport { presetOrHexToHex, hexToPresetOrHex } from '../utils/ColorUtils';\nimport * as THREE from 'three';\nimport type { ConfigFormDefinition } from '../types';\n\n/**\n * Default Visual factory for not yet defined components\n *\n * Creates:\n * - Box squared mesh (white)\n * - Component hitbox for raycasting\n *\n * This default visual is pinless no matter the component definition.\n */\nexport class DefaultVisualFactory extends ComponentVisualFactoryBase {\n createVisual(component: Component): THREE.Object3D {\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n isPlaceholder: true,\n };\n\n // Component hitbox (invisible, raycastable)\n const hitbox = this.createComponentHitbox(component.id, group.id, 2, 2, 2);\n group.add(hitbox);\n\n // Visual box\n const boxGeometry = new THREE.BoxGeometry(0.5, 0.5, 0.5, 4, 4, 4);\n const boxMaterial = new THREE.MeshStandardMaterial({ color: 0xffffff });\n const box = new THREE.Mesh(boxGeometry, boxMaterial);\n box.userData = {\n type: 'component',\n componentId: component.id,\n };\n group.add(box);\n\n return group;\n }\n\n /**\n * Get config form definition (T028)\n * Returns form for Cube (color) - RectangleLED uses SmallLEDVisualFactory\n *\n * @returns Form definition with color field for Cube\n */\n override getConfigFormDefinition(): ConfigFormDefinition | null {\n // Default factory is used for Cube, which has a simple color config\n return {\n fields: [{ key: 'color', type: 'color' }],\n };\n }\n\n /**\n * Map core config to form data (T028)\n * Converts hex/preset strings to hex values for color picker\n *\n * @param config - Core component config\n * @returns Form data with hex color string\n */\n override mapCoreConfigToForm(config: Map<string, string>): Map<string, any> {\n const formData = new Map<string, any>();\n const color = config.get('color') || '#ffffff';\n\n // Convert preset names to hex if needed\n formData.set('color', presetOrHexToHex(color));\n\n return formData;\n }\n\n /**\n * Map form data to core config (T028)\n * Converts hex colors to preset names if they match, otherwise keeps hex\n *\n * @param formData - Form data with hex color string\n * @returns Core config with hex or preset name string\n */\n override mapFormToCoreConfig(formData: Map<string, any>): Map<string, string> {\n const config = new Map<string, string>();\n const color = formData.get('color');\n\n // Convert hex to preset name if it matches a preset\n if (color) {\n config.set('color', hexToPresetOrHex(color));\n }\n\n return config;\n }\n\n /**\n * Update visual from configuration (T023)\n * Updates Cube/RectangleLED color based on color config\n *\n * @param object3D - The Object3D created by createVisual()\n * @param config - Component configuration map\n *\n * @remarks\n * Updates the box mesh color if 'color' config exists\n * Supports both hex colors and color presets\n */\n override updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void {\n const color = config.get('color');\n if (!color) return;\n\n // Find the box mesh\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.type === 'component') {\n if (child.material instanceof THREE.MeshStandardMaterial) {\n // Convert preset to hex if needed, then parse\n const hexColor = presetOrHexToHex(color);\n const colorHex = parseInt(hexColor.replace('#', ''), 16);\n child.material.color.setHex(colorHex);\n }\n }\n });\n }\n}\n","/**\n * Grouped Factory Registry Implementation\n * @module scene/shared/GroupedFactoryRegistry\n *\n * Extends the basic factory registry with named group support for UI/UX-oriented\n * component palette organization. Components can only be added inside a group\n * context via addGroup(), enforcing the \"no floating components\" invariant.\n */\n\nimport type { ComponentType } from 'simple-circuit-engine/core';\nimport type { AnimationContext } from '../types.js';\nimport type { IComponentVisualFactory, IFactoryRegistry } from './ComponentVisualFactory.js';\n\n/**\n * Describes a named group of component types\n */\nexport interface ComponentGroup {\n readonly id: string;\n readonly label: string;\n}\n\n/**\n * Builder interface for adding components to a group.\n * Used as the callback parameter type in addGroup().\n *\n * @example\n * ```typescript\n * registry.addGroup('basic', 'Basic Components', (group: IComponentGroupBuilder) => {\n * group\n * .add(ComponentType.Battery, new BatteryVisualFactory())\n * .add(ComponentType.Switch, new SwitchVisualFactory());\n * });\n * ```\n */\nexport interface IComponentGroupBuilder {\n add(type: ComponentType, factory: IComponentVisualFactory): IComponentGroupBuilder;\n}\n\n/**\n * Extended registry interface with grouping support.\n *\n * Adds group management on top of the basic factory retrieval,\n * allowing client UIs to build organized component palettes.\n */\nexport interface IGroupedFactoryRegistry {\n addGroup(\n id: string,\n label: string,\n builderCallback: (group: IComponentGroupBuilder) => void\n ): IGroupedFactoryRegistry;\n get(type: ComponentType): IComponentVisualFactory;\n has(type: ComponentType): boolean;\n getFallbackFactory(): IComponentVisualFactory;\n getGroups(): ComponentGroup[];\n getGroupOf(type: ComponentType): ComponentGroup | undefined;\n getRegisteredTypes(groupId?: string): ComponentType[];\n unregister(type: ComponentType): boolean;\n}\n\n/** Internal mutable group record */\ninterface GroupRecord {\n readonly id: string;\n readonly label: string;\n types: ComponentType[];\n}\n\n/**\n * Internal builder for adding components within a named group.\n * Handles factory registration and cross-group type movement.\n */\nclass ComponentGroupBuilder implements IComponentGroupBuilder {\n constructor(\n private readonly _groupRecord: GroupRecord,\n private readonly _factories: Map<ComponentType, IComponentVisualFactory>,\n private readonly _typeToGroupId: Map<ComponentType, string>,\n private readonly _groups: Map<string, GroupRecord>\n ) {}\n\n add(type: ComponentType, factory: IComponentVisualFactory): IComponentGroupBuilder {\n if (typeof type !== 'string' || type.trim() === '') {\n throw new TypeError('Component type must be a non-empty string');\n }\n if (!factory) {\n throw new TypeError(`Factory cannot be null or undefined for type: ${type}`);\n }\n if (typeof factory !== 'object' || typeof (factory as any).createVisual !== 'function') {\n throw new TypeError(`Factory must be an object with createVisual method for type: ${type}`);\n }\n\n // If type is already registered in a different group, remove it from that group's list\n const existingGroupId = this._typeToGroupId.get(type);\n if (existingGroupId !== undefined && existingGroupId !== this._groupRecord.id) {\n const oldGroup = this._groups.get(existingGroupId);\n if (oldGroup) {\n oldGroup.types = oldGroup.types.filter((t) => t !== type);\n }\n }\n\n this._factories.set(type, factory);\n this._typeToGroupId.set(type, this._groupRecord.id);\n\n if (!this._groupRecord.types.includes(type)) {\n this._groupRecord.types.push(type);\n }\n\n return this;\n }\n}\n\n/**\n * Factory registry with named group support.\n *\n * Implements both IFactoryRegistry (backward compat) and IGroupedFactoryRegistry.\n * Components may only be registered inside a group context via addGroup().\n *\n * Group insertion order is preserved (Map maintains insertion order).\n * Duplicate group ids merge their components; the label from the first\n * registration is preserved.\n *\n * @example\n * ```typescript\n * const registry = new GroupedFactoryRegistry(new DefaultVisualFactory())\n * .addGroup('basic', 'Basic Components', group => group\n * .add(ComponentType.Battery, new BatteryVisualFactory())\n * .add(ComponentType.Switch, new SwitchVisualFactory())\n * )\n * .addGroup('outputs', 'Output Components', group => group\n * .add(ComponentType.Lightbulb, new LightbulbVisualFactory())\n * );\n *\n * registry.getGroups(); // [{ id: 'basic', ... }, { id: 'outputs', ... }]\n * registry.getGroupOf(ComponentType.Battery); // { id: 'basic', label: 'Basic Components' }\n * registry.getRegisteredTypes('basic'); // [ComponentType.Battery, ComponentType.Switch]\n * registry.get(ComponentType.Battery); // BatteryVisualFactory instance\n * ```\n */\nexport class GroupedFactoryRegistry implements IFactoryRegistry, IGroupedFactoryRegistry {\n private readonly _factories: Map<ComponentType, IComponentVisualFactory> = new Map();\n private readonly _fallbackFactory: IComponentVisualFactory;\n private readonly _groups: Map<string, GroupRecord> = new Map();\n private readonly _typeToGroupId: Map<ComponentType, string> = new Map();\n\n /**\n * Create a new grouped factory registry.\n *\n * @param fallbackFactory - Factory to use for unregistered component types\n * @throws {TypeError} If fallbackFactory is null or undefined\n */\n constructor(fallbackFactory: IComponentVisualFactory) {\n if (!fallbackFactory) {\n throw new TypeError('GroupedFactoryRegistry requires a valid fallback factory');\n }\n this._fallbackFactory = fallbackFactory;\n }\n\n /**\n * Add a named group and register component factories within it.\n *\n * If a group with the same id already exists, the new components are merged\n * into it and the original label is preserved.\n *\n * @param id - Unique group identifier\n * @param label - Human-readable group display name\n * @param builderCallback - Callback receiving a builder for adding components\n * @throws {TypeError} If id or label is empty/whitespace\n * @returns this (for chaining)\n */\n addGroup(\n id: string,\n label: string,\n builderCallback: (group: IComponentGroupBuilder) => void\n ): this {\n if (typeof id !== 'string' || id.trim() === '') {\n throw new TypeError('Group id must be a non-empty string');\n }\n if (typeof label !== 'string' || label.trim() === '') {\n throw new TypeError('Group label must be a non-empty string');\n }\n\n let groupRecord = this._groups.get(id);\n if (!groupRecord) {\n groupRecord = { id, label, types: [] };\n this._groups.set(id, groupRecord);\n }\n // If group already exists: merge components, label preserved from first registration\n\n const builder = new ComponentGroupBuilder(\n groupRecord,\n this._factories,\n this._typeToGroupId,\n this._groups\n );\n builderCallback(builder);\n\n return this;\n }\n\n /**\n * Retrieve the factory for a component type.\n *\n * @returns Factory, or the fallback factory if type not registered.\n * Never returns null or undefined.\n */\n get(type: ComponentType): IComponentVisualFactory {\n return this._factories.get(type) ?? this._fallbackFactory;\n }\n\n /**\n * Check if a factory is registered for a component type.\n *\n * @returns true if explicitly registered, false if would use fallback\n */\n has(type: ComponentType): boolean {\n return this._factories.has(type);\n }\n\n /**\n * Get the fallback factory used for unregistered types.\n */\n getFallbackFactory(): IComponentVisualFactory {\n return this._fallbackFactory;\n }\n\n /**\n * Get all defined groups in insertion order.\n *\n * @returns New array of ComponentGroup objects (safe to mutate)\n */\n getGroups(): ComponentGroup[] {\n return Array.from(this._groups.values()).map((g) => ({ id: g.id, label: g.label }));\n }\n\n /**\n * Get the group a component type belongs to.\n *\n * @param type - Component type to look up\n * @returns ComponentGroup, or undefined if type is not registered\n */\n getGroupOf(type: ComponentType): ComponentGroup | undefined {\n const groupId = this._typeToGroupId.get(type);\n if (!groupId) return undefined;\n const group = this._groups.get(groupId);\n if (!group) return undefined;\n return { id: group.id, label: group.label };\n }\n\n /**\n * Get registered component types, optionally filtered by group.\n *\n * @param groupId - If provided, returns only types in that group\n * @returns New array of ComponentType values (safe to mutate).\n * Returns empty array if groupId is unknown.\n */\n getRegisteredTypes(groupId?: string): ComponentType[] {\n if (groupId === undefined) {\n return Array.from(this._factories.keys());\n }\n const group = this._groups.get(groupId);\n if (!group) return [];\n return [...group.types];\n }\n\n /**\n * Unregister a factory for a component type.\n *\n * The type is removed from its group's list and from the reverse lookup.\n * The group record itself is preserved even if it becomes empty.\n *\n * @param type - Component type to remove\n * @returns true if factory was registered and removed, false otherwise\n */\n unregister(type: ComponentType): boolean {\n if (!this._factories.has(type)) return false;\n\n this._factories.delete(type);\n\n const groupId = this._typeToGroupId.get(type);\n if (groupId) {\n const group = this._groups.get(groupId);\n if (group) {\n group.types = group.types.filter((t) => t !== type);\n }\n this._typeToGroupId.delete(type);\n }\n\n return true;\n }\n\n /**\n * Fan out animation context to all registered factories and the fallback.\n */\n setAnimationContext(ctx: AnimationContext | null): void {\n for (const factory of this._factories.values()) {\n factory.setAnimationContext(ctx);\n }\n this._fallbackFactory.setAnimationContext(ctx);\n }\n\n /**\n * Not supported. Use addGroup() to register components within a named group.\n *\n * @throws {Error} Always throws\n */\n register(_type: ComponentType, _factory: IComponentVisualFactory): IFactoryRegistry {\n throw new Error(\n 'GroupedFactoryRegistry does not support register(). Use addGroup() to add components within a named group.'\n );\n }\n}\n","import { ComponentVisualFactoryBase } from '../ComponentVisualFactory';\nimport { ComponentType, type Component } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport type { VisualContext } from '../../types';\nimport { CmpMatCategory } from '../types';\n\n/**\n * Visual factory for Battery components\n */\nexport class BatteryVisualFactory extends ComponentVisualFactoryBase {\n private readonly BATTERY_GEOMETRY = new THREE.CylinderGeometry(0.5, 0.5, 2, 24);\n\n constructor() {\n super();\n this._componentType = ComponentType.Battery;\n }\n\n createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n // Root group (not rendered, just organizational)\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n // Component hitbox (invisible, raycastable)\n const hitbox = this.createComponentHitbox(component.id, group.id, 2, 2, 3);\n group.add(hitbox);\n\n // Visual: battery cylinder\n const cylinder = new THREE.Mesh(this.BATTERY_GEOMETRY, this.getMat(CmpMatCategory.WHITE));\n cylinder.userData = {\n type: 'component',\n componentId: component.id,\n };\n cylinder.rotateX(Math.PI / 2);\n group.add(cylinder);\n\n // pins (not called if preview - no pins)\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n return group;\n }\n\n private createPinsVisual(component: Component, context: VisualContext, group: THREE.Group) {\n const cathodeNode = context.getENode(component.pins[0]!);\n if (cathodeNode) {\n const cathodeGroup = this.createPinGroup(cathodeNode, 'top');\n cathodeGroup.position.set(0, 0, -1);\n group.add(cathodeGroup);\n }\n\n const anodeNode = context.getENode(component.pins[1]!);\n if (anodeNode) {\n const anodeGroup = this.createPinGroup(anodeNode, 'bottom');\n anodeGroup.position.set(0, 0, 1);\n group.add(anodeGroup);\n }\n }\n\n // Uses default hover implementation\n // No animation (battery is static)\n}\n","/**\n * Label Visual Factory\n * @module scene/shared/components/LabelVisualFactory\n *\n * Provides visual factory for Label components - decorative text elements\n * with no electrical connections. Uses CanvasTexture for crisp text rendering.\n */\n\nimport { ComponentVisualFactoryBase } from '../ComponentVisualFactory';\nimport { ComponentType, type Component } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport { BoxGeometry } from 'three';\nimport type { ConfigFormDefinition, VisualContext } from '../../types';\n\n/**\n * Visual factory for Label components\n *\n * Creates:\n * - Text mesh using CanvasTexture for stencil/technical font styling\n * - Component hitbox for raycasting\n * - No pin groups (Label has zero pins)\n *\n * Configuration:\n * - text: Display text content (max 64 characters, default \"Label\")\n * - size: Scale multiplier (1-10, default 1)\n *\n * @example\n * ```typescript\n * const factory = new LabelVisualFactory();\n * const visual = factory.createVisual(labelComponent);\n * scene.add(visual);\n *\n * // Update text\n * const newConfig = new Map([['text', 'Power Supply'], ['size', '2']]);\n * factory.updateFromConfiguration(visual, newConfig);\n * ```\n */\nexport class LabelVisualFactory extends ComponentVisualFactoryBase {\n /** Maximum text length in characters */\n private static readonly MAX_TEXT_LENGTH = 64;\n\n /** Font family for technical/stencil aesthetic */\n private static readonly FONT_FAMILY = '\"Courier New\", Courier, \"Liberation Mono\", monospace';\n\n /** Text color (dark gray for readability) */\n private static readonly TEXT_COLOR = '#333333';\n\n /** Hover text color (blue tint matching other components) */\n private static readonly HOVER_COLOR = '#4488ff';\n\n /** Selection text color (orange tint matching other components) */\n private static readonly SELECTION_COLOR = '#ff8800';\n\n /** Base font size in pixels */\n private static readonly BASE_FONT_SIZE = 32;\n\n /** Padding around text in pixels */\n private static readonly PADDING = 8;\n\n constructor() {\n super();\n this._componentType = ComponentType.Label;\n }\n\n createVisual(component: Component, _context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n // Get initial text from config\n const text = component.config.get('text') || 'Label';\n\n // Create text mesh first to get dimensions for hitbox\n const textMesh = this.createTextMesh(text);\n // Preserve canvas/texture refs from createTextMesh, add component metadata\n textMesh.userData.type = 'component';\n textMesh.userData.componentId = component.id;\n textMesh.userData.part = 'text';\n group.add(textMesh);\n\n // Create hitbox based on text mesh dimensions\n const textGeometry = textMesh.geometry as THREE.PlaneGeometry;\n const width = textGeometry.parameters.width;\n const height = textGeometry.parameters.height;\n const hitbox = this.createComponentHitbox(component.id, group.id, width, height, 0.2);\n hitbox.userData.componentType = component.type;\n group.add(hitbox);\n\n // Apply initial configuration (size scaling)\n this.updateFromConfiguration(group, component.config);\n\n return group;\n }\n\n /**\n * Create a canvas with rendered text\n *\n * @param text - Text to render (truncated to MAX_TEXT_LENGTH)\n * @returns HTMLCanvasElement with rendered text\n */\n private createTextCanvas(text: string): HTMLCanvasElement {\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d')!;\n\n // Handle device pixel ratio for sharp rendering\n const pixelRatio = Math.min(window.devicePixelRatio || 1, 2);\n const scaledFontSize = LabelVisualFactory.BASE_FONT_SIZE * pixelRatio;\n const scaledPadding = LabelVisualFactory.PADDING * pixelRatio;\n\n // Truncate text to max length\n const displayText = text.slice(0, LabelVisualFactory.MAX_TEXT_LENGTH) || 'Label';\n\n // Set font to measure text\n ctx.font = `bold ${scaledFontSize}px ${LabelVisualFactory.FONT_FAMILY}`;\n const metrics = ctx.measureText(displayText);\n\n // Calculate canvas dimensions\n const textWidth = Math.ceil(metrics.width);\n const textHeight = Math.ceil(scaledFontSize * 1.2); // Add line height factor\n\n canvas.width = textWidth + scaledPadding * 2;\n canvas.height = textHeight + scaledPadding * 2;\n\n // Re-apply font after resize (canvas resize clears context state)\n ctx.font = `bold ${scaledFontSize}px ${LabelVisualFactory.FONT_FAMILY}`;\n ctx.fillStyle = LabelVisualFactory.TEXT_COLOR;\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n\n // Draw text centered\n ctx.fillText(displayText, canvas.width / 2, canvas.height / 2);\n\n return canvas;\n }\n\n /**\n * Create a text mesh using CanvasTexture\n *\n * @param text - Text to display\n * @returns THREE.Mesh with text texture\n */\n private createTextMesh(text: string): THREE.Mesh {\n const displayText = this.normalizeDisplayText(text);\n\n const canvas = this.createTextCanvas(displayText);\n const texture = new THREE.CanvasTexture(canvas);\n texture.minFilter = THREE.LinearFilter;\n texture.magFilter = THREE.LinearFilter;\n\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n side: THREE.DoubleSide,\n depthWrite: false,\n });\n\n // Calculate world-space dimensions\n const pixelRatio = Math.min(window.devicePixelRatio || 1, 2);\n const worldWidth = canvas.width / pixelRatio / 50; // Scale factor for scene units\n const worldHeight = canvas.height / pixelRatio / 50;\n\n const geometry = new THREE.PlaneGeometry(worldWidth, worldHeight);\n const mesh = new THREE.Mesh(geometry, material);\n\n // Store canvas and texture references for updates\n mesh.userData.canvas = canvas;\n mesh.userData.texture = texture;\n mesh.userData.text = displayText;\n\n // Position slightly above ground plane\n mesh.position.set(0, 0.01, 0);\n mesh.rotation.x = -Math.PI / 2; // Lay flat on XZ plane\n\n return mesh;\n }\n\n /**\n * Find the text mesh within the component group\n *\n * @param object3D - The component group\n * @returns The text mesh or null if not found\n */\n private findTextMesh(object3D: THREE.Object3D): THREE.Mesh | null {\n let textMesh: THREE.Mesh | null = null;\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === 'text') {\n textMesh = child;\n }\n });\n return textMesh;\n }\n\n /**\n * Normalize display text by truncating and providing default\n * @param text\n * @private\n */\n private normalizeDisplayText(text: string): string {\n const displayText = text.slice(0, LabelVisualFactory.MAX_TEXT_LENGTH) || 'Label';\n return displayText;\n }\n\n /**\n * Update the text mesh with new text content\n *\n * Resizes canvas and geometry to fit the new text, then redraws.\n *\n * @param mesh - The text mesh to update\n * @param text - New text content\n * @param group - The parent group containing the hitbox to update\n */\n private updateTextMesh(mesh: THREE.Mesh, text: string, group: THREE.Object3D): void {\n const displayText = this.normalizeDisplayText(text);\n if (displayText === mesh.userData.text) return;\n\n mesh.geometry.dispose();\n\n const canvas = this.createTextCanvas(displayText);\n const texture = new THREE.CanvasTexture(canvas);\n texture.minFilter = THREE.LinearFilter;\n texture.magFilter = THREE.LinearFilter;\n\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n side: THREE.DoubleSide,\n depthWrite: false,\n });\n\n // Calculate world-space dimensions\n const pixelRatio = Math.min(window.devicePixelRatio || 1, 2);\n const worldWidth = canvas.width / pixelRatio / 50; // Scale factor for scene units\n const worldHeight = canvas.height / pixelRatio / 50;\n\n mesh.geometry = new THREE.PlaneGeometry(worldWidth, worldHeight);\n mesh.material = material;\n\n // Update hitbox size\n const hitbox = this.findHitbox(group);\n if (hitbox) {\n hitbox.geometry.dispose();\n hitbox.geometry = new BoxGeometry(worldWidth, 0.1, worldHeight);\n }\n\n // Store canvas and texture references for updates\n mesh.userData.canvas = canvas;\n mesh.userData.texture = texture;\n mesh.userData.text = displayText;\n }\n\n /**\n * Update visual based on Label configuration\n *\n * @param object3D - The component group\n * @param config - Configuration map with text and size\n */\n override updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void {\n // Update text\n const textMesh = this.findTextMesh(object3D);\n if (textMesh) {\n const newText = config.get('text') || 'Label';\n if (textMesh.userData.text !== newText) {\n this.updateTextMesh(textMesh, newText, object3D);\n }\n }\n\n // Update scale\n const size = parseFloat(config.get('size') || '1');\n const clampedSize = Math.max(1, size);\n object3D.scale.set(clampedSize, clampedSize, clampedSize);\n }\n\n /**\n * Get config form definition for Label component\n *\n * @returns Form definition with text and size fields\n */\n override getConfigFormDefinition(): ConfigFormDefinition {\n return {\n fields: [\n { key: 'text', type: 'text' },\n { key: 'size', type: 'number', min: 1, max: 16, step: 1 },\n ],\n };\n }\n\n /**\n * Map core config to form data\n *\n * @param config - Core config from Component.config\n * @returns Form data with appropriate types\n */\n override mapCoreConfigToForm(config: Map<string, string>): Map<string, any> {\n const formData = new Map<string, any>();\n formData.set('text', config.get('text') || 'Label');\n formData.set('size', parseFloat(config.get('size') || '1'));\n return formData;\n }\n\n /**\n * Map form data back to core config\n *\n * @param formData - Form data from UI\n * @returns Core config with string values\n */\n override mapFormToCoreConfig(formData: Map<string, any>): Map<string, string> {\n const config = new Map<string, string>();\n\n // Handle text with truncation and default fallback\n let text = String(formData.get('text') || 'Label');\n text = text.slice(0, LabelVisualFactory.MAX_TEXT_LENGTH);\n if (!text.trim()) {\n text = 'Label';\n }\n config.set('text', text);\n\n // Handle size\n const size = Math.max(1, Number(formData.get('size')) || 1);\n config.set('size', String(size));\n\n return config;\n }\n\n /**\n * Apply hover visual effect to the Label\n *\n * Changes text color to hover color (blue) by redrawing the canvas.\n *\n * @param object3D - The component group\n */\n override applyHover(object3D: THREE.Object3D): void {\n object3D.userData.isHovered = true;\n // Selection takes precedence over hover\n if (object3D.userData.isSelected) return;\n this.redrawTextWithColor(object3D, LabelVisualFactory.HOVER_COLOR);\n }\n\n /**\n * Remove hover visual effect from the Label\n *\n * Restores text color to default by redrawing the canvas.\n *\n * @param object3D - The component group\n */\n override removeHover(object3D: THREE.Object3D): void {\n object3D.userData.isHovered = false;\n // Keep selection color if selected\n if (object3D.userData.isSelected) return;\n this.redrawTextWithColor(object3D, LabelVisualFactory.TEXT_COLOR);\n }\n\n /**\n * Apply selection visual effect to the Label\n *\n * Changes text color to selection color (orange) by redrawing the canvas.\n *\n * @param object3D - The component group\n */\n override applySelection(object3D: THREE.Object3D): void {\n object3D.userData.isSelected = true;\n this.redrawTextWithColor(object3D, LabelVisualFactory.SELECTION_COLOR);\n }\n\n /**\n * Remove selection visual effect from the Label\n *\n * Restores text color based on hover state.\n *\n * @param object3D - The component group\n */\n override removeSelection(object3D: THREE.Object3D): void {\n object3D.userData.isSelected = false;\n\n if (object3D.userData.isHovered) {\n this.redrawTextWithColor(object3D, LabelVisualFactory.HOVER_COLOR);\n } else {\n this.redrawTextWithColor(object3D, LabelVisualFactory.TEXT_COLOR);\n }\n }\n\n /**\n * Redraw the text canvas with a specific color\n *\n * @param object3D - The component group\n * @param color - The CSS color string to use\n */\n private redrawTextWithColor(object3D: THREE.Object3D, color: string): void {\n const textMesh = this.findTextMesh(object3D);\n if (!textMesh) {\n return;\n }\n\n const canvas = textMesh.userData.canvas as HTMLCanvasElement;\n const texture = textMesh.userData.texture as THREE.CanvasTexture;\n const displayText = textMesh.userData.text as string;\n\n if (!canvas || !texture || !displayText) {\n return;\n }\n\n const ctx = canvas.getContext('2d')!;\n const pixelRatio = Math.min(window.devicePixelRatio || 1, 2);\n const scaledFontSize = LabelVisualFactory.BASE_FONT_SIZE * pixelRatio;\n\n // Clear and redraw with new color\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.font = `bold ${scaledFontSize}px ${LabelVisualFactory.FONT_FAMILY}`;\n ctx.fillStyle = color;\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.fillText(displayText, canvas.width / 2, canvas.height / 2);\n\n texture.needsUpdate = true;\n }\n}\n","import { ComponentVisualFactoryBase } from '../ComponentVisualFactory';\nimport { CmpMatCategory, CmpMatType } from '../types';\nimport { ComponentType, type Component, type ComponentState, type LightbulbState } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\n\nimport type { ConfigFormDefinition, VisualContext } from '../../types';\n\n/**\n * Visual factory for Lightbulb components\n * Animation:\n * - Smooth emissive glow transition when Lightbulb lights up or turns off\n * - Uses AnimationMixer with material property tracks (emissive, opacity)\n * - Clones shared GLASS material per-instance during simulation for independent animation\n */\nexport class LightbulbVisualFactory extends ComponentVisualFactoryBase {\n /** Lightbulb lit color (yellow glow) */\n private readonly BULB_LIT_COLOR = 0xffff00;\n /** Lightbulb lit emissive intensity */\n private readonly BULB_LIT_INTENSITY = 1.0;\n /** Lightbulb unlit opacity */\n private readonly BULB_UNLIT_OPACITY = 0.55;\n\n private readonly BASE_GEOMETRY = new THREE.CylinderGeometry(\n 0.3,\n 0.15,\n 0.4,\n 12,\n 6,\n false,\n 0,\n Math.PI * 2\n );\n private readonly BULB_GEOMETRY = new THREE.SphereGeometry(0.5, 16, 16);\n\n /** Yellow in normalized RGB for ColorKeyframeTrack */\n private readonly LIT_RGB = [1, 1, 0];\n /** Black in normalized RGB for ColorKeyframeTrack */\n private readonly UNLIT_RGB = [0, 0, 0];\n\n constructor() {\n super();\n this._componentType = ComponentType.Lightbulb;\n }\n\n createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n // Root group (not rendered, just organizational)\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n // Component hitbox (invisible, raycastable)\n const hitbox = this.createComponentHitbox(component.id, group.id, 1, 3, 1);\n group.add(hitbox);\n\n // Visuals\n const base = new THREE.Mesh(this.BASE_GEOMETRY, this.getMat(CmpMatCategory.WHITE));\n base.userData = {\n type: 'component',\n componentId: component.id,\n part: 'base',\n };\n base.position.set(0, 0.2, 0);\n group.add(base);\n\n const bulb = new THREE.Mesh(this.BULB_GEOMETRY, this.getMat(CmpMatCategory.GLASS));\n bulb.name = 'bulb'; // required for AnimationMixer property binding\n bulb.userData = {\n type: 'component',\n componentId: component.id,\n part: 'bulb',\n };\n bulb.position.set(0, 0.8, 0);\n group.add(bulb);\n bulb.renderOrder = -1; // to prevent rendering collision issue with pins hover area\n\n // pins (not called if preview - no pins)\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n\n this.updateFromConfiguration(group, component.config);\n return group;\n }\n\n private createPinsVisual(component: Component, context: VisualContext, group: THREE.Group) {\n const pin1Node = context.getENode(component.pins[0]!);\n if (pin1Node) {\n const pin1Group = this.createPinGroup(pin1Node, 'left');\n pin1Group.position.set(-0.25, 0, 0);\n group.add(pin1Group);\n\n const pin1Counterpart = this.createPinCounterpart(\n pin1Group,\n this.getMat(CmpMatCategory.WHITE)\n );\n if (!!pin1Counterpart) {\n group.add(pin1Counterpart);\n }\n }\n\n const pin2Node = context.getENode(component.pins[1]!);\n if (pin2Node) {\n const pin2Group = this.createPinGroup(pin2Node, 'right');\n pin2Group.position.set(0.25, 0, 0);\n group.add(pin2Group);\n\n const pin2Counterpart = this.createPinCounterpart(\n pin2Group,\n this.getMat(CmpMatCategory.WHITE)\n );\n if (!!pin2Counterpart) {\n group.add(pin2Counterpart);\n }\n }\n }\n\n /**\n * Get config form definition for Lightbulb\n *\n * @returns Form definition with size\n */\n override getConfigFormDefinition(): ConfigFormDefinition | null {\n return {\n fields: [\n { key: 'transitionSpan', type: 'number', min: 1, step: 1 },\n { key: 'size', type: 'number', min: 1, max: 16, step: 1 },\n ],\n };\n }\n\n override mapCoreConfigToForm(config: Map<string, string>): Map<string, any> {\n const formData = new Map<string, any>();\n formData.set('transitionSpan', parseFloat(config.get('transitionSpan') || '1'));\n formData.set('size', parseFloat(config.get('size') || '1'));\n return formData;\n }\n\n /**\n * Map form data to core config\n *\n * @param formData - Form data with size\n * @returns Core config with string size\n */\n override mapFormToCoreConfig(formData: Map<string, any>): Map<string, string> {\n const config = new Map<string, string>();\n config.set('transitionSpan', formData.get('transitionSpan').toString());\n config.set('size', formData.get('size').toString());\n return config;\n }\n\n override updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>) {\n const scale = parseFloat(config.get('size') || '1');\n object3D.scale.set(scale, scale, scale);\n this.updateAnimation(object3D, null);\n }\n\n /**\n * Update Lightbulb animation based on simulation state.\n *\n * - null state / no context: restore shared material, cleanup mixer\n * - paused/initial: snap to target state\n * - playing + transitional (goingOn/goingOff): smooth material animation\n * - playing + stable (on/off): snap to final state\n */\n override updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void {\n const bulbMesh = this.findBulbMesh(object3D);\n if (!bulbMesh) return;\n\n // Leaving simulation: restore shared material\n if (!state || !this._animationContext) {\n this._cleanupMixer(object3D);\n this._restoreSharedMaterial(bulbMesh);\n return;\n }\n\n const lightbulbState = state as LightbulbState;\n\n // Paused/initial: snap to current state (mixer won't advance)\n if (this._animationContext.simulationStatus !== 'playing') {\n this._cleanupMixer(object3D);\n this._snapToState(bulbMesh, lightbulbState.isLit);\n return;\n }\n\n // Playing + transitional state: animate\n if (state.hasExpiration) {\n this._animateBulb(object3D, bulbMesh, state);\n return;\n }\n\n // Playing + stable state: snap, cleanup mixer\n this._cleanupMixer(object3D);\n this._snapToState(bulbMesh, lightbulbState.isLit);\n }\n\n // ---------------------------------------------------------------------------\n // Material clone / restore\n // ---------------------------------------------------------------------------\n\n /**\n * Clone the shared GLASS material for independent per-instance animation.\n * No-op if already cloned.\n */\n private _ensureClonedMaterial(bulbMesh: THREE.Mesh): void {\n const mat = bulbMesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType === CmpMatType.ANIMATION_CLONE) return;\n bulbMesh.material = mat.clone();\n (bulbMesh.material as THREE.MeshLambertMaterial).userData.matType = CmpMatType.ANIMATION_CLONE;\n }\n\n /**\n * Dispose the per-instance clone and restore the shared GLASS.NORMAL material.\n * No-op if not cloned.\n */\n private _restoreSharedMaterial(bulbMesh: THREE.Mesh): void {\n const mat = bulbMesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType !== CmpMatType.ANIMATION_CLONE) return;\n mat.dispose();\n bulbMesh.material = this.getMat(CmpMatCategory.GLASS);\n }\n\n // ---------------------------------------------------------------------------\n // Snap (immediate state application)\n // ---------------------------------------------------------------------------\n\n private _snapToState(bulbMesh: THREE.Mesh, isLit: boolean): void {\n this._ensureClonedMaterial(bulbMesh);\n const mat = bulbMesh.material as THREE.MeshLambertMaterial;\n\n if (isLit) {\n mat.emissive.setHex(this.BULB_LIT_COLOR);\n mat.emissiveIntensity = this.BULB_LIT_INTENSITY;\n mat.opacity = 1;\n } else {\n mat.emissive.setHex(0x000000);\n mat.emissiveIntensity = 0;\n mat.opacity = this.BULB_UNLIT_OPACITY;\n }\n }\n\n // ---------------------------------------------------------------------------\n // AnimationMixer management\n // ---------------------------------------------------------------------------\n\n /**\n * Create a smooth material animation for goingOn / goingOff transitions.\n */\n private _animateBulb(\n object3D: THREE.Object3D,\n bulbMesh: THREE.Mesh,\n state: ComponentState\n ): void {\n // Prevent duplicate animation for same transition\n if (object3D.userData.currentActionStart === state.startTick) return;\n\n this._ensureClonedMaterial(bulbMesh);\n\n const tps = this._animationContext!.ticksPerSecond;\n const span = state.expirationTick - state.startTick;\n const durationSeconds = span / tps;\n\n // Get or create mixer\n let mixer: THREE.AnimationMixer = object3D.userData.mixer;\n if (!mixer) {\n mixer = new THREE.AnimationMixer(object3D);\n object3D.userData.mixer = mixer;\n }\n\n // Read current material values BEFORE stopping previous action,\n // so mid-transition interrupts (goingOff→goingOn) animate from actual visual state\n const mat = bulbMesh.material as THREE.MeshLambertMaterial;\n const currentIntensity = mat.emissiveIntensity;\n const currentOpacity = mat.opacity;\n const currentRGB = [mat.emissive.r, mat.emissive.g, mat.emissive.b];\n\n // Stop previous animation\n if (object3D.userData.currentAction) {\n (object3D.userData.currentAction as THREE.AnimationAction).stop();\n }\n if (object3D.userData.currentClip) {\n mixer.uncacheClip(object3D.userData.currentClip as THREE.AnimationClip);\n }\n\n // Determine end values based on transition direction; start from current visual state\n const isGoingOn = state.state === 'goingOn';\n const endIntensity = isGoingOn ? this.BULB_LIT_INTENSITY : 0;\n const endOpacity = isGoingOn ? 1 : this.BULB_UNLIT_OPACITY;\n const endRGB = isGoingOn ? this.LIT_RGB : this.UNLIT_RGB;\n\n const tracks = [\n new THREE.NumberKeyframeTrack(\n 'bulb.material.emissiveIntensity',\n [0, durationSeconds],\n [currentIntensity, endIntensity]\n ),\n new THREE.NumberKeyframeTrack(\n 'bulb.material.opacity',\n [0, durationSeconds],\n [currentOpacity, endOpacity]\n ),\n new THREE.ColorKeyframeTrack(\n 'bulb.material.emissive',\n [0, durationSeconds],\n [...currentRGB, ...endRGB]\n ),\n ];\n\n const clip = new THREE.AnimationClip('bulbGlow', durationSeconds, tracks);\n const action = mixer.clipAction(clip);\n action.loop = THREE.LoopOnce;\n action.clampWhenFinished = true;\n action.play();\n\n // Store references for cleanup/interruption\n object3D.userData.currentActionStart = state.startTick;\n object3D.userData.currentAction = action;\n object3D.userData.currentClip = clip;\n }\n\n /**\n * Stop all animations, clean up mixer.\n */\n private _cleanupMixer(object3D: THREE.Object3D): void {\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n if (mixer) {\n mixer.stopAllAction();\n mixer.uncacheRoot(object3D);\n delete object3D.userData.mixer;\n }\n delete object3D.userData.currentAction;\n delete object3D.userData.currentClip;\n delete object3D.userData.currentActionStart;\n }\n\n // ---------------------------------------------------------------------------\n // Helpers\n // ---------------------------------------------------------------------------\n\n private findBulbMesh(object3D: THREE.Object3D): THREE.Mesh | null {\n let bulbMesh: THREE.Mesh | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === 'bulb') {\n bulbMesh = child as THREE.Mesh;\n }\n });\n\n return bulbMesh;\n }\n\n // Uses default hover implementation\n}\n","import { ComponentVisualFactoryBase } from '../ComponentVisualFactory';\nimport { CmpMatCategory, CmpMatType } from '../types';\nimport { ComponentType, type Component, type ComponentState, type SmallLEDState } from 'simple-circuit-engine/core';\nimport { presetOrHexToHex, hexToPresetOrHex } from '../../utils/ColorUtils';\nimport * as THREE from 'three';\nimport type { ConfigFormDefinition, VisualContext } from '../../types';\n\n/**\n * Visual factory for RectangleLED components\n *\n * Creates:\n * - LED box mesh\n * - Input pin group\n * - Output pin group\n * - Component hitbox for raycasting\n *\n * Animation:\n * - Smooth emissive glow transition when LED lights up or turns off\n * - Uses AnimationMixer with material property tracks (emissive, emissiveIntensity)\n * - Clones PRIVATE material per-instance during simulation for independent animation\n */\nexport class RectangleLEDVisualFactory extends ComponentVisualFactoryBase {\n /** LED lit color (yellow glow) */\n private readonly LED_LIT_COLOR = 0xffff00;\n\n /** LED lit emissive intensity */\n private readonly LED_LIT_INTENSITY = 1.0;\n\n /** Black in normalized RGB for ColorKeyframeTrack */\n private readonly UNLIT_RGB = [0, 0, 0];\n\n constructor() {\n super();\n this._componentType = ComponentType.RectangleLED;\n }\n\n createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n // Root group (not rendered, just organizational)\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n // Component hitbox (invisible, raycastable)\n const hitbox = this.createComponentHitbox(component.id, group.id, 1, 3, 1);\n group.add(hitbox);\n\n // Visual LED\n const ledGeometry = new THREE.BoxGeometry(1, 1, 1);\n const ledMat = this.getMat(CmpMatCategory.WHITE).clone();\n ledMat.userData.matType = CmpMatType.PRIVATE;\n const led = new THREE.Mesh(ledGeometry, ledMat);\n led.name = 'led'; // required for AnimationMixer property binding\n led.userData = {\n type: 'component',\n componentId: component.id,\n part: 'led',\n idleColorHex: 0xffffff,\n activeColorHex: this.LED_LIT_COLOR,\n };\n led.position.set(0, 0.25, 0);\n group.add(led);\n\n // pins (not called if preview - no pins)\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n this.updateFromConfiguration(group, component.config);\n return group;\n }\n\n private createPinsVisual(component: Component, context: VisualContext, group: THREE.Group) {\n const inputNode = context.getENode(component.pins[0]!);\n if (inputNode) {\n const pin1Group = this.createPinGroup(inputNode, 'left');\n pin1Group.position.set(-0.5, 0, 0);\n group.add(pin1Group);\n }\n\n const outputNode = context.getENode(component.pins[1]!);\n if (outputNode) {\n const outputPinGroup = this.createPinGroup(outputNode, 'right');\n outputPinGroup.position.set(0.5, 0, 0);\n group.add(outputPinGroup);\n }\n }\n\n /**\n * Get config form definition for RectangleLED (T027)\n *\n * @returns Form definition with activeColor and idleColor color fields\n */\n override getConfigFormDefinition(): ConfigFormDefinition | null {\n return {\n fields: [\n { key: 'transitionSpan', type: 'number', min: 1, step: 1 },\n { key: 'idleColor', type: 'color' },\n { key: 'activeColor', type: 'color' },\n { key: 'size', type: 'number', min: 1, max: 16, step: 1 },\n { key: 'hwRatio', type: 'number' },\n { key: 'ywRatio', type: 'number' },\n ],\n };\n }\n\n /**\n * Map core config to form data (T027)\n * Converts hex/preset strings to hex values for color picker\n *\n * @param config - Core component config\n * @returns Form data with hex color strings\n */\n override mapCoreConfigToForm(config: Map<string, string>): Map<string, any> {\n const formData = new Map<string, any>();\n formData.set('transitionSpan', parseFloat(config.get('transitionSpan') || '1'));\n const idleColor = config.get('idleColor') || '#ffffff';\n const activeColor = config.get('activeColor') || '#ffff00';\n\n // Convert preset names to hex if needed\n formData.set('idleColor', presetOrHexToHex(idleColor));\n formData.set('activeColor', presetOrHexToHex(activeColor));\n formData.set('size', parseFloat(config.get('size') || '1'));\n formData.set('hwRatio', parseFloat(config.get('hwRatio') || '1'));\n formData.set('ywRatio', parseFloat(config.get('ywRatio') || '1'));\n\n return formData;\n }\n\n /**\n * Map form data to core config (T027)\n * Converts hex colors to preset names if they match, otherwise keeps hex\n *\n * @param formData - Form data with hex color strings\n * @returns Core config with hex or preset name strings\n */\n override mapFormToCoreConfig(formData: Map<string, any>): Map<string, string> {\n const config = new Map<string, string>();\n config.set('transitionSpan', formData.get('transitionSpan').toString());\n const activeColor = formData.get('activeColor');\n const idleColor = formData.get('idleColor');\n\n // Convert hex to preset name if it matches a preset\n if (activeColor) {\n config.set('activeColor', hexToPresetOrHex(activeColor));\n }\n if (idleColor) {\n config.set('idleColor', hexToPresetOrHex(idleColor));\n }\n\n config.set('size', formData.get('size').toString());\n config.set('hwRatio', formData.get('hwRatio').toString());\n config.set('ywRatio', formData.get('ywRatio').toString());\n return config;\n }\n\n /**\n * Update visual from configuration (T022)\n * Updates LED color and geometry based on config\n */\n override updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void {\n const ledMesh = this.findLedMesh(object3D);\n const pin1 = this.findPinGroup(object3D, 'pin1');\n const pin2 = this.findPinGroup(object3D, 'pin2');\n const hitbox = this.findHitbox(object3D);\n\n if (!ledMesh || !pin1 || !pin2 || !hitbox) return;\n\n const ledMat = ledMesh.material as THREE.MeshLambertMaterial;\n\n // changing colors\n const idleColor = config.get('idleColor');\n if (idleColor) {\n // Convert preset to hex if needed, then parse\n const idleColorHex = parseInt(presetOrHexToHex(idleColor).replace('#', ''), 16);\n ledMat.color.setHex(idleColorHex);\n ledMesh.userData.idleColorHex = idleColorHex;\n }\n const activeColor = config.get('activeColor');\n if (activeColor) {\n // Convert preset to hex if needed, then parse\n ledMesh.userData.activeColorHex = parseInt(\n presetOrHexToHex(activeColor).replace('#', ''),\n 16\n );\n }\n // changing geometry\n const hwRatio = parseFloat(config.get('hwRatio') || '1');\n const ywRatio = parseFloat(config.get('ywRatio') || '1');\n ledMesh.geometry.dispose();\n ledMesh.geometry = new THREE.BoxGeometry(1, ywRatio, hwRatio);\n ledMesh.position.set(0, 0.25 * ywRatio, 0);\n hitbox.geometry.dispose();\n hitbox.geometry = new THREE.BoxGeometry(1, 1.5 * ywRatio, hwRatio);\n // scaling the pins (1 if hwRatio>=0.5, else scaled down to fit better)\n const pinScale = hwRatio >= 0.5 ? 1 : hwRatio * 2;\n pin1.scale.set(pinScale, pinScale, pinScale);\n pin2.scale.set(pinScale, pinScale, pinScale);\n\n // scaling\n const scale = parseFloat(config.get('size') || '1');\n object3D.scale.set(scale, scale, scale);\n\n this.updateAnimation(object3D, null);\n }\n\n /**\n * Update LED animation based on simulation state.\n *\n * - null state / no context: restore private material, cleanup mixer\n * - paused/initial: snap to target state\n * - playing + transitional (goingOn/goingOff): smooth material animation\n * - playing + stable (on/off): snap to final state\n */\n override updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void {\n const ledMesh = this.findLedMesh(object3D);\n if (!ledMesh) return;\n\n // Leaving simulation: restore private material\n if (!state || !this._animationContext) {\n this._cleanupMixer(object3D);\n this._restorePrivateMaterial(ledMesh);\n return;\n }\n\n const ledState = state as SmallLEDState;\n\n // Paused/initial: snap to current state (mixer won't advance)\n if (this._animationContext.simulationStatus !== 'playing') {\n this._cleanupMixer(object3D);\n this._snapToState(ledMesh, ledState.isLit);\n return;\n }\n\n // Playing + transitional state: animate\n if (state.hasExpiration) {\n this._animateLed(object3D, ledMesh, state);\n return;\n }\n\n // Playing + stable state: snap, cleanup mixer\n this._cleanupMixer(object3D);\n this._snapToState(ledMesh, ledState.isLit);\n }\n\n // ---------------------------------------------------------------------------\n // Material clone / restore (PRIVATE → ANIMATION_CLONE → PRIVATE)\n // ---------------------------------------------------------------------------\n\n /**\n * Clone the PRIVATE material for independent per-instance animation.\n * Stashes the original PRIVATE material in mesh.userData.privateMat.\n * No-op if already cloned.\n */\n private _ensureClonedMaterial(ledMesh: THREE.Mesh): void {\n const mat = ledMesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType === CmpMatType.ANIMATION_CLONE) return;\n ledMesh.userData.privateMat = mat;\n ledMesh.material = mat.clone();\n (ledMesh.material as THREE.MeshLambertMaterial).userData.matType = CmpMatType.ANIMATION_CLONE;\n }\n\n /**\n * Dispose the animation clone and restore the stashed PRIVATE material.\n * No-op if not cloned.\n */\n private _restorePrivateMaterial(ledMesh: THREE.Mesh): void {\n const mat = ledMesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType !== CmpMatType.ANIMATION_CLONE) return;\n mat.dispose();\n if (ledMesh.userData.privateMat) {\n ledMesh.material = ledMesh.userData.privateMat;\n delete ledMesh.userData.privateMat;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Snap (immediate state application)\n // ---------------------------------------------------------------------------\n\n private _snapToState(ledMesh: THREE.Mesh, isLit: boolean): void {\n this._ensureClonedMaterial(ledMesh);\n const mat = ledMesh.material as THREE.MeshLambertMaterial;\n\n if (isLit) {\n mat.emissive.setHex(ledMesh.userData.activeColorHex);\n mat.emissiveIntensity = this.LED_LIT_INTENSITY;\n } else {\n mat.emissive.setHex(0x000000);\n mat.emissiveIntensity = 0;\n }\n }\n\n // ---------------------------------------------------------------------------\n // AnimationMixer management\n // ---------------------------------------------------------------------------\n\n /**\n * Create a smooth material animation for goingOn / goingOff transitions.\n */\n private _animateLed(object3D: THREE.Object3D, ledMesh: THREE.Mesh, state: ComponentState): void {\n // Prevent duplicate animation for same transition\n if (object3D.userData.currentActionStart === state.startTick) return;\n\n this._ensureClonedMaterial(ledMesh);\n\n const tps = this._animationContext!.ticksPerSecond;\n const span = state.expirationTick - state.startTick;\n const durationSeconds = span / tps;\n\n // Get or create mixer\n let mixer: THREE.AnimationMixer = object3D.userData.mixer;\n if (!mixer) {\n mixer = new THREE.AnimationMixer(object3D);\n object3D.userData.mixer = mixer;\n }\n\n // Read current material values BEFORE stopping previous action,\n // so mid-transition interrupts animate from actual visual state\n const mat = ledMesh.material as THREE.MeshLambertMaterial;\n const currentIntensity = mat.emissiveIntensity;\n const currentRGB = [mat.emissive.r, mat.emissive.g, mat.emissive.b];\n\n // Stop previous animation\n if (object3D.userData.currentAction) {\n (object3D.userData.currentAction as THREE.AnimationAction).stop();\n }\n if (object3D.userData.currentClip) {\n mixer.uncacheClip(object3D.userData.currentClip as THREE.AnimationClip);\n }\n\n // Determine end values based on transition direction\n const isGoingOn = state.state === 'goingOn';\n const endIntensity = isGoingOn ? this.LED_LIT_INTENSITY : 0;\n const activeHex = ledMesh.userData.activeColorHex as number;\n const activeColor = new THREE.Color(activeHex);\n const endRGB = isGoingOn ? [activeColor.r, activeColor.g, activeColor.b] : this.UNLIT_RGB;\n\n const tracks = [\n new THREE.NumberKeyframeTrack(\n 'led.material.emissiveIntensity',\n [0, durationSeconds],\n [currentIntensity, endIntensity]\n ),\n new THREE.ColorKeyframeTrack(\n 'led.material.emissive',\n [0, durationSeconds],\n [...currentRGB, ...endRGB]\n ),\n ];\n\n const clip = new THREE.AnimationClip('ledGlow', durationSeconds, tracks);\n const action = mixer.clipAction(clip);\n action.loop = THREE.LoopOnce;\n action.clampWhenFinished = true;\n action.play();\n\n // Store references for cleanup/interruption\n object3D.userData.currentActionStart = state.startTick;\n object3D.userData.currentAction = action;\n object3D.userData.currentClip = clip;\n }\n\n /**\n * Stop all animations, clean up mixer.\n */\n private _cleanupMixer(object3D: THREE.Object3D): void {\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n if (mixer) {\n mixer.stopAllAction();\n mixer.uncacheRoot(object3D);\n delete object3D.userData.mixer;\n }\n delete object3D.userData.currentAction;\n delete object3D.userData.currentClip;\n delete object3D.userData.currentActionStart;\n }\n\n // ---------------------------------------------------------------------------\n // Helpers\n // ---------------------------------------------------------------------------\n\n private findLedMesh(object3D: THREE.Object3D): THREE.Mesh | null {\n let ledMesh: THREE.Mesh | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === 'led') {\n ledMesh = child as THREE.Mesh;\n }\n });\n\n return ledMesh;\n }\n\n // Uses default hover implementation\n}\n","import { ComponentVisualFactoryBase } from '../ComponentVisualFactory';\nimport { CmpMatCategory, CmpMatType } from '../types';\nimport { ComponentType, type Component, type ComponentState } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport type { ConfigFormDefinition, VisualContext } from '../../types';\nimport { RingGeometry, LGeometry } from '../../utils/GeometryUtils';\n\n/**\n * Lightweight struct returned by _findParts for convenient access\n * to the relay's animated sub-objects.\n */\ninterface RelayParts {\n contactorGroup: THREE.Group;\n contactor: THREE.Mesh;\n coilBar: THREE.Mesh;\n firstCoil: THREE.Mesh;\n allCoils: THREE.Mesh[];\n powerInBar: THREE.Mesh;\n}\n\n/**\n * Visual factory for Relay components.\n *\n * Animation (3 independent clip groups on a single AnimationMixer):\n *\n * 1. **Mechanical clip** — contactor rotation + coil bar position + bar emissive glow\n * Triggered on state transition (closing/opening). Duration = transitionSpan.\n *\n * 2. **Coil color clip** — all coil rings share one cloned material, single color track\n * Triggered on cmd param change. Deduped by target color hex.\n *\n * 3. **Power color clip** — contactor + powerInBar share one cloned material, single color track\n * Triggered on power param change. Deduped by target color hex.\n */\nexport class RelayVisualFactory extends ComponentVisualFactoryBase {\n // ---------------------------------------------------------------------------\n // Geometries\n // ---------------------------------------------------------------------------\n\n private readonly COIL_GEOM = RingGeometry(0.35, 0.4, 0.1, 16);\n private readonly COIL_BAR_GEOM = new THREE.BoxGeometry(0.1, 0.4, 1.4, 2);\n private readonly PWIN_BAR_GEOM = new THREE.BoxGeometry(0.1, 0.4, 0.85, 2);\n private readonly CONTACTOR_GEOM = LGeometry(0.68, 1.48, 0.1, 140, false, 0.1, 0.4, 16);\n private readonly NEGATIVE_CONTACTOR_GEOM = LGeometry(0.68, 1.58, 0.1, 120, false, 0.1, 0.4, 16);\n\n // ---------------------------------------------------------------------------\n // Coil bar Z positions\n // ---------------------------------------------------------------------------\n\n private readonly COIL_BAR_Z_IDLE = 0;\n private readonly COIL_BAR_Z_ACTIVE = -0.2;\n\n // ---------------------------------------------------------------------------\n // Contactor rotations\n // ---------------------------------------------------------------------------\n\n private readonly IDLE_ROTATION = new THREE.Euler(-Math.PI / 2, 0, -Math.PI);\n private readonly ACTIVE_ROTATION = new THREE.Euler(-Math.PI / 2, 0, -Math.PI - 0.34);\n\n // ---------------------------------------------------------------------------\n // Colors\n // ---------------------------------------------------------------------------\n\n /** Coil ring colors (from cmd.voltage / cmd.current) */\n private readonly COIL_COLOR_BOTH = new THREE.Color(0xff00ff);\n private readonly COIL_COLOR_VOLTAGE = new THREE.Color(0xff4444);\n private readonly COIL_COLOR_CURRENT = new THREE.Color(0x4444ff);\n private readonly COIL_COLOR_NONE = new THREE.Color(0xffffff);\n\n /** Magnetic bar glow */\n private readonly BAR_GLOW_COLOR = new THREE.Color(0xffcc00);\n private readonly BAR_GLOW_INTENSITY = 0.9;\n\n /** Power element colors (from power_in / power_out) */\n private readonly POWER_COLOR_BOTH = new THREE.Color(0xff00ff);\n private readonly POWER_COLOR_VOLTAGE = new THREE.Color(0xff4444);\n private readonly POWER_COLOR_CURRENT = new THREE.Color(0x4444ff);\n private readonly POWER_COLOR_NONE = new THREE.Color(0xffffff);\n\n constructor() {\n super();\n this._componentType = ComponentType.Relay;\n }\n\n // ===================================================================\n // Visual Construction\n // ===================================================================\n\n createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n const hitbox = this.createComponentHitbox(component.id, group.id, 2, 1, 2);\n group.add(hitbox);\n\n const coilGroup = this._createCoilGroup(component);\n group.add(coilGroup);\n coilGroup.position.set(-0.5, 0, 0);\n\n const contactorGroup = this._createContactorGroup(component.id, component.config);\n group.add(contactorGroup);\n contactorGroup.position.set(0, 0, -0.7);\n contactorGroup.rotation.copy(this.IDLE_ROTATION);\n\n const powerInBar = new THREE.Mesh(this.PWIN_BAR_GEOM, this.getMat(CmpMatCategory.WHITE));\n powerInBar.name = 'powerInBar';\n powerInBar.userData = { part: 'powerInBar' };\n powerInBar.rotateY(-Math.PI / 2);\n powerInBar.position.set(0.48, 0, -0.7);\n group.add(powerInBar);\n\n if (component.pins.length > 0) {\n this._createPinsVisual(component, context, group);\n }\n\n this.updateFromConfiguration(group, component.config);\n return group;\n }\n\n private _createPinsVisual(\n component: Component,\n context: VisualContext,\n group: THREE.Group\n ): void {\n const cmdOutNode = context.getENode(component.pins[1]!);\n if (cmdOutNode) {\n const cmdOutGroup = this.createPinGroup(cmdOutNode, 'left');\n cmdOutGroup.position.set(-0.9, 0, 0.7);\n group.add(cmdOutGroup);\n const counterpart = this.createPinCounterpart(cmdOutGroup, this.getMat(CmpMatCategory.WHITE));\n if (counterpart) group.add(counterpart);\n }\n\n const powerInNode = context.getENode(component.pins[2]!);\n if (powerInNode) {\n const powerInGroup = this.createPinGroup(powerInNode, 'right');\n powerInGroup.position.set(0.9, 0, -0.7);\n powerInGroup.renderOrder = 1;\n powerInGroup.children.forEach((child) => {\n child.renderOrder = 1;\n });\n group.add(powerInGroup);\n const counterpart = this.createPinCounterpart(\n powerInGroup,\n this.getMat(CmpMatCategory.WHITE)\n );\n if (counterpart) group.add(counterpart);\n }\n\n const cmdInNode = context.getENode(component.pins[0]!);\n if (cmdInNode) {\n const cmdInGroup = this.createPinGroup(cmdInNode, 'left');\n cmdInGroup.position.set(-0.9, 0, -0.7);\n group.add(cmdInGroup);\n const counterpart = this.createPinCounterpart(cmdInGroup, this.getMat(CmpMatCategory.WHITE));\n if (counterpart) group.add(counterpart);\n }\n\n const powerOutNode = context.getENode(component.pins[3]!);\n if (powerOutNode) {\n const powerOutGroup = this.createPinGroup(powerOutNode, 'right');\n powerOutGroup.position.set(0.9, 0, 0.7);\n group.add(powerOutGroup);\n const counterpart = this.createPinCounterpart(\n powerOutGroup,\n this.getMat(CmpMatCategory.WHITE)\n );\n if (counterpart) group.add(counterpart);\n }\n }\n\n private _createCoilGroup(component: Component): THREE.Group {\n const coilGroup = new THREE.Group();\n coilGroup.userData = {\n type: 'component',\n componentId: component.id,\n part: 'coilGroup',\n };\n\n const partData = {\n type: 'component',\n componentId: component.id,\n part: 'coil',\n };\n\n let coilIndex = 0;\n const addCoil = (z: number, xr: number) => {\n const coil = new THREE.Mesh(this.COIL_GEOM, this.getMat(CmpMatCategory.WHITE));\n coil.name = `coil${coilIndex}`;\n coil.userData = { ...partData };\n coil.position.set(0, 0, z);\n coil.rotateX(xr);\n coilGroup.add(coil);\n coilIndex++;\n };\n\n addCoil(0.6, -0.03);\n addCoil(0.4, 0.03);\n addCoil(0.2, -0.03);\n addCoil(0, 0.03);\n addCoil(-0.2, -0.03);\n addCoil(-0.4, 0.03);\n addCoil(-0.6, -0.03);\n\n const bar = new THREE.Mesh(this.COIL_BAR_GEOM, this.getMat(CmpMatCategory.SHINY_SILVER));\n bar.name = 'coilBar';\n bar.userData = { ...partData, part: 'coilBar' };\n coilGroup.add(bar);\n\n return coilGroup;\n }\n\n private _createContactorGroup(componentId: String, config: Map<string, string>): THREE.Group {\n const contactorGroup = new THREE.Group();\n contactorGroup.name = 'contactorGroup';\n contactorGroup.userData = {\n type: 'component',\n componentId: componentId,\n part: 'contactorGroup',\n variant: config.get('activationLogic'),\n };\n\n const activationLogic = config.get('activationLogic');\n const geom =\n activationLogic === 'negative' ? this.NEGATIVE_CONTACTOR_GEOM : this.CONTACTOR_GEOM;\n\n const contactor = new THREE.Mesh(geom, this.getMat(CmpMatCategory.WHITE));\n contactor.name = 'contactor';\n contactor.userData = {\n type: 'component',\n componentId: componentId,\n part: 'contactor',\n variant: activationLogic,\n };\n contactor.translateZ(-0.2);\n contactorGroup.add(contactor);\n\n return contactorGroup;\n }\n\n private _disposeContactorGroup(object3D: THREE.Object3D): void {\n const contactorGroup = this._findContactorGroup(object3D);\n if (!contactorGroup) return;\n object3D.remove(contactorGroup);\n }\n\n // ===================================================================\n // Configuration Form\n // ===================================================================\n\n override getConfigFormDefinition(): ConfigFormDefinition | null {\n return {\n fields: [\n { key: 'activationLogic', type: 'boolean' },\n { key: 'transitionSpan', type: 'number' },\n { key: 'size', type: 'number', min: 1, max: 16, step: 1 },\n { key: 'initializationOrder', type: 'number' },\n ],\n };\n }\n\n override mapCoreConfigToForm(config: Map<string, string>): Map<string, any> {\n const formData = new Map<string, any>();\n formData.set('activationLogic', config.get('activationLogic') === 'positive');\n formData.set('transitionSpan', parseFloat(config.get('transitionSpan') || '1'));\n formData.set('size', parseFloat(config.get('size') || '1'));\n formData.set('initializationOrder', parseFloat(config.get('initializationOrder') || '0'));\n return formData;\n }\n\n override mapFormToCoreConfig(formData: Map<string, any>): Map<string, string> {\n const config = new Map<string, string>();\n config.set('activationLogic', formData.get('activationLogic') ? 'positive' : 'negative');\n config.set('transitionSpan', formData.get('transitionSpan').toString());\n config.set('size', formData.get('size').toString());\n config.set('initializationOrder', formData.get('initializationOrder').toString() || '0');\n return config;\n }\n\n override updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void {\n const contactorGroup = this._findContactorGroup(object3D);\n if (contactorGroup) {\n const currentVariant = contactorGroup.userData.variant;\n const newVariant = config.get('activationLogic');\n if (currentVariant !== newVariant) {\n this._disposeContactorGroup(object3D);\n const newGroup = this._createContactorGroup(object3D.userData.componentId, config);\n object3D.add(newGroup);\n newGroup.position.set(0, 0, -0.7);\n newGroup.rotation.copy(this.IDLE_ROTATION);\n }\n }\n\n object3D.userData.transitionSpanTicks = Math.max(\n 1,\n parseInt(config.get('transitionSpan') || '1', 10) || 1\n );\n\n const scale = parseFloat(config.get('size') || '1');\n object3D.scale.set(scale, scale, scale);\n this.updateAnimation(object3D, null);\n }\n\n // ===================================================================\n // Animation Entry Point\n // ===================================================================\n\n /**\n * Update relay animation based on simulation state.\n *\n * - null / no context → cleanup, restore materials, reset to idle\n * - paused → snap all parts to current state values\n * - playing + transitional → animate mechanical + bar effect; update color clips\n * - playing + stable → snap mechanical + bar; update color clips\n */\n override updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void {\n const parts = this._findParts(object3D);\n if (!parts) return;\n\n const variant = parts.contactorGroup.userData.variant as string | undefined;\n\n // Leaving simulation\n if (!state || !this._animationContext) {\n this._cleanupMixer(object3D);\n this._restoreAllMaterials(parts);\n this._resetToDefault(parts);\n delete object3D.userData.coilColorKey;\n delete object3D.userData.powerColorKey;\n return;\n }\n\n // Paused / initial: snap everything\n if (this._animationContext.simulationStatus !== 'playing') {\n this._cleanupMixer(object3D);\n this._snapMechanical(parts, state.state, variant);\n this._snapCoilColor(parts, state);\n this._snapBarEffect(parts, state.state, variant);\n this._snapPowerColor(parts, state);\n object3D.userData.coilColorKey = this._computeCoilColor(state).getHexString();\n object3D.userData.powerColorKey = this._computePowerColor(state).getHexString();\n return;\n }\n\n // Playing: always update independent color animations\n this._updateCoilColorAnim(object3D, parts, state);\n this._updatePowerColorAnim(object3D, parts, state);\n\n // Playing + transitional: animate mechanical (rotation + bar position + bar glow)\n if (state.hasExpiration) {\n this._animateMechanical(object3D, parts, state, variant);\n return;\n }\n\n // Playing + stable: snap mechanical + bar effect, keep color clips running\n this._cleanupMechanicalClip(object3D);\n this._snapMechanical(parts, state.state, variant);\n this._snapBarEffect(parts, state.state, variant);\n }\n\n // ===================================================================\n // idle / active helpers\n // ===================================================================\n\n /** Is the coil magnetically active in the given state? */\n private _isCmdActive(stateName: string, variant: string | undefined): boolean {\n if (variant === 'negative') {\n return stateName === 'open' || stateName === 'opening';\n }\n return stateName === 'closed' || stateName === 'closing';\n }\n\n /** Is the transition moving TOWARD the active position? */\n private _isGoingActive(stateName: string, variant: string | undefined): boolean {\n if (variant === 'negative') return stateName === 'opening';\n return stateName === 'closing';\n }\n\n // ===================================================================\n // Color computation\n // ===================================================================\n\n private _computeCoilColor(state: ComponentState): THREE.Color {\n const cmdUnion = state.pinStates.get('cmd_in*cmd_out');\n if (!cmdUnion) return this.COIL_COLOR_NONE;\n if (cmdUnion.hasVoltage && cmdUnion.hasCurrent) return this.COIL_COLOR_BOTH;\n if (cmdUnion.hasVoltage) return this.COIL_COLOR_VOLTAGE;\n if (cmdUnion.hasCurrent) return this.COIL_COLOR_CURRENT;\n return this.COIL_COLOR_NONE;\n }\n\n private _computePowerColor(state: ComponentState): THREE.Color {\n const powerIn = state.pinStates.get('power_in');\n const powerOut = state.pinStates.get('power_out');\n const powerUnion = state.pinStates.get('power_in*power_out');\n if (!powerIn || !powerOut || !powerUnion) return this.POWER_COLOR_NONE;\n\n // for closed or opening states we use the union / output as reference\n if (state.state === 'closed' || state.state === 'opening') {\n if (powerUnion.hasVoltage && powerUnion.hasCurrent) return this.POWER_COLOR_BOTH;\n if (powerOut.hasVoltage) return this.POWER_COLOR_VOLTAGE;\n if (powerOut.hasCurrent) return this.POWER_COLOR_CURRENT;\n return this.POWER_COLOR_NONE;\n }\n // else it's more the input\n if (powerIn.hasVoltage && powerIn.hasCurrent) return this.POWER_COLOR_BOTH;\n if (powerIn.hasVoltage) return this.POWER_COLOR_VOLTAGE;\n if (powerIn.hasCurrent) return this.POWER_COLOR_CURRENT;\n return this.POWER_COLOR_NONE;\n }\n\n private _getColorAnimDurationSecs(object3D: THREE.Object3D): number {\n const tps = this._animationContext!.ticksPerSecond;\n const spanTicks = (object3D.userData.transitionSpanTicks as number) || 1;\n return Math.max(spanTicks / tps, 0.01);\n }\n\n // ===================================================================\n // Snap helpers (immediate state application)\n // ===================================================================\n\n private _snapMechanical(parts: RelayParts, stateName: string, variant: string | undefined): void {\n const isTransitional = stateName === 'closing' || stateName === 'opening';\n if (isTransitional) {\n // Snap to start of transition\n const goingActive = this._isGoingActive(stateName, variant);\n parts.contactorGroup.rotation.copy(goingActive ? this.IDLE_ROTATION : this.ACTIVE_ROTATION);\n parts.coilBar.position.z = goingActive ? this.COIL_BAR_Z_IDLE : this.COIL_BAR_Z_ACTIVE;\n return;\n }\n const active = this._isCmdActive(stateName, variant);\n parts.contactorGroup.rotation.copy(active ? this.ACTIVE_ROTATION : this.IDLE_ROTATION);\n parts.coilBar.position.z = active ? this.COIL_BAR_Z_ACTIVE : this.COIL_BAR_Z_IDLE;\n }\n\n private _snapCoilColor(parts: RelayParts, state: ComponentState): void {\n this._ensureCoilClonedMaterial(parts);\n const mat = parts.firstCoil.material as THREE.MeshLambertMaterial;\n mat.color.copy(this._computeCoilColor(state));\n }\n\n private _snapBarEffect(parts: RelayParts, stateName: string, variant: string | undefined): void {\n this._ensureBarClonedMaterial(parts);\n const mat = parts.coilBar.material as THREE.MeshLambertMaterial;\n\n // During transition snap, match mechanical start position\n const isTransitional = stateName === 'closing' || stateName === 'opening';\n const active = isTransitional\n ? !this._isGoingActive(stateName, variant)\n : this._isCmdActive(stateName, variant);\n\n const barDefaultMat = this.getMat(CmpMatCategory.SHINY_SILVER);\n\n mat.emissive.copy(active ? this.BAR_GLOW_COLOR : barDefaultMat.emissive);\n mat.emissiveIntensity = active ? this.BAR_GLOW_INTENSITY : barDefaultMat.emissiveIntensity;\n }\n\n private _snapPowerColor(parts: RelayParts, state: ComponentState): void {\n this._ensurePowerClonedMaterial(parts);\n const mat = parts.contactor.material as THREE.MeshLambertMaterial;\n mat.color.copy(this._computePowerColor(state));\n }\n\n // ===================================================================\n // Material management (SHARED → ANIMATION_CLONE → SHARED)\n // ===================================================================\n\n /** Clone WHITE once and assign the same clone to all 7 coil rings. */\n private _ensureCoilClonedMaterial(parts: RelayParts): void {\n const mat = parts.firstCoil.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType === CmpMatType.ANIMATION_CLONE) return;\n\n const clone = this.getMat(CmpMatCategory.WHITE).clone();\n clone.userData.matType = CmpMatType.ANIMATION_CLONE;\n for (const coil of parts.allCoils) {\n coil.material = clone;\n }\n }\n\n /** Clone SHINY_SILVER for the coil bar (emissive animation). */\n private _ensureBarClonedMaterial(parts: RelayParts): void {\n const mat = parts.coilBar.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType === CmpMatType.ANIMATION_CLONE) return;\n\n parts.coilBar.material = this.getMat(CmpMatCategory.SHINY_SILVER).clone();\n (parts.coilBar.material as THREE.MeshLambertMaterial).userData.matType =\n CmpMatType.ANIMATION_CLONE;\n }\n\n /** Clone WHITE once and assign to both contactor and powerInBar. */\n private _ensurePowerClonedMaterial(parts: RelayParts): void {\n const mat = parts.contactor.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType === CmpMatType.ANIMATION_CLONE) return;\n\n const clone = this.getMat(CmpMatCategory.WHITE).clone();\n clone.userData.matType = CmpMatType.ANIMATION_CLONE;\n parts.contactor.material = clone;\n parts.powerInBar.material = clone;\n }\n\n /** Restore all animated materials to their SHARED originals. */\n private _restoreAllMaterials(parts: RelayParts): void {\n // Coil rings\n const coilMat = parts.firstCoil.material as THREE.MeshLambertMaterial;\n if (coilMat.userData.matType === CmpMatType.ANIMATION_CLONE) {\n coilMat.dispose();\n const shared = this.getMat(CmpMatCategory.WHITE);\n for (const coil of parts.allCoils) {\n coil.material = shared;\n }\n }\n\n // Coil bar\n const barMat = parts.coilBar.material as THREE.MeshLambertMaterial;\n if (barMat.userData.matType === CmpMatType.ANIMATION_CLONE) {\n barMat.dispose();\n parts.coilBar.material = this.getMat(CmpMatCategory.SHINY_SILVER);\n }\n\n // Contactor + powerInBar (share same clone — dispose once)\n const powerMat = parts.contactor.material as THREE.MeshLambertMaterial;\n if (powerMat.userData.matType === CmpMatType.ANIMATION_CLONE) {\n powerMat.dispose();\n const shared = this.getMat(CmpMatCategory.WHITE);\n parts.contactor.material = shared;\n parts.powerInBar.material = shared;\n }\n }\n\n // ===================================================================\n // Animation clip: Mechanical + bar effect\n // ===================================================================\n\n private _animateMechanical(\n object3D: THREE.Object3D,\n parts: RelayParts,\n state: ComponentState,\n variant: string | undefined\n ): void {\n if (object3D.userData.mechStart === state.startTick) return;\n\n this._ensureBarClonedMaterial(parts);\n\n const tps = this._animationContext!.ticksPerSecond;\n const durationSecs = (state.expirationTick - state.startTick) / tps;\n\n let mixer = object3D.userData.mixer as THREE.AnimationMixer;\n if (!mixer) {\n mixer = new THREE.AnimationMixer(object3D);\n object3D.userData.mixer = mixer;\n }\n\n // Read current values BEFORE stopping (mid-transition support)\n const currentZ = parts.contactorGroup.rotation.z;\n const currentBarZ = parts.coilBar.position.z;\n const barMat = parts.coilBar.material as THREE.MeshLambertMaterial;\n const curEmRGB = [barMat.emissive.r, barMat.emissive.g, barMat.emissive.b];\n const curEmInt = barMat.emissiveIntensity;\n\n // Stop previous mechanical clip\n if (object3D.userData.mechAction) {\n (object3D.userData.mechAction as THREE.AnimationAction).stop();\n }\n if (object3D.userData.mechClip) {\n mixer.uncacheClip(object3D.userData.mechClip as THREE.AnimationClip);\n }\n\n // Targets\n const goingActive = this._isGoingActive(state.state, variant);\n const targetRotZ = goingActive ? this.ACTIVE_ROTATION.z : this.IDLE_ROTATION.z;\n const targetBarZ = goingActive ? this.COIL_BAR_Z_ACTIVE : this.COIL_BAR_Z_IDLE;\n\n const barDefaultMat = this.getMat(CmpMatCategory.SHINY_SILVER);\n const targetGlow = goingActive ? this.BAR_GLOW_COLOR : barDefaultMat.emissive;\n const targetGlowInt = goingActive ? this.BAR_GLOW_INTENSITY : barDefaultMat.emissiveIntensity;\n\n const tracks = [\n new THREE.NumberKeyframeTrack(\n 'contactorGroup.rotation[z]',\n [0, durationSecs],\n [currentZ, targetRotZ]\n ),\n new THREE.NumberKeyframeTrack(\n 'coilBar.position[z]',\n [0, durationSecs],\n [currentBarZ, targetBarZ]\n ),\n new THREE.ColorKeyframeTrack(\n 'coilBar.material.emissive',\n [0, durationSecs],\n [...curEmRGB, targetGlow.r, targetGlow.g, targetGlow.b]\n ),\n new THREE.NumberKeyframeTrack(\n 'coilBar.material.emissiveIntensity',\n [0, durationSecs],\n [curEmInt, targetGlowInt]\n ),\n ];\n\n const clip = new THREE.AnimationClip('relayMechanical', durationSecs, tracks);\n const action = mixer.clipAction(clip);\n action.loop = THREE.LoopOnce;\n action.clampWhenFinished = true;\n action.play();\n\n object3D.userData.mechStart = state.startTick;\n object3D.userData.mechAction = action;\n object3D.userData.mechClip = clip;\n }\n\n // ===================================================================\n // Animation clip: Coil ring color\n // ===================================================================\n\n private _updateCoilColorAnim(\n object3D: THREE.Object3D,\n parts: RelayParts,\n state: ComponentState\n ): void {\n const target = this._computeCoilColor(state);\n const key = target.getHexString();\n if (object3D.userData.coilColorKey === key) return;\n\n this._ensureCoilClonedMaterial(parts);\n const durationSecs = this._getColorAnimDurationSecs(object3D);\n\n let mixer = object3D.userData.mixer as THREE.AnimationMixer;\n if (!mixer) {\n mixer = new THREE.AnimationMixer(object3D);\n object3D.userData.mixer = mixer;\n }\n\n const mat = parts.firstCoil.material as THREE.MeshLambertMaterial;\n const curRGB = [mat.color.r, mat.color.g, mat.color.b];\n\n // Stop previous\n if (object3D.userData.coilColorAction) {\n (object3D.userData.coilColorAction as THREE.AnimationAction).stop();\n }\n if (object3D.userData.coilColorClip) {\n mixer.uncacheClip(object3D.userData.coilColorClip as THREE.AnimationClip);\n }\n\n const clip = new THREE.AnimationClip('relayCoilColor', durationSecs, [\n new THREE.ColorKeyframeTrack(\n 'coil0.material.color',\n [0, durationSecs],\n [...curRGB, target.r, target.g, target.b]\n ),\n ]);\n const action = mixer.clipAction(clip);\n action.loop = THREE.LoopOnce;\n action.clampWhenFinished = true;\n action.play();\n\n object3D.userData.coilColorKey = key;\n object3D.userData.coilColorAction = action;\n object3D.userData.coilColorClip = clip;\n }\n\n // ===================================================================\n // Animation clip: Power color (contactor + powerInBar)\n // ===================================================================\n\n private _updatePowerColorAnim(\n object3D: THREE.Object3D,\n parts: RelayParts,\n state: ComponentState\n ): void {\n const target = this._computePowerColor(state);\n const key = target.getHexString();\n if (object3D.userData.powerColorKey === key) return;\n\n this._ensurePowerClonedMaterial(parts);\n const durationSecs = this._getColorAnimDurationSecs(object3D);\n\n let mixer = object3D.userData.mixer as THREE.AnimationMixer;\n if (!mixer) {\n mixer = new THREE.AnimationMixer(object3D);\n object3D.userData.mixer = mixer;\n }\n\n const mat = parts.contactor.material as THREE.MeshLambertMaterial;\n const curRGB = [mat.color.r, mat.color.g, mat.color.b];\n\n // Stop previous\n if (object3D.userData.powerColorAction) {\n (object3D.userData.powerColorAction as THREE.AnimationAction).stop();\n }\n if (object3D.userData.powerColorClip) {\n mixer.uncacheClip(object3D.userData.powerColorClip as THREE.AnimationClip);\n }\n\n const clip = new THREE.AnimationClip('relayPowerColor', durationSecs, [\n new THREE.ColorKeyframeTrack(\n 'contactor.material.color',\n [0, durationSecs],\n [...curRGB, target.r, target.g, target.b]\n ),\n ]);\n const action = mixer.clipAction(clip);\n action.loop = THREE.LoopOnce;\n action.clampWhenFinished = true;\n action.play();\n\n object3D.userData.powerColorKey = key;\n object3D.userData.powerColorAction = action;\n object3D.userData.powerColorClip = clip;\n }\n\n // ===================================================================\n // Cleanup\n // ===================================================================\n\n /** Stop only the mechanical clip (rotation + bar position + bar glow). */\n private _cleanupMechanicalClip(object3D: THREE.Object3D): void {\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n if (object3D.userData.mechAction) {\n (object3D.userData.mechAction as THREE.AnimationAction).stop();\n if (mixer && object3D.userData.mechClip) {\n mixer.uncacheClip(object3D.userData.mechClip as THREE.AnimationClip);\n }\n }\n delete object3D.userData.mechAction;\n delete object3D.userData.mechClip;\n delete object3D.userData.mechStart;\n }\n\n /** Stop all animations, dispose mixer, clear all animation userData. */\n private _cleanupMixer(object3D: THREE.Object3D): void {\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n if (mixer) {\n mixer.stopAllAction();\n mixer.uncacheRoot(object3D);\n delete object3D.userData.mixer;\n }\n delete object3D.userData.mechAction;\n delete object3D.userData.mechClip;\n delete object3D.userData.mechStart;\n delete object3D.userData.coilColorAction;\n delete object3D.userData.coilColorClip;\n delete object3D.userData.powerColorAction;\n delete object3D.userData.powerColorClip;\n }\n\n /** Reset positions/rotations to idle (no coil energized). */\n private _resetToDefault(parts: RelayParts): void {\n parts.contactorGroup.rotation.copy(this.IDLE_ROTATION);\n parts.coilBar.position.z = this.COIL_BAR_Z_IDLE;\n }\n\n // ===================================================================\n // Part finders\n // ===================================================================\n\n /** Find all animated sub-objects in a single traverse. */\n private _findParts(object3D: THREE.Object3D): RelayParts | null {\n let contactorGroup: THREE.Group | null = null;\n let contactor: THREE.Mesh | null = null;\n let coilBar: THREE.Mesh | null = null;\n let firstCoil: THREE.Mesh | null = null;\n const allCoils: THREE.Mesh[] = [];\n let powerInBar: THREE.Mesh | null = null;\n\n object3D.traverse((child) => {\n const part = child.userData.part;\n if (part === 'contactorGroup' && child instanceof THREE.Group) {\n contactorGroup = child;\n } else if (part === 'contactor' && child instanceof THREE.Mesh) {\n contactor = child;\n } else if (part === 'coilBar' && child instanceof THREE.Mesh) {\n coilBar = child;\n } else if (part === 'coil' && child instanceof THREE.Mesh) {\n allCoils.push(child);\n if (!firstCoil) firstCoil = child;\n } else if (part === 'powerInBar' && child instanceof THREE.Mesh) {\n powerInBar = child;\n }\n });\n\n if (!contactorGroup || !contactor || !coilBar || !firstCoil || !powerInBar) return null;\n return { contactorGroup, contactor, coilBar, firstCoil, allCoils, powerInBar };\n }\n\n private _findContactorGroup(object3D: THREE.Object3D): THREE.Object3D | null {\n let found: THREE.Object3D | null = null;\n object3D.traverse((child) => {\n if (child instanceof THREE.Group && child.userData.part === 'contactorGroup') {\n found = child;\n }\n });\n return found;\n }\n}\n","import { ComponentVisualFactoryBase } from '../ComponentVisualFactory';\nimport { CmpMatCategory, CmpMatType } from '../types';\nimport { ComponentType, type Component, type ComponentState, type SmallLEDState } from 'simple-circuit-engine/core';\nimport { presetOrHexToHex, hexToPresetOrHex } from '../../utils/ColorUtils';\nimport * as THREE from 'three';\nimport type { ConfigFormDefinition, VisualContext } from '../../types';\n\n/**\n * Visual factory for SmallLED components\n *\n * Creates:\n * - LED cylinder mesh\n * - Input pin group\n * - Output pin group\n * - Component hitbox for raycasting\n *\n * Animation:\n * - Smooth emissive glow transition when LED lights up or turns off\n * - Uses AnimationMixer with material property tracks (emissive, emissiveIntensity)\n * - Clones PRIVATE material per-instance during simulation for independent animation\n */\nexport class SmallLEDVisualFactory extends ComponentVisualFactoryBase {\n /** LED lit color (yellow glow) */\n private readonly LED_LIT_COLOR = 0xffff00;\n /** LED lit emissive intensity */\n private readonly LED_LIT_INTENSITY = 1.0;\n\n /** Black in normalized RGB for ColorKeyframeTrack */\n private readonly UNLIT_RGB = [0, 0, 0];\n\n constructor() {\n super();\n this._componentType = ComponentType.SmallLED;\n }\n\n createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n // Root group (not rendered, just organizational)\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n // Component hitbox (invisible, raycastable)\n const hitbox = this.createComponentHitbox(component.id, group.id, 1, 3, 1);\n group.add(hitbox);\n\n // Visual LED\n const ledGeometry = new THREE.CylinderGeometry(0.25, 0.25, 1, 16, 3, false, 0, Math.PI * 2);\n const ledMat = this.getMat(CmpMatCategory.WHITE).clone();\n ledMat.userData.matType = CmpMatType.PRIVATE;\n\n const led = new THREE.Mesh(ledGeometry, ledMat);\n led.name = 'led'; // required for AnimationMixer property binding\n led.userData = {\n type: 'component',\n componentId: component.id,\n part: 'led',\n idleColorHex: 0xffffff,\n activeColorHex: this.LED_LIT_COLOR,\n };\n led.position.set(0, 0.25, 0);\n group.add(led);\n\n // pins (not called if preview - no pins)\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n this.updateFromConfiguration(group, component.config);\n return group;\n }\n\n private createPinsVisual(component: Component, context: VisualContext, group: THREE.Group) {\n const inputNode = context.getENode(component.pins[0]!);\n if (inputNode) {\n const pin1Group = this.createPinGroup(inputNode, 'left');\n pin1Group.position.set(-0.25, 0, 0);\n group.add(pin1Group);\n\n const pin1Counterpart = this.createPinCounterpart(\n pin1Group,\n this.getMat(CmpMatCategory.WHITE)\n );\n if (!!pin1Counterpart) {\n group.add(pin1Counterpart);\n }\n }\n\n const outputNode = context.getENode(component.pins[1]!);\n if (outputNode) {\n const pin2Group = this.createPinGroup(outputNode, 'right');\n pin2Group.position.set(0.25, 0, 0);\n group.add(pin2Group);\n\n const pin2Counterpart = this.createPinCounterpart(\n pin2Group,\n this.getMat(CmpMatCategory.WHITE)\n );\n if (!!pin2Counterpart) {\n group.add(pin2Counterpart);\n }\n }\n }\n\n /**\n * Get config form definition for SmallLED (T027)\n *\n * @returns Form definition with activeColor and idleColor color fields\n */\n override getConfigFormDefinition(): ConfigFormDefinition | null {\n return {\n fields: [\n { key: 'transitionSpan', type: 'number', min: 1, step: 1 },\n { key: 'idleColor', type: 'color' },\n { key: 'activeColor', type: 'color' },\n { key: 'size', type: 'number', min: 1, max: 16, step: 1 },\n { key: 'ywRatio', type: 'number' },\n ],\n };\n }\n\n /**\n * Map core config to form data (T027)\n * Converts hex/preset strings to hex values for color picker\n *\n * @param config - Core component config\n * @returns Form data with hex color strings\n */\n override mapCoreConfigToForm(config: Map<string, string>): Map<string, any> {\n const formData = new Map<string, any>();\n formData.set('transitionSpan', parseFloat(config.get('transitionSpan') || '1'));\n const activeColor = config.get('activeColor') || '#ffff00';\n const idleColor = config.get('idleColor') || '#ffffff';\n\n // Convert preset names to hex if needed\n formData.set('idleColor', presetOrHexToHex(idleColor));\n formData.set('activeColor', presetOrHexToHex(activeColor));\n formData.set('size', parseFloat(config.get('size') || '1'));\n formData.set('ywRatio', parseFloat(config.get('ywRatio') || '1'));\n\n return formData;\n }\n\n /**\n * Map form data to core config (T027)\n * Converts hex colors to preset names if they match, otherwise keeps hex\n *\n * @param formData - Form data with hex color strings\n * @returns Core config with hex or preset name strings\n */\n override mapFormToCoreConfig(formData: Map<string, any>): Map<string, string> {\n const config = new Map<string, string>();\n config.set('transitionSpan', formData.get('transitionSpan').toString());\n const activeColor = formData.get('activeColor');\n const idleColor = formData.get('idleColor');\n\n // Convert hex to preset name if it matches a preset\n if (activeColor) {\n config.set('activeColor', hexToPresetOrHex(activeColor));\n }\n if (idleColor) {\n config.set('idleColor', hexToPresetOrHex(idleColor));\n }\n\n config.set('size', formData.get('size').toString());\n config.set('ywRatio', formData.get('ywRatio').toString());\n return config;\n }\n\n /**\n * Update visual from configuration (T022)\n * Updates LED color based on activeColor config\n */\n override updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void {\n const ledMesh = this.findLedMesh(object3D);\n const hitbox = this.findHitbox(object3D);\n if (!ledMesh || !hitbox) return;\n\n const ledMat = ledMesh.material as THREE.MeshLambertMaterial;\n\n // changing colors\n const idleColor = config.get('idleColor');\n if (idleColor) {\n // Convert preset to hex if needed, then parse\n const idleColorHex = parseInt(presetOrHexToHex(idleColor).replace('#', ''), 16);\n ledMat.color.setHex(idleColorHex);\n ledMesh.userData.idleColorHex = idleColorHex;\n }\n const activeColor = config.get('activeColor');\n if (activeColor) {\n // Convert preset to hex if needed, then parse\n ledMesh.userData.activeColorHex = parseInt(\n presetOrHexToHex(activeColor).replace('#', ''),\n 16\n );\n }\n // changing geometry\n const ywRatio = parseFloat(config.get('ywRatio') || '1');\n ledMesh.geometry.dispose();\n ledMesh.geometry = new THREE.CylinderGeometry(\n 0.25,\n 0.25,\n ywRatio,\n 16,\n 4,\n false,\n 0,\n Math.PI * 2\n );\n ledMesh.position.set(0, 0.25 * ywRatio, 0);\n hitbox.geometry.dispose();\n hitbox.geometry = new THREE.BoxGeometry(1, 1.5 * ywRatio, 1);\n\n const scale = parseFloat(config.get('size') || '1');\n object3D.scale.set(scale, scale, scale);\n\n this.updateAnimation(object3D, null);\n }\n\n /**\n * Update LED animation based on simulation state.\n *\n * - null state / no context: restore private material, cleanup mixer\n * - paused/initial: snap to target state\n * - playing + transitional (goingOn/goingOff): smooth material animation\n * - playing + stable (on/off): snap to final state\n */\n override updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void {\n const ledMesh = this.findLedMesh(object3D);\n if (!ledMesh) return;\n\n // Leaving simulation: restore private material\n if (!state || !this._animationContext) {\n this._cleanupMixer(object3D);\n this._restorePrivateMaterial(ledMesh);\n return;\n }\n\n const ledState = state as SmallLEDState;\n\n // Paused/initial: snap to current state (mixer won't advance)\n if (this._animationContext.simulationStatus !== 'playing') {\n this._cleanupMixer(object3D);\n this._snapToState(ledMesh, ledState.isLit);\n return;\n }\n\n // Playing + transitional state: animate\n if (state.hasExpiration) {\n this._animateLed(object3D, ledMesh, state);\n return;\n }\n\n // Playing + stable state: snap, cleanup mixer\n this._cleanupMixer(object3D);\n this._snapToState(ledMesh, ledState.isLit);\n }\n\n // ---------------------------------------------------------------------------\n // Material clone / restore (PRIVATE → ANIMATION_CLONE → PRIVATE)\n // ---------------------------------------------------------------------------\n\n /**\n * Clone the PRIVATE material for independent per-instance animation.\n * Stashes the original PRIVATE material in mesh.userData.privateMat.\n * No-op if already cloned.\n */\n private _ensureClonedMaterial(ledMesh: THREE.Mesh): void {\n const mat = ledMesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType === CmpMatType.ANIMATION_CLONE) return;\n ledMesh.userData.privateMat = mat;\n ledMesh.material = mat.clone();\n (ledMesh.material as THREE.MeshLambertMaterial).userData.matType = CmpMatType.ANIMATION_CLONE;\n }\n\n /**\n * Dispose the animation clone and restore the stashed PRIVATE material.\n * No-op if not cloned.\n */\n private _restorePrivateMaterial(ledMesh: THREE.Mesh): void {\n const mat = ledMesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType !== CmpMatType.ANIMATION_CLONE) return;\n mat.dispose();\n if (ledMesh.userData.privateMat) {\n ledMesh.material = ledMesh.userData.privateMat;\n delete ledMesh.userData.privateMat;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Snap (immediate state application)\n // ---------------------------------------------------------------------------\n\n private _snapToState(ledMesh: THREE.Mesh, isLit: boolean): void {\n this._ensureClonedMaterial(ledMesh);\n const mat = ledMesh.material as THREE.MeshLambertMaterial;\n\n if (isLit) {\n mat.emissive.setHex(ledMesh.userData.activeColorHex);\n mat.emissiveIntensity = this.LED_LIT_INTENSITY;\n } else {\n mat.emissive.setHex(0x000000);\n mat.emissiveIntensity = 0;\n }\n }\n\n // ---------------------------------------------------------------------------\n // AnimationMixer management\n // ---------------------------------------------------------------------------\n\n /**\n * Create a smooth material animation for goingOn / goingOff transitions.\n */\n private _animateLed(object3D: THREE.Object3D, ledMesh: THREE.Mesh, state: ComponentState): void {\n // Prevent duplicate animation for same transition\n if (object3D.userData.currentActionStart === state.startTick) return;\n\n this._ensureClonedMaterial(ledMesh);\n\n const tps = this._animationContext!.ticksPerSecond;\n const span = state.expirationTick - state.startTick;\n const durationSeconds = span / tps;\n\n // Get or create mixer\n let mixer: THREE.AnimationMixer = object3D.userData.mixer;\n if (!mixer) {\n mixer = new THREE.AnimationMixer(object3D);\n object3D.userData.mixer = mixer;\n }\n\n // Read current material values BEFORE stopping previous action,\n // so mid-transition interrupts animate from actual visual state\n const mat = ledMesh.material as THREE.MeshLambertMaterial;\n const currentIntensity = mat.emissiveIntensity;\n const currentRGB = [mat.emissive.r, mat.emissive.g, mat.emissive.b];\n\n // Stop previous animation\n if (object3D.userData.currentAction) {\n (object3D.userData.currentAction as THREE.AnimationAction).stop();\n }\n if (object3D.userData.currentClip) {\n mixer.uncacheClip(object3D.userData.currentClip as THREE.AnimationClip);\n }\n\n // Determine end values based on transition direction\n const isGoingOn = state.state === 'goingOn';\n const endIntensity = isGoingOn ? this.LED_LIT_INTENSITY : 0;\n const activeHex = ledMesh.userData.activeColorHex as number;\n const activeColor = new THREE.Color(activeHex);\n const endRGB = isGoingOn ? [activeColor.r, activeColor.g, activeColor.b] : this.UNLIT_RGB;\n\n const tracks = [\n new THREE.NumberKeyframeTrack(\n 'led.material.emissiveIntensity',\n [0, durationSeconds],\n [currentIntensity, endIntensity]\n ),\n new THREE.ColorKeyframeTrack(\n 'led.material.emissive',\n [0, durationSeconds],\n [...currentRGB, ...endRGB]\n ),\n ];\n\n const clip = new THREE.AnimationClip('ledGlow', durationSeconds, tracks);\n const action = mixer.clipAction(clip);\n action.loop = THREE.LoopOnce;\n action.clampWhenFinished = true;\n action.play();\n\n // Store references for cleanup/interruption\n object3D.userData.currentActionStart = state.startTick;\n object3D.userData.currentAction = action;\n object3D.userData.currentClip = clip;\n }\n\n /**\n * Stop all animations, clean up mixer.\n */\n private _cleanupMixer(object3D: THREE.Object3D): void {\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n if (mixer) {\n mixer.stopAllAction();\n mixer.uncacheRoot(object3D);\n delete object3D.userData.mixer;\n }\n delete object3D.userData.currentAction;\n delete object3D.userData.currentClip;\n delete object3D.userData.currentActionStart;\n }\n\n // ---------------------------------------------------------------------------\n // Helpers\n // ---------------------------------------------------------------------------\n\n private findLedMesh(object3D: THREE.Object3D): THREE.Mesh | null {\n let ledMesh: THREE.Mesh | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === 'led') {\n ledMesh = child as THREE.Mesh;\n }\n });\n\n return ledMesh;\n }\n\n // Uses default hover implementation\n}\n","import { ComponentVisualFactoryBase } from '../ComponentVisualFactory';\nimport { CmpMatCategory, CmpMatType } from '../types';\nimport { ComponentType, type Component, type ComponentState } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport type { ConfigFormDefinition, VisualContext } from '../../types';\n\n/**\n * Visual factory for Switch components\n *\n * Creates:\n * - Contactor (box, rotatable for animation)\n * - Input pin group\n * - Output pin group\n * - Component hitbox for raycasting\n *\n * Animation:\n * - Smooth rotation of contactor between open/closed via AnimationMixer\n * - Supports mid-transition interruption (re-toggle during closing/opening)\n */\nexport class SwitchVisualFactory extends ComponentVisualFactoryBase {\n /** Rotation for closed switch (contactor aligned) */\n private readonly CLOSED_ROTATION = new THREE.Euler(-Math.PI / 2, 0, 0);\n /** Rotation for open switch (contactor misaligned) */\n private readonly OPEN_ROTATION = new THREE.Euler(-Math.PI / 2, -Math.PI / 4, 0);\n\n private readonly CONTACTOR_GEOMETRY = new THREE.BoxGeometry(1.3, 0.6, 0.1);\n\n /** Contactor color when output has both voltage and current */\n private readonly COLOR_VOLTAGE_CURRENT = new THREE.Color(0xff00ff);\n /** Contactor color when output has current only */\n private readonly COLOR_CURRENT = new THREE.Color(0x4444ff);\n /** Contactor color when output has voltage only */\n private readonly COLOR_VOLTAGE = new THREE.Color(0xff4444);\n /** Contactor color when output has neither */\n private readonly COLOR_NONE = new THREE.Color(0xffffff);\n\n constructor() {\n super();\n this._componentType = ComponentType.Switch;\n }\n\n createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n // Root group (not rendered, just organizational)\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n // Component hitbox (invisible, raycastable)\n const hitbox = this.createComponentHitbox(component.id, group.id, 2.4, 3, 0.8);\n group.add(hitbox);\n hitbox.position.set(-0.2, 0, 0);\n\n // Contactor\n const contactorGroup = new THREE.Group();\n contactorGroup.name = 'contactorGroup'; // required for AnimationMixer property binding\n contactorGroup.userData = {\n type: 'component',\n componentId: component.id,\n part: 'contactorGroup',\n initialState: 'open',\n };\n contactorGroup.position.set(0.6, 0, 0);\n contactorGroup.rotation.copy(this.OPEN_ROTATION);\n group.add(contactorGroup);\n\n const contactor = new THREE.Mesh(this.CONTACTOR_GEOMETRY, this.getMat(CmpMatCategory.WHITE));\n contactor.name = 'contactor'; // required for findContactorMesh lookup\n contactor.userData = { part: 'contactor' };\n contactor.position.set(-0.65, 0, 0);\n\n contactorGroup.add(contactor);\n\n // pins (not called if preview - no pins)\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n this.updateFromConfiguration(group, component.config);\n return group;\n }\n\n private createPinsVisual(component: Component, context: VisualContext, group: THREE.Group) {\n const inputNode = context.getENode(component.pins[0]!);\n if (inputNode) {\n const inputPinGroup = this.createPinGroup(inputNode, 'left');\n inputPinGroup.position.set(-1, 0, 0);\n group.add(inputPinGroup);\n\n const inputPinCounterpart = this.createPinCounterpart(\n inputPinGroup,\n this.getMat(CmpMatCategory.WHITE)\n );\n if (!!inputPinCounterpart) {\n group.add(inputPinCounterpart);\n }\n }\n\n const outputNode = context.getENode(component.pins[1]!);\n if (outputNode) {\n const outputPinGroup = this.createPinGroup(outputNode, 'right');\n outputPinGroup.position.set(0.6, 0, 0);\n group.add(outputPinGroup);\n\n const outputPinCounterpart = this.createPinCounterpart(\n outputPinGroup,\n this.getMat(CmpMatCategory.WHITE)\n );\n if (!!outputPinCounterpart) {\n group.add(outputPinCounterpart);\n }\n }\n }\n\n /**\n * Get config form definition for Switch\n *\n * @returns Form definition with initialState, transitionSpan and size\n */\n override getConfigFormDefinition(): ConfigFormDefinition | null {\n return {\n fields: [\n { key: 'initialState', type: 'boolean' },\n { key: 'transitionSpan', type: 'number', min: 1, step: 1 },\n { key: 'size', type: 'number', min: 1, max: 16, step: 1 },\n ],\n };\n }\n\n /**\n * Map core config to form data\n * Converts \"open\"/\"closed\" strings to boolean\n */\n override mapCoreConfigToForm(config: Map<string, string>): Map<string, any> {\n const formData = new Map<string, any>();\n const initialState = config.get('initialState');\n formData.set('initialState', initialState === 'open');\n formData.set('transitionSpan', parseFloat(config.get('transitionSpan') || '1'));\n formData.set('size', parseFloat(config.get('size') || '1'));\n return formData;\n }\n\n /**\n * Map form data to core config\n * Converts boolean to \"open\"/\"closed\" strings\n */\n override mapFormToCoreConfig(formData: Map<string, any>): Map<string, string> {\n const config = new Map<string, string>();\n const initialState = formData.get('initialState');\n config.set('initialState', initialState ? 'open' : 'closed');\n formData.set('transitionSpan', parseFloat(config.get('transitionSpan') || '1'));\n config.set('size', formData.get('size').toString());\n return config;\n }\n\n override updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>) {\n const contactorGroup = this.findContactorGroup(object3D);\n if (!contactorGroup) return;\n\n if (config.get('initialState') === 'closed') {\n contactorGroup.userData.initialState = 'closed';\n } else {\n contactorGroup.userData.initialState = 'open';\n }\n\n const scale = parseFloat(config.get('size') || '1');\n object3D.scale.set(scale, scale, scale);\n this.updateAnimation(object3D, null);\n }\n\n /**\n * Update switch animation based on simulation state.\n *\n * - null state / no context: cleanup mixer, reset to config default\n * - paused/initial + transitional: snap to start rotation\n * - paused/initial + stable: snap to state rotation\n * - playing + transitional: smooth rotation animation\n * - playing + stable: snap to final rotation, cleanup mixer\n */\n override updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void {\n const contactorGroup = this.findContactorGroup(object3D);\n if (!contactorGroup) return;\n\n const contactorMesh = this._findContactorMesh(object3D);\n\n // Leaving simulation: cleanup and reset to config default\n if (!state || !this._animationContext) {\n this._cleanupMixer(object3D, contactorGroup);\n if (contactorMesh) this._restoreSharedMaterial(contactorMesh);\n return;\n }\n\n // Stop color fade animation so _updateContactorColor can take over\n this._stopColorAnimation(object3D);\n\n // Update contactor color from output pin state\n if (contactorMesh) this._updateContactorColor(contactorMesh, state);\n\n // Paused/initial: snap to appropriate rotation\n if (this._animationContext.simulationStatus !== 'playing') {\n if (state.hasExpiration) {\n // Mid-transition while paused/stepping: snap to start rotation\n contactorGroup.rotation.copy(this._getStartRotation(state.state));\n } else {\n contactorGroup.rotation.copy(this._getStateRotation(state.state));\n }\n return;\n }\n\n // Playing + transitional: animate\n if (state.hasExpiration) {\n this._animateContactor(object3D, contactorGroup, state);\n return;\n }\n\n // Playing + stable: snap, cleanup mixer\n this._cleanupMixer(object3D, contactorGroup);\n contactorGroup.rotation.copy(this._getStateRotation(state.state));\n }\n\n // ---------------------------------------------------------------------------\n // Rotation helpers\n // ---------------------------------------------------------------------------\n\n /** Maps stable state → target rotation */\n private _getStateRotation(state: string): THREE.Euler {\n if (state === 'closed') return this.CLOSED_ROTATION;\n return this.OPEN_ROTATION;\n }\n\n /** Maps transitional state → the rotation it came FROM */\n private _getStartRotation(state: string): THREE.Euler {\n if (state === 'closing') return this.OPEN_ROTATION;\n if (state === 'opening') return this.CLOSED_ROTATION;\n return this._getStateRotation(state);\n }\n\n // ---------------------------------------------------------------------------\n // AnimationMixer management\n // ---------------------------------------------------------------------------\n\n /**\n * Create smooth rotation + color animations for closing/opening transitions.\n * Rotation clip: current → target state rotation (persists across updateAnimation calls)\n * Color clip: current contactor color → white (stopped on next updateAnimation so pin state takes over)\n */\n private _animateContactor(\n object3D: THREE.Object3D,\n contactorGroup: THREE.Object3D,\n state: ComponentState\n ): void {\n // Prevent duplicate animation for same transition\n if (object3D.userData.currentActionStart === state.startTick) return;\n\n const contactorMesh = this._findContactorMesh(object3D);\n\n const tps = this._animationContext!.ticksPerSecond;\n const span = state.expirationTick - state.startTick;\n const durationSeconds = span / tps;\n\n // Get or create mixer\n let mixer: THREE.AnimationMixer = object3D.userData.mixer;\n if (!mixer) {\n mixer = new THREE.AnimationMixer(object3D);\n object3D.userData.mixer = mixer;\n }\n\n // Read current values BEFORE stopping previous action (mid-transition support)\n const currentY = contactorGroup.rotation.y;\n let currentRGB = [1, 1, 1];\n if (contactorMesh) {\n this._ensureClonedMaterial(contactorMesh);\n const mat = contactorMesh.material as THREE.MeshLambertMaterial;\n currentRGB = [mat.color.r, mat.color.g, mat.color.b];\n }\n\n // Stop previous rotation animation\n if (object3D.userData.currentAction) {\n (object3D.userData.currentAction as THREE.AnimationAction).stop();\n }\n if (object3D.userData.currentClip) {\n mixer.uncacheClip(object3D.userData.currentClip as THREE.AnimationClip);\n }\n\n // Rotation clip (persists for the entire transition)\n const targetY = this._getStateRotation(state.nextState!).y;\n const rotationClip = new THREE.AnimationClip('switchRotation', durationSeconds, [\n new THREE.NumberKeyframeTrack(\n 'contactorGroup.rotation[y]',\n [0, durationSeconds],\n [currentY, targetY]\n ),\n ]);\n const rotationAction = mixer.clipAction(rotationClip);\n rotationAction.loop = THREE.LoopOnce;\n rotationAction.clampWhenFinished = true;\n rotationAction.play();\n\n object3D.userData.currentActionStart = state.startTick;\n object3D.userData.currentAction = rotationAction;\n object3D.userData.currentClip = rotationClip;\n\n // Color clip (fade to white, stopped on next updateAnimation call)\n if (contactorMesh) {\n this._stopColorAnimation(object3D);\n const colorClip = new THREE.AnimationClip('switchColor', durationSeconds, [\n new THREE.ColorKeyframeTrack(\n 'contactor.material.color',\n [0, durationSeconds],\n [...currentRGB, 1, 1, 1]\n ),\n ]);\n const colorAction = mixer.clipAction(colorClip);\n colorAction.loop = THREE.LoopOnce;\n colorAction.clampWhenFinished = true;\n colorAction.play();\n\n object3D.userData.colorAction = colorAction;\n object3D.userData.colorClip = colorClip;\n }\n }\n\n /**\n * Stop the color fade animation so _updateContactorColor can set color from pin state.\n */\n private _stopColorAnimation(object3D: THREE.Object3D): void {\n if (!object3D.userData.colorAction) return;\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n (object3D.userData.colorAction as THREE.AnimationAction).stop();\n if (mixer && object3D.userData.colorClip) {\n mixer.uncacheClip(object3D.userData.colorClip as THREE.AnimationClip);\n }\n delete object3D.userData.colorAction;\n delete object3D.userData.colorClip;\n }\n\n /**\n * Stop all animations, clean up mixer, reset to config default rotation.\n */\n private _cleanupMixer(object3D: THREE.Object3D, contactorGroup: THREE.Object3D): void {\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n if (mixer) {\n mixer.stopAllAction();\n mixer.uncacheRoot(object3D);\n delete object3D.userData.mixer;\n }\n delete object3D.userData.currentAction;\n delete object3D.userData.currentClip;\n delete object3D.userData.currentActionStart;\n delete object3D.userData.colorAction;\n delete object3D.userData.colorClip;\n\n // Reset to config-based default rotation\n if (contactorGroup.userData.initialState === 'closed') {\n contactorGroup.rotation.copy(this.CLOSED_ROTATION);\n } else {\n contactorGroup.rotation.copy(this.OPEN_ROTATION);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Contactor material (color from output pin state)\n // ---------------------------------------------------------------------------\n\n private _ensureClonedMaterial(contactorMesh: THREE.Mesh): void {\n const mat = contactorMesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType === CmpMatType.ANIMATION_CLONE) return;\n contactorMesh.material = this.getMat(CmpMatCategory.WHITE).clone();\n (contactorMesh.material as THREE.MeshLambertMaterial).userData.matType =\n CmpMatType.ANIMATION_CLONE;\n }\n\n private _restoreSharedMaterial(contactorMesh: THREE.Mesh): void {\n const mat = contactorMesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType !== CmpMatType.ANIMATION_CLONE) return;\n mat.dispose();\n contactorMesh.material = this.getMat(CmpMatCategory.WHITE);\n }\n\n private _updateContactorColor(contactorMesh: THREE.Mesh, state: ComponentState): void {\n if (!state.pinStates || !state.pinStates.has('output')) return;\n\n this._ensureClonedMaterial(contactorMesh);\n const mat = contactorMesh.material as THREE.MeshLambertMaterial;\n\n const outputState = state.pinStates.get('output')!;\n\n if (outputState.hasVoltage && outputState.hasCurrent) {\n mat.color.copy(this.COLOR_VOLTAGE_CURRENT);\n } else if (outputState.hasCurrent) {\n mat.color.copy(this.COLOR_CURRENT);\n } else if (outputState.hasVoltage) {\n mat.color.copy(this.COLOR_VOLTAGE);\n } else {\n mat.color.copy(this.COLOR_NONE);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Helpers\n // ---------------------------------------------------------------------------\n\n private findContactorGroup(object3D: THREE.Object3D): THREE.Object3D | null {\n let contactorGroup: THREE.Object3D | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Group && child.userData.part === 'contactorGroup') {\n contactorGroup = child;\n }\n });\n\n return contactorGroup;\n }\n\n private _findContactorMesh(object3D: THREE.Object3D): THREE.Mesh | null {\n let mesh: THREE.Mesh | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === 'contactor') {\n mesh = child;\n }\n });\n\n return mesh;\n }\n\n // Uses default hover implementation\n}\n","import { ComponentVisualFactoryBase } from '../ComponentVisualFactory';\nimport { CmpMatCategory, CmpMatType } from '../types';\nimport { ComponentType, type Component, type ComponentState } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport type { ConfigFormDefinition, VisualContext } from '../../types';\n\n/**\n * Visual factory for DoubleThrowSwitch (SPDT) components\n *\n * Creates:\n * - Input1 pin group\n * - Input2 pin group\n * - Output pin group\n * - Contactor (box, rotatable for animation)\n * - Component hitbox for raycasting\n *\n * Animation:\n * - Smooth rotation of contactor between input1/input2 via AnimationMixer\n * - Contactor material color reflects output pin state (voltage/current)\n * - Supports mid-transition interruption (re-toggle during transition)\n */\nexport class DoubleThrowSwitchVisualFactory extends ComponentVisualFactoryBase {\n /** Rotation for switch connected to input1 */\n private readonly INPUT1_ROTATION = new THREE.Euler(-Math.PI / 2, -Math.PI / 8, 0);\n /** Rotation for switch connected to input2 */\n private readonly INPUT2_ROTATION = new THREE.Euler(-Math.PI / 2, Math.PI / 8, 0);\n\n private readonly CONTACTOR_GEOMETRY = new THREE.BoxGeometry(1.4, 0.6, 0.1);\n\n /** Contactor color when output has both voltage and current */\n private readonly COLOR_VOLTAGE_CURRENT = new THREE.Color(0xff00ff);\n /** Contactor color when output has current only */\n private readonly COLOR_CURRENT = new THREE.Color(0x4444ff);\n /** Contactor color when output has voltage only */\n private readonly COLOR_VOLTAGE = new THREE.Color(0xff4444);\n /** Contactor color when output has neither */\n private readonly COLOR_NONE = new THREE.Color(0xffffff);\n\n constructor() {\n super();\n this._componentType = ComponentType.DoubleThrowSwitch;\n }\n\n createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n // Root group (not rendered, just organizational)\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n // Component hitbox (invisible, raycastable)\n const hitbox = this.createComponentHitbox(component.id, group.id, 2.4, 3, 0.8);\n group.add(hitbox);\n hitbox.position.set(-0.2, 0, 0);\n\n // Contactor\n const contactorGroup = new THREE.Group();\n contactorGroup.name = 'contactorGroup'; // required for AnimationMixer property binding\n contactorGroup.userData = {\n type: 'component',\n componentId: component.id,\n part: 'contactorGroup',\n initialState: 'input1',\n };\n contactorGroup.position.set(0.6, 0, 0);\n contactorGroup.rotation.copy(this.INPUT1_ROTATION);\n group.add(contactorGroup);\n\n const contactor = new THREE.Mesh(this.CONTACTOR_GEOMETRY, this.getMat(CmpMatCategory.WHITE));\n contactor.name = 'contactor'; // required for findContactorMesh lookup\n contactor.userData = { part: 'contactor' };\n contactor.position.set(-0.7, 0, 0);\n contactorGroup.add(contactor);\n\n // pins (not called if preview - no pins)\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n this.updateFromConfiguration(group, component.config);\n return group;\n }\n\n private createPinsVisual(component: Component, context: VisualContext, group: THREE.Group) {\n const input1Node = context.getENode(component.pins[0]!);\n if (input1Node) {\n const input1PinGroup = this.createPinGroup(input1Node, 'left', new THREE.Euler(0, 0.4, 0));\n input1PinGroup.position.set(-1, -0.5, 0);\n group.add(input1PinGroup);\n\n const input1PinCounterpart = this.createPinCounterpart(\n input1PinGroup,\n this.getMat(CmpMatCategory.WHITE)\n );\n if (!!input1PinCounterpart) {\n group.add(input1PinCounterpart);\n }\n }\n\n const input2Node = context.getENode(component.pins[1]!);\n if (input2Node) {\n const input2PinGroup = this.createPinGroup(input2Node, 'left', new THREE.Euler(0, -0.4, 0));\n input2PinGroup.position.set(-1, 0.5, 0);\n group.add(input2PinGroup);\n\n const input2PinCounterpart = this.createPinCounterpart(\n input2PinGroup,\n this.getMat(CmpMatCategory.WHITE)\n );\n if (!!input2PinCounterpart) {\n group.add(input2PinCounterpart);\n }\n }\n\n const outputNode = context.getENode(component.pins[2]!);\n if (outputNode) {\n const outputPinGroup = this.createPinGroup(outputNode, 'right');\n outputPinGroup.position.set(0.6, 0, 0);\n group.add(outputPinGroup);\n\n const outputPinCounterpart = this.createPinCounterpart(\n outputPinGroup,\n this.getMat(CmpMatCategory.WHITE)\n );\n if (!!outputPinCounterpart) {\n group.add(outputPinCounterpart);\n }\n }\n }\n\n /**\n * Get config form definition for DoubleThrowSwitch\n *\n * @returns Form definition with initialState, transitionSpan and size\n */\n override getConfigFormDefinition(): ConfigFormDefinition | null {\n return {\n fields: [\n { key: 'initialState', type: 'boolean' },\n { key: 'transitionSpan', type: 'number', min: 1, step: 1 },\n { key: 'size', type: 'number', min: 1, max: 16, step: 1 },\n ],\n };\n }\n\n /**\n * Map core config to form data\n * Converts \"input1\"/\"input2\" strings to boolean\n */\n override mapCoreConfigToForm(config: Map<string, string>): Map<string, any> {\n const formData = new Map<string, any>();\n const initialState = config.get('initialState');\n formData.set('initialState', initialState === 'input1');\n formData.set('transitionSpan', parseFloat(config.get('transitionSpan') || '1'));\n formData.set('size', parseFloat(config.get('size') || '1'));\n return formData;\n }\n\n /**\n * Map form data to core config\n * Converts boolean to \"input1\"/\"input2\" strings\n */\n override mapFormToCoreConfig(formData: Map<string, any>): Map<string, string> {\n const config = new Map<string, string>();\n const initialState = formData.get('initialState');\n config.set('initialState', initialState ? 'input1' : 'input2');\n config.set('transitionSpan', formData.get('transitionSpan').toString());\n config.set('size', formData.get('size').toString());\n return config;\n }\n\n override updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>) {\n const contactorGroup = this.findContactorGroup(object3D);\n if (!contactorGroup) return;\n\n if (config.get('initialState') === 'input2') {\n contactorGroup.userData.initialState = 'input2';\n } else {\n contactorGroup.userData.initialState = 'input1';\n }\n\n const scale = parseFloat(config.get('size') || '1');\n object3D.scale.set(scale, scale, scale);\n this.updateAnimation(object3D, null);\n }\n\n /**\n * Update switch animation based on simulation state.\n *\n * - null state / no context: cleanup mixer, reset to config default\n * - paused/initial + transitional: snap to start rotation\n * - paused/initial + stable: snap to state rotation\n * - playing + transitional: smooth rotation animation\n * - playing + stable: snap to final rotation, cleanup mixer\n */\n override updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void {\n const contactorGroup = this.findContactorGroup(object3D);\n if (!contactorGroup) return;\n\n const contactorMesh = this._findContactorMesh(object3D);\n\n // Leaving simulation: cleanup and reset to config default\n if (!state || !this._animationContext) {\n this._cleanupMixer(object3D, contactorGroup);\n if (contactorMesh) this._restoreSharedMaterial(contactorMesh);\n return;\n }\n\n // Stop color fade animation so _updateContactorColor can take over\n this._stopColorAnimation(object3D);\n\n // Update contactor color from output pin state parameters\n if (contactorMesh) this._updateContactorColor(contactorMesh, state);\n\n // Paused/initial: snap to appropriate rotation\n if (this._animationContext.simulationStatus !== 'playing') {\n if (state.hasExpiration) {\n // Mid-transition while paused/stepping: snap to start rotation\n contactorGroup.rotation.copy(this._getStartRotation(state.state));\n } else {\n contactorGroup.rotation.copy(this._getStateRotation(state.state));\n }\n return;\n }\n\n // Playing + transitional: animate\n if (state.hasExpiration) {\n this._animateContactor(object3D, contactorGroup, state);\n return;\n }\n\n // Playing + stable: snap, cleanup mixer\n this._cleanupMixer(object3D, contactorGroup);\n contactorGroup.rotation.copy(this._getStateRotation(state.state));\n }\n\n // ---------------------------------------------------------------------------\n // Rotation helpers\n // ---------------------------------------------------------------------------\n\n /** Maps stable state → target rotation */\n private _getStateRotation(state: string): THREE.Euler {\n if (state === 'input2') return this.INPUT2_ROTATION;\n return this.INPUT1_ROTATION;\n }\n\n /** Maps transitional state → the rotation it came FROM */\n private _getStartRotation(state: string): THREE.Euler {\n if (state === '1to2') return this.INPUT1_ROTATION;\n if (state === '2to1') return this.INPUT2_ROTATION;\n return this._getStateRotation(state);\n }\n\n // ---------------------------------------------------------------------------\n // AnimationMixer management\n // ---------------------------------------------------------------------------\n\n /**\n * Create smooth rotation + color animations for transitional states.\n * Rotation clip: current → target state rotation (persists across updateAnimation calls)\n * Color clip: current contactor color → white (stopped on next updateAnimation so pin state takes over)\n */\n private _animateContactor(\n object3D: THREE.Object3D,\n contactorGroup: THREE.Object3D,\n state: ComponentState\n ): void {\n // Prevent duplicate animation for same transition\n if (object3D.userData.currentActionStart === state.startTick) return;\n\n const contactorMesh = this._findContactorMesh(object3D);\n\n const tps = this._animationContext!.ticksPerSecond;\n const span = state.expirationTick - state.startTick;\n const durationSeconds = span / tps;\n\n // Get or create mixer\n let mixer: THREE.AnimationMixer = object3D.userData.mixer;\n if (!mixer) {\n mixer = new THREE.AnimationMixer(object3D);\n object3D.userData.mixer = mixer;\n }\n\n // Read current values BEFORE stopping previous action (mid-transition support)\n const currentY = contactorGroup.rotation.y;\n let currentRGB = [1, 1, 1];\n if (contactorMesh) {\n this._ensureClonedMaterial(contactorMesh);\n const mat = contactorMesh.material as THREE.MeshLambertMaterial;\n currentRGB = [mat.color.r, mat.color.g, mat.color.b];\n }\n\n // Stop previous rotation animation\n if (object3D.userData.currentAction) {\n (object3D.userData.currentAction as THREE.AnimationAction).stop();\n }\n if (object3D.userData.currentClip) {\n mixer.uncacheClip(object3D.userData.currentClip as THREE.AnimationClip);\n }\n\n // Rotation clip (persists for the entire transition)\n const targetY = this._getStateRotation(state.nextState!).y;\n const rotationClip = new THREE.AnimationClip('doubleThrowRotation', durationSeconds, [\n new THREE.NumberKeyframeTrack(\n 'contactorGroup.rotation[y]',\n [0, durationSeconds],\n [currentY, targetY]\n ),\n ]);\n const rotationAction = mixer.clipAction(rotationClip);\n rotationAction.loop = THREE.LoopOnce;\n rotationAction.clampWhenFinished = true;\n rotationAction.play();\n\n object3D.userData.currentActionStart = state.startTick;\n object3D.userData.currentAction = rotationAction;\n object3D.userData.currentClip = rotationClip;\n\n // Color clip (fade to white, stopped on next updateAnimation call)\n if (contactorMesh) {\n this._stopColorAnimation(object3D);\n const colorClip = new THREE.AnimationClip('doubleThrowColor', durationSeconds, [\n new THREE.ColorKeyframeTrack(\n 'contactor.material.color',\n [0, durationSeconds],\n [...currentRGB, 1, 1, 1]\n ),\n ]);\n const colorAction = mixer.clipAction(colorClip);\n colorAction.loop = THREE.LoopOnce;\n colorAction.clampWhenFinished = true;\n colorAction.play();\n\n object3D.userData.colorAction = colorAction;\n object3D.userData.colorClip = colorClip;\n }\n }\n\n /**\n * Stop the color fade animation so _updateContactorColor can set color from pin state.\n */\n private _stopColorAnimation(object3D: THREE.Object3D): void {\n if (!object3D.userData.colorAction) return;\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n (object3D.userData.colorAction as THREE.AnimationAction).stop();\n if (mixer && object3D.userData.colorClip) {\n mixer.uncacheClip(object3D.userData.colorClip as THREE.AnimationClip);\n }\n delete object3D.userData.colorAction;\n delete object3D.userData.colorClip;\n }\n\n /**\n * Stop all animations, clean up mixer, reset to config default rotation.\n */\n private _cleanupMixer(object3D: THREE.Object3D, contactorGroup: THREE.Object3D): void {\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n if (mixer) {\n mixer.stopAllAction();\n mixer.uncacheRoot(object3D);\n delete object3D.userData.mixer;\n }\n delete object3D.userData.currentAction;\n delete object3D.userData.currentClip;\n delete object3D.userData.currentActionStart;\n delete object3D.userData.colorAction;\n delete object3D.userData.colorClip;\n\n // Reset to config-based default rotation\n if (contactorGroup.userData.initialState === 'input2') {\n contactorGroup.rotation.copy(this.INPUT2_ROTATION);\n } else {\n contactorGroup.rotation.copy(this.INPUT1_ROTATION);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Contactor material (color from output pin state)\n // ---------------------------------------------------------------------------\n\n private _ensureClonedMaterial(contactorMesh: THREE.Mesh): void {\n const mat = contactorMesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType === CmpMatType.ANIMATION_CLONE) return;\n contactorMesh.material = this.getMat(CmpMatCategory.WHITE).clone();\n (contactorMesh.material as THREE.MeshLambertMaterial).userData.matType =\n CmpMatType.ANIMATION_CLONE;\n }\n\n private _restoreSharedMaterial(contactorMesh: THREE.Mesh): void {\n const mat = contactorMesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType !== CmpMatType.ANIMATION_CLONE) return;\n mat.dispose();\n contactorMesh.material = this.getMat(CmpMatCategory.WHITE);\n }\n\n private _updateContactorColor(contactorMesh: THREE.Mesh, state: ComponentState): void {\n if (!state.pinStates || !state.pinStates.has('output')) return;\n\n this._ensureClonedMaterial(contactorMesh);\n const mat = contactorMesh.material as THREE.MeshLambertMaterial;\n\n const outputState = state.pinStates.get('output')!;\n\n if (outputState.hasVoltage && outputState.hasCurrent) {\n mat.color.copy(this.COLOR_VOLTAGE_CURRENT);\n } else if (outputState.hasCurrent) {\n mat.color.copy(this.COLOR_CURRENT);\n } else if (outputState.hasVoltage) {\n mat.color.copy(this.COLOR_VOLTAGE);\n } else {\n mat.color.copy(this.COLOR_NONE);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Helpers\n // ---------------------------------------------------------------------------\n\n private findContactorGroup(object3D: THREE.Object3D): THREE.Object3D | null {\n let contactorGroup: THREE.Object3D | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Group && child.userData.part === 'contactorGroup') {\n contactorGroup = child;\n }\n });\n\n return contactorGroup;\n }\n\n private _findContactorMesh(object3D: THREE.Object3D): THREE.Mesh | null {\n let mesh: THREE.Mesh | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === 'contactor') {\n mesh = child;\n }\n });\n\n return mesh;\n }\n\n // Uses default hover implementation\n}\n","import { ComponentVisualFactoryBase } from '../ComponentVisualFactory';\nimport { CmpMatCategory, CmpMatType } from '../types';\nimport { ComponentType, type Component, type ComponentState } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport { RingGeometry, CyclicTrapezoidGeometry } from '../../utils/GeometryUtils';\nimport type { ConfigFormDefinition, VisualContext } from '../../types';\n\n/**\n * Visual factory for Clock\n */\nexport class ClockVisualFactory extends ComponentVisualFactoryBase {\n /** Shared Clock envelope geometry */\n private readonly ENVELOPE_GEOMETRY = RingGeometry(0.7, 0.8, 0.4, 32);\n /** Shared Clock hand geometry */\n private readonly HAND_GEOMETRY = CyclicTrapezoidGeometry(0.7, 0.16, 0.01, 0.1, 0.3, 16);\n /** Shared Clock area geometry */\n private readonly AREA_GEOMETRY = new THREE.CylinderGeometry(\n 0.69,\n 0.69,\n 0.4,\n 16,\n 3,\n false,\n Math.PI / 2,\n Math.PI\n );\n private readonly RED_AREA_MAT = new THREE.MeshLambertMaterial({\n color: 0xff0000,\n transparent: true,\n opacity: 0.4,\n emissive: 0xff0000,\n emissiveIntensity: 0.3,\n userData: { matType: CmpMatType.FACTORY },\n });\n private readonly BLUE_AREA_MAT = new THREE.MeshLambertMaterial({\n color: 0x0000ff,\n transparent: true,\n opacity: 0.4,\n emissive: 0x0000ff,\n emissiveIntensity: 0.3,\n userData: { matType: CmpMatType.FACTORY },\n });\n\n private readonly HIGH_TICK_ROTATION = new THREE.Euler(0, Math.PI, 0);\n private readonly LOW_TICK_ROTATION = new THREE.Euler(0, 0, 0);\n\n constructor() {\n super();\n this._componentType = ComponentType.Clock;\n }\n\n createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n // Root group (not rendered, just organizational)\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n // Component hitbox (invisible, raycastable)\n const hitbox = this.createComponentHitbox(component.id, group.id, 2, 2, 2);\n group.add(hitbox);\n\n const envelope = new THREE.Mesh(this.ENVELOPE_GEOMETRY, this.getMat(CmpMatCategory.WHITE));\n envelope.userData = {\n type: 'component',\n componentId: group.userData.componentId,\n part: 'envelope',\n };\n envelope.rotateX(-Math.PI / 2);\n envelope.position.set(0, 0, 0);\n group.add(envelope);\n\n // red and blue areas\n const highArea = new THREE.Mesh(this.AREA_GEOMETRY, this.RED_AREA_MAT);\n highArea.position.set(0, 0.2, 0);\n group.add(highArea);\n const lowArea = new THREE.Mesh(this.AREA_GEOMETRY, this.BLUE_AREA_MAT);\n lowArea.rotateY(Math.PI);\n lowArea.position.set(0, 0.2, 0);\n group.add(lowArea);\n\n // hand\n const handGroup = this.createHandGroup(component);\n group.add(handGroup);\n handGroup.rotation.copy(this.HIGH_TICK_ROTATION);\n\n // pins (not called if preview - no pins)\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n this.updateFromConfiguration(group, component.config);\n return group;\n }\n\n private createPinsVisual(component: Component, context: VisualContext, group: THREE.Group) {\n const vccNode = context.getENode(component.pins[0]!);\n if (vccNode) {\n const vccGroup = this.createPinGroup(vccNode, 'top');\n vccGroup.position.set(0, 0, -0.77);\n group.add(vccGroup);\n }\n\n const gndNode = context.getENode(component.pins[2]!);\n if (gndNode) {\n const gndGroup = this.createPinGroup(gndNode, 'bottom');\n gndGroup.position.set(0, 0, 0.77);\n group.add(gndGroup);\n }\n\n const outputNode = context.getENode(component.pins[1]!);\n if (outputNode) {\n const outputGroup = this.createPinGroup(outputNode, 'right');\n outputGroup.position.set(0.75, 0, 0);\n group.add(outputGroup);\n }\n }\n\n private createHandGroup(component: Component): THREE.Group {\n const handGroup = new THREE.Group();\n handGroup.name = 'handGroup';\n handGroup.userData = {\n type: 'component',\n componentId: component.id,\n part: 'handGroup',\n initialState: 'open',\n };\n handGroup.updateMatrix();\n handGroup.updateMatrixWorld(true);\n\n const hand = new THREE.Mesh(this.HAND_GEOMETRY, this.getMat(CmpMatCategory.SHINY_SILVER));\n hand.userData = {\n type: 'component',\n componentId: component.id,\n part: 'hand',\n };\n handGroup.add(hand);\n hand.rotateX(Math.PI / 2);\n hand.translateZ(-0.4);\n hand.translateX(0.25);\n\n hand.updateMatrix();\n hand.updateMatrixWorld(true);\n\n return handGroup;\n }\n\n /**\n * Get config form definition for Clock\n *\n * @param config - config\n * @returns Form definition\n */\n override getConfigFormDefinition(_config?: Map<string, string>): ConfigFormDefinition | null {\n return {\n fields: [\n { key: 'startHigh', type: 'boolean' },\n { key: 'halfPeriod', type: 'number', min: 1 },\n ],\n };\n }\n\n /**\n * Map core config to form data\n *\n * @param config - Core component config\n * @returns Form data\n */\n override mapCoreConfigToForm(config: Map<string, string>): Map<string, any> {\n const formData = new Map<string, any>();\n const startHigh = config.get('startHigh');\n formData.set('startHigh', startHigh == 'true');\n formData.set('halfPeriod', parseFloat(config.get('halfPeriod') || '1'));\n return formData;\n }\n\n /**\n * Map form data to core config\n *\n * @param formData - Form data\n * @returns Core config\n */\n override mapFormToCoreConfig(formData: Map<string, any>): Map<string, string> {\n const config = new Map<string, string>();\n const startHigh = formData.get('startHigh');\n config.set('startHigh', startHigh ? 'true' : 'false');\n config.set('halfPeriod', formData.get('halfPeriod').toString());\n return config;\n }\n\n override updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>) {\n const handGroup = this.findHandGroup(object3D);\n if (!handGroup) return;\n\n handGroup.userData.startHigh = config.get('startHigh') === 'true';\n\n if (handGroup.userData.startHigh) {\n handGroup.rotation.copy(this.HIGH_TICK_ROTATION);\n } else {\n handGroup.rotation.copy(this.LOW_TICK_ROTATION);\n }\n }\n\n /**\n * Update Clock animation based on simulation state.\n *\n * When state is non-null: animates hand rotation toward the current state's target.\n * If the state has a planned transition (hasExpiration), creates a smooth animation\n * from the current visual rotation to the target using Three.js AnimationMixer.\n * Otherwise snaps to the target position.\n *\n * When state is null (leaving simulation): stops all animations, cleans up mixer,\n * and resets hand to config-based default rotation.\n *\n * @param object3D - The Object3D created by createVisual()\n * @param state - The Clock's current simulation state, or null to reset\n */\n override updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void {\n const handGroup = this.findHandGroup(object3D);\n if (!handGroup) return;\n\n //console.log('update animation called', this._animationContext!!.simulationStatus );\n\n // No animation context / Leaving simulation: cleanup and reset\n if (!state || !this._animationContext) {\n this._cleanupMixer(object3D, handGroup);\n return;\n }\n // When paused or initial: snap to current state position (mixer won't advance)\n if (this._animationContext.simulationStatus !== 'playing') {\n handGroup.rotation.copy(this.getStateRotation(state.state));\n return;\n }\n\n // If there's no planned transition, cleanup mixer and snap to nominal position\n if (!state.hasExpiration) {\n this._cleanupMixer(object3D, handGroup);\n handGroup.rotation.copy(this.getStateRotation(state.state));\n return;\n }\n\n const startRotation = this.getStateRotation(state.state!!).y;\n const targetRotation = this.getStateRotation(state.nextState!!).y;\n\n this._animateHand(\n object3D,\n handGroup,\n startRotation,\n state.startTick,\n targetRotation,\n state.expirationTick\n );\n }\n\n /**\n * Create a smooth animation from current hand rotation to targetRotation\n */\n private _animateHand(\n object3D: THREE.Object3D,\n handGroup: THREE.Object3D,\n _startRotation: number,\n startTick: number,\n targetRotation: number,\n targetTick: number\n ): void {\n // prevent duplicate guard\n if (object3D.userData.currentActionStart == startTick) {\n return;\n }\n\n const ticksPerSecond: number = this._animationContext!.ticksPerSecond;\n const span = targetTick - startTick;\n // Get or create mixer\n let mixer: THREE.AnimationMixer = object3D.userData.mixer;\n if (!mixer) {\n mixer = new THREE.AnimationMixer(object3D);\n object3D.userData.mixer = mixer;\n }\n\n //reading before stopping\n const currentRotation = handGroup.rotation.y % (2 * Math.PI);\n // Stop and uncache previous animation\n if (object3D.userData.currentAction) {\n (object3D.userData.currentAction as THREE.AnimationAction).stop();\n }\n if (object3D.userData.currentClip) {\n mixer.uncacheClip(object3D.userData.currentClip as THREE.AnimationClip);\n }\n\n // Compute real duration in seconds: one tick duration = span/ticksPerSecond\n const durationSeconds = span / ticksPerSecond;\n targetRotation = currentRotation - Math.PI;\n\n // Create keyframe track for handGroup.rotation[y]\n const track = new THREE.NumberKeyframeTrack(\n 'handGroup.rotation[y]',\n [0, durationSeconds],\n [currentRotation, targetRotation]\n );\n\n const clip = new THREE.AnimationClip('clockHandRotation', durationSeconds, [track]);\n const action = mixer.clipAction(clip);\n action.loop = THREE.LoopOnce;\n action.clampWhenFinished = true;\n action.play();\n\n // Store references for cleanup/interruption\n object3D.userData.currentActionStart = startTick;\n object3D.userData.currentAction = action;\n object3D.userData.currentClip = clip;\n }\n\n /**\n * Stop all animations, clean up mixer, and reset hand to config-based default rotation.\n */\n private _cleanupMixer(object3D: THREE.Object3D, handGroup: THREE.Object3D): void {\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n if (mixer) {\n mixer.stopAllAction();\n mixer.uncacheRoot(object3D);\n delete object3D.userData.mixer;\n }\n delete object3D.userData.currentAction;\n delete object3D.userData.currentClip;\n\n // Reset to config-based default rotation\n if (handGroup.userData.startHigh) {\n handGroup.rotation.copy(this.HIGH_TICK_ROTATION);\n } else {\n handGroup.rotation.copy(this.LOW_TICK_ROTATION);\n }\n }\n\n private getStateRotation(state: string) {\n if (state === 'high') {\n return this.HIGH_TICK_ROTATION;\n }\n return this.LOW_TICK_ROTATION;\n }\n\n /**\n * Find the hand group mesh within the component group\n *\n * @param object3D - The Object3D group created by createVisual()\n * @returns The hand mesh if found, null otherwise\n *\n * @remarks\n * Searches for a mesh with userData.part === 'hand'\n */\n private findHandGroup(object3D: THREE.Object3D): THREE.Object3D | null {\n let handGroupMesh: THREE.Object3D | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Object3D && child.userData.part === 'handGroup') {\n handGroupMesh = child;\n }\n });\n return handGroupMesh;\n }\n}\n","import { ComponentVisualFactoryBase } from '../ComponentVisualFactory';\nimport { ComponentType, type Component, type ComponentState } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport { CyclicTrapezoidGeometry, CyclicTrapezoidHoleGeometry } from '../../utils/GeometryUtils';\nimport type { ConfigFormDefinition, VisualContext } from '../../types';\nimport { CmpMatCategory, CmpMatType } from '../types';\n\n/**\n * Visual factory for Inverter/Buffer components\n *\n * Creates:\n * - Inverter triangle or Buffer trapezoid extrude geom mesh\n * - Vcc, input and output pin group\n * - Component hitbox for raycasting\n *\n * Animation:\n * - Emissive glow when component is high (based on simulation state)\n */\nexport class InverterVisualFactory extends ComponentVisualFactoryBase {\n /** Shared Inverter envelope geometry */\n private readonly INVERTER_GEOM = CyclicTrapezoidGeometry(0.8, 1.6, 0, 0.08, 0.4, 16);\n /** Shared Inverter inner hole geometry */\n private readonly INVERTER_HOLE_GEOM = CyclicTrapezoidHoleGeometry(0.8, 1.6, 0, 0.08, 0.4, 16)!;\n\n /** Shared Buffer envelope geometry */\n private readonly BUFFER_GEOM = CyclicTrapezoidGeometry(1, 1.6, 0.61, 0.08, 0.4, 16);\n /** Shared Buffer inner hole geometry */\n private readonly BUFFER_HOLE_GEOM = CyclicTrapezoidHoleGeometry(1, 1.6, 0.61, 0.08, 0.4, 16)!;\n\n private readonly HOLE_COLOR_HIGH = new THREE.Color(0xff4444);\n private readonly HOLE_COLOR_LOW = new THREE.Color(0x4444ff);\n private readonly HOLE_EMISSIVE_HIGH_INTENSITY = 0.5;\n private readonly HOLE_EMISSIVE_LOW_INTENSITY = 0.2;\n\n constructor() {\n super();\n this._componentType = ComponentType.Inverter;\n }\n\n createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n variant: 'inverter',\n };\n\n // Component hitbox (invisible, raycastable)\n const hitbox = this.createComponentHitbox(component.id, group.id, 1.6, 2, 1.6);\n group.add(hitbox);\n // create an inverter by default\n this.replaceEnvelope(group, false);\n this.replaceHole(group, false);\n\n // pins (not called if preview - no pins)\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n this.updateFromConfiguration(group, component.config);\n return group;\n }\n\n private createPinsVisual(component: Component, context: VisualContext, group: THREE.Group) {\n const vccNode = context.getENode(component.pins[0]!);\n if (vccNode) {\n const vccGroup = this.createPinGroup(vccNode, 'top', new THREE.Euler(0, 0, -0.8));\n vccGroup.position.set(-0.27, 0, -0.56);\n group.add(vccGroup);\n }\n\n const gndNode = context.getENode(component.pins[3]!);\n if (gndNode) {\n const gndGroup = this.createPinGroup(gndNode, 'bottom', new THREE.Euler(0, 0, -0.8));\n gndGroup.position.set(-0.27, 0, 0.56);\n group.add(gndGroup);\n }\n\n const inputNode = context.getENode(component.pins[1]!);\n if (inputNode) {\n const inputGroup = this.createPinGroup(inputNode, 'left');\n inputGroup.position.set(-0.5, 0, 0);\n group.add(inputGroup);\n }\n\n const outputNode = context.getENode(component.pins[2]!);\n if (outputNode) {\n const outputGroup = this.createPinGroup(outputNode, 'right');\n outputGroup.position.set(0.45, 0, 0);\n group.add(outputGroup);\n\n // this counterpart act as negativeMarker when component is in its inverter config\n const outputPinCounterpart = this.createPinCounterpart(\n outputGroup,\n this.getMat(CmpMatCategory.WHITE)\n );\n if (!!outputPinCounterpart) {\n outputPinCounterpart.userData.part = 'negativeMarker';\n group.add(outputPinCounterpart);\n }\n }\n }\n\n private replaceEnvelope(group: THREE.Object3D, activationLogic: boolean): THREE.Mesh {\n const oldEnvelope = this.findEnvelopeMesh(group);\n if (oldEnvelope) {\n // case where envelope of the good type (buffer or inverter) already exists\n if (activationLogic === oldEnvelope.userData.activationLogic) {\n return oldEnvelope;\n }\n // else we remove the old envelope before recreating it\n group.remove(oldEnvelope);\n }\n\n const envelope = activationLogic\n ? new THREE.Mesh(this.BUFFER_GEOM, this.getMat(CmpMatCategory.WHITE))\n : new THREE.Mesh(this.INVERTER_GEOM, this.getMat(CmpMatCategory.WHITE));\n envelope.userData = {\n type: 'component',\n componentId: group.userData.componentId,\n part: 'envelope',\n activationLogic: activationLogic,\n };\n envelope.rotateX(-Math.PI / 2);\n const envX = activationLogic ? -0.05 : -0.1;\n envelope.position.set(envX, -0.05, 0);\n group.add(envelope);\n return envelope;\n }\n\n private replaceHole(group: THREE.Object3D, activationLogic: boolean): THREE.Mesh {\n const oldHole = this.findHoleMesh(group);\n if (oldHole) {\n // case where hole of the good type (buffer or inverter) already exists\n if (activationLogic === oldHole.userData.activationLogic) {\n return oldHole;\n }\n // else we remove the old hole before recreating it\n group.remove(oldHole);\n }\n\n const hole = activationLogic\n ? new THREE.Mesh(this.BUFFER_HOLE_GEOM, this.getMat(CmpMatCategory.DARK_GRAY))\n : new THREE.Mesh(this.INVERTER_HOLE_GEOM, this.getMat(CmpMatCategory.DARK_GRAY));\n hole.name = 'hole'; // required for AnimationMixer property binding\n hole.userData = {\n type: 'component',\n componentId: group.userData.componentId,\n part: 'hole',\n activationLogic: activationLogic,\n initialState: activationLogic ? 'low' : 'high',\n };\n hole.rotateX(-Math.PI / 2);\n const envX = activationLogic ? -0.05 : -0.1;\n hole.position.set(envX, -0.05, 0);\n group.add(hole);\n return hole;\n }\n\n /**\n * Get config form definition for Inverter\n *\n * @param config - Optional current config to determine disabled state of transitionSpan\n * @returns Form definition with defaultLogicFamily dropdown, activationLogic boolean, and transitionSpan number\n */\n override getConfigFormDefinition(config?: Map<string, string>): ConfigFormDefinition | null {\n const logicFamily = config?.get('defaultLogicFamily') ?? 'CMOS1';\n return {\n fields: [\n {\n key: 'defaultLogicFamily',\n type: 'dropdown',\n options: { CMOS: 'CMOS1', TTL: 'TTL1', Sandbox: 'Sandbox' },\n },\n { key: 'activationLogic', type: 'boolean' },\n { key: 'transitionSpan', type: 'number', min: 1, disabled: logicFamily !== 'Sandbox' },\n { key: 'initializationOrder', type: 'number' },\n ],\n };\n }\n\n /**\n * Map core config to form data\n * Converts \"positive\"/\"negative\" strings to boolean\n *\n * @param config - Core component config\n * @returns Form data with boolean activationLogic\n */\n override mapCoreConfigToForm(config: Map<string, string>): Map<string, any> {\n const formData = new Map<string, any>();\n formData.set('defaultLogicFamily', config.get('defaultLogicFamily') ?? 'CMOS1');\n const activationLogic = config.get('activationLogic');\n formData.set('activationLogic', activationLogic === 'positive');\n formData.set('transitionSpan', parseFloat(config.get('transitionSpan') || '1'));\n formData.set('initializationOrder', parseFloat(config.get('initializationOrder') || '0'));\n return formData;\n }\n\n /**\n * Map form data to core config\n * Converts boolean to \"positive\"/\"negative\" strings\n *\n * @param formData - Form data with boolean activationLogic\n * @returns Core config with string activationLogic\n */\n override mapFormToCoreConfig(formData: Map<string, any>): Map<string, string> {\n const config = new Map<string, string>();\n config.set('defaultLogicFamily', formData.get('defaultLogicFamily') ?? 'CMOS1');\n const activationLogic = formData.get('activationLogic');\n config.set('activationLogic', activationLogic ? 'positive' : 'negative');\n config.set('transitionSpan', formData.get('transitionSpan').toString());\n config.set('initializationOrder', formData.get('initializationOrder').toString() || null);\n return config;\n }\n\n override updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>) {\n const envelopeMesh = this.findEnvelopeMesh(object3D);\n if (!envelopeMesh) return;\n\n const negativeMarkerMesh = this.findNegativeMarkerMesh(object3D);\n const vccVisual = this.findPinVisual(object3D, 'vcc');\n const gndVisual = this.findPinVisual(object3D, 'gnd');\n\n const inverterVariant =\n config && config.has('activationLogic') ? config.get('activationLogic') !== 'positive' : true;\n\n if (inverterVariant) {\n this.replaceEnvelope(object3D, false);\n this.replaceHole(object3D, false);\n if (object3D.userData.variant !== 'inverter') {\n object3D.userData.variant = 'inverter';\n // @ts-ignore\n negativeMarkerMesh?.geometry.scale(10, 10, 10);\n\n if (!!vccVisual) {\n vccVisual.setRotationFromEuler(new THREE.Euler(0, 0, -0.8));\n vccVisual.parent!.position.set(-0.27, 0, -0.55);\n }\n if (!!gndVisual) {\n gndVisual.setRotationFromEuler(new THREE.Euler(0, 0, -0.8));\n gndVisual.parent!.position.set(-0.27, 0, 0.56);\n }\n }\n } else {\n if (object3D.userData.variant !== 'buffer') {\n object3D.userData.variant = 'buffer';\n this.replaceEnvelope(object3D, true);\n this.replaceHole(object3D, true);\n // @ts-ignore\n negativeMarkerMesh?.geometry.scale(0.1, 0.1, 0.1);\n\n if (!!vccVisual) {\n vccVisual.setRotationFromEuler(new THREE.Euler(0, 0, -0.5));\n vccVisual.parent!.position.set(-0.27, 0, -0.65);\n }\n if (!!gndVisual) {\n gndVisual.setRotationFromEuler(new THREE.Euler(0, 0, -0.5));\n gndVisual.parent!.position.set(-0.27, 0, 0.65);\n }\n }\n }\n }\n\n /**\n * Update Inverter animation based on simulation state\n *\n * @param object3D - The Object3D created by createVisual()\n * @param state - The Inverter's current simulation state\n */\n override updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void {\n const holeMesh = this.findHoleMesh(object3D);\n if (!holeMesh) return;\n\n // Leaving simulation, no animation context, or indeterminate: restore default appearance\n if (!state || !this._animationContext || state.state === 'indeterminate') {\n this._cleanupMixer(object3D);\n this._restoreSharedHoleMaterial(holeMesh);\n return;\n }\n\n if (state.state === 'high') {\n this._cleanupMixer(object3D);\n this._setHoleColor(holeMesh, this.HOLE_COLOR_HIGH, this.HOLE_EMISSIVE_HIGH_INTENSITY);\n return;\n }\n\n if (state.state === 'low') {\n this._cleanupMixer(object3D);\n this._setHoleColor(holeMesh, this.HOLE_COLOR_LOW, this.HOLE_EMISSIVE_LOW_INTENSITY);\n return;\n }\n\n // Paused + transitional: snap to start color (before the transition began)\n if (this._animationContext.simulationStatus !== 'playing') {\n if (state.state === 'rising') {\n this._setHoleColor(holeMesh, this.HOLE_COLOR_LOW, this.HOLE_EMISSIVE_LOW_INTENSITY);\n } else {\n // falling\n this._setHoleColor(holeMesh, this.HOLE_COLOR_HIGH, this.HOLE_EMISSIVE_HIGH_INTENSITY);\n }\n return;\n }\n\n // Playing + transitional: animate\n if (state.hasExpiration) {\n this._animateHoleColor(object3D, holeMesh, state);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Hole material helpers\n // ---------------------------------------------------------------------------\n\n private _setHoleColor(holeMesh: THREE.Mesh, color: THREE.Color, emissiveIntensity: number): void {\n this._ensureClonedHoleMaterial(holeMesh);\n const mat = holeMesh.material as THREE.MeshLambertMaterial;\n mat.color.copy(color);\n mat.emissive.copy(color);\n mat.emissiveIntensity = emissiveIntensity;\n }\n\n private _ensureClonedHoleMaterial(holeMesh: THREE.Mesh): void {\n const mat = holeMesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType === CmpMatType.ANIMATION_CLONE) return;\n holeMesh.material = this.getMat(CmpMatCategory.DARK_GRAY).clone();\n (holeMesh.material as THREE.MeshLambertMaterial).userData.matType = CmpMatType.ANIMATION_CLONE;\n }\n\n private _restoreSharedHoleMaterial(holeMesh: THREE.Mesh): void {\n const mat = holeMesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType !== CmpMatType.ANIMATION_CLONE) return;\n mat.dispose();\n holeMesh.material = this.getMat(CmpMatCategory.DARK_GRAY);\n }\n\n private _cleanupMixer(object3D: THREE.Object3D): void {\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n if (mixer) {\n mixer.stopAllAction();\n mixer.uncacheRoot(object3D);\n delete object3D.userData.mixer;\n }\n delete object3D.userData.currentAction;\n delete object3D.userData.currentClip;\n delete object3D.userData.currentActionStart;\n }\n\n /**\n * Animate the hole color between LOW and HIGH over the transition span.\n * Reads current color for mid-transition support.\n */\n private _animateHoleColor(\n object3D: THREE.Object3D,\n holeMesh: THREE.Mesh,\n state: ComponentState\n ): void {\n // Prevent duplicate animation for same transition\n if (object3D.userData.currentActionStart === state.startTick) return;\n\n const tps = this._animationContext!.ticksPerSecond;\n const span = state.expirationTick - state.startTick;\n const durationSeconds = span / tps;\n\n this._ensureClonedHoleMaterial(holeMesh);\n const mat = holeMesh.material as THREE.MeshLambertMaterial;\n\n const toColor = state.state === 'rising' ? this.HOLE_COLOR_HIGH : this.HOLE_COLOR_LOW;\n const toIntensity =\n state.state === 'rising'\n ? this.HOLE_EMISSIVE_HIGH_INTENSITY\n : this.HOLE_EMISSIVE_LOW_INTENSITY;\n\n // Read current values for mid-transition support\n const currentRGB = [mat.color.r, mat.color.g, mat.color.b];\n const endRGB = [toColor.r, toColor.g, toColor.b];\n const currentIntensity = mat.emissiveIntensity;\n\n let mixer: THREE.AnimationMixer = object3D.userData.mixer;\n if (!mixer) {\n mixer = new THREE.AnimationMixer(object3D);\n object3D.userData.mixer = mixer;\n }\n\n // Stop previous action\n if (object3D.userData.currentAction) {\n (object3D.userData.currentAction as THREE.AnimationAction).stop();\n }\n if (object3D.userData.currentClip) {\n mixer.uncacheClip(object3D.userData.currentClip as THREE.AnimationClip);\n }\n\n const clip = new THREE.AnimationClip('holeColor', durationSeconds, [\n new THREE.ColorKeyframeTrack(\n 'hole.material.color',\n [0, durationSeconds],\n [...currentRGB, ...endRGB]\n ),\n new THREE.ColorKeyframeTrack(\n 'hole.material.emissive',\n [0, durationSeconds],\n [...currentRGB, ...endRGB]\n ),\n new THREE.NumberKeyframeTrack(\n 'hole.material.emissiveIntensity',\n [0, durationSeconds],\n [currentIntensity, toIntensity]\n ),\n ]);\n const action = mixer.clipAction(clip);\n action.loop = THREE.LoopOnce;\n action.clampWhenFinished = true;\n action.play();\n\n object3D.userData.currentActionStart = state.startTick;\n object3D.userData.currentAction = action;\n object3D.userData.currentClip = clip;\n }\n\n /**\n * Find the envelope mesh within the component group\n *\n * @param object3D - The Object3D group created by createVisual()\n * @returns The envelope mesh if found, null otherwise\n *\n * @remarks\n * Searches for a mesh with userData.part === 'envelope'\n */\n private findEnvelopeMesh(object3D: THREE.Object3D): THREE.Mesh | null {\n let envelopeMesh: THREE.Mesh | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === 'envelope') {\n envelopeMesh = child as THREE.Mesh;\n }\n });\n return envelopeMesh;\n }\n\n /**\n * Find the hole mesh within the component group\n *\n * @param object3D - The Object3D group created by createVisual()\n * @returns The hole mesh if found, null otherwise\n *\n * @remarks\n * Searches for a mesh with userData.part === 'hole'\n */\n private findHoleMesh(object3D: THREE.Object3D): THREE.Mesh | null {\n let holeMesh: THREE.Mesh | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === 'hole') {\n holeMesh = child as THREE.Mesh;\n }\n });\n return holeMesh;\n }\n\n /**\n * Find the negative marker mesh within the component group\n *\n * @param object3D - The Object3D group created by createVisual()\n * @returns The negative marker mesh if found, null otherwise\n *\n * @remarks\n * Searches for a mesh with userData.part === 'negativeMarker'\n */\n protected findNegativeMarkerMesh(object3D: THREE.Object3D): THREE.Mesh | null {\n let negativeMarkerMesh: THREE.Mesh | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === 'negativeMarker') {\n negativeMarkerMesh = child as THREE.Mesh;\n }\n });\n return negativeMarkerMesh;\n }\n}\n","import { ComponentVisualFactoryBase } from '../ComponentVisualFactory';\nimport { ComponentType, type Component, type ComponentState } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport { AndGateGeometry, AndGateHoleGeometry } from '../../utils/GeometryUtils';\nimport type { ConfigFormDefinition, VisualContext } from '../../types';\nimport { CmpMatCategory, CmpMatType } from '../types';\n\n/**\n * Visual factory for NAND gates components\n *\n * Creates:\n * - Gate mesh\n * - vcc, gnd, inputs and output pin groups\n * - Component hitbox for raycasting\n *\n * Animation:\n * - Emissive glow when Gate is high (based on simulation state)\n */\nexport class NandGateVisualFactory extends ComponentVisualFactoryBase {\n /** Shared envelope geometry */\n protected readonly ENVELOPE_GEOM = AndGateGeometry(1.5, 1.6, 0.1, 0.4, 16);\n /** Shared inner hole geometry */\n protected readonly HOLE_GEOM = AndGateHoleGeometry(1.5, 1.6, 0.1, 0.4, 16)!;\n /** Shared geometry for negative marker **/\n protected readonly NEG_MARKER_GEOM = new THREE.CylinderGeometry(\n 0.2,\n 0.2,\n 0.4,\n 16,\n 4,\n false,\n 0,\n Math.PI * 2\n );\n\n protected readonly HOLE_COLOR_HIGH = new THREE.Color(0xff4444);\n protected readonly HOLE_COLOR_LOW = new THREE.Color(0x4444ff);\n protected readonly HOLE_EMISSIVE_HIGH_INTENSITY = 0.5;\n protected readonly HOLE_EMISSIVE_LOW_INTENSITY = 0.2;\n\n constructor() {\n super();\n this._componentType = ComponentType.NandGate;\n }\n\n override defaultRotation() {\n return Math.PI;\n }\n\n createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n // Root group (not rendered, just organizational)\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n // Component hitbox (invisible, raycastable)\n const hitbox = this.createComponentHitbox(component.id, group.id, 2, 2, 1.8);\n group.add(hitbox);\n\n // Visual Gate\n const envelope = new THREE.Mesh(this.ENVELOPE_GEOM, this.getMat(CmpMatCategory.WHITE));\n envelope.userData = {\n type: 'component',\n componentId: component.id,\n part: 'envelope',\n };\n envelope.rotateX(-Math.PI / 2);\n envelope.rotateY(Math.PI);\n envelope.position.set(0, 0.35, 0);\n group.add(envelope);\n\n const hole = new THREE.Mesh(this.HOLE_GEOM, this.getMat(CmpMatCategory.DARK_GRAY));\n hole.name = 'hole'; // required for AnimationMixer property binding\n hole.userData = {\n type: 'component',\n componentId: component.id,\n part: 'hole',\n initialState: 'low',\n };\n hole.rotateX(-Math.PI / 2);\n hole.rotateY(Math.PI);\n hole.position.set(0, 0.35, 0);\n group.add(hole);\n\n // pins (not called if preview - no pins)\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n this.updateFromConfiguration(group, component.config);\n return group;\n }\n\n protected createPinsVisual(component: Component, context: VisualContext, group: THREE.Group) {\n const vccNode = context.getENode(component.pins[0]!);\n if (vccNode) {\n const vccGroup = this.createPinGroup(vccNode, 'bottom');\n vccGroup.position.set(0.15, 0, 0.79);\n group.add(vccGroup);\n }\n\n const gndNode = context.getENode(component.pins[4]!);\n if (gndNode) {\n const gndGroup = this.createPinGroup(gndNode, 'top');\n gndGroup.position.set(0.15, 0, -0.79);\n group.add(gndGroup);\n }\n\n const input1Node = context.getENode(component.pins[1]!);\n if (input1Node) {\n const input1Group = this.createPinGroup(input1Node, 'right');\n input1Group.position.set(0.75, 0, 0.5);\n group.add(input1Group);\n }\n\n const input2Node = context.getENode(component.pins[2]!);\n if (input2Node) {\n const input2Group = this.createPinGroup(input2Node, 'right');\n input2Group.position.set(0.75, 0, -0.5);\n group.add(input2Group);\n }\n\n const outputNode = context.getENode(component.pins[3]!);\n if (outputNode) {\n const outputGroup = this.createPinGroup(outputNode, 'left');\n outputGroup.position.set(-0.7, 0, 0);\n group.add(outputGroup);\n }\n }\n\n /**\n * Get config form definition\n *\n * @param config - Optional current config to determine disabled state of transitionSpan\n * @returns Form definition with defaultLogicFamily dropdown, activationLogic boolean, and transitionSpan number\n */\n override getConfigFormDefinition(config?: Map<string, string>): ConfigFormDefinition | null {\n const logicFamily = config?.get('defaultLogicFamily') ?? 'CMOS1';\n return {\n fields: [\n {\n key: 'defaultLogicFamily',\n type: 'dropdown',\n options: { CMOS: 'CMOS1', TTL: 'TTL1', Sandbox: 'Sandbox' },\n },\n { key: 'activationLogic', type: 'boolean' },\n { key: 'transitionSpan', type: 'number', min: 1, disabled: logicFamily !== 'Sandbox' },\n { key: 'initializationOrder', type: 'number' },\n ],\n };\n }\n\n /**\n * Map core config to form data\n * Converts \"positive\"/\"negative\" strings to boolean\n *\n * @param config - Core component config\n * @returns Form data with boolean activationLogic\n */\n override mapCoreConfigToForm(config: Map<string, string>): Map<string, any> {\n const formData = new Map<string, any>();\n formData.set('defaultLogicFamily', config.get('defaultLogicFamily') ?? 'CMOS1');\n const activationLogic = config.get('activationLogic');\n formData.set('activationLogic', activationLogic === 'positive');\n formData.set('transitionSpan', parseFloat(config.get('transitionSpan') || '1'));\n formData.set('initializationOrder', parseFloat(config.get('initializationOrder') || '0'));\n return formData;\n }\n\n /**\n * Map form data to core config\n * Converts boolean to \"positive\"/\"negative\" strings\n *\n * @param formData - Form data with boolean activationLogic\n * @returns Core config with string activationLogic\n */\n override mapFormToCoreConfig(formData: Map<string, any>): Map<string, string> {\n const config = new Map<string, string>();\n config.set('defaultLogicFamily', formData.get('defaultLogicFamily') ?? 'CMOS1');\n const activationLogic = formData.get('activationLogic');\n config.set('activationLogic', activationLogic ? 'positive' : 'negative');\n config.set('transitionSpan', formData.get('transitionSpan').toString());\n config.set('initializationOrder', formData.get('initializationOrder').toString() || null);\n return config;\n }\n\n override updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>) {\n const holeMesh = this.findHoleMesh(object3D);\n if (!holeMesh) return;\n\n let negativeMarkerMesh = this.findNegativeMarkerMesh(object3D);\n\n if (config.get('activationLogic') === 'negative') {\n holeMesh.userData.initialState = 'high';\n if (!negativeMarkerMesh) {\n negativeMarkerMesh = new THREE.Mesh(\n this.NEG_MARKER_GEOM,\n this.getMat(CmpMatCategory.WHITE)\n );\n negativeMarkerMesh.userData = {\n type: 'component',\n componentId: holeMesh.userData.componentId,\n part: 'negativeMarker',\n };\n\n negativeMarkerMesh.position.set(-0.7, 0.15, -0.65);\n object3D.add(negativeMarkerMesh);\n }\n } else {\n holeMesh.userData.initialState = 'low';\n if (negativeMarkerMesh) {\n object3D.remove(negativeMarkerMesh);\n }\n }\n }\n\n /**\n * Update animation based on simulation state\n *\n * @param object3D - The Object3D created by createVisual()\n * @param state - The component current simulation state\n */\n override updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void {\n const holeMesh = this.findHoleMesh(object3D);\n if (!holeMesh) return;\n\n if (!state || !this._animationContext || state.state === 'indeterminate') {\n this._cleanupMixer(object3D);\n this._restoreSharedHoleMaterial(holeMesh);\n return;\n }\n\n if (state.state === 'high') {\n this._cleanupMixer(object3D);\n this._setHoleColor(holeMesh, this.HOLE_COLOR_HIGH, this.HOLE_EMISSIVE_HIGH_INTENSITY);\n return;\n }\n\n if (state.state === 'low') {\n this._cleanupMixer(object3D);\n this._setHoleColor(holeMesh, this.HOLE_COLOR_LOW, this.HOLE_EMISSIVE_LOW_INTENSITY);\n return;\n }\n\n // Paused + transitional: snap to start color (before the transition began)\n if (this._animationContext.simulationStatus !== 'playing') {\n if (state.state === 'rising') {\n this._setHoleColor(holeMesh, this.HOLE_COLOR_LOW, this.HOLE_EMISSIVE_LOW_INTENSITY);\n } else {\n // falling\n this._setHoleColor(holeMesh, this.HOLE_COLOR_HIGH, this.HOLE_EMISSIVE_HIGH_INTENSITY);\n }\n return;\n }\n\n // Playing + transitional: animate\n if (state.hasExpiration) {\n this._animateHoleColor(object3D, holeMesh, state);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Hole material helpers\n // ---------------------------------------------------------------------------\n\n private _setHoleColor(holeMesh: THREE.Mesh, color: THREE.Color, emissiveIntensity: number): void {\n this._ensureClonedHoleMaterial(holeMesh);\n const mat = holeMesh.material as THREE.MeshLambertMaterial;\n mat.color.copy(color);\n mat.emissive.copy(color);\n mat.emissiveIntensity = emissiveIntensity;\n }\n\n private _ensureClonedHoleMaterial(holeMesh: THREE.Mesh): void {\n const mat = holeMesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType === CmpMatType.ANIMATION_CLONE) return;\n holeMesh.material = this.getMat(CmpMatCategory.DARK_GRAY).clone();\n (holeMesh.material as THREE.MeshLambertMaterial).userData.matType = CmpMatType.ANIMATION_CLONE;\n }\n\n private _restoreSharedHoleMaterial(holeMesh: THREE.Mesh): void {\n const mat = holeMesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType !== CmpMatType.ANIMATION_CLONE) return;\n mat.dispose();\n holeMesh.material = this.getMat(CmpMatCategory.DARK_GRAY);\n }\n\n private _cleanupMixer(object3D: THREE.Object3D): void {\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n if (mixer) {\n mixer.stopAllAction();\n mixer.uncacheRoot(object3D);\n delete object3D.userData.mixer;\n }\n delete object3D.userData.currentAction;\n delete object3D.userData.currentClip;\n delete object3D.userData.currentActionStart;\n }\n\n /**\n * Animate the hole color between LOW and HIGH over the transition span.\n * Reads current color for mid-transition support.\n */\n private _animateHoleColor(\n object3D: THREE.Object3D,\n holeMesh: THREE.Mesh,\n state: ComponentState\n ): void {\n if (object3D.userData.currentActionStart === state.startTick) return;\n\n const tps = this._animationContext!.ticksPerSecond;\n const span = state.expirationTick - state.startTick;\n const durationSeconds = span / tps;\n\n this._ensureClonedHoleMaterial(holeMesh);\n const mat = holeMesh.material as THREE.MeshLambertMaterial;\n\n const toColor = state.state === 'rising' ? this.HOLE_COLOR_HIGH : this.HOLE_COLOR_LOW;\n const toIntensity =\n state.state === 'rising'\n ? this.HOLE_EMISSIVE_HIGH_INTENSITY\n : this.HOLE_EMISSIVE_LOW_INTENSITY;\n\n const currentRGB = [mat.color.r, mat.color.g, mat.color.b];\n const endRGB = [toColor.r, toColor.g, toColor.b];\n const currentIntensity = mat.emissiveIntensity;\n\n let mixer: THREE.AnimationMixer = object3D.userData.mixer;\n if (!mixer) {\n mixer = new THREE.AnimationMixer(object3D);\n object3D.userData.mixer = mixer;\n }\n\n if (object3D.userData.currentAction) {\n (object3D.userData.currentAction as THREE.AnimationAction).stop();\n }\n if (object3D.userData.currentClip) {\n mixer.uncacheClip(object3D.userData.currentClip as THREE.AnimationClip);\n }\n\n const clip = new THREE.AnimationClip('holeColor', durationSeconds, [\n new THREE.ColorKeyframeTrack(\n 'hole.material.color',\n [0, durationSeconds],\n [...currentRGB, ...endRGB]\n ),\n new THREE.ColorKeyframeTrack(\n 'hole.material.emissive',\n [0, durationSeconds],\n [...currentRGB, ...endRGB]\n ),\n new THREE.NumberKeyframeTrack(\n 'hole.material.emissiveIntensity',\n [0, durationSeconds],\n [currentIntensity, toIntensity]\n ),\n ]);\n const action = mixer.clipAction(clip);\n action.loop = THREE.LoopOnce;\n action.clampWhenFinished = true;\n action.play();\n\n object3D.userData.currentActionStart = state.startTick;\n object3D.userData.currentAction = action;\n object3D.userData.currentClip = clip;\n }\n\n /**\n * Find the envelope mesh within the component group\n *\n * @param object3D - The Object3D group created by createVisual()\n * @returns The envelope mesh if found, null otherwise\n *\n * @remarks\n * Searches for a mesh with userData.part === 'envelope'\n */\n protected findEnvelopeMesh(object3D: THREE.Object3D): THREE.Mesh | null {\n let envelopeMesh: THREE.Mesh | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === 'envelope') {\n envelopeMesh = child as THREE.Mesh;\n }\n });\n return envelopeMesh;\n }\n\n /**\n * Find the hole mesh within the component group\n *\n * @param object3D - The Object3D group created by createVisual()\n * @returns The hole mesh if found, null otherwise\n *\n * @remarks\n * Searches for a mesh with userData.part === 'hole'\n */\n protected findHoleMesh(object3D: THREE.Object3D): THREE.Mesh | null {\n let holeMesh: THREE.Mesh | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === 'hole') {\n holeMesh = child as THREE.Mesh;\n }\n });\n return holeMesh;\n }\n\n /**\n * Find the negative marker mesh within the component group\n *\n * @param object3D - The Object3D group created by createVisual()\n * @returns The negative marker mesh if found, null otherwise\n *\n * @remarks\n * Searches for a mesh with userData.part === 'negativeMarker'\n */\n protected findNegativeMarkerMesh(object3D: THREE.Object3D): THREE.Mesh | null {\n let negativeMarkerMesh: THREE.Mesh | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === 'negativeMarker') {\n negativeMarkerMesh = child as THREE.Mesh;\n }\n });\n return negativeMarkerMesh;\n }\n}\n","import { ComponentType, type Component } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport { AndGateGeometry, AndGateHoleGeometry } from '../../utils/GeometryUtils';\nimport { NandGateVisualFactory } from './NandGateVisualFactory';\nimport type { VisualContext } from '../../types';\nimport { CmpMatCategory } from '../types';\n\n/**\n * Visual factory for NAND gates components\n *\n * Creates:\n * - Gate mesh\n * - vcc, gnd, inputs and output pin groups\n * - Component hitbox for raycasting\n *\n * Animation:\n * - Emissive glow when Gate is high (based on simulation state)\n */\nexport class Nand4GateVisualFactory extends NandGateVisualFactory {\n /** Shared open envelope geometry */\n protected override readonly ENVELOPE_GEOM = AndGateGeometry(2, 3.6, 0.13, 0.4, 16);\n /** Shared inner hole geometry */\n protected override readonly HOLE_GEOM = AndGateHoleGeometry(2, 3.6, 0.13, 0.4, 16)!;\n /** Shared geometry for negative marker **/\n protected override readonly NEG_MARKER_GEOM = new THREE.CylinderGeometry(\n 0.25,\n 0.25,\n 0.4,\n 16,\n 4,\n false,\n 0,\n Math.PI * 2\n );\n\n constructor() {\n super();\n this._componentType = ComponentType.Nand4Gate;\n }\n\n override createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n // Root group (not rendered, just organizational)\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n // Component hitbox (invisible, raycastable)\n const hitbox = this.createComponentHitbox(component.id, group.id, 2.5, 2, 3.8);\n group.add(hitbox);\n\n // Visual Gate\n const envelope = new THREE.Mesh(this.ENVELOPE_GEOM, this.getMat(CmpMatCategory.WHITE));\n envelope.userData = {\n type: 'component',\n componentId: component.id,\n part: 'envelope',\n };\n envelope.rotateX(-Math.PI / 2);\n envelope.rotateY(Math.PI);\n envelope.position.set(-0.25, 0.35, 0);\n group.add(envelope);\n\n const hole = new THREE.Mesh(this.HOLE_GEOM, this.getMat(CmpMatCategory.DARK_GRAY));\n hole.name = 'hole'; // required for AnimationMixer property binding\n hole.userData = {\n type: 'component',\n componentId: component.id,\n part: 'hole',\n initialState: 'low',\n };\n hole.rotateX(-Math.PI / 2);\n hole.rotateY(Math.PI);\n hole.position.set(-0.25, 0.35, 0);\n group.add(hole);\n\n // pins (not called if preview - no pins)\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n this.updateFromConfiguration(group, component.config);\n return group;\n }\n\n protected override createPinsVisual(\n component: Component,\n context: VisualContext,\n group: THREE.Group\n ) {\n const vccNode = context.getENode(component.pins[0]!);\n if (vccNode) {\n const vccGroup = this.createPinGroup(vccNode, 'bottom', new THREE.Euler(0, 0, 0.23));\n vccGroup.position.set(0.15, 0, 1.73);\n group.add(vccGroup);\n }\n\n const gndNode = context.getENode(component.pins[6]!);\n if (gndNode) {\n const gndGroup = this.createPinGroup(gndNode, 'top', new THREE.Euler(0, 0, 0.23));\n gndGroup.position.set(0.15, 0, -1.73);\n group.add(gndGroup);\n }\n\n const input1Node = context.getENode(component.pins[1]!);\n if (input1Node) {\n const input1Group = this.createPinGroup(input1Node, 'right');\n input1Group.position.set(0.75, 0, 1.5);\n group.add(input1Group);\n }\n\n const input2Node = context.getENode(component.pins[2]!);\n if (input2Node) {\n const input2Group = this.createPinGroup(input2Node, 'right');\n input2Group.position.set(0.75, 0, 0.5);\n group.add(input2Group);\n }\n\n const input3Node = context.getENode(component.pins[3]!);\n if (input3Node) {\n const input3Group = this.createPinGroup(input3Node, 'right');\n input3Group.position.set(0.75, 0, -0.5);\n group.add(input3Group);\n }\n\n const input4Node = context.getENode(component.pins[4]!);\n if (input4Node) {\n const input4Group = this.createPinGroup(input4Node, 'right');\n input4Group.position.set(0.75, 0, -1.5);\n group.add(input4Group);\n }\n\n const outputNode = context.getENode(component.pins[5]!);\n if (outputNode) {\n const outputGroup = this.createPinGroup(outputNode, 'left');\n outputGroup.position.set(-1.22, 0, 0);\n group.add(outputGroup);\n }\n }\n\n override updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>) {\n const holeMesh = this.findHoleMesh(object3D);\n if (!holeMesh) return;\n\n let negativeMarkerMesh = this.findNegativeMarkerMesh(object3D);\n\n if (config.get('activationLogic') === 'negative') {\n holeMesh.userData.initialState = 'high';\n if (!negativeMarkerMesh) {\n negativeMarkerMesh = new THREE.Mesh(\n this.NEG_MARKER_GEOM,\n this.getMat(CmpMatCategory.WHITE)\n );\n negativeMarkerMesh.userData = {\n type: 'component',\n componentId: holeMesh.userData.componentId,\n part: 'negativeMarker',\n };\n\n negativeMarkerMesh.position.set(-1, 0.15, -1.3);\n object3D.add(negativeMarkerMesh);\n }\n } else {\n holeMesh.userData.initialState = 'low';\n if (negativeMarkerMesh) {\n object3D.remove(negativeMarkerMesh);\n }\n }\n }\n}\n","import { ComponentType, type Component } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport { AndGateGeometry, AndGateHoleGeometry } from '../../utils/GeometryUtils';\nimport { NandGateVisualFactory } from './NandGateVisualFactory';\nimport type { VisualContext } from '../../types';\nimport { CmpMatCategory } from '../types';\n\n/**\n * Visual factory for NAND gates components\n *\n * Creates:\n * - Gate mesh\n * - vcc, gnd, inputs and output pin groups\n * - Component hitbox for raycasting\n *\n * Animation:\n * - Emissive glow when Gate is high (based on simulation state)\n */\nexport class Nand8GateVisualFactory extends NandGateVisualFactory {\n /** Shared open envelope geometry */\n protected override readonly ENVELOPE_GEOM = AndGateGeometry(2.6, 7.5, 0.16, 0.4, 16);\n /** Shared inner hole geometry */\n protected override readonly HOLE_GEOM = AndGateHoleGeometry(2.6, 7.5, 0.16, 0.4, 16)!;\n /** Shared geometry for negative marker **/\n protected override readonly NEG_MARKER_GEOM = new THREE.CylinderGeometry(\n 0.35,\n 0.35,\n 0.4,\n 16,\n 4,\n false,\n 0,\n Math.PI * 2\n );\n\n constructor() {\n super();\n this._componentType = ComponentType.Nand8Gate;\n }\n\n override createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n // Root group (not rendered, just organizational)\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n // Component hitbox (invisible, raycastable)\n const hitbox = this.createComponentHitbox(component.id, group.id, 3, 2, 7.6);\n group.add(hitbox);\n\n // Visual Gate\n const envelope = new THREE.Mesh(this.ENVELOPE_GEOM, this.getMat(CmpMatCategory.WHITE));\n envelope.userData = {\n type: 'component',\n componentId: component.id,\n part: 'envelope',\n };\n envelope.rotateX(-Math.PI / 2);\n envelope.rotateY(Math.PI);\n envelope.position.set(-0.1, 0.35, 0);\n group.add(envelope);\n\n const hole = new THREE.Mesh(this.HOLE_GEOM, this.getMat(CmpMatCategory.DARK_GRAY));\n hole.name = 'hole'; // required for AnimationMixer property binding\n hole.userData = {\n type: 'component',\n componentId: component.id,\n part: 'hole',\n initialState: 'low',\n };\n hole.rotateX(-Math.PI / 2);\n hole.rotateY(Math.PI);\n hole.position.set(-0.1, 0.35, 0);\n group.add(hole);\n\n // pins (not called if preview - no pins)\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n this.updateFromConfiguration(group, component.config);\n return group;\n }\n\n protected override createPinsVisual(\n component: Component,\n context: VisualContext,\n group: THREE.Group\n ) {\n const vccNode = context.getENode(component.pins[0]!);\n if (vccNode) {\n const vccGroup = this.createPinGroup(vccNode, 'bottom', new THREE.Euler(0, 0, 0.5));\n vccGroup.position.set(0.7, 0, 3.5);\n group.add(vccGroup);\n }\n\n const gndNode = context.getENode(component.pins[10]!);\n if (gndNode) {\n const gndGroup = this.createPinGroup(gndNode, 'top', new THREE.Euler(0, 0, 0.5));\n gndGroup.position.set(0.7, 0, -3.5);\n group.add(gndGroup);\n }\n\n const input1Node = context.getENode(component.pins[1]!);\n if (input1Node) {\n const input1Group = this.createPinGroup(input1Node, 'right');\n input1Group.position.set(1.2, 0, 3.45);\n group.add(input1Group);\n }\n\n const input2Node = context.getENode(component.pins[2]!);\n if (input2Node) {\n const input2Group = this.createPinGroup(input2Node, 'right');\n input2Group.position.set(1.2, 0, 2.5);\n group.add(input2Group);\n }\n\n const input3Node = context.getENode(component.pins[3]!);\n if (input3Node) {\n const input3Group = this.createPinGroup(input3Node, 'right');\n input3Group.position.set(1.2, 0, 1.5);\n group.add(input3Group);\n }\n\n const input4Node = context.getENode(component.pins[4]!);\n if (input4Node) {\n const input4Group = this.createPinGroup(input4Node, 'right');\n input4Group.position.set(1.2, 0, 0.5);\n group.add(input4Group);\n }\n\n const input5Node = context.getENode(component.pins[5]!);\n if (input5Node) {\n const input5Group = this.createPinGroup(input5Node, 'right');\n input5Group.position.set(1.2, 0, -0.5);\n group.add(input5Group);\n }\n\n const input6Node = context.getENode(component.pins[6]!);\n if (input6Node) {\n const input6Group = this.createPinGroup(input6Node, 'right');\n input6Group.position.set(1.2, 0, -1.5);\n group.add(input6Group);\n }\n\n const input7Node = context.getENode(component.pins[7]!);\n if (input7Node) {\n const input7Group = this.createPinGroup(input7Node, 'right');\n input7Group.position.set(1.2, 0, -2.5);\n group.add(input7Group);\n }\n\n const input8Node = context.getENode(component.pins[8]!);\n if (input8Node) {\n const input8Group = this.createPinGroup(input8Node, 'right');\n input8Group.position.set(1.2, 0, -3.45);\n group.add(input8Group);\n }\n\n const outputNode = context.getENode(component.pins[9]!);\n if (outputNode) {\n const outputGroup = this.createPinGroup(outputNode, 'left');\n outputGroup.position.set(-1.39, 0, -0.05);\n group.add(outputGroup);\n }\n }\n\n override updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>) {\n const holeMesh = this.findHoleMesh(object3D);\n if (!holeMesh) return;\n\n let negativeMarkerMesh = this.findNegativeMarkerMesh(object3D);\n\n if (config.get('activationLogic') === 'negative') {\n holeMesh.userData.initialState = 'high';\n if (!negativeMarkerMesh) {\n negativeMarkerMesh = new THREE.Mesh(\n this.NEG_MARKER_GEOM,\n this.getMat(CmpMatCategory.WHITE)\n );\n negativeMarkerMesh.userData = {\n type: 'component',\n componentId: holeMesh.userData.componentId,\n part: 'negativeMarker',\n };\n\n negativeMarkerMesh.position.set(-0.8, 0.15, -2.7);\n object3D.add(negativeMarkerMesh);\n }\n } else {\n holeMesh.userData.initialState = 'low';\n if (negativeMarkerMesh) {\n object3D.remove(negativeMarkerMesh);\n }\n }\n }\n}\n","import { ComponentVisualFactoryBase } from '../ComponentVisualFactory';\nimport { ComponentType, type Component, type ComponentState } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport { OrGateGeometry, OrGateHoleGeometry } from '../../utils/GeometryUtils';\nimport type { ConfigFormDefinition, VisualContext } from '../../types';\nimport { CmpMatCategory, CmpMatType } from '../types';\n\n/**\n * Visual factory for NOR gates components\n *\n * Creates:\n * - Gate mesh\n * - vcc, gnd, inputs and output pin groups\n * - Component hitbox for raycasting\n *\n * Animation:\n * - Emissive glow when Gate is high (based on simulation state)\n */\nexport class NorGateVisualFactory extends ComponentVisualFactoryBase {\n /** Shared envelope geometry */\n protected readonly ENVELOPE_GEOM = OrGateGeometry(1.5, 1.6, 0.1, 0.4, 16);\n /** Shared inner hole geometry */\n protected readonly HOLE_GEOM = OrGateHoleGeometry(1.5, 1.6, 0.1, 0.4, 16)!;\n /** Shared geometry for negative marker **/\n protected readonly NEG_MARKER_GEOM = new THREE.CylinderGeometry(\n 0.2,\n 0.2,\n 0.4,\n 16,\n 4,\n false,\n 0,\n Math.PI * 2\n );\n\n protected readonly HOLE_COLOR_HIGH = new THREE.Color(0xff4444);\n protected readonly HOLE_COLOR_LOW = new THREE.Color(0x4444ff);\n protected readonly HOLE_EMISSIVE_HIGH_INTENSITY = 0.5;\n protected readonly HOLE_EMISSIVE_LOW_INTENSITY = 0.2;\n\n constructor() {\n super();\n this._componentType = ComponentType.NorGate;\n }\n\n override defaultRotation() {\n return Math.PI;\n }\n\n createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n // Component hitbox (invisible, raycastable)\n const hitbox = this.createComponentHitbox(component.id, group.id, 2, 2, 1.8);\n group.add(hitbox);\n\n // Visual Gate\n const envelope = new THREE.Mesh(this.ENVELOPE_GEOM, this.getMat(CmpMatCategory.WHITE));\n envelope.userData = {\n type: 'component',\n componentId: component.id,\n part: 'envelope',\n };\n envelope.rotateX(-Math.PI / 2);\n envelope.rotateY(Math.PI);\n envelope.position.set(0, 0.35, 0);\n group.add(envelope);\n\n const hole = new THREE.Mesh(this.HOLE_GEOM, this.getMat(CmpMatCategory.DARK_GRAY));\n hole.name = 'hole'; // required for AnimationMixer property binding\n hole.userData = {\n type: 'component',\n componentId: component.id,\n part: 'hole',\n initialState: 'low',\n };\n hole.rotateX(-Math.PI / 2);\n hole.rotateY(Math.PI);\n hole.position.set(0, 0.35, 0);\n group.add(hole);\n\n // pins (not called if preview - no pins)\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n this.updateFromConfiguration(group, component.config);\n return group;\n }\n\n protected createPinsVisual(component: Component, context: VisualContext, group: THREE.Group) {\n const vccNode = context.getENode(component.pins[0]!);\n if (vccNode) {\n const vccGroup = this.createPinGroup(vccNode, 'bottom');\n vccGroup.position.set(0.15, 0, 0.79);\n group.add(vccGroup);\n }\n\n const gndNode = context.getENode(component.pins[4]!);\n if (gndNode) {\n const gndGroup = this.createPinGroup(gndNode, 'top');\n gndGroup.position.set(0.15, 0, -0.79);\n group.add(gndGroup);\n }\n\n const input1Node = context.getENode(component.pins[1]!);\n if (input1Node) {\n const input1Group = this.createPinGroup(input1Node, 'right', new THREE.Euler(0.45, 0, 0));\n input1Group.position.set(0.54, 0, 0.5);\n group.add(input1Group);\n }\n\n const input2Node = context.getENode(component.pins[2]!);\n if (input2Node) {\n const input2Group = this.createPinGroup(input2Node, 'right', new THREE.Euler(-0.45, 0, 0));\n input2Group.position.set(0.54, 0, -0.5);\n group.add(input2Group);\n }\n\n const outputNode = context.getENode(component.pins[3]!);\n if (outputNode) {\n const outputGroup = this.createPinGroup(outputNode, 'left');\n outputGroup.position.set(-0.7, 0, 0);\n group.add(outputGroup);\n }\n }\n\n /**\n * Get config form definition\n *\n * @param config - Optional current config to determine disabled state of transitionSpan\n * @returns Form definition with defaultLogicFamily dropdown, activationLogic boolean, and transitionSpan number\n */\n override getConfigFormDefinition(config?: Map<string, string>): ConfigFormDefinition | null {\n const logicFamily = config?.get('defaultLogicFamily') ?? 'CMOS1';\n return {\n fields: [\n {\n key: 'defaultLogicFamily',\n type: 'dropdown',\n options: { CMOS: 'CMOS1', TTL: 'TTL1', Sandbox: 'Sandbox' },\n },\n { key: 'activationLogic', type: 'boolean' },\n { key: 'transitionSpan', type: 'number', min: 1, disabled: logicFamily !== 'Sandbox' },\n { key: 'initializationOrder', type: 'number' },\n ],\n };\n }\n\n /**\n * Map core config to form data\n * Converts \"positive\"/\"negative\" strings to boolean\n *\n * @param config - Core component config\n * @returns Form data with boolean activationLogic\n */\n override mapCoreConfigToForm(config: Map<string, string>): Map<string, any> {\n const formData = new Map<string, any>();\n formData.set('defaultLogicFamily', config.get('defaultLogicFamily') ?? 'CMOS1');\n const activationLogic = config.get('activationLogic');\n formData.set('activationLogic', activationLogic === 'positive');\n formData.set('transitionSpan', parseFloat(config.get('transitionSpan') || '1'));\n formData.set('initializationOrder', parseFloat(config.get('initializationOrder') || '0'));\n return formData;\n }\n\n /**\n * Map form data to core config\n * Converts boolean to \"positive\"/\"negative\" strings\n *\n * @param formData - Form data with boolean activationLogic\n * @returns Core config with string activationLogic\n */\n override mapFormToCoreConfig(formData: Map<string, any>): Map<string, string> {\n const config = new Map<string, string>();\n config.set('defaultLogicFamily', formData.get('defaultLogicFamily') ?? 'CMOS1');\n const activationLogic = formData.get('activationLogic');\n config.set('activationLogic', activationLogic ? 'positive' : 'negative');\n config.set('transitionSpan', formData.get('transitionSpan').toString());\n config.set('initializationOrder', formData.get('initializationOrder').toString() || null);\n return config;\n }\n\n override updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>) {\n const holeMesh = this.findHoleMesh(object3D);\n if (!holeMesh) return;\n\n let negativeMarkerMesh = this.findNegativeMarkerMesh(object3D);\n\n if (config.get('activationLogic') === 'negative') {\n holeMesh.userData.initialState = 'high';\n if (!negativeMarkerMesh) {\n negativeMarkerMesh = new THREE.Mesh(\n this.NEG_MARKER_GEOM,\n this.getMat(CmpMatCategory.WHITE)\n );\n negativeMarkerMesh.userData = {\n type: 'component',\n componentId: holeMesh.userData.componentId,\n part: 'negativeMarker',\n };\n\n negativeMarkerMesh.position.set(-0.7, 0.15, -0.65);\n object3D.add(negativeMarkerMesh);\n }\n } else {\n holeMesh.userData.initialState = 'low';\n if (negativeMarkerMesh) {\n object3D.remove(negativeMarkerMesh);\n }\n }\n }\n\n /**\n * Update animation based on simulation state\n *\n * @param object3D - The Object3D created by createVisual()\n * @param state - The component current simulation state\n */\n override updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void {\n const holeMesh = this.findHoleMesh(object3D);\n if (!holeMesh) return;\n\n if (!state || !this._animationContext || state.state === 'indeterminate') {\n this._cleanupMixer(object3D);\n this._restoreSharedHoleMaterial(holeMesh);\n return;\n }\n\n if (state.state === 'high') {\n this._cleanupMixer(object3D);\n this._setHoleColor(holeMesh, this.HOLE_COLOR_HIGH, this.HOLE_EMISSIVE_HIGH_INTENSITY);\n return;\n }\n\n if (state.state === 'low') {\n this._cleanupMixer(object3D);\n this._setHoleColor(holeMesh, this.HOLE_COLOR_LOW, this.HOLE_EMISSIVE_LOW_INTENSITY);\n return;\n }\n\n // Paused + transitional: snap to start color (before the transition began)\n if (this._animationContext.simulationStatus !== 'playing') {\n if (state.state === 'rising') {\n this._setHoleColor(holeMesh, this.HOLE_COLOR_LOW, this.HOLE_EMISSIVE_LOW_INTENSITY);\n } else {\n // falling\n this._setHoleColor(holeMesh, this.HOLE_COLOR_HIGH, this.HOLE_EMISSIVE_HIGH_INTENSITY);\n }\n return;\n }\n\n // Playing + transitional: animate\n if (state.hasExpiration) {\n this._animateHoleColor(object3D, holeMesh, state);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Hole material helpers\n // ---------------------------------------------------------------------------\n\n private _setHoleColor(holeMesh: THREE.Mesh, color: THREE.Color, emissiveIntensity: number): void {\n this._ensureClonedHoleMaterial(holeMesh);\n const mat = holeMesh.material as THREE.MeshLambertMaterial;\n mat.color.copy(color);\n mat.emissive.copy(color);\n mat.emissiveIntensity = emissiveIntensity;\n }\n\n private _ensureClonedHoleMaterial(holeMesh: THREE.Mesh): void {\n const mat = holeMesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType === CmpMatType.ANIMATION_CLONE) return;\n holeMesh.material = this.getMat(CmpMatCategory.DARK_GRAY).clone();\n (holeMesh.material as THREE.MeshLambertMaterial).userData.matType = CmpMatType.ANIMATION_CLONE;\n }\n\n private _restoreSharedHoleMaterial(holeMesh: THREE.Mesh): void {\n const mat = holeMesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType !== CmpMatType.ANIMATION_CLONE) return;\n mat.dispose();\n holeMesh.material = this.getMat(CmpMatCategory.DARK_GRAY);\n }\n\n private _cleanupMixer(object3D: THREE.Object3D): void {\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n if (mixer) {\n mixer.stopAllAction();\n mixer.uncacheRoot(object3D);\n delete object3D.userData.mixer;\n }\n delete object3D.userData.currentAction;\n delete object3D.userData.currentClip;\n delete object3D.userData.currentActionStart;\n }\n\n /**\n * Animate the hole color between LOW and HIGH over the transition span.\n * Reads current color for mid-transition support.\n */\n private _animateHoleColor(\n object3D: THREE.Object3D,\n holeMesh: THREE.Mesh,\n state: ComponentState\n ): void {\n if (object3D.userData.currentActionStart === state.startTick) return;\n\n const tps = this._animationContext!.ticksPerSecond;\n const span = state.expirationTick - state.startTick;\n const durationSeconds = span / tps;\n\n this._ensureClonedHoleMaterial(holeMesh);\n const mat = holeMesh.material as THREE.MeshLambertMaterial;\n\n const toColor = state.state === 'rising' ? this.HOLE_COLOR_HIGH : this.HOLE_COLOR_LOW;\n const toIntensity =\n state.state === 'rising'\n ? this.HOLE_EMISSIVE_HIGH_INTENSITY\n : this.HOLE_EMISSIVE_LOW_INTENSITY;\n\n const currentRGB = [mat.color.r, mat.color.g, mat.color.b];\n const endRGB = [toColor.r, toColor.g, toColor.b];\n const currentIntensity = mat.emissiveIntensity;\n\n let mixer: THREE.AnimationMixer = object3D.userData.mixer;\n if (!mixer) {\n mixer = new THREE.AnimationMixer(object3D);\n object3D.userData.mixer = mixer;\n }\n\n if (object3D.userData.currentAction) {\n (object3D.userData.currentAction as THREE.AnimationAction).stop();\n }\n if (object3D.userData.currentClip) {\n mixer.uncacheClip(object3D.userData.currentClip as THREE.AnimationClip);\n }\n\n const clip = new THREE.AnimationClip('holeColor', durationSeconds, [\n new THREE.ColorKeyframeTrack(\n 'hole.material.color',\n [0, durationSeconds],\n [...currentRGB, ...endRGB]\n ),\n new THREE.ColorKeyframeTrack(\n 'hole.material.emissive',\n [0, durationSeconds],\n [...currentRGB, ...endRGB]\n ),\n new THREE.NumberKeyframeTrack(\n 'hole.material.emissiveIntensity',\n [0, durationSeconds],\n [currentIntensity, toIntensity]\n ),\n ]);\n const action = mixer.clipAction(clip);\n action.loop = THREE.LoopOnce;\n action.clampWhenFinished = true;\n action.play();\n\n object3D.userData.currentActionStart = state.startTick;\n object3D.userData.currentAction = action;\n object3D.userData.currentClip = clip;\n }\n\n /**\n * Find the envelope mesh within the component group\n *\n * @param object3D - The Object3D group created by createVisual()\n * @returns The envelope mesh if found, null otherwise\n *\n * @remarks\n * Searches for a mesh with userData.part === 'envelope'\n */\n protected findEnvelopeMesh(object3D: THREE.Object3D): THREE.Mesh | null {\n let envelopeMesh: THREE.Mesh | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === 'envelope') {\n envelopeMesh = child as THREE.Mesh;\n }\n });\n return envelopeMesh;\n }\n\n /**\n * Find the hole mesh within the component group\n *\n * @param object3D - The Object3D group created by createVisual()\n * @returns The hole mesh if found, null otherwise\n *\n * @remarks\n * Searches for a mesh with userData.part === 'hole'\n */\n protected findHoleMesh(object3D: THREE.Object3D): THREE.Mesh | null {\n let holeMesh: THREE.Mesh | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === 'hole') {\n holeMesh = child as THREE.Mesh;\n }\n });\n return holeMesh;\n }\n\n /**\n * Find the negative marker mesh within the component group\n *\n * @param object3D - The Object3D group created by createVisual()\n * @returns The negative marker mesh if found, null otherwise\n *\n * @remarks\n * Searches for a mesh with userData.part === 'negativeMarker'\n */\n protected findNegativeMarkerMesh(object3D: THREE.Object3D): THREE.Mesh | null {\n let negativeMarkerMesh: THREE.Mesh | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === 'negativeMarker') {\n negativeMarkerMesh = child as THREE.Mesh;\n }\n });\n return negativeMarkerMesh;\n }\n}\n","import { ComponentType, type Component } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport { OrGateGeometry, OrGateHoleGeometry } from '../../utils/GeometryUtils';\nimport { NorGateVisualFactory } from './NorGateVisualFactory';\nimport type { VisualContext } from '../../types';\nimport { CmpMatCategory } from '../types';\n\n/**\n * Visual factory for NOR gates components\n *\n * Creates:\n * - Gate mesh\n * - vcc, gnd, inputs and output pin groups\n * - Component hitbox for raycasting\n *\n * Animation:\n * - Emissive glow when Gate is high (based on simulation state)\n */\nexport class Nor4GateVisualFactory extends NorGateVisualFactory {\n /** Shared open envelope geometry */\n protected override readonly ENVELOPE_GEOM = OrGateGeometry(2, 3.6, 0.13, 0.4, 16);\n /** Shared inner hole geometry */\n protected override readonly HOLE_GEOM = OrGateHoleGeometry(2, 3.6, 0.13, 0.4, 16)!;\n /** Shared geometry for negative marker **/\n protected override readonly NEG_MARKER_GEOM = new THREE.CylinderGeometry(\n 0.25,\n 0.25,\n 0.4,\n 16,\n 4,\n false,\n 0,\n Math.PI * 2\n );\n\n constructor() {\n super();\n this._componentType = ComponentType.Nor4Gate;\n }\n\n override createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n // Component hitbox (invisible, raycastable)\n const hitbox = this.createComponentHitbox(component.id, group.id, 2.5, 2, 3.8);\n group.add(hitbox);\n\n // Visual Gate\n const envelope = new THREE.Mesh(this.ENVELOPE_GEOM, this.getMat(CmpMatCategory.WHITE));\n envelope.userData = {\n type: 'component',\n componentId: component.id,\n part: 'envelope',\n };\n envelope.rotateX(-Math.PI / 2);\n envelope.rotateY(Math.PI);\n envelope.position.set(-0.25, 0.35, 0);\n group.add(envelope);\n\n const hole = new THREE.Mesh(this.HOLE_GEOM, this.getMat(CmpMatCategory.DARK_GRAY));\n hole.name = 'hole'; // required for AnimationMixer property binding\n hole.userData = {\n type: 'component',\n componentId: component.id,\n part: 'hole',\n initialState: 'low',\n };\n hole.rotateX(-Math.PI / 2);\n hole.rotateY(Math.PI);\n hole.position.set(-0.25, 0.35, 0);\n group.add(hole);\n\n // pins (not called if preview - no pins)\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n this.updateFromConfiguration(group, component.config);\n return group;\n }\n\n protected override createPinsVisual(\n component: Component,\n context: VisualContext,\n group: THREE.Group\n ) {\n const vccNode = context.getENode(component.pins[0]!);\n if (vccNode) {\n const vccGroup = this.createPinGroup(vccNode, 'bottom', new THREE.Euler(0, 0, 0.23));\n vccGroup.position.set(0.15, 0, 1.73);\n group.add(vccGroup);\n }\n\n const gndNode = context.getENode(component.pins[6]!);\n if (gndNode) {\n const gndGroup = this.createPinGroup(gndNode, 'top', new THREE.Euler(0, 0, 0.23));\n gndGroup.position.set(0.15, 0, -1.73);\n group.add(gndGroup);\n }\n\n const input1Node = context.getENode(component.pins[1]!);\n if (input1Node) {\n const input1Group = this.createPinGroup(input1Node, 'right', new THREE.Euler(0.6, 0, 0));\n input1Group.position.set(0.51, 0, 1.5);\n group.add(input1Group);\n }\n\n const input2Node = context.getENode(component.pins[2]!);\n if (input2Node) {\n const input2Group = this.createPinGroup(input2Node, 'right', new THREE.Euler(0.1, 0, 0));\n input2Group.position.set(0.07, 0, 0.5);\n group.add(input2Group);\n }\n\n const input3Node = context.getENode(component.pins[3]!);\n if (input3Node) {\n const input3Group = this.createPinGroup(input3Node, 'right', new THREE.Euler(-0.1, 0, 0));\n input3Group.position.set(0.07, 0, -0.5);\n group.add(input3Group);\n }\n\n const input4Node = context.getENode(component.pins[4]!);\n if (input4Node) {\n const input4Group = this.createPinGroup(input4Node, 'right', new THREE.Euler(-0.6, 0, 0));\n input4Group.position.set(0.51, 0, -1.5);\n group.add(input4Group);\n }\n\n const outputNode = context.getENode(component.pins[5]!);\n if (outputNode) {\n const outputGroup = this.createPinGroup(outputNode, 'left');\n outputGroup.position.set(-1.22, 0, 0);\n group.add(outputGroup);\n }\n }\n\n override updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>) {\n const holeMesh = this.findHoleMesh(object3D);\n if (!holeMesh) return;\n\n let negativeMarkerMesh = this.findNegativeMarkerMesh(object3D);\n\n if (config.get('activationLogic') === 'negative') {\n holeMesh.userData.initialState = 'high';\n if (!negativeMarkerMesh) {\n negativeMarkerMesh = new THREE.Mesh(\n this.NEG_MARKER_GEOM,\n this.getMat(CmpMatCategory.WHITE)\n );\n negativeMarkerMesh.userData = {\n type: 'component',\n componentId: holeMesh.userData.componentId,\n part: 'negativeMarker',\n };\n\n negativeMarkerMesh.position.set(-1, 0.15, -1.3);\n object3D.add(negativeMarkerMesh);\n }\n } else {\n holeMesh.userData.initialState = 'low';\n if (negativeMarkerMesh) {\n object3D.remove(negativeMarkerMesh);\n }\n }\n }\n}\n","import { ComponentType, type Component } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport { OrGateGeometry, OrGateHoleGeometry } from '../../utils/GeometryUtils';\nimport { NorGateVisualFactory } from './NorGateVisualFactory';\nimport type { VisualContext } from '../../types';\nimport { CmpMatCategory } from '../types';\n\n/**\n * Visual factory for NOR gates components\n *\n * Creates:\n * - Gate mesh\n * - vcc, gnd, inputs and output pin groups\n * - Component hitbox for raycasting\n *\n * Animation:\n * - Emissive glow when Gate is high (based on simulation state)\n */\nexport class Nor8GateVisualFactory extends NorGateVisualFactory {\n /** Shared open envelope geometry */\n protected override readonly ENVELOPE_GEOM = OrGateGeometry(2.6, 7.5, 0.16, 0.4, 16);\n /** Shared inner hole geometry */\n protected override readonly HOLE_GEOM = OrGateHoleGeometry(2.6, 7.5, 0.16, 0.4, 16)!;\n /** Shared geometry for negative marker **/\n protected override readonly NEG_MARKER_GEOM = new THREE.CylinderGeometry(\n 0.35,\n 0.35,\n 0.4,\n 16,\n 4,\n false,\n 0,\n Math.PI * 2\n );\n\n constructor() {\n super();\n this._componentType = ComponentType.Nor8Gate;\n }\n\n override createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n // Component hitbox (invisible, raycastable)\n const hitbox = this.createComponentHitbox(component.id, group.id, 3, 2, 7.6);\n group.add(hitbox);\n\n // Visual Gate\n const envelope = new THREE.Mesh(this.ENVELOPE_GEOM, this.getMat(CmpMatCategory.WHITE));\n envelope.userData = {\n type: 'component',\n componentId: component.id,\n part: 'envelope',\n };\n envelope.rotateX(-Math.PI / 2);\n envelope.rotateY(Math.PI);\n envelope.position.set(-0.1, 0.35, 0);\n group.add(envelope);\n\n const hole = new THREE.Mesh(this.HOLE_GEOM, this.getMat(CmpMatCategory.DARK_GRAY));\n hole.name = 'hole'; // required for AnimationMixer property binding\n hole.userData = {\n type: 'component',\n componentId: component.id,\n part: 'hole',\n initialState: 'low',\n };\n hole.rotateX(-Math.PI / 2);\n hole.rotateY(Math.PI);\n hole.position.set(-0.1, 0.35, 0);\n group.add(hole);\n\n // pins (not called if preview - no pins)\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n this.updateFromConfiguration(group, component.config);\n return group;\n }\n\n protected override createPinsVisual(\n component: Component,\n context: VisualContext,\n group: THREE.Group\n ) {\n const vccNode = context.getENode(component.pins[0]!);\n if (vccNode) {\n const vccGroup = this.createPinGroup(vccNode, 'bottom', new THREE.Euler(0, 0, 0.5));\n vccGroup.position.set(0.7, 0, 3.5);\n group.add(vccGroup);\n }\n\n const gndNode = context.getENode(component.pins[10]!);\n if (gndNode) {\n const gndGroup = this.createPinGroup(gndNode, 'top', new THREE.Euler(0, 0, 0.5));\n gndGroup.position.set(0.7, 0, -3.5);\n group.add(gndGroup);\n }\n\n const input1Node = context.getENode(component.pins[1]!);\n if (input1Node) {\n const input1Group = this.createPinGroup(input1Node, 'right', new THREE.Euler(0.7, 0, 0));\n input1Group.position.set(0.9, 0, 3.45);\n group.add(input1Group);\n }\n\n const input2Node = context.getENode(component.pins[2]!);\n if (input2Node) {\n const input2Group = this.createPinGroup(input2Node, 'right', new THREE.Euler(0.53, 0, 0));\n input2Group.position.set(0.31, 0, 2.5);\n group.add(input2Group);\n }\n\n const input3Node = context.getENode(component.pins[3]!);\n if (input3Node) {\n const input3Group = this.createPinGroup(input3Node, 'right', new THREE.Euler(0.3, 0, 0));\n input3Group.position.set(-0.09, 0, 1.5);\n group.add(input3Group);\n }\n\n const input4Node = context.getENode(component.pins[4]!);\n if (input4Node) {\n const input4Group = this.createPinGroup(input4Node, 'right', new THREE.Euler(0.11, 0, 0));\n input4Group.position.set(-0.27, 0, 0.5);\n group.add(input4Group);\n }\n\n const input5Node = context.getENode(component.pins[5]!);\n if (input5Node) {\n const input5Group = this.createPinGroup(input5Node, 'right', new THREE.Euler(-0.11, 0, 0));\n input5Group.position.set(-0.27, 0, -0.5);\n group.add(input5Group);\n }\n\n const input6Node = context.getENode(component.pins[6]!);\n if (input6Node) {\n const input6Group = this.createPinGroup(input6Node, 'right', new THREE.Euler(-0.3, 0, 0));\n input6Group.position.set(-0.09, 0, -1.5);\n group.add(input6Group);\n }\n\n const input7Node = context.getENode(component.pins[7]!);\n if (input7Node) {\n const input7Group = this.createPinGroup(input7Node, 'right', new THREE.Euler(-0.53, 0, 0));\n input7Group.position.set(0.31, 0, -2.5);\n group.add(input7Group);\n }\n\n const input8Node = context.getENode(component.pins[8]!);\n if (input8Node) {\n const input8Group = this.createPinGroup(input8Node, 'right', new THREE.Euler(-0.7, 0, 0));\n input8Group.position.set(0.9, 0, -3.45);\n group.add(input8Group);\n }\n\n const outputNode = context.getENode(component.pins[9]!);\n if (outputNode) {\n const outputGroup = this.createPinGroup(outputNode, 'left');\n outputGroup.position.set(-1.39, 0, -0.05);\n group.add(outputGroup);\n }\n }\n\n override updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>) {\n const holeMesh = this.findHoleMesh(object3D);\n if (!holeMesh) return;\n\n let negativeMarkerMesh = this.findNegativeMarkerMesh(object3D);\n\n if (config.get('activationLogic') === 'negative') {\n holeMesh.userData.initialState = 'high';\n if (!negativeMarkerMesh) {\n negativeMarkerMesh = new THREE.Mesh(\n this.NEG_MARKER_GEOM,\n this.getMat(CmpMatCategory.WHITE)\n );\n negativeMarkerMesh.userData = {\n type: 'component',\n componentId: holeMesh.userData.componentId,\n part: 'negativeMarker',\n };\n\n negativeMarkerMesh.position.set(-0.8, 0.15, -2.7);\n object3D.add(negativeMarkerMesh);\n }\n } else {\n holeMesh.userData.initialState = 'low';\n if (negativeMarkerMesh) {\n object3D.remove(negativeMarkerMesh);\n }\n }\n }\n}\n","import { ComponentVisualFactoryBase } from '../ComponentVisualFactory';\nimport { ComponentType, type Component, type ComponentState } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport { OrGateGeometry, OrGateHoleGeometry, XorGateTailGeometry } from '../../utils/GeometryUtils';\nimport type { ConfigFormDefinition, VisualContext } from '../../types';\nimport { CmpMatCategory, CmpMatType } from '../types';\n\n/**\n * Visual factory for XOR gates components\n *\n * Creates:\n * - Gate mesh\n * - vcc, gnd, inputs and output pin groups\n * - Component hitbox for raycasting\n *\n * Animation:\n * - Emissive glow when Gate is high (based on simulation state)\n */\nexport class XorGateVisualFactory extends ComponentVisualFactoryBase {\n /** XOR tail geometry */\n protected readonly TAIL_GEOM = XorGateTailGeometry(1.5, 1.6, 0.8, 0.1, 0.8, 0.4, 16);\n /** Shared open envelope geometry */\n protected readonly ENVELOPE_GEOM = OrGateGeometry(1.5, 1.6, 0.1, 0.4, 16);\n /** Shared inner hole geometry */\n protected readonly HOLE_GEOM = OrGateHoleGeometry(1.5, 1.6, 0.1, 0.4, 16)!;\n /** Shared geometry for negative marker **/\n protected readonly NEG_MARKER_GEOM = new THREE.CylinderGeometry(\n 0.2,\n 0.2,\n 0.4,\n 16,\n 4,\n false,\n 0,\n Math.PI * 2\n );\n\n protected readonly HOLE_COLOR_HIGH = new THREE.Color(0xff4444);\n protected readonly HOLE_COLOR_LOW = new THREE.Color(0x4444ff);\n protected readonly HOLE_EMISSIVE_HIGH_INTENSITY = 0.5;\n protected readonly HOLE_EMISSIVE_LOW_INTENSITY = 0.2;\n\n constructor() {\n super();\n this._componentType = ComponentType.XorGate;\n }\n\n override defaultRotation() {\n return Math.PI;\n }\n\n createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n // Component hitbox (invisible, raycastable)\n const hitbox = this.createComponentHitbox(component.id, group.id, 2.4, 2, 1.8);\n group.add(hitbox);\n\n // Visual Gate\n const envelope = new THREE.Mesh(this.ENVELOPE_GEOM, this.getMat(CmpMatCategory.WHITE));\n envelope.userData = {\n type: 'component',\n componentId: component.id,\n part: 'envelope',\n };\n envelope.rotateX(-Math.PI / 2);\n envelope.rotateY(Math.PI);\n envelope.position.set(0, 0.35, 0);\n group.add(envelope);\n\n const hole = new THREE.Mesh(this.HOLE_GEOM, this.getMat(CmpMatCategory.DARK_GRAY));\n hole.name = 'hole'; // required for AnimationMixer property binding\n hole.userData = {\n type: 'component',\n componentId: component.id,\n part: 'hole',\n initialState: 'low',\n };\n hole.rotateX(-Math.PI / 2);\n hole.rotateY(Math.PI);\n hole.position.set(0, 0.35, 0);\n group.add(hole);\n\n const tail = new THREE.Mesh(this.TAIL_GEOM, this.getMat(CmpMatCategory.WHITE));\n tail.userData = {\n type: 'component',\n componentId: component.id,\n part: 'tail',\n };\n tail.rotateX(-Math.PI / 2);\n tail.rotateY(Math.PI);\n tail.position.set(-0.25, 0.35, 0);\n group.add(tail);\n\n // pins (not called if preview - no pins)\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n this.updateFromConfiguration(group, component.config);\n return group;\n }\n\n protected createPinsVisual(component: Component, context: VisualContext, group: THREE.Group) {\n const vccNode = context.getENode(component.pins[0]!);\n if (vccNode) {\n const vccGroup = this.createPinGroup(vccNode, 'bottom');\n vccGroup.position.set(0.15, 0, 0.79);\n group.add(vccGroup);\n }\n\n const gndNode = context.getENode(component.pins[4]!);\n if (gndNode) {\n const gndGroup = this.createPinGroup(gndNode, 'top');\n gndGroup.position.set(0.15, 0, -0.79);\n group.add(gndGroup);\n }\n\n const input1Node = context.getENode(component.pins[1]!);\n if (input1Node) {\n const input1Group = this.createPinGroup(input1Node, 'right', new THREE.Euler(0.45, 0, 0));\n input1Group.position.set(1.1, 0, 0.5);\n group.add(input1Group);\n }\n\n const input2Node = context.getENode(component.pins[2]!);\n if (input2Node) {\n const input2Group = this.createPinGroup(input2Node, 'right', new THREE.Euler(-0.45, 0, 0));\n input2Group.position.set(1.1, 0, -0.5);\n group.add(input2Group);\n }\n\n const outputNode = context.getENode(component.pins[3]!);\n if (outputNode) {\n const outputGroup = this.createPinGroup(outputNode, 'left');\n outputGroup.position.set(-0.7, 0, -0.05);\n group.add(outputGroup);\n }\n }\n\n /**\n * Get config form definition for XOR/XNOR Gate\n *\n * @param config - Optional current config to determine disabled state of transitionSpan\n * @returns Form definition with defaultLogicFamily dropdown, activationLogic boolean, and transitionSpan number\n */\n override getConfigFormDefinition(config?: Map<string, string>): ConfigFormDefinition | null {\n const logicFamily = config?.get('defaultLogicFamily') ?? 'CMOS1';\n return {\n fields: [\n {\n key: 'defaultLogicFamily',\n type: 'dropdown',\n options: { CMOS: 'CMOS1', TTL: 'TTL1', Sandbox: 'Sandbox' },\n },\n { key: 'activationLogic', type: 'boolean' },\n { key: 'transitionSpan', type: 'number', min: 1, disabled: logicFamily !== 'Sandbox' },\n { key: 'initializationOrder', type: 'number' },\n ],\n };\n }\n\n /**\n * Map core config to form data\n * Converts \"positive\"/\"negative\" strings to boolean\n *\n * @param config - Core component config\n * @returns Form data with boolean activationLogic\n */\n override mapCoreConfigToForm(config: Map<string, string>): Map<string, any> {\n const formData = new Map<string, any>();\n formData.set('defaultLogicFamily', config.get('defaultLogicFamily') ?? 'CMOS1');\n const activationLogic = config.get('activationLogic');\n formData.set('activationLogic', activationLogic === 'positive');\n formData.set('transitionSpan', parseFloat(config.get('transitionSpan') || '1'));\n formData.set('initializationOrder', parseFloat(config.get('initializationOrder') || '0'));\n return formData;\n }\n\n /**\n * Map form data to core config\n * Converts boolean to \"positive\"/\"negative\" strings\n *\n * @param formData - Form data with boolean activationLogic\n * @returns Core config with string activationLogic\n */\n override mapFormToCoreConfig(formData: Map<string, any>): Map<string, string> {\n const config = new Map<string, string>();\n config.set('defaultLogicFamily', formData.get('defaultLogicFamily') ?? 'CMOS1');\n const activationLogic = formData.get('activationLogic');\n config.set('activationLogic', activationLogic ? 'positive' : 'negative');\n config.set('transitionSpan', formData.get('transitionSpan').toString());\n config.set('initializationOrder', formData.get('initializationOrder').toString() || null);\n return config;\n }\n\n override updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>) {\n const holeMesh = this.findHoleMesh(object3D);\n if (!holeMesh) return;\n\n let negativeMarkerMesh = this.findNegativeMarkerMesh(object3D);\n\n if (config.get('activationLogic') === 'negative') {\n holeMesh.userData.initialState = 'high';\n if (!negativeMarkerMesh) {\n negativeMarkerMesh = new THREE.Mesh(\n this.NEG_MARKER_GEOM,\n this.getMat(CmpMatCategory.WHITE)\n );\n negativeMarkerMesh.userData = {\n type: 'component',\n componentId: holeMesh.userData.componentId,\n part: 'negativeMarker',\n };\n\n negativeMarkerMesh.position.set(-0.7, 0.15, -0.6);\n object3D.add(negativeMarkerMesh);\n }\n } else {\n holeMesh.userData.initialState = 'low';\n if (negativeMarkerMesh) {\n object3D.remove(negativeMarkerMesh);\n }\n }\n }\n\n /**\n * Update animation based on simulation state\n *\n * @param object3D - The Object3D created by createVisual()\n * @param state - The component current simulation state\n */\n override updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void {\n const holeMesh = this.findHoleMesh(object3D);\n if (!holeMesh) return;\n\n if (!state || !this._animationContext || state.state === 'indeterminate') {\n this._cleanupMixer(object3D);\n this._restoreSharedHoleMaterial(holeMesh);\n return;\n }\n\n if (state.state === 'high') {\n this._cleanupMixer(object3D);\n this._setHoleColor(holeMesh, this.HOLE_COLOR_HIGH, this.HOLE_EMISSIVE_HIGH_INTENSITY);\n return;\n }\n\n if (state.state === 'low') {\n this._cleanupMixer(object3D);\n this._setHoleColor(holeMesh, this.HOLE_COLOR_LOW, this.HOLE_EMISSIVE_LOW_INTENSITY);\n return;\n }\n\n // Paused + transitional: snap to start color (before the transition began)\n if (this._animationContext.simulationStatus !== 'playing') {\n if (state.state === 'rising') {\n this._setHoleColor(holeMesh, this.HOLE_COLOR_LOW, this.HOLE_EMISSIVE_LOW_INTENSITY);\n } else {\n // falling\n this._setHoleColor(holeMesh, this.HOLE_COLOR_HIGH, this.HOLE_EMISSIVE_HIGH_INTENSITY);\n }\n return;\n }\n\n // Playing + transitional: animate\n if (state.hasExpiration) {\n this._animateHoleColor(object3D, holeMesh, state);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Hole material helpers\n // ---------------------------------------------------------------------------\n\n private _setHoleColor(holeMesh: THREE.Mesh, color: THREE.Color, emissiveIntensity: number): void {\n this._ensureClonedHoleMaterial(holeMesh);\n const mat = holeMesh.material as THREE.MeshLambertMaterial;\n mat.color.copy(color);\n mat.emissive.copy(color);\n mat.emissiveIntensity = emissiveIntensity;\n }\n\n private _ensureClonedHoleMaterial(holeMesh: THREE.Mesh): void {\n const mat = holeMesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType === CmpMatType.ANIMATION_CLONE) return;\n holeMesh.material = this.getMat(CmpMatCategory.DARK_GRAY).clone();\n (holeMesh.material as THREE.MeshLambertMaterial).userData.matType = CmpMatType.ANIMATION_CLONE;\n }\n\n private _restoreSharedHoleMaterial(holeMesh: THREE.Mesh): void {\n const mat = holeMesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType !== CmpMatType.ANIMATION_CLONE) return;\n mat.dispose();\n holeMesh.material = this.getMat(CmpMatCategory.DARK_GRAY);\n }\n\n private _cleanupMixer(object3D: THREE.Object3D): void {\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n if (mixer) {\n mixer.stopAllAction();\n mixer.uncacheRoot(object3D);\n delete object3D.userData.mixer;\n }\n delete object3D.userData.currentAction;\n delete object3D.userData.currentClip;\n delete object3D.userData.currentActionStart;\n }\n\n /**\n * Animate the hole color between LOW and HIGH over the transition span.\n * Reads current color for mid-transition support.\n */\n private _animateHoleColor(\n object3D: THREE.Object3D,\n holeMesh: THREE.Mesh,\n state: ComponentState\n ): void {\n if (object3D.userData.currentActionStart === state.startTick) return;\n\n const tps = this._animationContext!.ticksPerSecond;\n const span = state.expirationTick - state.startTick;\n const durationSeconds = span / tps;\n\n this._ensureClonedHoleMaterial(holeMesh);\n const mat = holeMesh.material as THREE.MeshLambertMaterial;\n\n const toColor = state.state === 'rising' ? this.HOLE_COLOR_HIGH : this.HOLE_COLOR_LOW;\n const toIntensity =\n state.state === 'rising'\n ? this.HOLE_EMISSIVE_HIGH_INTENSITY\n : this.HOLE_EMISSIVE_LOW_INTENSITY;\n\n const currentRGB = [mat.color.r, mat.color.g, mat.color.b];\n const endRGB = [toColor.r, toColor.g, toColor.b];\n const currentIntensity = mat.emissiveIntensity;\n\n let mixer: THREE.AnimationMixer = object3D.userData.mixer;\n if (!mixer) {\n mixer = new THREE.AnimationMixer(object3D);\n object3D.userData.mixer = mixer;\n }\n\n if (object3D.userData.currentAction) {\n (object3D.userData.currentAction as THREE.AnimationAction).stop();\n }\n if (object3D.userData.currentClip) {\n mixer.uncacheClip(object3D.userData.currentClip as THREE.AnimationClip);\n }\n\n const clip = new THREE.AnimationClip('holeColor', durationSeconds, [\n new THREE.ColorKeyframeTrack(\n 'hole.material.color',\n [0, durationSeconds],\n [...currentRGB, ...endRGB]\n ),\n new THREE.ColorKeyframeTrack(\n 'hole.material.emissive',\n [0, durationSeconds],\n [...currentRGB, ...endRGB]\n ),\n new THREE.NumberKeyframeTrack(\n 'hole.material.emissiveIntensity',\n [0, durationSeconds],\n [currentIntensity, toIntensity]\n ),\n ]);\n const action = mixer.clipAction(clip);\n action.loop = THREE.LoopOnce;\n action.clampWhenFinished = true;\n action.play();\n\n object3D.userData.currentActionStart = state.startTick;\n object3D.userData.currentAction = action;\n object3D.userData.currentClip = clip;\n }\n\n /**\n * Find the envelope mesh within the component group\n *\n * @param object3D - The Object3D group created by createVisual()\n * @returns The envelope mesh if found, null otherwise\n *\n * @remarks\n * Searches for a mesh with userData.part === 'envelope'\n */\n protected findEnvelopeMesh(object3D: THREE.Object3D): THREE.Mesh | null {\n let envelopeMesh: THREE.Mesh | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === 'envelope') {\n envelopeMesh = child as THREE.Mesh;\n }\n });\n return envelopeMesh;\n }\n\n /**\n * Find the hole mesh within the component group\n *\n * @param object3D - The Object3D group created by createVisual()\n * @returns The hole mesh if found, null otherwise\n *\n * @remarks\n * Searches for a mesh with userData.part === 'hole'\n */\n protected findHoleMesh(object3D: THREE.Object3D): THREE.Mesh | null {\n let holeMesh: THREE.Mesh | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === 'hole') {\n holeMesh = child as THREE.Mesh;\n }\n });\n return holeMesh;\n }\n\n /**\n * Find the negative marker mesh within the component group\n *\n * @param object3D - The Object3D group created by createVisual()\n * @returns The negative marker mesh if found, null otherwise\n *\n * @remarks\n * Searches for a mesh with userData.part === 'negativeMarker'\n */\n protected findNegativeMarkerMesh(object3D: THREE.Object3D): THREE.Mesh | null {\n let negativeMarkerMesh: THREE.Mesh | null = null;\n\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === 'negativeMarker') {\n negativeMarkerMesh = child as THREE.Mesh;\n }\n });\n return negativeMarkerMesh;\n }\n}\n","import { ComponentType, type Component } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport { OrGateGeometry, OrGateHoleGeometry, XorGateTailGeometry } from '../../utils/GeometryUtils';\nimport { XorGateVisualFactory } from './XorGateVisualFactory';\nimport type { VisualContext } from '../../types';\nimport { CmpMatCategory } from '../types';\n\n/**\n * Visual factory for XOR gates components\n *\n * Creates:\n * - Gate mesh\n * - vcc, gnd, inputs and output pin groups\n * - Component hitbox for raycasting\n *\n * Animation:\n * - Emissive glow when Gate is high (based on simulation state)\n */\nexport class Xor4GateVisualFactory extends XorGateVisualFactory {\n /** XOR tail geometry */\n protected override readonly TAIL_GEOM = XorGateTailGeometry(2, 3.6, 1.45, 0.13, 1.8, 0.4, 16);\n /** Shared open envelope geometry */\n protected override readonly ENVELOPE_GEOM = OrGateGeometry(2, 3.6, 0.13, 0.4, 16);\n /** Shared inner hole geometry */\n protected override readonly HOLE_GEOM = OrGateHoleGeometry(2, 3.6, 0.13, 0.4, 16)!;\n /** Shared geometry for negative marker **/\n protected override readonly NEG_MARKER_GEOM = new THREE.CylinderGeometry(\n 0.25,\n 0.25,\n 0.4,\n 16,\n 4,\n false,\n 0,\n Math.PI * 2\n );\n\n constructor() {\n super();\n this._componentType = ComponentType.Xor4Gate;\n }\n\n override createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n // Component hitbox (invisible, raycastable)\n const hitbox = this.createComponentHitbox(component.id, group.id, 3, 2, 3.8);\n group.add(hitbox);\n\n // Visual Gate\n const envelope = new THREE.Mesh(this.ENVELOPE_GEOM, this.getMat(CmpMatCategory.WHITE));\n envelope.userData = {\n type: 'component',\n componentId: component.id,\n part: 'envelope',\n };\n envelope.rotateX(-Math.PI / 2);\n envelope.rotateY(Math.PI);\n envelope.position.set(-0.25, 0.35, 0);\n group.add(envelope);\n\n const hole = new THREE.Mesh(this.HOLE_GEOM, this.getMat(CmpMatCategory.DARK_GRAY));\n hole.name = 'hole'; // required for AnimationMixer property binding\n hole.userData = {\n type: 'component',\n componentId: component.id,\n part: 'hole',\n initialState: 'low',\n };\n hole.rotateX(-Math.PI / 2);\n hole.rotateY(Math.PI);\n hole.position.set(-0.25, 0.35, 0);\n group.add(hole);\n\n const tail = new THREE.Mesh(this.TAIL_GEOM, this.getMat(CmpMatCategory.WHITE));\n tail.userData = {\n type: 'component',\n componentId: component.id,\n part: 'tail',\n };\n tail.rotateX(-Math.PI / 2);\n tail.rotateY(Math.PI);\n tail.position.set(-0.85, 0.35, 0);\n group.add(tail);\n\n // pins (not called if preview - no pins)\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n this.updateFromConfiguration(group, component.config);\n return group;\n }\n\n protected override createPinsVisual(\n component: Component,\n context: VisualContext,\n group: THREE.Group\n ) {\n const vccNode = context.getENode(component.pins[0]!);\n if (vccNode) {\n const vccGroup = this.createPinGroup(vccNode, 'bottom', new THREE.Euler(0, 0, 0.23));\n vccGroup.position.set(0.15, 0, 1.73);\n group.add(vccGroup);\n }\n\n const gndNode = context.getENode(component.pins[6]!);\n if (gndNode) {\n const gndGroup = this.createPinGroup(gndNode, 'top', new THREE.Euler(0, 0, 0.23));\n gndGroup.position.set(0.15, 0, -1.73);\n group.add(gndGroup);\n }\n\n const input1Node = context.getENode(component.pins[1]!);\n if (input1Node) {\n const input1Group = this.createPinGroup(input1Node, 'right', new THREE.Euler(0.6, 0, 0));\n input1Group.position.set(1.37, 0, 1.5);\n group.add(input1Group);\n }\n\n const input2Node = context.getENode(component.pins[2]!);\n if (input2Node) {\n const input2Group = this.createPinGroup(input2Node, 'right', new THREE.Euler(0.1, 0, 0));\n input2Group.position.set(0.93, 0, 0.5);\n group.add(input2Group);\n }\n\n const input3Node = context.getENode(component.pins[3]!);\n if (input3Node) {\n const input3Group = this.createPinGroup(input3Node, 'right', new THREE.Euler(-0.1, 0, 0));\n input3Group.position.set(0.93, 0, -0.5);\n group.add(input3Group);\n }\n\n const input4Node = context.getENode(component.pins[4]!);\n if (input4Node) {\n const input4Group = this.createPinGroup(input4Node, 'right', new THREE.Euler(-0.6, 0, 0));\n input4Group.position.set(1.37, 0, -1.5);\n group.add(input4Group);\n }\n\n const outputNode = context.getENode(component.pins[5]!);\n if (outputNode) {\n const outputGroup = this.createPinGroup(outputNode, 'left');\n outputGroup.position.set(-1.22, 0, 0);\n group.add(outputGroup);\n }\n }\n\n override updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>) {\n const holeMesh = this.findHoleMesh(object3D);\n if (!holeMesh) return;\n\n let negativeMarkerMesh = this.findNegativeMarkerMesh(object3D);\n\n if (config.get('activationLogic') === 'negative') {\n holeMesh.userData.initialState = 'high';\n if (!negativeMarkerMesh) {\n negativeMarkerMesh = new THREE.Mesh(\n this.NEG_MARKER_GEOM,\n this.getMat(CmpMatCategory.WHITE)\n );\n negativeMarkerMesh.userData = {\n type: 'component',\n componentId: holeMesh.userData.componentId,\n part: 'negativeMarker',\n };\n\n negativeMarkerMesh.position.set(-1, 0.15, -1.3);\n object3D.add(negativeMarkerMesh);\n }\n } else {\n holeMesh.userData.initialState = 'low';\n if (negativeMarkerMesh) {\n object3D.remove(negativeMarkerMesh);\n }\n }\n }\n}\n","import { ComponentType, type Component } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport { OrGateGeometry, OrGateHoleGeometry, XorGateTailGeometry } from '../../utils/GeometryUtils';\nimport { XorGateVisualFactory } from './XorGateVisualFactory';\nimport type { VisualContext } from '../../types';\nimport { CmpMatCategory } from '../types';\n\n/**\n * Visual factory for XOR gates components\n *\n * Creates:\n * - Gate mesh\n * - vcc, gnd, inputs and output pin groups\n * - Component hitbox for raycasting\n *\n * Animation:\n * - Emissive glow when Gate is high (based on simulation state)\n */\nexport class Xor8GateVisualFactory extends XorGateVisualFactory {\n /** XOR tail geometry */\n protected override readonly TAIL_GEOM = XorGateTailGeometry(2.6, 7.5, 2.4, 0.16, 3.5, 0.4, 16);\n /** Shared open envelope geometry */\n protected override readonly ENVELOPE_GEOM = OrGateGeometry(2.6, 7.5, 0.16, 0.4, 16);\n /** Shared inner hole geometry */\n protected override readonly HOLE_GEOM = OrGateHoleGeometry(2.6, 7.5, 0.16, 0.4, 16)!;\n /** Shared geometry for negative marker **/\n protected override readonly NEG_MARKER_GEOM = new THREE.CylinderGeometry(\n 0.35,\n 0.35,\n 0.4,\n 16,\n 4,\n false,\n 0,\n Math.PI * 2\n );\n\n constructor() {\n super();\n this._componentType = ComponentType.Xor8Gate;\n }\n\n override createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n // Component hitbox (invisible, raycastable)\n const hitbox = this.createComponentHitbox(component.id, group.id, 4, 2, 7.6);\n group.add(hitbox);\n\n // Visual Gate\n const envelope = new THREE.Mesh(this.ENVELOPE_GEOM, this.getMat(CmpMatCategory.WHITE));\n envelope.userData = {\n type: 'component',\n componentId: component.id,\n part: 'envelope',\n };\n envelope.rotateX(-Math.PI / 2);\n envelope.rotateY(Math.PI);\n envelope.position.set(-0.45, 0.35, 0);\n group.add(envelope);\n\n const hole = new THREE.Mesh(this.HOLE_GEOM, this.getMat(CmpMatCategory.DARK_GRAY));\n hole.name = 'hole'; // required for AnimationMixer property binding\n hole.userData = {\n type: 'component',\n componentId: component.id,\n part: 'hole',\n initialState: 'low',\n };\n hole.rotateX(-Math.PI / 2);\n hole.rotateY(Math.PI);\n hole.position.set(-0.45, 0.35, 0);\n group.add(hole);\n\n const tail = new THREE.Mesh(this.TAIL_GEOM, this.getMat(CmpMatCategory.WHITE));\n tail.userData = {\n type: 'component',\n componentId: component.id,\n part: 'tail',\n };\n tail.rotateX(-Math.PI / 2);\n tail.rotateY(Math.PI);\n tail.position.set(-1.7, 0.35, 0);\n group.add(tail);\n\n // pins (not called if preview - no pins)\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n this.updateFromConfiguration(group, component.config);\n return group;\n }\n\n protected override createPinsVisual(\n component: Component,\n context: VisualContext,\n group: THREE.Group\n ) {\n const vccNode = context.getENode(component.pins[0]!);\n if (vccNode) {\n const vccGroup = this.createPinGroup(vccNode, 'bottom', new THREE.Euler(0, 0, 0.5));\n vccGroup.position.set(0.3, 0, 3.5);\n group.add(vccGroup);\n }\n\n const gndNode = context.getENode(component.pins[10]!);\n if (gndNode) {\n const gndGroup = this.createPinGroup(gndNode, 'top', new THREE.Euler(0, 0, 0.5));\n gndGroup.position.set(0.3, 0, -3.5);\n group.add(gndGroup);\n }\n\n const input1Node = context.getENode(component.pins[1]!);\n if (input1Node) {\n const input1Group = this.createPinGroup(input1Node, 'right', new THREE.Euler(0.7, 0, 0));\n input1Group.position.set(1.65, 0, 3.45);\n group.add(input1Group);\n }\n\n const input2Node = context.getENode(component.pins[2]!);\n if (input2Node) {\n const input2Group = this.createPinGroup(input2Node, 'right', new THREE.Euler(0.53, 0, 0));\n input2Group.position.set(1.09, 0, 2.5);\n group.add(input2Group);\n }\n\n const input3Node = context.getENode(component.pins[3]!);\n if (input3Node) {\n const input3Group = this.createPinGroup(input3Node, 'right', new THREE.Euler(0.3, 0, 0));\n input3Group.position.set(0.72, 0, 1.5);\n group.add(input3Group);\n }\n\n const input4Node = context.getENode(component.pins[4]!);\n if (input4Node) {\n const input4Group = this.createPinGroup(input4Node, 'right', new THREE.Euler(0.11, 0, 0));\n input4Group.position.set(0.54, 0, 0.5);\n group.add(input4Group);\n }\n\n const input5Node = context.getENode(component.pins[5]!);\n if (input5Node) {\n const input5Group = this.createPinGroup(input5Node, 'right', new THREE.Euler(-0.11, 0, 0));\n input5Group.position.set(0.54, 0, -0.5);\n group.add(input5Group);\n }\n\n const input6Node = context.getENode(component.pins[6]!);\n if (input6Node) {\n const input6Group = this.createPinGroup(input6Node, 'right', new THREE.Euler(-0.3, 0, 0));\n input6Group.position.set(0.72, 0, -1.5);\n group.add(input6Group);\n }\n\n const input7Node = context.getENode(component.pins[7]!);\n if (input7Node) {\n const input7Group = this.createPinGroup(input7Node, 'right', new THREE.Euler(-0.53, 0, 0));\n input7Group.position.set(1.1, 0, -2.5);\n group.add(input7Group);\n }\n\n const input8Node = context.getENode(component.pins[8]!);\n if (input8Node) {\n const input8Group = this.createPinGroup(input8Node, 'right', new THREE.Euler(-0.7, 0, 0));\n input8Group.position.set(1.7, 0, -3.45);\n group.add(input8Group);\n }\n\n const outputNode = context.getENode(component.pins[9]!);\n if (outputNode) {\n const outputGroup = this.createPinGroup(outputNode, 'left');\n outputGroup.position.set(-1.69, 0, 0);\n group.add(outputGroup);\n }\n }\n\n override updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>) {\n const holeMesh = this.findHoleMesh(object3D);\n if (!holeMesh) return;\n\n let negativeMarkerMesh = this.findNegativeMarkerMesh(object3D);\n\n if (config.get('activationLogic') === 'negative') {\n holeMesh.userData.initialState = 'high';\n if (!negativeMarkerMesh) {\n negativeMarkerMesh = new THREE.Mesh(\n this.NEG_MARKER_GEOM,\n this.getMat(CmpMatCategory.WHITE)\n );\n negativeMarkerMesh.userData = {\n type: 'component',\n componentId: holeMesh.userData.componentId,\n part: 'negativeMarker',\n };\n\n negativeMarkerMesh.position.set(-1.02, 0.15, -2.86);\n object3D.add(negativeMarkerMesh);\n }\n } else {\n holeMesh.userData.initialState = 'low';\n if (negativeMarkerMesh) {\n object3D.remove(negativeMarkerMesh);\n }\n }\n }\n}\n","import { ComponentVisualFactoryBase } from '../ComponentVisualFactory';\nimport { ComponentType, type Component, type ComponentState } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport {\n EmptyRectangleGeometry,\n RectangleWithNailGeometry,\n} from '../../utils/GeometryUtils';\nimport type { ConfigFormDefinition, VisualContext } from '../../types';\nimport { CmpMatCategory, CmpMatType } from '../types';\n\n/**\n * Visual factory for Half Adder components.\n *\n * Creates:\n * - An `envelope` rectangular frame (EmptyRectangleGeometry)\n * - A horizontal `separator` box dividing upper/lower chambers\n * - A `sumFiller` (RectangleWithNailGeometry) in the upper chamber;\n * its material is animated blue↔red following the sum output state\n * - A `halfPlus` small box inside the sumFiller (visual cue)\n * - A `carryFiller` box in the lower chamber; its material is animated\n * blue↔red following the carry output state\n * - pin groups for vcc, inputA, inputB, sum, carry, gnd\n *\n * Animation:\n * - sumFiller ↔ blue/red based on sum output (current/next state)\n * - carryFiller ↔ blue/red based on carry output (current/next state)\n */\nexport class HalfAdderVisualFactory extends ComponentVisualFactoryBase {\n private readonly ENVELOPE_GEOM = EmptyRectangleGeometry(2.2,2.2,0.1,0.4);\n /** Horizontal separator splitting upper/lower chambers at Z=0 */\n private readonly SEPARATOR_GEOM = new THREE.BoxGeometry(1.6, 0.4,0.2);\n private readonly SEPARATOR_SIDE_GEOM = new THREE.BoxGeometry(0.2, 0.4,0.2);\n\n private readonly SUM_FILLER_GEOM = RectangleWithNailGeometry(\n 2,\n 0.9,\n 0.2,\n 0.7,\n 1,\n false,\n 0.4\n );\n\n /** Small box inside sumFiller (visual cue for the XOR \"+\") */\n private readonly HALF_PLUS_GEOM = new THREE.BoxGeometry(0.2, 0.4, 0.7);\n private readonly CARRY_FILLER_GEOM = new THREE.BoxGeometry(2, 0.4, 0.9);\n\n protected readonly FILLER_COLOR_HIGH = new THREE.Color(0xff4444);\n protected readonly FILLER_COLOR_LOW = new THREE.Color(0x4444ff);\n protected readonly FILLER_EMISSIVE_HIGH_INTENSITY = 0.5;\n protected readonly FILLER_EMISSIVE_LOW_INTENSITY = 0.2;\n\n constructor() {\n super();\n this._componentType = ComponentType.HalfAdder;\n }\n\n createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n const hitbox = this.createComponentHitbox(\n component.id,\n group.id,\n 2.5,2,2.5);\n group.add(hitbox);\n\n // Envelope frame\n const envelope = new THREE.Mesh(this.ENVELOPE_GEOM, this.getMat(CmpMatCategory.WHITE));\n envelope.userData = {\n type: 'component',\n componentId: component.id,\n part: 'envelope',\n };\n envelope.rotateX(-Math.PI / 2);\n envelope.position.set(0, 0, 0);\n group.add(envelope);\n\n // Separator (horizontal bar, Y-centered between envelope extrude extents)\n const separator = new THREE.Mesh(this.SEPARATOR_GEOM, this.getMat(CmpMatCategory.WHITE));\n separator.userData = {\n type: 'component',\n componentId: component.id,\n part: 'separator',\n };\n separator.position.set(0, 0.2, 0);\n group.add(separator);\n const separatorSideLeft = new THREE.Mesh(this.SEPARATOR_SIDE_GEOM, this.getMat(CmpMatCategory.DARK_GRAY));\n separatorSideLeft.userData = {...separator.userData, part: 'separatorSideLeft'};\n separatorSideLeft.position.set(-0.9, 0.2, 0);\n group.add(separatorSideLeft);\n const separatorSideRight = new THREE.Mesh(this.SEPARATOR_SIDE_GEOM, this.getMat(CmpMatCategory.DARK_GRAY));\n separatorSideRight.userData = {...separator.userData, part: 'separatorSideRight'};\n separatorSideRight.position.set(0.9, 0.2, 0);\n group.add(separatorSideRight);\n\n // Sum filler (upper chamber: world Z < 0). Nail points toward +Z into the separator.\n const sumFiller = new THREE.Mesh(\n this.SUM_FILLER_GEOM,\n this.getMat(CmpMatCategory.DARK_GRAY)\n );\n sumFiller.name = 'sumFiller'; // required for AnimationMixer property binding\n sumFiller.userData = {\n type: 'component',\n componentId: component.id,\n part: 'sumFiller',\n initialState: 'low',\n };\n sumFiller.rotateX(-Math.PI / 2);\n // Body centered in upper chamber; nail crosses separator\n sumFiller.position.set(0, 0, -0.55);\n group.add(sumFiller);\n\n // Half plus box inside sum filler body\n const halfPlus = new THREE.Mesh(this.HALF_PLUS_GEOM, this.getMat(CmpMatCategory.WHITE));\n halfPlus.userData = {\n type: 'component',\n componentId: component.id,\n part: 'halfPlus',\n };\n halfPlus.position.set(0, 0.2, -0.45);\n group.add(halfPlus);\n\n // Carry filler (lower chamber: world Z > 0)\n const carryFiller = new THREE.Mesh(\n this.CARRY_FILLER_GEOM,\n this.getMat(CmpMatCategory.DARK_GRAY)\n );\n carryFiller.name = 'carryFiller'; // required for AnimationMixer property binding\n carryFiller.userData = {\n type: 'component',\n componentId: component.id,\n part: 'carryFiller',\n initialState: 'low',\n };\n carryFiller.position.set(0, 0.2, 0.55);\n group.add(carryFiller);\n\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n return group;\n }\n\n protected createPinsVisual(component: Component, context: VisualContext, group: THREE.Group) {\n const vccNode = context.getENode(component.pins[0]!);\n if (vccNode) {\n const vccGroup = this.createPinGroup(vccNode, 'top');\n vccGroup.position.set(0, 0, -1.1);\n group.add(vccGroup);\n }\n\n const inputANode = context.getENode(component.pins[1]!);\n if (inputANode) {\n const inputAGroup = this.createPinGroup(inputANode, 'left');\n inputAGroup.position.set(-1.1, 0, -0.5);\n group.add(inputAGroup);\n }\n\n const inputBNode = context.getENode(component.pins[2]!);\n if (inputBNode) {\n const inputBGroup = this.createPinGroup(inputBNode, 'left');\n inputBGroup.position.set(-1.1, 0, 0.5);\n group.add(inputBGroup);\n }\n\n const sumNode = context.getENode(component.pins[3]!);\n if (sumNode) {\n const sumGroup = this.createPinGroup(sumNode, 'right');\n sumGroup.position.set(1.1, 0, -0.5);\n group.add(sumGroup);\n }\n\n const carryNode = context.getENode(component.pins[4]!);\n if (carryNode) {\n const carryGroup = this.createPinGroup(carryNode, 'right');\n carryGroup.position.set(1.1, 0, 0.5);\n group.add(carryGroup);\n }\n\n const gndNode = context.getENode(component.pins[5]!);\n if (gndNode) {\n const gndGroup = this.createPinGroup(gndNode, 'bottom');\n gndGroup.position.set(0, 0, 1.1);\n group.add(gndGroup);\n }\n }\n\n override getConfigFormDefinition(config?: Map<string, string>): ConfigFormDefinition | null {\n const logicFamily = config?.get('defaultLogicFamily') ?? 'CMOS1';\n return {\n fields: [\n {\n key: 'defaultLogicFamily',\n type: 'dropdown',\n options: { CMOS: 'CMOS1', TTL: 'TTL1', Sandbox: 'Sandbox' },\n },\n { key: 'transitionSpan', type: 'number', min: 1, disabled: logicFamily !== 'Sandbox' },\n { key: 'initializationOrder', type: 'number' },\n ],\n };\n }\n\n override mapCoreConfigToForm(config: Map<string, string>): Map<string, any> {\n const formData = new Map<string, any>();\n formData.set('defaultLogicFamily', config.get('defaultLogicFamily') ?? 'CMOS1');\n formData.set('transitionSpan', parseFloat(config.get('transitionSpan') || '2'));\n formData.set('initializationOrder', parseFloat(config.get('initializationOrder') || '0'));\n return formData;\n }\n\n override mapFormToCoreConfig(formData: Map<string, any>): Map<string, string> {\n const config = new Map<string, string>();\n config.set('defaultLogicFamily', formData.get('defaultLogicFamily') ?? 'CMOS1');\n config.set('transitionSpan', formData.get('transitionSpan').toString());\n config.set('initializationOrder', formData.get('initializationOrder').toString() || null);\n return config;\n }\n\n\n override updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void {\n const sumFiller = this.findPartMesh(object3D, 'sumFiller');\n const carryFiller = this.findPartMesh(object3D, 'carryFiller');\n if (!sumFiller || !carryFiller) return;\n\n if (!state || !this._animationContext || state.state === 'indeterminate') {\n this._cleanupMixer(object3D);\n this._restoreSharedFillerMaterial(sumFiller);\n this._restoreSharedFillerMaterial(carryFiller);\n return;\n }\n\n const isTransition = state.state.startsWith('to');\n const prevStable: string = isTransition\n ? (state.parameters.get('prevState') ?? '0')\n : state.state;\n const nextStable: string = isTransition ? (state.nextState ?? prevStable) : state.state;\n\n const prevValue = parseInt(prevStable, 16);\n const nextValue = parseInt(nextStable, 16);\n const prevSumHigh = (prevValue & 1) !== 0;\n const nextSumHigh = (nextValue & 1) !== 0;\n const prevCarryHigh = (prevValue & 2) !== 0;\n const nextCarryHigh = (nextValue & 2) !== 0;\n\n // Stable state: snap colors, no animation\n if (!isTransition || !state.hasExpiration) {\n this._cleanupMixer(object3D);\n this._setFillerColor(sumFiller, nextSumHigh);\n this._setFillerColor(carryFiller, nextCarryHigh);\n return;\n }\n\n // Paused + transitional: snap to start color\n if (this._animationContext.simulationStatus !== 'playing') {\n this._setFillerColor(sumFiller, prevSumHigh);\n this._setFillerColor(carryFiller, prevCarryHigh);\n return;\n }\n\n // Playing + transitional: animate each filler whose value actually changes\n this._animateFillers(\n object3D,\n sumFiller,\n carryFiller,\n state,\n prevSumHigh,\n nextSumHigh,\n prevCarryHigh,\n nextCarryHigh\n );\n }\n\n // ---------------------------------------------------------------------------\n // Material helpers\n // ---------------------------------------------------------------------------\n\n private _setFillerColor(mesh: THREE.Mesh, high: boolean): void {\n this._ensureClonedFillerMaterial(mesh);\n const mat = mesh.material as THREE.MeshLambertMaterial;\n const color = high ? this.FILLER_COLOR_HIGH : this.FILLER_COLOR_LOW;\n const intensity = high\n ? this.FILLER_EMISSIVE_HIGH_INTENSITY\n : this.FILLER_EMISSIVE_LOW_INTENSITY;\n mat.color.copy(color);\n mat.emissive.copy(color);\n mat.emissiveIntensity = intensity;\n }\n\n private _ensureClonedFillerMaterial(mesh: THREE.Mesh): void {\n const mat = mesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType === CmpMatType.ANIMATION_CLONE) return;\n mesh.material = this.getMat(CmpMatCategory.DARK_GRAY).clone();\n (mesh.material as THREE.MeshLambertMaterial).userData.matType = CmpMatType.ANIMATION_CLONE;\n }\n\n private _restoreSharedFillerMaterial(mesh: THREE.Mesh): void {\n const mat = mesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType !== CmpMatType.ANIMATION_CLONE) return;\n mat.dispose();\n mesh.material = this.getMat(CmpMatCategory.DARK_GRAY);\n }\n\n private _cleanupMixer(object3D: THREE.Object3D): void {\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n if (mixer) {\n mixer.stopAllAction();\n mixer.uncacheRoot(object3D);\n delete object3D.userData.mixer;\n }\n delete object3D.userData.currentAction;\n delete object3D.userData.currentClip;\n delete object3D.userData.currentActionStart;\n }\n\n /**\n * Animate sumFiller and carryFiller fill colors over the transition span.\n * Builds a single AnimationClip with up to two target tracks per filler\n * (color, emissive, emissiveIntensity) — only for fillers whose value changes.\n */\n private _animateFillers(\n object3D: THREE.Object3D,\n sumFiller: THREE.Mesh,\n carryFiller: THREE.Mesh,\n state: ComponentState,\n prevSumHigh: boolean,\n nextSumHigh: boolean,\n prevCarryHigh: boolean,\n nextCarryHigh: boolean\n ): void {\n if (object3D.userData.currentActionStart === state.startTick) return;\n\n const tps = this._animationContext!.ticksPerSecond;\n const span = state.expirationTick - state.startTick;\n const durationSeconds = span / tps;\n\n this._ensureClonedFillerMaterial(sumFiller);\n this._ensureClonedFillerMaterial(carryFiller);\n\n // Snap fillers whose value does not change to the (unchanged) color.\n if (prevSumHigh === nextSumHigh) {\n this._setFillerColor(sumFiller, nextSumHigh);\n }\n if (prevCarryHigh === nextCarryHigh) {\n this._setFillerColor(carryFiller, nextCarryHigh);\n }\n\n const tracks: THREE.KeyframeTrack[] = [];\n if (prevSumHigh !== nextSumHigh) {\n this._pushFillerTracks(tracks, 'sumFiller', sumFiller, nextSumHigh, durationSeconds);\n }\n if (prevCarryHigh !== nextCarryHigh) {\n this._pushFillerTracks(tracks, 'carryFiller', carryFiller, nextCarryHigh, durationSeconds);\n }\n\n // No tracks: nothing to animate (both outputs unchanged). Still record start to dedupe.\n if (tracks.length === 0) {\n object3D.userData.currentActionStart = state.startTick;\n return;\n }\n\n let mixer: THREE.AnimationMixer = object3D.userData.mixer;\n if (!mixer) {\n mixer = new THREE.AnimationMixer(object3D);\n object3D.userData.mixer = mixer;\n }\n if (object3D.userData.currentAction) {\n (object3D.userData.currentAction as THREE.AnimationAction).stop();\n }\n if (object3D.userData.currentClip) {\n mixer.uncacheClip(object3D.userData.currentClip as THREE.AnimationClip);\n }\n\n const clip = new THREE.AnimationClip('halfAdderFillers', durationSeconds, tracks);\n const action = mixer.clipAction(clip);\n action.loop = THREE.LoopOnce;\n action.clampWhenFinished = true;\n action.play();\n\n object3D.userData.currentActionStart = state.startTick;\n object3D.userData.currentAction = action;\n object3D.userData.currentClip = clip;\n }\n\n private _pushFillerTracks(\n tracks: THREE.KeyframeTrack[],\n meshName: string,\n mesh: THREE.Mesh,\n toHigh: boolean,\n durationSeconds: number\n ): void {\n const mat = mesh.material as THREE.MeshLambertMaterial;\n const fromRGB = [mat.color.r, mat.color.g, mat.color.b];\n const toColor = toHigh ? this.FILLER_COLOR_HIGH : this.FILLER_COLOR_LOW;\n const toRGB = [toColor.r, toColor.g, toColor.b];\n const fromIntensity = mat.emissiveIntensity;\n const toIntensity = toHigh\n ? this.FILLER_EMISSIVE_HIGH_INTENSITY\n : this.FILLER_EMISSIVE_LOW_INTENSITY;\n\n tracks.push(\n new THREE.ColorKeyframeTrack(\n `${meshName}.material.color`,\n [0, durationSeconds],\n [...fromRGB, ...toRGB]\n ),\n new THREE.ColorKeyframeTrack(\n `${meshName}.material.emissive`,\n [0, durationSeconds],\n [...fromRGB, ...toRGB]\n ),\n new THREE.NumberKeyframeTrack(\n `${meshName}.material.emissiveIntensity`,\n [0, durationSeconds],\n [fromIntensity, toIntensity]\n )\n );\n }\n\n /** Find a child mesh by its userData.part name */\n protected findPartMesh(object3D: THREE.Object3D, part: string): THREE.Mesh | null {\n let found: THREE.Mesh | null = null;\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === part) {\n found = child as THREE.Mesh;\n }\n });\n return found;\n }\n}\n","import { ComponentVisualFactoryBase } from '../ComponentVisualFactory';\nimport { ComponentType, type Component, type ComponentState } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport {\n EmptyRectangleGeometry,\n RectangleWithNailGeometry,\n} from '../../utils/GeometryUtils';\nimport type { ConfigFormDefinition, VisualContext } from '../../types';\nimport { CmpMatCategory, CmpMatType } from '../types';\n\n/**\n * Visual factory for Adder components.\n *\n * Creates:\n * - An `envelope` rectangular frame (EmptyRectangleGeometry)\n * - A horizontal `separator` box dividing upper/lower chambers\n * - A `sumFiller` (RectangleWithNailGeometry) in the upper chamber;\n * its material is animated blue↔red following the sum output state\n * - A `plus` box inside the sumFiller (visual cue)\n * - A `carryFiller` (RectangleWithNailGeometry) box in the lower chamber; its material is animated\n * blue↔red following the carry output state\n * - pin groups for vcc, inputA, inputB, sum, carry, gnd\n *\n * Animation:\n * - sumFiller ↔ blue/red based on sum output (current/next state)\n * - carryFiller ↔ blue/red based on carry output (current/next state)\n */\nexport class AdderVisualFactory extends ComponentVisualFactoryBase {\n private readonly ENVELOPE_GEOM = EmptyRectangleGeometry(2.6,2.6,0.1,0.4);\n /** Horizontal separator splitting upper/lower chambers at Z=0 */\n private readonly SEPARATOR_GEOM = new THREE.BoxGeometry(1.85, 0.4,0.25);\n private readonly SEPARATOR_SIDE_GEOM = new THREE.BoxGeometry(0.275, 0.4,0.25);\n\n private readonly CELL_FILLER_GEOM = RectangleWithNailGeometry(\n 2.4,\n 1.075,\n 0.25,\n 0.8,\n 1.2,\n false,\n 0.4\n );\n\n /** Small box inside sumFiller (visual cue for the XOR \"+\") */\n private readonly PLUS_GEOM = new THREE.BoxGeometry(0.25, 0.4, 1.85);\n\n protected readonly FILLER_COLOR_HIGH = new THREE.Color(0xff4444);\n protected readonly FILLER_COLOR_LOW = new THREE.Color(0x4444ff);\n protected readonly FILLER_EMISSIVE_HIGH_INTENSITY = 0.5;\n protected readonly FILLER_EMISSIVE_LOW_INTENSITY = 0.2;\n\n constructor() {\n super();\n this._componentType = ComponentType.Adder;\n }\n\n createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n const hitbox = this.createComponentHitbox(\n component.id,\n group.id,\n 2.5,2,2.5);\n group.add(hitbox);\n\n // Envelope frame\n const envelope = new THREE.Mesh(this.ENVELOPE_GEOM, this.getMat(CmpMatCategory.WHITE));\n envelope.userData = {\n type: 'component',\n componentId: component.id,\n part: 'envelope',\n };\n envelope.rotateX(-Math.PI / 2);\n envelope.position.set(0, 0, 0);\n group.add(envelope);\n\n // Separator (horizontal bar, Y-centered between envelope extrude extents)\n const separator = new THREE.Mesh(this.SEPARATOR_GEOM, this.getMat(CmpMatCategory.WHITE));\n separator.userData = {\n type: 'component',\n componentId: component.id,\n part: 'separator',\n };\n separator.position.set(0, 0.2, 0);\n group.add(separator);\n const separatorSideLeft = new THREE.Mesh(this.SEPARATOR_SIDE_GEOM, this.getMat(CmpMatCategory.DARK_GRAY));\n separatorSideLeft.userData = {...separator.userData, part: 'separatorSideLeft'};\n separatorSideLeft.position.set(-1.0625, 0.2, 0);\n group.add(separatorSideLeft);\n const separatorSideRight = new THREE.Mesh(this.SEPARATOR_SIDE_GEOM, this.getMat(CmpMatCategory.DARK_GRAY));\n separatorSideRight.userData = {...separator.userData, part: 'separatorSideRight'};\n separatorSideRight.position.set(1.0625, 0.2, 0);\n group.add(separatorSideRight);\n\n // Sum filler (upper chamber: world Z < 0). Nail points toward +Z into the separator.\n const sumFiller = new THREE.Mesh(\n this.CELL_FILLER_GEOM,\n this.getMat(CmpMatCategory.DARK_GRAY)\n );\n sumFiller.name = 'sumFiller'; // required for AnimationMixer property binding\n sumFiller.userData = {\n type: 'component',\n componentId: component.id,\n part: 'sumFiller',\n initialState: 'low',\n };\n sumFiller.rotateX(-Math.PI / 2);\n // Body centered in upper chamber; nail crosses separator\n sumFiller.position.set(0, 0, -0.6625);\n group.add(sumFiller);\n\n // Plus box inside sum filler body\n const plus = new THREE.Mesh(this.PLUS_GEOM, this.getMat(CmpMatCategory.WHITE));\n plus.userData = {\n type: 'component',\n componentId: component.id,\n part: 'plus',\n };\n plus.position.set(0, 0.2, 0);\n group.add(plus);\n\n // Carry filler (lower chamber: world Z > 0)\n const carryFiller = new THREE.Mesh(\n this.CELL_FILLER_GEOM,\n this.getMat(CmpMatCategory.DARK_GRAY)\n );\n carryFiller.name = 'carryFiller'; // required for AnimationMixer property binding\n carryFiller.userData = {\n type: 'component',\n componentId: component.id,\n part: 'carryFiller',\n initialState: 'low',\n };\n carryFiller.rotateX(Math.PI / 2);\n carryFiller.rotateY(-Math.PI);\n carryFiller.position.set(0, 0, 0.6625);\n group.add(carryFiller);\n\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n return group;\n }\n\n protected createPinsVisual(component: Component, context: VisualContext, group: THREE.Group) {\n const vccNode = context.getENode(component.pins[0]!);\n if (vccNode) {\n const vccGroup = this.createPinGroup(vccNode, 'top');\n vccGroup.position.set(0, 0, -1.3);\n group.add(vccGroup);\n }\n\n const carryInNode = context.getENode(component.pins[1]!);\n if (carryInNode) {\n const carryInGroup = this.createPinGroup(carryInNode, 'top');\n carryInGroup.position.set(-1, 0, -1.3);\n group.add(carryInGroup);\n }\n\n const inputANode = context.getENode(component.pins[2]!);\n if (inputANode) {\n const inputAGroup = this.createPinGroup(inputANode, 'left');\n inputAGroup.position.set(-1.3, 0, -0.65);\n group.add(inputAGroup);\n }\n\n const inputBNode = context.getENode(component.pins[3]!);\n if (inputBNode) {\n const inputBGroup = this.createPinGroup(inputBNode, 'left');\n inputBGroup.position.set(-1.3, 0, 0.65);\n group.add(inputBGroup);\n }\n\n const sumNode = context.getENode(component.pins[4]!);\n if (sumNode) {\n const sumGroup = this.createPinGroup(sumNode, 'right');\n sumGroup.position.set(1.3, 0, -0.65);\n group.add(sumGroup);\n }\n\n const carryOutNode = context.getENode(component.pins[5]!);\n if (carryOutNode) {\n const carryOutGroup = this.createPinGroup(carryOutNode, 'right');\n carryOutGroup.position.set(1.3, 0, 0.65);\n group.add(carryOutGroup);\n }\n\n const gndNode = context.getENode(component.pins[6]!);\n if (gndNode) {\n const gndGroup = this.createPinGroup(gndNode, 'bottom');\n gndGroup.position.set(0, 0, 1.3);\n group.add(gndGroup);\n }\n }\n\n override getConfigFormDefinition(config?: Map<string, string>): ConfigFormDefinition | null {\n const logicFamily = config?.get('defaultLogicFamily') ?? 'CMOS1';\n return {\n fields: [\n {\n key: 'defaultLogicFamily',\n type: 'dropdown',\n options: { CMOS: 'CMOS1', TTL: 'TTL1', Sandbox: 'Sandbox' },\n },\n { key: 'transitionSpan', type: 'number', min: 1, disabled: logicFamily !== 'Sandbox' },\n { key: 'initializationOrder', type: 'number' },\n ],\n };\n }\n\n override mapCoreConfigToForm(config: Map<string, string>): Map<string, any> {\n const formData = new Map<string, any>();\n formData.set('defaultLogicFamily', config.get('defaultLogicFamily') ?? 'CMOS1');\n formData.set('transitionSpan', parseFloat(config.get('transitionSpan') || '2'));\n formData.set('initializationOrder', parseFloat(config.get('initializationOrder') || '0'));\n return formData;\n }\n\n override mapFormToCoreConfig(formData: Map<string, any>): Map<string, string> {\n const config = new Map<string, string>();\n config.set('defaultLogicFamily', formData.get('defaultLogicFamily') ?? 'CMOS1');\n config.set('transitionSpan', formData.get('transitionSpan').toString());\n config.set('initializationOrder', formData.get('initializationOrder').toString() || null);\n return config;\n }\n\n\n override updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void {\n const sumFiller = this.findPartMesh(object3D, 'sumFiller');\n const carryFiller = this.findPartMesh(object3D, 'carryFiller');\n if (!sumFiller || !carryFiller) return;\n\n if (!state || !this._animationContext || state.state === 'indeterminate') {\n this._cleanupMixer(object3D);\n this._restoreSharedFillerMaterial(sumFiller);\n this._restoreSharedFillerMaterial(carryFiller);\n return;\n }\n\n const isTransition = state.state.startsWith('to');\n const prevStable: string = isTransition\n ? (state.parameters.get('prevState') ?? '0')\n : state.state;\n const nextStable: string = isTransition ? (state.nextState ?? prevStable) : state.state;\n\n const prevValue = parseInt(prevStable, 16);\n const nextValue = parseInt(nextStable, 16);\n const prevSumHigh = (prevValue & 1) !== 0;\n const nextSumHigh = (nextValue & 1) !== 0;\n const prevCarryHigh = (prevValue & 2) !== 0;\n const nextCarryHigh = (nextValue & 2) !== 0;\n\n // Stable state: snap colors, no animation\n if (!isTransition || !state.hasExpiration) {\n this._cleanupMixer(object3D);\n this._setFillerColor(sumFiller, nextSumHigh);\n this._setFillerColor(carryFiller, nextCarryHigh);\n return;\n }\n\n // Paused + transitional: snap to start color\n if (this._animationContext.simulationStatus !== 'playing') {\n this._setFillerColor(sumFiller, prevSumHigh);\n this._setFillerColor(carryFiller, prevCarryHigh);\n return;\n }\n\n // Playing + transitional: animate each filler whose value actually changes\n this._animateFillers(\n object3D,\n sumFiller,\n carryFiller,\n state,\n prevSumHigh,\n nextSumHigh,\n prevCarryHigh,\n nextCarryHigh\n );\n }\n\n // ---------------------------------------------------------------------------\n // Material helpers\n // ---------------------------------------------------------------------------\n\n private _setFillerColor(mesh: THREE.Mesh, high: boolean): void {\n this._ensureClonedFillerMaterial(mesh);\n const mat = mesh.material as THREE.MeshLambertMaterial;\n const color = high ? this.FILLER_COLOR_HIGH : this.FILLER_COLOR_LOW;\n const intensity = high\n ? this.FILLER_EMISSIVE_HIGH_INTENSITY\n : this.FILLER_EMISSIVE_LOW_INTENSITY;\n mat.color.copy(color);\n mat.emissive.copy(color);\n mat.emissiveIntensity = intensity;\n }\n\n private _ensureClonedFillerMaterial(mesh: THREE.Mesh): void {\n const mat = mesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType === CmpMatType.ANIMATION_CLONE) return;\n mesh.material = this.getMat(CmpMatCategory.DARK_GRAY).clone();\n (mesh.material as THREE.MeshLambertMaterial).userData.matType = CmpMatType.ANIMATION_CLONE;\n }\n\n private _restoreSharedFillerMaterial(mesh: THREE.Mesh): void {\n const mat = mesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType !== CmpMatType.ANIMATION_CLONE) return;\n mat.dispose();\n mesh.material = this.getMat(CmpMatCategory.DARK_GRAY);\n }\n\n private _cleanupMixer(object3D: THREE.Object3D): void {\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n if (mixer) {\n mixer.stopAllAction();\n mixer.uncacheRoot(object3D);\n delete object3D.userData.mixer;\n }\n delete object3D.userData.currentAction;\n delete object3D.userData.currentClip;\n delete object3D.userData.currentActionStart;\n }\n\n /**\n * Animate sumFiller and carryFiller fill colors over the transition span.\n * Builds a single AnimationClip with up to two target tracks per filler\n * (color, emissive, emissiveIntensity) — only for fillers whose value changes.\n */\n private _animateFillers(\n object3D: THREE.Object3D,\n sumFiller: THREE.Mesh,\n carryFiller: THREE.Mesh,\n state: ComponentState,\n prevSumHigh: boolean,\n nextSumHigh: boolean,\n prevCarryHigh: boolean,\n nextCarryHigh: boolean\n ): void {\n if (object3D.userData.currentActionStart === state.startTick) return;\n\n const tps = this._animationContext!.ticksPerSecond;\n const span = state.expirationTick - state.startTick;\n const durationSeconds = span / tps;\n\n this._ensureClonedFillerMaterial(sumFiller);\n this._ensureClonedFillerMaterial(carryFiller);\n\n // Snap fillers whose value does not change to the (unchanged) color.\n if (prevSumHigh === nextSumHigh) {\n this._setFillerColor(sumFiller, nextSumHigh);\n }\n if (prevCarryHigh === nextCarryHigh) {\n this._setFillerColor(carryFiller, nextCarryHigh);\n }\n\n const tracks: THREE.KeyframeTrack[] = [];\n if (prevSumHigh !== nextSumHigh) {\n this._pushFillerTracks(tracks, 'sumFiller', sumFiller, nextSumHigh, durationSeconds);\n }\n if (prevCarryHigh !== nextCarryHigh) {\n this._pushFillerTracks(tracks, 'carryFiller', carryFiller, nextCarryHigh, durationSeconds);\n }\n\n // No tracks: nothing to animate (both outputs unchanged). Still record start to dedupe.\n if (tracks.length === 0) {\n object3D.userData.currentActionStart = state.startTick;\n return;\n }\n\n let mixer: THREE.AnimationMixer = object3D.userData.mixer;\n if (!mixer) {\n mixer = new THREE.AnimationMixer(object3D);\n object3D.userData.mixer = mixer;\n }\n if (object3D.userData.currentAction) {\n (object3D.userData.currentAction as THREE.AnimationAction).stop();\n }\n if (object3D.userData.currentClip) {\n mixer.uncacheClip(object3D.userData.currentClip as THREE.AnimationClip);\n }\n\n const clip = new THREE.AnimationClip('adderFillers', durationSeconds, tracks);\n const action = mixer.clipAction(clip);\n action.loop = THREE.LoopOnce;\n action.clampWhenFinished = true;\n action.play();\n\n object3D.userData.currentActionStart = state.startTick;\n object3D.userData.currentAction = action;\n object3D.userData.currentClip = clip;\n }\n\n private _pushFillerTracks(\n tracks: THREE.KeyframeTrack[],\n meshName: string,\n mesh: THREE.Mesh,\n toHigh: boolean,\n durationSeconds: number\n ): void {\n const mat = mesh.material as THREE.MeshLambertMaterial;\n const fromRGB = [mat.color.r, mat.color.g, mat.color.b];\n const toColor = toHigh ? this.FILLER_COLOR_HIGH : this.FILLER_COLOR_LOW;\n const toRGB = [toColor.r, toColor.g, toColor.b];\n const fromIntensity = mat.emissiveIntensity;\n const toIntensity = toHigh\n ? this.FILLER_EMISSIVE_HIGH_INTENSITY\n : this.FILLER_EMISSIVE_LOW_INTENSITY;\n\n tracks.push(\n new THREE.ColorKeyframeTrack(\n `${meshName}.material.color`,\n [0, durationSeconds],\n [...fromRGB, ...toRGB]\n ),\n new THREE.ColorKeyframeTrack(\n `${meshName}.material.emissive`,\n [0, durationSeconds],\n [...fromRGB, ...toRGB]\n ),\n new THREE.NumberKeyframeTrack(\n `${meshName}.material.emissiveIntensity`,\n [0, durationSeconds],\n [fromIntensity, toIntensity]\n )\n );\n }\n\n /** Find a child mesh by its userData.part name */\n protected findPartMesh(object3D: THREE.Object3D, part: string): THREE.Mesh | null {\n let found: THREE.Mesh | null = null;\n object3D.traverse((child) => {\n if (child instanceof THREE.Mesh && child.userData.part === part) {\n found = child as THREE.Mesh;\n }\n });\n return found;\n }\n}\n","import { ComponentVisualFactoryBase } from '../ComponentVisualFactory';\nimport { ComponentType, type Component, type ComponentState } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport {\n KuKoGeometry,\n} from '../../utils/GeometryUtils';\nimport type { ConfigFormDefinition, VisualContext } from '../../types';\nimport { CmpMatCategory, CmpMatType } from '../types';\n\n/**\n * Visual factory for EightBitAdder components.\n *\n * Creates:\n * - An `envelope` frame (KuKoGeometry)\n * - A vertical `separator` dividing carry/sum cells\n * - 7 horizontal separators dividing ith cells\n * - 16 cells carry i / sum i representing state\n * cells materials are animated blue↔red following the component state\n * - pin groups for vcc, 2*8 inputs, 8*sums, carryIn, carryOut and gnd\n *\n */\nexport class EightBitAdderVisualFactory extends ComponentVisualFactoryBase {\n private readonly ENVELOPE_GEOM = KuKoGeometry(4.4,4.4,16.8,20,0.4,0.4);\n private readonly VERTICAL_SEPARATOR_GEOM = new THREE.BoxGeometry(0.2, 0.4,16.2);\n private readonly HORIZONTAL_SEPARATOR_GEOM = new THREE.BoxGeometry(4, 0.4,0.2);\n private readonly CELL_FILLER_GEOM = new THREE.BoxGeometry(1.9, 0.4, 1.825);\n\n protected readonly FILLER_COLOR_HIGH = new THREE.Color(0xff4444);\n protected readonly FILLER_COLOR_LOW = new THREE.Color(0x4444ff);\n protected readonly FILLER_EMISSIVE_HIGH_INTENSITY = 0.5;\n protected readonly FILLER_EMISSIVE_LOW_INTENSITY = 0.2;\n\n constructor() {\n super();\n this._componentType = ComponentType.EightBitAdder;\n }\n\n createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n const hitbox = this.createComponentHitbox(\n component.id,\n group.id,\n 9,2.5,17);\n group.add(hitbox);\n\n // Envelope frame\n const envelope = new THREE.Mesh(this.ENVELOPE_GEOM, this.getMat(CmpMatCategory.WHITE));\n envelope.userData = {\n type: 'component',\n componentId: component.id,\n part: 'envelope',\n };\n envelope.rotateX(-Math.PI / 2);\n envelope.position.set(0, 0, 0);\n group.add(envelope);\n\n // vertical Separator\n const separator = new THREE.Mesh(this.VERTICAL_SEPARATOR_GEOM, this.getMat(CmpMatCategory.WHITE));\n separator.userData = {\n type: 'component',\n componentId: component.id,\n part: 'vertical_separator',\n };\n separator.position.set(2, 0.2, 0);\n group.add(separator);\n\n let outIdx = 0;\n while(outIdx < 8){\n if(outIdx < 7){\n const hSeparator = new THREE.Mesh(this.HORIZONTAL_SEPARATOR_GEOM, this.getMat(CmpMatCategory.WHITE));\n hSeparator.userData = {\n type: 'component',\n componentId: component.id,\n part: `horizontal_separator-${outIdx}`,\n };\n hSeparator.position.set(2, 0.2, -6.075 + 2.025*outIdx);\n group.add(hSeparator);\n }\n const carryCell = new THREE.Mesh(this.CELL_FILLER_GEOM, this.getMat(CmpMatCategory.DARK_GRAY));\n carryCell.name = `carry_cell-${outIdx}`; // required for AnimationMixer property binding\n carryCell.userData = {\n type: 'component',\n componentId: component.id,\n part: `carry_cell-${outIdx}`,\n };\n carryCell.position.set(0.95, 0.2, -7.0875 + 2.025*outIdx);\n group.add(carryCell);\n const sumCell = new THREE.Mesh(this.CELL_FILLER_GEOM, this.getMat(CmpMatCategory.DARK_GRAY));\n sumCell.name = `sum_cell-${outIdx}`; // required for AnimationMixer property binding\n sumCell.userData = {\n type: 'component',\n componentId: component.id,\n part: `sum_cell-${outIdx}`,\n };\n sumCell.position.set(3.05, 0.2, -7.0875 + 2.025*outIdx);\n group.add(sumCell);\n\n outIdx +=1;\n }\n\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n return group;\n }\n\n protected createPinsVisual(component: Component, context: VisualContext, group: THREE.Group) {\n const vccNode = context.getENode(component.pins[0]!);\n if (vccNode) {\n const vccGroup = this.createPinGroup(vccNode, 'top');\n vccGroup.position.set(2, 0, -8.4);\n group.add(vccGroup);\n }\n\n const carryInNode = context.getENode(component.pins[1]!);\n if (carryInNode) {\n const carryInGroup = this.createPinGroup(carryInNode, 'top');\n carryInGroup.position.set(0.8, 0, -8.4);\n group.add(carryInGroup);\n }\n\n let pinIdx = 2;\n // input A\n while(pinIdx < 10){\n const node = context.getENode(component.pins[pinIdx]!);\n if(!node){\n pinIdx += 1;\n continue;\n }\n const pinGroup = this.createPinGroup(node, 'left', new THREE.Euler(0.51, 0, 0));\n\n const interfaceIndex = pinIdx - 2;\n pinGroup.position.set(-0.2 - interfaceIndex*0.55, 0, -8 + 1.05 * interfaceIndex);\n group.add(pinGroup);\n pinIdx += 1;\n }\n\n // input B\n while(pinIdx < 18){\n const node = context.getENode(component.pins[pinIdx]!);\n if(!node){\n pinIdx += 1;\n continue;\n }\n const pinGroup = this.createPinGroup(node, 'left', new THREE.Euler(-0.51, 0, 0));\n\n const interfaceIndex = pinIdx - 10;\n pinGroup.position.set(-4.05 + interfaceIndex*0.55, 0, 0.65 + 1.05 * interfaceIndex);\n group.add(pinGroup);\n pinIdx += 1;\n }\n\n // output sum\n while(pinIdx < 26){\n const node = context.getENode(component.pins[pinIdx]!);\n if(!node){\n pinIdx += 1;\n continue;\n }\n const pinGroup = this.createPinGroup(node, 'right');\n\n const interfaceIndex = pinIdx - 18;\n pinGroup.position.set(4.4, 0, -7 + 2 * interfaceIndex);\n group.add(pinGroup);\n pinIdx += 1;\n }\n\n const carryOutNode = context.getENode(component.pins[26]!);\n if (carryOutNode) {\n const carryOutGroup = this.createPinGroup(carryOutNode, 'bottom');\n carryOutGroup.position.set(3.4, 0, 8.4);\n group.add(carryOutGroup);\n }\n\n const gndNode = context.getENode(component.pins[27]!);\n if (gndNode) {\n const gndGroup = this.createPinGroup(gndNode, 'bottom');\n gndGroup.position.set(2, 0, 8.4);\n group.add(gndGroup);\n }\n }\n\n override getConfigFormDefinition(config?: Map<string, string>): ConfigFormDefinition | null {\n const logicFamily = config?.get('defaultLogicFamily') ?? 'CMOS1';\n return {\n fields: [\n {\n key: 'defaultLogicFamily',\n type: 'dropdown',\n options: { CMOS: 'CMOS1', TTL: 'TTL1', Sandbox: 'Sandbox' },\n },\n { key: 'transitionSpan', type: 'number', min: 1, disabled: logicFamily !== 'Sandbox' },\n { key: 'initializationOrder', type: 'number' },\n ],\n };\n }\n\n override mapCoreConfigToForm(config: Map<string, string>): Map<string, any> {\n const formData = new Map<string, any>();\n formData.set('defaultLogicFamily', config.get('defaultLogicFamily') ?? 'CMOS1');\n formData.set('transitionSpan', parseFloat(config.get('transitionSpan') || '2'));\n formData.set('initializationOrder', parseFloat(config.get('initializationOrder') || '0'));\n return formData;\n }\n\n override mapFormToCoreConfig(formData: Map<string, any>): Map<string, string> {\n const config = new Map<string, string>();\n config.set('defaultLogicFamily', formData.get('defaultLogicFamily') ?? 'CMOS1');\n config.set('transitionSpan', formData.get('transitionSpan').toString());\n config.set('initializationOrder', formData.get('initializationOrder').toString() || null);\n return config;\n }\n\n\n override updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void {\n const cells = this._findCellMeshes(object3D);\n if (cells.length === 0) return;\n\n if (!state || !this._animationContext || state.state === 'indeterminate') {\n this._cleanupMixer(object3D);\n for (const { mesh } of cells) this._restoreSharedMaterial(mesh);\n return;\n }\n\n const isTransition = state.state.startsWith('to');\n const prevStable: string = isTransition\n ? (state.parameters.get('prevState') ?? '0000')\n : state.state;\n const nextStable: string = isTransition ? (state.nextState ?? prevStable) : state.state;\n\n const prevValue = parseInt(prevStable, 16);\n const nextValue = parseInt(nextStable, 16);\n\n // Stable state: snap colors, no animation\n if (!isTransition || !state.hasExpiration) {\n this._cleanupMixer(object3D);\n for (const { mesh, bitPos } of cells) {\n this._setCellColor(mesh, ((nextValue >> bitPos) & 1) !== 0);\n }\n return;\n }\n\n // Paused + transitional: snap to prev color\n if (this._animationContext.simulationStatus !== 'playing') {\n for (const { mesh, bitPos } of cells) {\n this._setCellColor(mesh, ((prevValue >> bitPos) & 1) !== 0);\n }\n return;\n }\n\n // Playing + transitional: animate cells whose bit changes\n this._animateCells(object3D, cells, state, prevValue, nextValue);\n }\n\n // ---------------------------------------------------------------------------\n // Material helpers\n // ---------------------------------------------------------------------------\n\n private _setCellColor(mesh: THREE.Mesh, high: boolean): void {\n this._ensureClonedMaterial(mesh);\n const mat = mesh.material as THREE.MeshLambertMaterial;\n const color = high ? this.FILLER_COLOR_HIGH : this.FILLER_COLOR_LOW;\n const intensity = high\n ? this.FILLER_EMISSIVE_HIGH_INTENSITY\n : this.FILLER_EMISSIVE_LOW_INTENSITY;\n mat.color.copy(color);\n mat.emissive.copy(color);\n mat.emissiveIntensity = intensity;\n }\n\n private _ensureClonedMaterial(mesh: THREE.Mesh): void {\n const mat = mesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType === CmpMatType.ANIMATION_CLONE) return;\n mesh.material = this.getMat(CmpMatCategory.DARK_GRAY).clone();\n (mesh.material as THREE.MeshLambertMaterial).userData.matType = CmpMatType.ANIMATION_CLONE;\n }\n\n private _restoreSharedMaterial(mesh: THREE.Mesh): void {\n const mat = mesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType !== CmpMatType.ANIMATION_CLONE) return;\n mat.dispose();\n mesh.material = this.getMat(CmpMatCategory.DARK_GRAY);\n }\n\n private _cleanupMixer(object3D: THREE.Object3D): void {\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n if (mixer) {\n mixer.stopAllAction();\n mixer.uncacheRoot(object3D);\n delete object3D.userData.mixer;\n }\n delete object3D.userData.currentAction;\n delete object3D.userData.currentClip;\n delete object3D.userData.currentActionStart;\n }\n\n // ---------------------------------------------------------------------------\n // Cell discovery\n // ---------------------------------------------------------------------------\n\n /** Collect all 16 cell meshes with their state bit position. */\n private _findCellMeshes(object3D: THREE.Object3D): { mesh: THREE.Mesh; bitPos: number; name: string }[] {\n const result: { mesh: THREE.Mesh; bitPos: number; name: string }[] = [];\n object3D.traverse((child) => {\n if (!(child instanceof THREE.Mesh)) return;\n const part = child.userData.part as string | undefined;\n if (!part) return;\n\n const sumMatch = part.match(/^sum_cell-(\\d)$/);\n if (sumMatch) {\n const i = parseInt(sumMatch[1]!);\n result.push({ mesh: child, bitPos: 2 * i, name: child.name });\n return;\n }\n const carryMatch = part.match(/^carry_cell-(\\d)$/);\n if (carryMatch) {\n const i = parseInt(carryMatch[1]!);\n result.push({ mesh: child, bitPos: 2 * i + 1, name: child.name });\n }\n });\n return result;\n }\n\n // ---------------------------------------------------------------------------\n // Animation\n // ---------------------------------------------------------------------------\n\n /**\n * Animate the 16 cells over the transition span.\n * Builds a single AnimationClip with tracks only for cells whose bit changes.\n */\n private _animateCells(\n object3D: THREE.Object3D,\n cells: { mesh: THREE.Mesh; bitPos: number; name: string }[],\n state: ComponentState,\n prevValue: number,\n nextValue: number\n ): void {\n if (object3D.userData.currentActionStart === state.startTick) return;\n\n const tps = this._animationContext!.ticksPerSecond;\n const span = state.expirationTick - state.startTick;\n const durationSeconds = span / tps;\n\n const tracks: THREE.KeyframeTrack[] = [];\n\n for (const { mesh, bitPos, name } of cells) {\n const prevHigh = ((prevValue >> bitPos) & 1) !== 0;\n const nextHigh = ((nextValue >> bitPos) & 1) !== 0;\n\n this._ensureClonedMaterial(mesh);\n\n if (prevHigh === nextHigh) {\n // No change — snap to stable color\n this._setCellColor(mesh, nextHigh);\n continue;\n }\n // Push color transition tracks for this cell\n this._pushCellTracks(tracks, name, mesh, nextHigh, durationSeconds);\n }\n\n // No tracks → nothing to animate. Record start to dedupe.\n if (tracks.length === 0) {\n object3D.userData.currentActionStart = state.startTick;\n return;\n }\n\n let mixer: THREE.AnimationMixer = object3D.userData.mixer;\n if (!mixer) {\n mixer = new THREE.AnimationMixer(object3D);\n object3D.userData.mixer = mixer;\n }\n if (object3D.userData.currentAction) {\n (object3D.userData.currentAction as THREE.AnimationAction).stop();\n }\n if (object3D.userData.currentClip) {\n mixer.uncacheClip(object3D.userData.currentClip as THREE.AnimationClip);\n }\n\n const clip = new THREE.AnimationClip('adderCells', durationSeconds, tracks);\n const action = mixer.clipAction(clip);\n action.loop = THREE.LoopOnce;\n action.clampWhenFinished = true;\n action.play();\n\n object3D.userData.currentActionStart = state.startTick;\n object3D.userData.currentAction = action;\n object3D.userData.currentClip = clip;\n }\n\n private _pushCellTracks(\n tracks: THREE.KeyframeTrack[],\n meshName: string,\n mesh: THREE.Mesh,\n toHigh: boolean,\n durationSeconds: number\n ): void {\n const mat = mesh.material as THREE.MeshLambertMaterial;\n const fromRGB = [mat.color.r, mat.color.g, mat.color.b];\n const toColor = toHigh ? this.FILLER_COLOR_HIGH : this.FILLER_COLOR_LOW;\n const toRGB = [toColor.r, toColor.g, toColor.b];\n const fromIntensity = mat.emissiveIntensity;\n const toIntensity = toHigh\n ? this.FILLER_EMISSIVE_HIGH_INTENSITY\n : this.FILLER_EMISSIVE_LOW_INTENSITY;\n\n tracks.push(\n new THREE.ColorKeyframeTrack(\n `${meshName}.material.color`,\n [0, durationSeconds],\n [...fromRGB, ...toRGB]\n ),\n new THREE.ColorKeyframeTrack(\n `${meshName}.material.emissive`,\n [0, durationSeconds],\n [...fromRGB, ...toRGB]\n ),\n new THREE.NumberKeyframeTrack(\n `${meshName}.material.emissiveIntensity`,\n [0, durationSeconds],\n [fromIntensity, toIntensity]\n )\n );\n }\n}\n","import { ComponentVisualFactoryBase } from '../ComponentVisualFactory';\nimport { ComponentType, type Component, type ComponentState } from 'simple-circuit-engine/core';\nimport * as THREE from 'three';\nimport {\n EmptyRectangleGeometry,\n RectangleWithNailGeometry\n} from '../../utils/GeometryUtils';\nimport type { ConfigFormDefinition, VisualContext } from '../../types';\nimport { CmpMatCategory, CmpMatType } from '../types';\n\n/**\n * Visual factory for EightBitOnesComplement components.\n *\n * Creates:\n * - An `envelope` frame (EmptyRectangleGeometry)\n * - A vertical `separator` dividing flag area / outputs cells\n * - 7 horizontal separators dividing ith cells\n * - 2 rectangles with nail filler, 3 animated box geometry and 2 filler boxes for flag area\n * - 8 cells outputs i representing output state\n * cells materials are animated blue↔red following the component state\n * - pin groups for vcc, 8 inputs, 8*outputs and gnd\n *\n */\nexport class EightBitOnesComplementVisualFactory extends ComponentVisualFactoryBase {\n private readonly ENVELOPE_GEOM = EmptyRectangleGeometry(2.4,8.4,0.2,0.4);\n private readonly VERTICAL_SEPARATOR_GEOM = new THREE.BoxGeometry(0.1, 0.4,8.1);\n private readonly HORIZONTAL_SEPARATOR_GEOM = new THREE.BoxGeometry(1.05, 0.4,0.1);\n private readonly CELL_FILLER_GEOM = new THREE.BoxGeometry(0.95, 0.4, 0.9125);\n\n private readonly INDICATOR_MINUS_GEOM = new THREE.BoxGeometry(0.25, 0.4, 1);\n private readonly INDICATOR_PLUS_GEOM = new THREE.BoxGeometry(0.35, 0.4, 0.25);\n\n private readonly INDICATOR_FILLER_GEOM = RectangleWithNailGeometry(\n 0.95,\n 3.875,\n 0.25,\n 0.375,\n 0.475,\n false,\n 0.4\n );\n\n protected readonly FILLER_COLOR_HIGH = new THREE.Color(0xff4444);\n protected readonly FILLER_COLOR_LOW = new THREE.Color(0x4444ff);\n protected readonly FILLER_COLOR_NEUTRAL = new THREE.Color(0xffffff);\n protected readonly FILLER_EMISSIVE_BLACK = new THREE.Color(0x000000);\n protected readonly FILLER_EMISSIVE_HIGH_INTENSITY = 0.5;\n protected readonly FILLER_EMISSIVE_LOW_INTENSITY = 0.2;\n protected readonly FILLER_EMISSIVE_NEUTRAL_INTENSITY = 0;\n\n constructor() {\n super();\n this._componentType = ComponentType.EightBitOnesComplement;\n }\n\n createVisual(component: Component, context: VisualContext): THREE.Object3D {\n if (component.type !== this._componentType) {\n throw new Error(`Factory mismatch: expected \"${this._componentType}\", got \"${component.type}\"`);\n }\n const group = new THREE.Group();\n group.userData = {\n type: 'componentGroup',\n componentId: component.id,\n componentType: component.type,\n };\n\n const hitbox = this.createComponentHitbox(\n component.id,\n group.id,\n 3,2.5,8.5);\n group.add(hitbox);\n\n // Envelope frame\n const envelope = new THREE.Mesh(this.ENVELOPE_GEOM, this.getMat(CmpMatCategory.WHITE));\n envelope.userData = {\n type: 'component',\n componentId: component.id,\n part: 'envelope',\n };\n envelope.rotateX(-Math.PI / 2);\n envelope.position.set(0, 0, 0);\n group.add(envelope);\n\n // vertical Separator\n const separator = new THREE.Mesh(this.VERTICAL_SEPARATOR_GEOM, this.getMat(CmpMatCategory.WHITE));\n separator.userData = {\n type: 'component',\n componentId: component.id,\n part: 'vertical_separator',\n };\n separator.position.set(0, 0.2, 0);\n group.add(separator);\n\n let outIdx = 0;\n while(outIdx < 8){\n if(outIdx < 7){\n const hSeparator = new THREE.Mesh(this.HORIZONTAL_SEPARATOR_GEOM, this.getMat(CmpMatCategory.WHITE));\n hSeparator.userData = {\n type: 'component',\n componentId: component.id,\n part: `horizontal_separator-${outIdx}`,\n };\n hSeparator.position.set(0.525, 0.2, -3.0375 + 1.0125*outIdx);\n group.add(hSeparator);\n }\n const outputCell = new THREE.Mesh(this.CELL_FILLER_GEOM, this.getMat(CmpMatCategory.DARK_GRAY));\n outputCell.name = `output_cell-${outIdx}`; // required for AnimationMixer property binding\n outputCell.userData = {\n type: 'component',\n componentId: component.id,\n part: `output_cell-${outIdx}`,\n };\n outputCell.position.set(0.525, 0.2, -3.54375 + 1.0125*outIdx);\n group.add(outputCell);\n\n outIdx +=1;\n }\n\n // inverter indicator part\n // big fillers\n const upperFiller = new THREE.Mesh(this.INDICATOR_FILLER_GEOM, this.getMat(CmpMatCategory.WHITE));\n upperFiller.userData = {\n type: 'component',\n componentId: component.id,\n part: 'indicator_upper_filler',\n };\n upperFiller.rotateX(-Math.PI / 2);\n upperFiller.rotateY(-Math.PI);\n upperFiller.position.set(-0.525, 0.4, -2.0625);\n group.add(upperFiller);\n\n const lowerFiller = new THREE.Mesh(this.INDICATOR_FILLER_GEOM, this.getMat(CmpMatCategory.WHITE));\n lowerFiller.userData = {\n type: 'component',\n componentId: component.id,\n part: 'indicator_lower_filler',\n };\n lowerFiller.rotateX(Math.PI / 2);\n lowerFiller.rotateY(-Math.PI);\n lowerFiller.position.set(-0.525, 0, 2.0625);\n group.add(lowerFiller);\n\n const minusCell = new THREE.Mesh(this.INDICATOR_MINUS_GEOM, this.getMat(CmpMatCategory.DARK_GRAY));\n minusCell.name = `indicator_minus_cell`; // required for AnimationMixer property binding\n minusCell.userData = {\n type: 'component',\n componentId: component.id,\n part: `indicator_minus_cell`,\n };\n minusCell.position.set(-0.525, 0.2,0);\n group.add(minusCell);\n\n const plusLeftCell = new THREE.Mesh(this.INDICATOR_PLUS_GEOM, this.getMat(CmpMatCategory.WHITE));\n plusLeftCell.name = `indicator_plus_left_cell`; // required for AnimationMixer property binding\n plusLeftCell.userData = {\n type: 'component',\n componentId: component.id,\n part: `plus_left_cell`,\n };\n plusLeftCell.position.set(-0.825, 0.2,0);\n group.add(plusLeftCell);\n\n const plusRightCell = new THREE.Mesh(this.INDICATOR_PLUS_GEOM, this.getMat(CmpMatCategory.WHITE));\n plusRightCell.name = `indicator_plus_right_cell`; // required for AnimationMixer property binding\n plusRightCell.userData = {\n type: 'component',\n componentId: component.id,\n part: `plus_right_cell`,\n };\n plusRightCell.position.set(-0.225, 0.2,0);\n group.add(plusRightCell);\n\n\n\n\n if (component.pins.length > 0) {\n this.createPinsVisual(component, context, group);\n }\n\n return group;\n }\n\n protected createPinsVisual(component: Component, context: VisualContext, group: THREE.Group) {\n const vccNode = context.getENode(component.pins[0]!);\n if (vccNode) {\n const vccGroup = this.createPinGroup(vccNode, 'top');\n vccGroup.position.set(0, 0, -4.2);\n group.add(vccGroup);\n }\n\n const invertNode = context.getENode(component.pins[1]!);\n if (invertNode) {\n const invertGroup = this.createPinGroup(invertNode, 'top');\n invertGroup.position.set(-0.8, 0, -4.2);\n group.add(invertGroup);\n }\n\n let pinIdx = 2;\n // input\n while(pinIdx < 10){\n const node = context.getENode(component.pins[pinIdx]!);\n if(!node){\n pinIdx += 1;\n continue;\n }\n const pinGroup = this.createPinGroup(node, 'left');\n\n const interfaceIndex = pinIdx - 2;\n pinGroup.position.set(-1.2, 0, -3.85 + 1.1 * interfaceIndex);\n group.add(pinGroup);\n pinIdx += 1;\n }\n\n // output\n while(pinIdx < 18){\n const node = context.getENode(component.pins[pinIdx]!);\n if(!node){\n pinIdx += 1;\n continue;\n }\n const pinGroup = this.createPinGroup(node, 'right');\n\n const interfaceIndex = pinIdx - 10;\n pinGroup.position.set(1.2, 0, -3.85 + 1.1 * interfaceIndex);\n group.add(pinGroup);\n pinIdx += 1;\n }\n\n const gndNode = context.getENode(component.pins[18]!);\n if (gndNode) {\n const gndGroup = this.createPinGroup(gndNode, 'bottom');\n gndGroup.position.set(0, 0, 4.2);\n group.add(gndGroup);\n }\n }\n\n override getConfigFormDefinition(config?: Map<string, string>): ConfigFormDefinition | null {\n const logicFamily = config?.get('defaultLogicFamily') ?? 'CMOS1';\n return {\n fields: [\n {\n key: 'defaultLogicFamily',\n type: 'dropdown',\n options: { CMOS: 'CMOS1', TTL: 'TTL1', Sandbox: 'Sandbox' },\n },\n { key: 'transitionSpan', type: 'number', min: 1, disabled: logicFamily !== 'Sandbox' },\n { key: 'initializationOrder', type: 'number' },\n ],\n };\n }\n\n override mapCoreConfigToForm(config: Map<string, string>): Map<string, any> {\n const formData = new Map<string, any>();\n formData.set('defaultLogicFamily', config.get('defaultLogicFamily') ?? 'CMOS1');\n formData.set('transitionSpan', parseFloat(config.get('transitionSpan') || '2'));\n formData.set('initializationOrder', parseFloat(config.get('initializationOrder') || '0'));\n return formData;\n }\n\n override mapFormToCoreConfig(formData: Map<string, any>): Map<string, string> {\n const config = new Map<string, string>();\n config.set('defaultLogicFamily', formData.get('defaultLogicFamily') ?? 'CMOS1');\n config.set('transitionSpan', formData.get('transitionSpan').toString());\n config.set('initializationOrder', formData.get('initializationOrder').toString() || null);\n return config;\n }\n\n\n override updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void {\n const cells = this._findCellMeshes(object3D);\n if (cells.length === 0) return;\n\n if (!state || !this._animationContext || state.state === 'indeterminate') {\n this._cleanupMixer(object3D);\n for (const cell of cells) this._restoreSharedMaterial(cell);\n return;\n }\n\n const isTransition = state.state.startsWith('to');\n const prevStable: string = isTransition\n ? (state.parameters.get('prevState') ?? '000')\n : state.state;\n const nextStable: string = isTransition ? (state.nextState ?? prevStable) : state.state;\n\n const prevValue = parseInt(prevStable, 16);\n const nextValue = parseInt(nextStable, 16);\n\n // Stable state: snap colors, no animation\n if (!isTransition || !state.hasExpiration) {\n this._cleanupMixer(object3D);\n for (const cell of cells) {\n this._setCellColor(cell, ((nextValue >> cell.bitPos) & 1) !== 0);\n }\n return;\n }\n\n // Paused + transitional: snap to prev color\n if (this._animationContext.simulationStatus !== 'playing') {\n for (const cell of cells) {\n this._setCellColor(cell, ((prevValue >> cell.bitPos) & 1) !== 0);\n }\n return;\n }\n\n // Playing + transitional: animate cells whose bit changes\n this._animateCells(object3D, cells, state, prevValue, nextValue);\n }\n\n // ---------------------------------------------------------------------------\n // Cell kind → target color mapping\n // ---------------------------------------------------------------------------\n\n /**\n * Resolve the target color state for a cell given its kind and the bit value:\n * - `output`: high→red, low→blue\n * - `minus`: high→blue, low→red (inverted vs output — blue minus = complement active)\n * - `plus`: high→white (blends with envelope, hiding the +), low→red\n */\n private _cellTarget(kind: CellKind, high: boolean): CellTarget {\n if (kind === 'plus') {\n return high\n ? { color: this.FILLER_COLOR_NEUTRAL, emissive: this.FILLER_EMISSIVE_BLACK, intensity: this.FILLER_EMISSIVE_NEUTRAL_INTENSITY }\n : { color: this.FILLER_COLOR_HIGH, emissive: this.FILLER_COLOR_HIGH, intensity: this.FILLER_EMISSIVE_HIGH_INTENSITY };\n }\n const showHigh = kind === 'minus' ? !high : high;\n return showHigh\n ? { color: this.FILLER_COLOR_HIGH, emissive: this.FILLER_COLOR_HIGH, intensity: this.FILLER_EMISSIVE_HIGH_INTENSITY }\n : { color: this.FILLER_COLOR_LOW, emissive: this.FILLER_COLOR_LOW, intensity: this.FILLER_EMISSIVE_LOW_INTENSITY };\n }\n\n // ---------------------------------------------------------------------------\n // Material helpers\n // ---------------------------------------------------------------------------\n\n private _setCellColor(cell: CellInfo, high: boolean): void {\n this._ensureClonedMaterial(cell.mesh);\n const mat = cell.mesh.material as THREE.MeshLambertMaterial;\n const target = this._cellTarget(cell.kind, high);\n mat.color.copy(target.color);\n mat.emissive.copy(target.emissive);\n mat.emissiveIntensity = target.intensity;\n }\n\n private _ensureClonedMaterial(mesh: THREE.Mesh): void {\n const mat = mesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType === CmpMatType.ANIMATION_CLONE) return;\n mesh.material = this.getMat(CmpMatCategory.DARK_GRAY).clone();\n (mesh.material as THREE.MeshLambertMaterial).userData.matType = CmpMatType.ANIMATION_CLONE;\n }\n\n private _restoreSharedMaterial(cell: CellInfo): void {\n const mat = cell.mesh.material as THREE.MeshLambertMaterial;\n if (mat.userData.matType !== CmpMatType.ANIMATION_CLONE) return;\n mat.dispose();\n cell.mesh.material = this.getMat(cell.originalCategory);\n }\n\n private _cleanupMixer(object3D: THREE.Object3D): void {\n const mixer = object3D.userData.mixer as THREE.AnimationMixer | undefined;\n if (mixer) {\n mixer.stopAllAction();\n mixer.uncacheRoot(object3D);\n delete object3D.userData.mixer;\n }\n delete object3D.userData.currentAction;\n delete object3D.userData.currentClip;\n delete object3D.userData.currentActionStart;\n }\n\n // ---------------------------------------------------------------------------\n // Cell discovery\n // ---------------------------------------------------------------------------\n\n /**\n * Collect all animated cells (8 outputs + minus + 2 plus parts).\n * Output cells read bits 0–7; the three indicator cells all read bit 8\n * (the invert flag) but render it with different color rules.\n */\n private _findCellMeshes(object3D: THREE.Object3D): CellInfo[] {\n const result: CellInfo[] = [];\n object3D.traverse((child) => {\n if (!(child instanceof THREE.Mesh)) return;\n const part = child.userData.part as string | undefined;\n if (!part) return;\n\n const outputMatch = part.match(/^output_cell-(\\d)$/);\n if (outputMatch) {\n const i = parseInt(outputMatch[1]!);\n result.push({ mesh: child, bitPos: i, name: child.name, kind: 'output', originalCategory: CmpMatCategory.DARK_GRAY });\n return;\n }\n if (part === 'indicator_minus_cell') {\n result.push({ mesh: child, bitPos: 8, name: child.name, kind: 'minus', originalCategory: CmpMatCategory.DARK_GRAY });\n return;\n }\n if (part === 'plus_left_cell' || part === 'plus_right_cell') {\n result.push({ mesh: child, bitPos: 8, name: child.name, kind: 'plus', originalCategory: CmpMatCategory.WHITE });\n }\n });\n return result;\n }\n\n // ---------------------------------------------------------------------------\n // Animation\n // ---------------------------------------------------------------------------\n\n /**\n * Animate all cells over the transition span.\n * Builds a single AnimationClip with tracks only for cells whose bit changes.\n */\n private _animateCells(\n object3D: THREE.Object3D,\n cells: CellInfo[],\n state: ComponentState,\n prevValue: number,\n nextValue: number\n ): void {\n if (object3D.userData.currentActionStart === state.startTick) return;\n\n const tps = this._animationContext!.ticksPerSecond;\n const span = state.expirationTick - state.startTick;\n const durationSeconds = span / tps;\n\n const tracks: THREE.KeyframeTrack[] = [];\n\n for (const cell of cells) {\n const prevHigh = ((prevValue >> cell.bitPos) & 1) !== 0;\n const nextHigh = ((nextValue >> cell.bitPos) & 1) !== 0;\n\n this._ensureClonedMaterial(cell.mesh);\n\n if (prevHigh === nextHigh) {\n this._setCellColor(cell, nextHigh);\n continue;\n }\n this._pushCellTracks(tracks, cell, nextHigh, durationSeconds);\n }\n\n if (tracks.length === 0) {\n object3D.userData.currentActionStart = state.startTick;\n return;\n }\n\n let mixer: THREE.AnimationMixer = object3D.userData.mixer;\n if (!mixer) {\n mixer = new THREE.AnimationMixer(object3D);\n object3D.userData.mixer = mixer;\n }\n if (object3D.userData.currentAction) {\n (object3D.userData.currentAction as THREE.AnimationAction).stop();\n }\n if (object3D.userData.currentClip) {\n mixer.uncacheClip(object3D.userData.currentClip as THREE.AnimationClip);\n }\n\n const clip = new THREE.AnimationClip('onesComplementCells', durationSeconds, tracks);\n const action = mixer.clipAction(clip);\n action.loop = THREE.LoopOnce;\n action.clampWhenFinished = true;\n action.play();\n\n object3D.userData.currentActionStart = state.startTick;\n object3D.userData.currentAction = action;\n object3D.userData.currentClip = clip;\n }\n\n private _pushCellTracks(\n tracks: THREE.KeyframeTrack[],\n cell: CellInfo,\n toHigh: boolean,\n durationSeconds: number\n ): void {\n const mat = cell.mesh.material as THREE.MeshLambertMaterial;\n const target = this._cellTarget(cell.kind, toHigh);\n const fromColor = [mat.color.r, mat.color.g, mat.color.b];\n const fromEmissive = [mat.emissive.r, mat.emissive.g, mat.emissive.b];\n const toColor = [target.color.r, target.color.g, target.color.b];\n const toEmissive = [target.emissive.r, target.emissive.g, target.emissive.b];\n\n tracks.push(\n new THREE.ColorKeyframeTrack(\n `${cell.name}.material.color`,\n [0, durationSeconds],\n [...fromColor, ...toColor]\n ),\n new THREE.ColorKeyframeTrack(\n `${cell.name}.material.emissive`,\n [0, durationSeconds],\n [...fromEmissive, ...toEmissive]\n ),\n new THREE.NumberKeyframeTrack(\n `${cell.name}.material.emissiveIntensity`,\n [0, durationSeconds],\n [mat.emissiveIntensity, target.intensity]\n )\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Cell typing\n// ---------------------------------------------------------------------------\n\ntype CellKind = 'output' | 'minus' | 'plus';\n\ntype CellInfo = {\n mesh: THREE.Mesh;\n bitPos: number;\n name: string;\n kind: CellKind;\n originalCategory: CmpMatCategory;\n};\n\ntype CellTarget = {\n color: THREE.Color;\n emissive: THREE.Color;\n intensity: number;\n};\n","import {ComponentType} from 'simple-circuit-engine/core';\nimport {\n AdderVisualFactory,\n BatteryVisualFactory,\n ClockVisualFactory,\n DoubleThrowSwitchVisualFactory,\n EightBitAdderVisualFactory, EightBitOnesComplementVisualFactory,\n HalfAdderVisualFactory,\n type IGroupedFactoryRegistry,\n InverterVisualFactory,\n LabelVisualFactory,\n LightbulbVisualFactory,\n Nand4GateVisualFactory,\n Nand8GateVisualFactory,\n NandGateVisualFactory,\n Nor4GateVisualFactory,\n Nor8GateVisualFactory,\n NorGateVisualFactory,\n RectangleLEDVisualFactory,\n RelayVisualFactory,\n SmallLEDVisualFactory,\n SwitchVisualFactory,\n Xor4GateVisualFactory,\n Xor8GateVisualFactory,\n XorGateVisualFactory,\n} from './shared/components';\n\n/**\n * Register all basic components visual factories in the basic group\n * Basic components are : Battery, Clock, Label, Switches, Lightbulb, RectangleLED, Relay, SmallLED\n * @public\n * @param registry - A grouped factory registry to populate\n * @returns The input registry for chaining\n */\nexport function registerBasicComponentsFactories(\n registry: IGroupedFactoryRegistry\n): IGroupedFactoryRegistry {\n return registry.addGroup('basic', 'Basic Components', (group) =>\n group\n .add(ComponentType.Battery, new BatteryVisualFactory())\n .add(ComponentType.Clock, new ClockVisualFactory())\n .add(ComponentType.Label, new LabelVisualFactory())\n .add(ComponentType.Switch, new SwitchVisualFactory())\n .add(ComponentType.DoubleThrowSwitch, new DoubleThrowSwitchVisualFactory())\n .add(ComponentType.Lightbulb, new LightbulbVisualFactory())\n .add(ComponentType.RectangleLED, new RectangleLEDVisualFactory())\n .add(ComponentType.Relay, new RelayVisualFactory())\n .add(ComponentType.SmallLED, new SmallLEDVisualFactory())\n );\n}\n\n/**\n * Register all logic gates components visual factories in the gates group\n * gates are : Inverter, NAND (2,4,8 inputs), NOR (2,4,8 inputs) and XOR (2,4,8 inputs)\n * AND/OR are gotten by changing the activationLogic of NAND/NOR\n * @public\n * @param registry - A grouped factory registry to populate\n * @returns The input registry for chaining\n */\nexport function registerGatesComponentsFactories(\n registry: IGroupedFactoryRegistry\n): IGroupedFactoryRegistry {\n return registry.addGroup('gates', 'Logic Gates', (group) =>\n group\n .add(ComponentType.Inverter, new InverterVisualFactory())\n .add(ComponentType.NandGate, new NandGateVisualFactory())\n .add(ComponentType.Nand4Gate, new Nand4GateVisualFactory())\n .add(ComponentType.Nand8Gate, new Nand8GateVisualFactory())\n .add(ComponentType.NorGate, new NorGateVisualFactory())\n .add(ComponentType.Nor4Gate, new Nor4GateVisualFactory())\n .add(ComponentType.Nor8Gate, new Nor8GateVisualFactory())\n .add(ComponentType.XorGate, new XorGateVisualFactory())\n .add(ComponentType.Xor4Gate, new Xor4GateVisualFactory())\n .add(ComponentType.Xor8Gate, new Xor8GateVisualFactory())\n );\n}\n\n/**\n * Register all arithmetic components visual factories in the arithmetic group\n * Arithmetic components are : HalfAdder\n * @public\n * @param registry - A grouped factory registry to populate\n * @returns The input registry for chaining\n */\nexport function registerArithmeticComponentsFactories(\n registry: IGroupedFactoryRegistry\n): IGroupedFactoryRegistry {\n return registry.addGroup('arithmetic', 'Arithmetic', (group) =>\n group\n .add(ComponentType.HalfAdder, new HalfAdderVisualFactory())\n .add(ComponentType.Adder, new AdderVisualFactory())\n .add(ComponentType.EightBitAdder, new EightBitAdderVisualFactory())\n .add(ComponentType.EightBitOnesComplement, new EightBitOnesComplementVisualFactory())\n );\n}\n"],"mappings":";;;;;;;;;AAyBA,IAAa,KAAb,MAAgE;AAAA,EAC9D,YAAqD,oBAAI,IAAA;AAAA,EAYzD,GAA6B,GAAU,GAAgD;AACrF,IAAK,KAAK,UAAU,IAAI,CAAA,KACtB,KAAK,UAAU,IAAI,GAAO,CAAA,CAAE,GAE9B,KAAK,UAAU,IAAI,CAAA,EAAQ,KAAK,CAAA;AAAA;EAYlC,IAA8B,GAAU,GAAgD;AACtF,UAAM,IAAY,KAAK,UAAU,IAAI,CAAA;AACrC,QAAI,GAAW;AACb,YAAM,IAAQ,EAAU,QAAQ,CAAA;AAChC,MAAI,MAAU,MACZ,EAAU,OAAO,GAAO,CAAA,GAEtB,EAAU,WAAW,KACvB,KAAK,UAAU,OAAO,CAAA;AAAA;;EAgB5B,KAA+B,GAAU,GAA4B;AACnE,UAAM,IAAY,KAAK,UAAU,IAAI,CAAA;AACrC,QAAI,EACF,YAAW,KAAY,EACrB,KAAI;AACF,MAAA,EAAS,CAAA;AAAA,aACF,GAAO;AAEd,cAAQ,MAAM,gCAAgC,OAAO,CAAA,CAAM,MAAM,CAAA;AAAA;;EAWzE,mBAA6C,GAAiB;AAC5D,IAAI,MAAU,SACZ,KAAK,UAAU,OAAO,CAAA,IAEtB,KAAK,UAAU,MAAA;AAAA;EAUnB,cAAwC,GAAkB;AACxD,WAAO,KAAK,UAAU,IAAI,CAAA,GAAQ,UAAU;AAAA;EAqB9C,MAAM,GAA0F;AAE9F,UAAM,IAAe,KAAK,KAAK,KAAK,IAAA;AAGpC,gBAAK,OAAA,CAAkC,GAAU,MAAyB;AACxE,MAAA,EAAa,GAAO,CAAA;AACpB,UAAI;AACF,QAAA,EAAS,GAAO,CAAA;AAAA,eACT,GAAO;AACd,gBAAQ,MAAM,gCAAgC,OAAO,CAAA,CAAM,MAAM,CAAA;AAAA;OAKrE,MAAa;AACX,WAAK,OAAO;AAAA;;;AC5HlB,SAAgB,EACd,GACA,GACA,GACA,GACkB;AAClB,QAAM,IAAO,IAAI,EAAM,WAAW,GAAM,GAAW,GAAiB,CAAA;AACpE,SAAA,EAAK,SAAS,IAAI,GAAG,GAAG,CAAA,GAExB,EAAK,cAAc,IACZ;;AAOT,SAAgB,GAAwB,GAAsB;AAC5D,MAAI,KAAQ,GAAI,QAAO;AACvB,MAAI,IAAQ,IACR,IAAY;AAChB,SAAI,KAAQ,KACH,IAAQ,KAAK,OAAO,IAAO,KAAa,CAAA,KAEjD,IAAQ,IACR,IAAY,IACR,KAAQ,KACH,IAAQ,KAAK,OAAO,IAAO,KAAa,CAAA,KAEjD,IAAQ,IACR,IAAY,IACR,KAAQ,MACH,IAAQ,KAAK,OAAO,IAAO,KAAa,CAAA,KAEjD,IAAQ,IACR,IAAY,KACR,KAAQ,MACH,IAAQ,KAAK,OAAO,IAAO,KAAa,EAAA,KAEjD,IAAQ,IACR,IAAY,KACR,KAAQ,MACH,IAAQ,KAAK,OAAO,IAAO,KAAa,EAAA,KAEjD,IAAQ,IACR,IAAY,KACL,KAAK,IAAI,IAAI,IAAQ,KAAK,OAAO,IAAO,KAAa,EAAA,CAAG;;AAQjE,SAAgB,GAAyB,GAAwC;AAC/E,SAAO,IAAI,EAAM,QAAQ,KAAK,MAAM,EAAS,CAAA,GAAI,GAAG,KAAK,MAAM,EAAS,CAAA,CAAE;;AAQ5E,SAAgB,EAAoB,GAAmC;AACrE,SAAO,IAAI,EAAS,KAAK,MAAM,EAAS,CAAA,GAAI,KAAK,MAAM,CAAC,EAAS,CAAA,CAAE;;AAQrE,SAAgB,EAAoB,GAAmC;AACrE,SAAO,IAAI,EAAM,QAAQ,EAAS,GAAG,GAAG,CAAC,EAAS,CAAA;;AAQpD,SAAgB,GAAoB,GAAiC;AACnE,SAAO,IAAI,GAAS,KAAK,MAAM,EAAM,UAAU,SAAS,CAAC,EAAS,CAAA,CAAE,CAAC;;AAQvE,SAAgB,EAAoB,GAAiC;AACnE,SAAO,IAAI,EAAM,MAAM,GAAG,EAAM,UAAU,SAAS,CAAC,EAAS,KAAA,GAAQ,CAAA;;AAwBvE,SAAgB,GACd,GACA,GACA,GACA,GAC0B;AAC1B,QAAM,IAAS,EAAc,MAAA;AAC7B,SAAA,EAAO,QAAQ,CAAA,GAKR;AAAA,IAAE,IAHG,EAAO,IAAI,KAAK,IAAK;AAAA,IAGrB,IAFA,CAAC,EAAO,IAAI,KAAK,IAAK;AAAA;;AAepC,SAAgB,GACd,GACA,GACA,GACA,GACA,GACS;AACT,QAAM,IAAS,GAAsB,GAAe,GAAQ,GAAO,CAAA;AACnE,SACE,EAAO,KAAK,EAAK,QAAQ,EAAO,KAAK,EAAK,QAAQ,EAAO,KAAK,EAAK,QAAQ,EAAO,KAAK,EAAK;;AAmChG,SAAgB,GACd,GACA,GACA,GACA,GACiB;AAEjB,QAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,EAAA,EAAM,OAAO,GAAa,CAAA,GAC1B,EAAM,OAAO,GAAG,GAAG,GAAa,GAAG,KAAK,KAAK,GAAG,EAAA;AAGhD,QAAM,IAAkB;AAAA,IACf,OAAA;AAAA,IACP,cAAc;AAAA,IACP,OAAA;AAAA;AAGT,MAAI,IAAc,MAAM,EACtB,QAAO,IAAI,EAAM,gBAAgB,GAAO,CAAA;AAI1C,QAAM,IAAW,IAAI,EAAM,KAAA;AAC3B,SAAA,EAAS,OAAO,GAAa,CAAA,GAC7B,EAAS,OAAO,GAAG,GAAG,GAAa,GAAG,KAAK,KAAK,GAAG,EAAA,GACnD,EAAM,MAAM,KAAK,CAAA,GAGV,IAAI,EAAM,gBAAgB,GAAO,CAAA;;AAY1C,SAAS,GAAiB,GAAe,GAAgB,GAAsC;AAC7F,QAAM,IAAQ,IAAQ,GAChB,IAAQ,IAAS;AACvB,MAAI,IAAY,MAAM,KAAK,IAAI,GAAO,CAAA,EAAQ,QAAO;AAErD,QAAM,IAAO,IAAI,EAAM,KAAA;AAEvB,MAAI,KAAS,IAAS,GAAG;AAEvB,UAAM,IAAa,IAAQ,GACrB,IAAa,IAAQ,GACrB,IAAW,EAAE,IAAa,MAAe,IAAI,IAC7C,KAAgB,IAAa,IAAa,IAAI,IAAa,MAAe,IAAI,IAC9E,IAAc,CAAC,IAAa,GAC5B,IAAkB,KAAK,MAAM,GAAY,CAAA;AAE/C,WAAA,EAAK,OAAO,CAAC,IAAQ,GAAW,CAAA,GAChC,EAAK,OAAO,CAAC,IAAQ,GAAW,CAAC,CAAA,GACjC,EAAK,OAAO,GAAU,GAAG,GAAc,CAAC,GAAiB,GAAiB,EAAA,GAEnE;AAAA;AAIT,QAAM,IAAa,IAAQ,GACrB,IAAa,IAAQ;AAC3B,SAAA,EAAK,OAAO,CAAC,IAAQ,GAAW,CAAC,CAAA,GACjC,EAAK,OAAO,GAAY,CAAC,CAAA,GACzB,EAAK,OAAO,GAAY,GAAG,GAAY,CAAC,KAAK,KAAK,GAAG,KAAK,KAAK,GAAG,EAAA,GAClE,EAAK,OAAO,CAAC,IAAQ,GAAW,CAAA,GAEzB;;AAoBT,SAAS,GACP,GACA,GACA,GACA,GACA,GACiB;AACjB,MAAI,IAAQ,IAAS,EACnB,OAAM,IAAI,MACR,mFAAA;AAGJ,QAAM,IAAQ,IAAQ,GAChB,IAAQ,IAAS,GAGjB,IAAK,EAAE,IAAQ,MAAU,IAAI,IAC7B,KAAU,IAAQ,IAAQ,IAAI,IAAQ,MAAU,IAAI,IAEpD,IAAQ,CAAC,IAAQ,GACjB,IAAY,KAAK,MAAM,GAAO,CAAA,GAG9B,IAAQ,IAAI,EAAM,MAAA;AACxB,EAAA,EAAM,OAAO,CAAC,GAAO,CAAC,CAAA,GACtB,EAAM,OAAO,CAAC,GAAO,CAAA,GACrB,EAAM,OAAO,GAAI,GAAG,GAAQ,GAAW,CAAC,GAAW,EAAA;AAGnD,QAAM,IAAkB;AAAA,IACtB,OAAA;AAAA,IACA,cAAc;AAAA,IACd,OAAA;AAAA,KAEI,IAAO,GAAiB,GAAO,GAAQ,CAAA;AAC7C,SAAI,MAAS,QAAM,EAAM,MAAM,KAAK,CAAA,GAC7B,IAAI,EAAM,gBAAgB,GAAO,CAAA;;AAwB1C,SAAgB,GACd,GACA,GACA,GACA,GACA,IAAgB,GACC;AAEjB,MAAI,KAAS,IAAS,EACpB,QAAO,GAAqB,GAAO,GAAQ,GAAW,GAAO,CAAA;AAG/D,QAAM,IAAQ,IAAQ,GAChB,IAAQ,IAAS,GAGjB,IAAa,IAAQ,GAGrB,IAAQ,IAAI,EAAM,MAAA;AACxB,EAAA,EAAM,OAAO,CAAC,GAAO,CAAC,CAAA,GACtB,EAAM,OAAO,CAAC,GAAO,CAAA,GACrB,EAAM,OAAO,GAAY,CAAA,GACzB,EAAM,OAAO,GAAY,GAAG,GAAO,KAAK,KAAK,GAAG,CAAC,KAAK,KAAK,GAAG,EAAA,GAC9D,EAAM,OAAO,CAAC,GAAO,CAAC,CAAA;AAEtB,QAAM,IAAkB;AAAA,IACtB,OAAA;AAAA,IACA,cAAc;AAAA,IACd,OAAA;AAAA,KAEI,IAAO,GAAiB,GAAO,GAAQ,CAAA;AAC7C,SAAI,MAAS,QAAM,EAAM,MAAM,KAAK,CAAA,GAC7B,IAAI,EAAM,gBAAgB,GAAO,CAAA;;AAc1C,SAAgB,GACd,GACA,GACA,GACA,GACA,IAAgB,GACQ;AACxB,QAAM,IAAO,GAAiB,GAAO,GAAQ,CAAA;AAC7C,MAAI,MAAS,KAAM,QAAO;AAC1B,QAAM,IAAQ,IAAI,EAAM,MAAM,EAAK,UAAU,EAAA,EAAI,QAAA,CAAS;AAC1D,SAAO,IAAI,EAAM,gBAAgB,GAAO;AAAA,IAAE,OAAA;AAAA,IAAO,cAAc;AAAA,IAAO,OAAA;AAAA,GAAO;;AAY/E,SAAS,GAAgB,GAAe,GAAgB,GAAsC;AAC5F,QAAM,IAAQ,IAAQ,GAChB,IAAQ,IAAS;AACvB,MAAI,IAAY,MAAM,KAAK,IAAI,GAAO,CAAA,EAAQ,QAAO;AAGrD,QAAM,IAAY,IAAQ,KACpB,KAAO,IAAQ,IAAQ,IAAY,MAAc,IAAI,IACrD,IAAO,CAAC,IAAQ,GAChB,IAAM,IAAM,GAEZ,IAAa,IAAQ,GAErB,IAAY,IADQ,KAAK,IAAI,KAAK,IAAY,CAAA,GAE9C,IAAe,KAAK,KAAK,IAAY,IAAY,IAAa,CAAA,GAC9D,IAAgB,KAAK,MAAM,GAAY,CAAA,GACvC,IAAe,IAAO,GAEtB,IAAO,IAAI,EAAM,KAAA;AAEvB,MAAI,KAAS,IAAS,GAAG;AAEvB,UAAM,IAAI,IAAQ,GACZ,IAAI,GACJ,KAAQ,IAAI,IAAI,IAAa,IAAa,IAAI,MAAM,KAAK,IAAI,KAC7D,IAAW,IAAI,GACf,IAAc,KAAK,MAAM,GAAY,IAAe,CAAA;AAE1D,WAAA,EAAK,OAAO,GAAc,CAAA,GAC1B,EAAK,OAAO,GAAM,GAAG,GAAW,GAAe,CAAC,GAAe,EAAA,GAC/D,EAAK,OAAO,GAAM,GAAG,GAAU,CAAC,GAAa,GAAa,EAAA,GAEnD;AAAA;AAIT,QAAM,IAAa,IAAQ;AAC3B,SAAA,EAAK,OAAO,GAAc,CAAA,GAC1B,EAAK,OAAO,GAAM,GAAG,GAAW,GAAe,CAAC,GAAe,EAAA,GAC/D,EAAK,OAAO,GAAY,CAAC,CAAA,GACzB,EAAK,OAAO,GAAY,GAAG,GAAY,CAAC,KAAK,KAAK,GAAG,KAAK,KAAK,GAAG,EAAA,GAClE,EAAK,OAAO,GAAc,CAAA,GAEnB;;AAiBT,SAAS,GACP,GACA,GACA,GACA,GACA,GACiB;AACjB,MAAI,IAAQ,IAAS,EACnB,OAAM,IAAI,MACR,qFAAA;AAGJ,QAAM,IAAQ,IAAQ,GAChB,IAAQ,IAAS,GAGjB,IAAY,IAAQ,KACpB,KAAO,IAAQ,IAAQ,IAAY,MAAc,IAAI,IACrD,IAAO,CAAC,IAAQ,GAChB,IAAM,IAAM,GACZ,IAAU,KAAK,MAAM,GAAO,CAAA,GAI5B,IAAK,EAAE,IAAQ,MAAU,IAAI,IAC7B,KAAU,IAAQ,IAAQ,IAAI,IAAQ,MAAU,IAAI,IACpD,IAAQ,CAAC,IAAQ,GACjB,IAAY,KAAK,MAAM,GAAO,CAAA,GAG9B,IAAQ,IAAI,EAAM,MAAA;AACxB,EAAA,EAAM,OAAO,CAAC,GAAO,CAAC,CAAA,GACtB,EAAM,OAAO,GAAM,GAAG,GAAK,CAAC,GAAS,GAAS,EAAA,GAC9C,EAAM,OAAO,GAAI,GAAG,GAAQ,GAAW,CAAC,GAAW,EAAA;AAGnD,QAAM,IAAkB;AAAA,IAAE,OAAA;AAAA,IAAO,cAAc;AAAA,IAAO,OAAA;AAAA,KAChD,IAAO,GAAgB,GAAO,GAAQ,CAAA;AAC5C,SAAI,MAAS,QAAM,EAAM,MAAM,KAAK,CAAA,GAC7B,IAAI,EAAM,gBAAgB,GAAO,CAAA;;AA8B1C,SAAgB,EACd,GACA,GACA,GACA,GACA,IAAgB,GACC;AAEjB,MAAI,KAAS,IAAS,EACpB,QAAO,GAAoB,GAAO,GAAQ,GAAW,GAAO,CAAA;AAG9D,QAAM,IAAQ,IAAQ,GAChB,IAAQ,IAAS,GAGjB,IAAY,IAAQ,KAGpB,KAAO,IAAQ,IAAQ,IAAY,MAAc,IAAI,IACrD,IAAO,CAAC,IAAQ,GAChB,IAAM,IAAM,GACZ,IAAU,KAAK,MAAM,GAAO,CAAA,GAG5B,IAAa,IAAQ,GAGrB,IAAQ,IAAI,EAAM,MAAA;AACxB,EAAA,EAAM,OAAO,CAAC,GAAO,CAAC,CAAA,GACtB,EAAM,OAAO,GAAM,GAAG,GAAK,CAAC,GAAS,GAAS,EAAA,GAC9C,EAAM,OAAO,GAAY,CAAA,GACzB,EAAM,OAAO,GAAY,GAAG,GAAO,KAAK,KAAK,GAAG,CAAC,KAAK,KAAK,GAAG,EAAA,GAC9D,EAAM,OAAO,CAAC,GAAO,CAAC,CAAA;AAEtB,QAAM,IAAkB;AAAA,IAAE,OAAA;AAAA,IAAO,cAAc;AAAA,IAAO,OAAA;AAAA,KAChD,IAAO,GAAgB,GAAO,GAAQ,CAAA;AAC5C,SAAI,MAAS,QAAM,EAAM,MAAM,KAAK,CAAA,GAC7B,IAAI,EAAM,gBAAgB,GAAO,CAAA;;AAc1C,SAAgB,EACd,GACA,GACA,GACA,GACA,IAAgB,GACQ;AACxB,QAAM,IAAO,GAAgB,GAAO,GAAQ,CAAA;AAC5C,MAAI,MAAS,KAAM,QAAO;AAC1B,QAAM,IAAQ,IAAI,EAAM,MAAM,EAAK,UAAU,EAAA,EAAI,QAAA,CAAS;AAC1D,SAAO,IAAI,EAAM,gBAAgB,GAAO;AAAA,IAAE,OAAA;AAAA,IAAO,cAAc;AAAA,IAAO,OAAA;AAAA,GAAO;;AAsB/E,SAAgB,GACd,GACA,GACA,GACA,GACA,GACA,GACA,IAAgB,GACC;AACjB,QAAM,IAAQ,IAAU,GAClB,IAAQ,IAAW,GAGnB,IAAY,IAAQ,KACpB,KAAO,IAAQ,IAAQ,IAAY,MAAc,IAAI,IACrD,IAAO,CAAC,IAAQ,GAChB,IAAM,IAAM,GACZ,IAAU,KAAK,MAAM,GAAO,CAAA,GAO5B,IAAY,IAAO,GAGnB,IAAU,IAAM,GAGhB,IAAiB,IAAY,IAAM,KAAK,IAAI,CAAA,GAE5C,IAAS,CAAC,GAGV,IAAK,IAAiB,GACtB,IAAK,IAAK,GAGV,IAAA,CAAU,MACd,IAAY,KAAK,KAAK,KAAK,IAAI,GAAG,IAAU,IAAU,IAAI,CAAA,CAAE,GACxD,IAAA,CAAc,MAClB,KAAK,MAAM,GAAG,KAAK,KAAK,KAAK,IAAI,GAAG,IAAU,IAAU,IAAI,CAAA,CAAE,CAAC,GAG3D,KAAY,IAAK,KAAS,EAAO,CAAA,IAAM,GACvC,KAAY,MAAa,IAAK,GAE9B,KAAkB;AAAA,IAAE,OAAA;AAAA,IAAO,cAAc;AAAA,IAAO,OAAA;AAAA,KAYhD,IAAQ,IAAI,EAAM,MAAA;AACxB,SAAA,EAAM,OAAO,GAAgB,CAAC,CAAA,GAEzB,KAIO,MAWV,EAAM,OAAO,EAAO,CAAC,CAAA,GAAQ,CAAC,CAAA,GAC9B,EAAM,OAAO,GAAW,GAAG,GAAS,EAAW,CAAC,CAAA,GAAQ,EAAW,CAAC,CAAA,GAAK,EAAA,GACzE,EAAM,OAAO,GAAQ,CAAC,CAAA,GACtB,EAAM,OAAO,GAAQ,CAAC,CAAA,GACtB,EAAM,OAAO,EAAO,CAAC,CAAA,GAAK,CAAC,CAAA,GAC3B,EAAM,OAAO,GAAW,GAAG,GAAS,EAAW,CAAC,CAAA,GAAK,EAAW,CAAA,GAAK,EAAA,GACrE,EAAM,OAAO,GAAQ,CAAA,GACrB,EAAM,OAAO,GAAQ,CAAA,GACrB,EAAM,OAAO,EAAO,CAAA,GAAK,CAAA,GACzB,EAAM,OAAO,GAAW,GAAG,GAAS,EAAW,CAAA,GAAK,EAAW,CAAA,GAAQ,EAAA,MAjBvE,EAAM,OAAO,GAAQ,CAAC,CAAA,GACtB,EAAM,OAAO,GAAQ,CAAC,CAAA,GACtB,EAAM,OAAO,EAAO,CAAC,CAAA,GAAK,CAAC,CAAA,GAC3B,EAAM,OAAO,GAAW,GAAG,GAAS,EAAW,CAAC,CAAA,GAAK,EAAW,CAAA,GAAK,EAAA,GACrE,EAAM,OAAO,GAAQ,CAAA,GACrB,EAAM,OAAO,GAAQ,CAAA,MAVrB,EAAM,OAAO,EAAO,CAAC,CAAA,GAAQ,CAAC,CAAA,GAC9B,EAAM,OAAO,GAAW,GAAG,GAAS,EAAW,CAAC,CAAA,GAAQ,EAAW,CAAA,GAAQ,EAAA,IAwB7E,EAAM,OAAO,GAAgB,CAAA,GAC7B,EAAM,OAAO,GAAW,GAAG,GAAK,GAAS,CAAC,GAAS,EAAA,GAG5C,IAAI,EAAM,gBAAgB,GAAO,EAAA;;AAoC1C,SAAgB,GACd,GACA,GACA,GACA,GACA,GACA,GACA,GACA,IAAgB,GACC;AACjB,QAAM,IAAI,GACJ,IAAQ,EAAM,UAAU,SAAS,CAAA,GACjC,IAAY,IAAQ,GACpB,IAAO,KAAK,IAAI,CAAA,GAChB,IAAO,KAAK,IAAI,CAAA,GAChB,IAAU,KAAK,IAAI,CAAA,IAAa,KAAK,IAAI,CAAA,GAGzC,IAAI,IAAQ,GACZ,IAAI,IAAS,GAIb,IAAO,IAAU,IAAI,KAAK,IAAI,GAAG,CAAA,IAAK,IAAU,OAChD,IAAI,KAAK,IAAI,KAAK,IAAI,GAAG,CAAA,GAAiB,CAAA,GAG1C,IAAQ,IAAI,GAIZ,KAAW,IAAI,KAAK,GACpB,IAAU,IAAI,GAEd,IAAQ,IAAI,EAAM,MAAA;AAExB,SAAK,IA+BC,IAAI,KAEN,EAAM,OAAO,CAAC,IAAU,IAAI,GAAM,IAAU,IAAI,CAAA,GAChD,EAAM,OAAO,CAAC,IAAI,IAAO,IAAI,GAAM,IAAI,IAAO,IAAI,CAAA,GAClD,EAAM,OAAO,CAAC,IAAI,GAAM,IAAI,CAAA,GAC5B,EAAM,OAAO,CAAC,IAAQ,GAAM,IAAQ,CAAA,GACpC,EAAM,OAAO,CAAC,GAAO,GAAG,GAAG,KAAK,KAAK,IAAI,GAAO,CAAC,KAAK,KAAK,GAAG,EAAA,GAC9D,EAAM,OAAO,CAAC,GAAG,CAAA,GACjB,EAAM,OAAO,CAAC,GAAG,CAAC,CAAA,GAElB,EAAM,OAAO,CAAC,GAAS,CAAC,CAAA,GAExB,EAAM,OAAO,CAAC,GAAS,GAAS,GAAG,CAAC,KAAK,KAAK,GAAG,KAAK,KAAK,IAAI,GAAO,EAAA,MAGtE,EAAM,OAAO,IAAI,GAAS,CAAC,CAAA,GAC3B,EAAM,OAAO,CAAC,IAAI,IAAO,IAAI,GAAM,IAAI,IAAO,IAAI,CAAA,GAClD,EAAM,OAAO,CAAC,IAAI,GAAM,IAAI,CAAA,GAC5B,EAAM,OAAO,GAAG,CAAA,GAChB,EAAM,OAAO,CAAC,GAAG,CAAA,GACjB,EAAM,OAAO,CAAC,GAAG,CAAC,CAAA,KA/ChB,IAAI,KAEN,EAAM,OAAO,GAAS,CAAC,CAAA,GACvB,EAAM,OAAO,GAAG,CAAC,CAAA,GACjB,EAAM,OAAO,GAAG,CAAA,GAChB,EAAM,OAAO,GAAO,CAAA,GACpB,EAAM,OAAO,GAAO,GAAG,GAAG,CAAC,KAAK,KAAK,GAAG,IAAQ,KAAK,KAAK,GAAG,EAAA,GAC7D,EAAM,OAAO,IAAI,GAAM,IAAI,CAAA,GAC3B,EAAM,OAAO,IAAI,IAAO,IAAI,GAAM,IAAI,IAAO,IAAI,CAAA,GAEjD,EAAM,OAAO,IAAU,IAAI,GAAM,IAAU,IAAI,CAAA,GAE/C,EAAM,OAAO,GAAS,GAAS,GAAG,IAAQ,KAAK,KAAK,GAAG,CAAC,KAAK,KAAK,GAAG,EAAA,MAGrE,EAAM,OAAO,CAAC,IAAI,GAAS,CAAC,CAAA,GAC5B,EAAM,OAAO,GAAG,CAAC,CAAA,GACjB,EAAM,OAAO,GAAG,CAAA,GAChB,EAAM,OAAO,GAAG,CAAA,GAChB,EAAM,OAAO,IAAI,GAAM,IAAI,CAAA,GAC3B,EAAM,OAAO,IAAI,IAAO,IAAI,GAAM,IAAI,IAAO,IAAI,CAAA,IAgCpC,IAAI,EAAM,gBAAgB,GAAO;AAAA,IAAE,OAAA;AAAA,IAAO,cAAc;AAAA,IAAO,OAAA;AAAA,GAAO;;AAgBzF,SAAS,GACP,GACA,GACA,GACA,GACmB;AACnB,QAAM,IAAQ,IAAQ,GAChB,IAAY,IAAa,GACzB,IAAY,IAAa;AAE/B,MAAI,IAAY,MAAM,KAAK,IAAI,GAAW,CAAA,EAAQ,QAAO;AASzD,QAAM,IAAK,IAAY,GACjB,IAAI,KAAK,KAAK,IAAQ,IAAQ,IAAK,CAAA,GACnC,IAAiB,IAAa,KAAa,IAAI,KAAO;AAC5D,MAAI,KAAkB,EAAG,QAAO;AAEhC,QAAM,IAAO,IAAI,EAAM,KAAA;AAEvB,MAAI,IAAa,GAAG;AAClB,UAAM,IAAiB,IAAa,KAAa,IAAI,KAAO;AAC5D,QAAI,KAAkB,KAAK,IAAQ,IAAI,KAAa,EAAG,QAAO;AAG9D,IAAA,EAAK,OAAO,CAAC,IAAQ,GAAW,CAAA,GAChC,EAAK,OAAO,CAAC,IAAQ,GAAW,CAAC,CAAA,GACjC,EAAK,OAAO,IAAQ,GAAW,CAAC,CAAA,GAChC,EAAK,OAAO,IAAQ,GAAW,CAAA;AAAA,SAC1B;AAGL,UAAM,IAAO,IAAS,IAAY,IAAK;AACvC,QAAI,KAAQ,CAAC,IAAQ,EAAW,QAAO;AAGvC,IAAA,EAAK,OAAO,CAAC,IAAQ,GAAW,CAAA,GAChC,EAAK,OAAO,CAAC,IAAQ,GAAW,CAAC,CAAA,GACjC,EAAK,OAAO,GAAM,CAAA;AAAA;AAGpB,SAAO;;AAGT,SAAgB,GACd,GACA,GACA,GACA,GACA,GACA,IAAgB,GACC;AACjB,QAAM,IAAQ,IAAQ,GAChB,IAAY,IAAa,GACzB,IAAY,IAAa,GAGzB,IAAQ,IAAI,EAAM,MAAA;AACxB,EAAA,EAAM,OAAO,CAAC,GAAO,CAAC,CAAA,GAClB,IAAa,KACf,EAAM,OAAO,GAAO,CAAC,CAAA,GACrB,EAAM,OAAO,GAAO,CAAA,KAEpB,EAAM,OAAO,GAAO,CAAA,GAEtB,EAAM,OAAO,CAAC,GAAO,CAAA;AAGrB,QAAM,IAAkB;AAAA,IAAE,OAAA;AAAA,IAAO,cAAc;AAAA,IAAO,OAAA;AAAA,KAChD,IAAO,GAAyB,GAAO,GAAY,GAAY,CAAA;AACrE,SAAI,MAAS,QAAM,EAAM,MAAM,KAAK,CAAA,GAC7B,IAAI,EAAM,gBAAgB,GAAO,CAAA;;AAe1C,SAAgB,GACd,GACA,GACA,GACA,GACA,IAAgB,GACC;AACjB,QAAM,IAAQ,IAAQ,GAChB,IAAQ,IAAS,GAGjB,IAAQ,IAAI,EAAM,MAAA;AACxB,EAAA,EAAM,OAAO,CAAC,GAAO,CAAC,CAAA,GACtB,EAAM,OAAO,GAAO,CAAC,CAAA,GACrB,EAAM,OAAO,GAAO,CAAA,GACpB,EAAM,OAAO,CAAC,GAAO,CAAA;AAGrB,QAAM,IAAa,IAAQ,GACrB,IAAa,IAAQ;AAE3B,MAAI,IAAa,KAAK,IAAa,GAAG;AAEpC,UAAM,IAAO,IAAI,EAAM,KAAA;AACvB,IAAA,EAAK,OAAO,CAAC,GAAY,CAAA,GACzB,EAAK,OAAO,CAAC,GAAY,CAAC,CAAA,GAC1B,EAAK,OAAO,GAAY,CAAC,CAAA,GACzB,EAAK,OAAO,GAAY,CAAA,GAExB,EAAM,MAAM,KAAK,CAAA;AAAA;AAGnB,SAAO,IAAI,EAAM,gBAAgB,GAAO;AAAA,IAAE,OAAA;AAAA,IAAO,cAAc;AAAA,IAAO,OAAA;AAAA,GAAO;;AAmB/E,SAAgB,GACd,GACA,GACA,GACA,GACA,GACA,GACA,GACA,IAAgB,GACC;AACjB,MAAI,CAAC,KAAa,KAAc,EAAQ,OAAM,IAAI,MAAM,8BAA8B,CAAA,uBAAiC,CAAA,GAAO;AAC9H,MAAI,KAAa,EAAO,OAAM,IAAI,MAAM,cAAc,CAAA,sBAA+B,CAAA,GAAM;AAC3F,MAAI,IAAQ,IAAY,KAAK,EAAG,OAAM,IAAI,MAAM,wBAAwB,IAAQ,IAAY,CAAA,eAAE;AAC9F,MAAI,IAAQ,IAAY,KAAK,EAAO,OAAM,IAAI,MAAM,wBAAwB,IAAQ,IAAY,CAAA,sBAAuB,CAAA,GAAM;AAE7H,QAAM,IAAQ,IAAQ,GAChB,IAAQ,IAAS,GACjB,IAAQ,IAAQ,IAAQ,IAAY,GACpC,IAAQ,IAAQ,IAAQ,IAAY,GAEpC,IAAW,CAAC,KAAS,IAAY,KAAK,KAAK,GAG3C,IAAQ,IAAI,EAAM,MAAA;AACxB,SAAA,EAAM,OAAO,CAAC,GAAO,CAAC,CAAA,GACtB,EAAM,OAAO,GAAO,CAAC,CAAA,GACrB,EAAM,OAAO,GAAO,CAAA,GACpB,EAAM,OAAO,GAAO,CAAA,GACpB,EAAM,OAAO,GAAO,CAAC,CAAA,GACrB,EAAM,OAAO,GAAO,CAAC,CAAA,GACrB,EAAM,OAAO,GAAO,CAAA,GACpB,EAAM,OAAO,CAAC,GAAO,CAAA,GAGd,IAAI,EAAM,gBAAgB,GAAO;AAAA,IAAE,OAAA;AAAA,IAAO,cAAc;AAAA,IAAO,OAAA;AAAA,GAAO;;AAc/E,SAAgB,GACd,GACA,GACA,GACA,GACA,GACA,IAAgB,GACQ;AACxB,QAAM,IAAO,GAAyB,GAAO,GAAY,GAAY,CAAA;AACrE,MAAI,MAAS,KAAM,QAAO;AAC1B,QAAM,IAAQ,IAAI,EAAM,MAAM,EAAK,UAAU,EAAA,EAAI,QAAA,CAAS;AAC1D,SAAO,IAAI,EAAM,gBAAgB,GAAO;AAAA,IAAE,OAAA;AAAA,IAAO,cAAc;AAAA,IAAO,OAAA;AAAA,GAAO;;AAiI/E,SAAgB,GACZ,GACA,GACA,GACA,GACA,GACA,GACA,IAAgB,GACD;AACjB,QAAM,IAAQ,IAAS,GAGjB,IAAQ,IAAI,EAAM,MAAA;AACxB,EAAA,EAAM,OAAO,GAAG,CAAC,CAAA,GACjB,EAAM,OAAO,CAAC,GAAS,CAAA,GACvB,EAAM,OAAO,GAAG,CAAA,GAChB,EAAM,OAAO,GAAS,CAAA,GACtB,EAAM,OAAO,GAAS,CAAC,CAAA;AAGvB,MAAI,IAAY,IAAc,OAAK,KAAW,IAAc,OAAK,GAC7D,IAAY,IAAc,OAAK,KAAW,IAAc,OAAK;AAEjE,MAAG,KAAa,GAAU;AACxB,UAAM,IAAO,IAAI,EAAM,KAAA;AACvB,IAAA,EAAK,OAAO,GAAG,CAAC,IAAQ,CAAA,GACxB,EAAK,OAAO,CAAC,IAAU,GAAa,CAAA,GACpC,EAAK,OAAO,GAAG,IAAQ,CAAA,GACvB,EAAK,OAAO,IAAU,GAAa,IAAQ,CAAA,GAC3C,EAAK,OAAO,IAAU,GAAa,CAAC,IAAQ,CAAA,GAE5C,EAAM,MAAM,KAAK,CAAA;AAAA,aAEX,CAAC,KAAa,GAAU;AAC9B,UAAM,IAAO,IAAI,EAAM,KAAA;AACvB,IAAA,EAAK,OAAO,GAAG,CAAC,IAAQ,CAAA,GACxB,EAAK,OAAO,GAAG,IAAQ,CAAA,GACvB,EAAK,OAAO,IAAU,GAAa,IAAQ,CAAA,GAC3C,EAAK,OAAO,IAAU,GAAa,CAAC,IAAQ,CAAA,GAE5C,EAAM,MAAM,KAAK,CAAA;AAAA,aAEX,KAAa,CAAC,GAAU;AAC9B,UAAM,IAAO,IAAI,EAAM,KAAA;AACvB,IAAA,EAAK,OAAO,GAAG,CAAC,IAAQ,CAAA,GACxB,EAAK,OAAO,CAAC,IAAU,GAAa,CAAA,GACpC,EAAK,OAAO,GAAG,IAAQ,CAAA,GAEvB,EAAM,MAAM,KAAK,CAAA;AAAA;AAEnB,iBAAQ,IAAI,CAAA,GAEL,IAAI,EAAM,gBAAgB,GAAO;AAAA,IAAE,OAAA;AAAA,IAAO,cAAc;AAAA,IAAO,OAAA;AAAA,GAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GG3tClE,KAAS,OAEhB,KAAU;AAAA,EAAE,IAAA;AAAA,EAAI,IAAA;;AAetB,SAAgB,GAAwB,GAAsB;AAC5D,aAAW,CAAC,GAAK,CAAA,KAAc,OAAO,QAAQ,EAAA,EAC5C,CAAA,EAAS,kBACP,GAAA,OAEA,GACW,IACK,EAAA;;AAetB,SAAgB,EAAK,GAAa,GAAuE;AACvG,SAAO,GAAQ,EAAE,OAAa,CAAA,IAAO,CAAA;;AC5CvC,IAAa,KAA2B,uBAkBlC,IAAwB,SAGxB,KAAgB,KAChB,KAAiB,KAGjB,KAAW,MACX,KAAW,MACX,IAAmB,IAYZ,KAAb,MAAmC;AAAA,EAEjC,YAA2C;AAAA,EAC3C,UAA0C;AAAA,EAC1C,gBAAkD;AAAA,EAClD,WAA0C;AAAA,EAG1C;AAAA,EAGA;AAAA,EACA;AAAA,EAGA,gBAA6D;AAAA,EAG7D,aAAsD;AAAA,EACtD,kBAA4D;AAAA,EAC5D,iBAA2D;AAAA,EAO3D,YACE,GACA,GACA,GACA;AAHiB,SAAA,WAAA,GAIjB,KAAK,oBAAoB,GACzB,KAAK,UAAU;AAIf,UAAM,IADS,EAAS,UAAA,EACE,CAAA;AAC1B,SAAK,QAAQ;AAAA,MACX,iBAAiB,IAAa,EAAW,KAAK;AAAA,MAC9C,cAAc;AAAA,MACd,aAAa;AAAA,MACb,cAAc;AAAA;;EAKlB,IAAI,SAAkB;AACpB,WAAO,KAAK,cAAc;AAAA;EAI5B,IAAI,mBAA2C;AAC7C,WAAO,KAAK,MAAM;AAAA;EASpB,KAAK,GAAgD;AACnD,QAAI,KAAK,WAAW;AAClB,WAAK,kBAAkB,CAAA;AACvB;AAAA;AAGF,SAAK,UAAA,GACL,KAAK,kBAAkB,CAAA,GACvB,KAAK,eAAA,GACL,KAAK,uBAAA;AAAA;EAOP,QAAc;AACZ,IAAK,KAAK,cAGV,KAAK,MAAM,cAAc,KAAK,UAAU,aACxC,KAAK,MAAM,eAAe,KAAK,UAAU,cAEzC,KAAK,qBAAA,GACL,SAAS,KAAK,YAAY,KAAK,SAAA,GAC/B,KAAK,YAAY,MACjB,KAAK,UAAU,MACf,KAAK,gBAAgB,MACrB,KAAK,WAAW;AAAA;EAMlB,UAAgB;AACd,SAAK,MAAA;AAAA;EAQP,YAAY,GAAoB;AAC9B,QAAK,KAAK,QAMV;AAAA,UAJI,KAAK,YACP,KAAK,QAAQ,cAAc,EAAK,gBAAgB,EAAE,cAAc,aAAA,CAAc,IAG5E,KAAK,eAAe;AACtB,cAAM,IAAgB,KAAK,cAAc;AACzC,mBAAW,KAAU,MAAM,KAAK,KAAK,cAAc,OAAA,EACjD,CAAA,EAAO,cAAc,EAAK,qBAAqB,EAAO,KAAA,SAAc,EAClE,cAAc,EAAO,eAAe,EAAO,MAAA,CAC5C;AAEH,aAAK,cAAc,QAAQ;AAAA;AAG7B,WAAK,eAAA;AAAA;AAAA;EAOP,YAA0B;AAExB,SAAK,YAAY,SAAS,cAAc,KAAA,GACxC,OAAO,OAAO,KAAK,UAAU,OAAO;AAAA,MAClC,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,OAAO,GAAG,KAAK,MAAM,WAAA;AAAA,MACrB,QAAQ,GAAG,KAAK,MAAM,YAAA;AAAA,MACtB,UAAU;AAAA,MACV,WAAW;AAAA,MACX,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,OAAO;AAAA,MACP,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,eAAe;AAAA,MACf,UAAU;AAAA,MACV,QAAQ;AAAA,KACT;AAGD,UAAM,IAAS,SAAS,cAAc,KAAA;AACtC,WAAO,OAAO,EAAO,OAAO;AAAA,MAC1B,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,YAAY;AAAA,KACb;AAED,UAAM,IAAQ,SAAS,cAAc,MAAA;AACrC,IAAA,EAAM,cAAc,EAAK,gBAAgB,EAAE,cAAc,aAAA,CAAc,GACvE,OAAO,OAAO,EAAM,OAAO;AAAA,MACzB,YAAY;AAAA,MACZ,UAAU;AAAA,KACX,GACD,KAAK,UAAU;AAEf,UAAM,IAAW,SAAS,cAAc,QAAA;AACxC,IAAA,EAAS,cAAc,KACvB,OAAO,OAAO,EAAS,OAAO;AAAA,MAC5B,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,YAAY;AAAA,KACb,GACD,EAAS,iBAAiB,cAAA,MAAoB;AAC5C,MAAA,EAAS,MAAM,QAAQ;AAAA,QAEzB,EAAS,iBAAiB,cAAA,MAAoB;AAC5C,MAAA,EAAS,MAAM,QAAQ;AAAA,QAEzB,EAAS,iBAAiB,SAAA,CAAU,MAAM;AACxC,MAAA,EAAE,gBAAA,GACF,KAAK,QAAA;AAAA,QAGP,EAAO,YAAY,CAAA,GACnB,EAAO,YAAY,CAAA,GAGnB,EAAO,iBAAiB,aAAA,CAAc,MAAM;AAC1C,MAAI,EAAE,WAAW,KACjB,KAAK,UAAU,CAAA;AAAA,QAIjB,KAAK,gBAAgB,SAAS,cAAc,QAAA,GAC5C,OAAO,OAAO,KAAK,cAAc,OAAO;AAAA,MACtC,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,QAAQ;AAAA,KACT;AAED,QAAI,IAAS,KAAK,SAAS,UAAA;AAG3B,IAAI,EAAO,OAAA,CAAQ,MAAM,EAAE,OAAO,CAAA,EAAuB,SAAS,MAChE,IAAS,CACP;AAAA,MACE,IAAI;AAAA,MACJ,OAAO,EAAK,qBAAqB,CAAA,SAA8B,EAC7D,cAAc,EAAA,CACf;AAAA,OAEH,GAAG,CAAA;AAIP,eAAW,KAAS,GAAQ;AAC1B,YAAM,IAAS,SAAS,cAAc,QAAA;AACtC,MAAA,EAAO,QAAQ,EAAM,IACrB,EAAO,cAAc,EAAK,qBAAqB,EAAM,EAAA,SAAW,EAC9D,cAAc,EAAM,MAAA,CACrB,GACD,KAAK,cAAc,YAAY,CAAA;AAAA;AAEjC,SAAK,cAAc,QAAQ,KAAK,MAAM,iBACtC,KAAK,cAAc,iBAAiB,UAAA,MAAgB;AAClD,WAAK,MAAM,kBAAkB,KAAK,cAAe,OAE7C,KAAK,MAAM,iBAAiB,SAC9B,KAAK,MAAM,eAAe,MAC1B,KAAK,kBAAkB,IAAA,IAEzB,KAAK,eAAA;AAAA,QAIP,KAAK,WAAW,SAAS,cAAc,KAAA,GACvC,OAAO,OAAO,KAAK,SAAS,OAAO;AAAA,MACjC,MAAM;AAAA,MACN,WAAW;AAAA,MACX,SAAS;AAAA,KACV,GAGD,KAAK,UAAU,YAAY,CAAA,GAC3B,KAAK,UAAU,YAAY,KAAK,aAAA,GAChC,KAAK,UAAU,YAAY,KAAK,QAAA,GAChC,SAAS,KAAK,YAAY,KAAK,SAAA;AAAA;EAOjC,iBAA+B;AAC7B,QAAI,CAAC,KAAK,SAAU;AACpB,SAAK,SAAS,YAAY;AAE1B,UAAM,IAAU,KAAK,MAAM,iBACrB,IAAQ,KAAK,SAAS,mBAAmB,CAAA;AAG/C,IAAI,MAAY,KACd,KAAK,SAAS,YACZ,KAAK,kBACH,EAAK,yBAAyB,EAAE,cAAc,kBAAA,CAAmB,GACjE,EAAA,CACD;AAIL,eAAW,KAAQ,GAAO;AACxB,YAAM,IAAQ,EAAK,cAAc,CAAA,SAAa,EAAE,cAAc,EAAA,CAAM;AACpE,WAAK,SAAS,YAAY,KAAK,kBAAkB,GAAO,CAAA,CAAK;AAAA;;EAIjE,kBAA0B,GAAe,GAAwC;AAC/E,UAAM,IAAO,SAAS,cAAc,KAAA;AACpC,IAAA,EAAK,cAAc;AACnB,UAAM,IAAa,KAAK,MAAM,iBAAiB;AAE/C,kBAAO,OAAO,EAAK,OAAO;AAAA,MACxB,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,QAAQ;AAAA,MACR,YAAY,IAAa,YAAY;AAAA,MACrC,YAAY;AAAA,KACb,GAED,EAAK,iBAAiB,cAAA,MAAoB;AACxC,MAAI,KAAK,MAAM,iBAAiB,MAC9B,EAAK,MAAM,aAAa;AAAA,QAG5B,EAAK,iBAAiB,cAAA,MAAoB;AACxC,MAAA,EAAK,MAAM,aAAa,KAAK,MAAM,iBAAiB,IAAQ,YAAY;AAAA,QAE1E,EAAK,iBAAiB,SAAA,CAAU,MAAM;AACpC,MAAA,EAAE,gBAAA,GACF,KAAK,WAAW,CAAA;AAAA,QAGX;AAAA;EAGT,WAAmB,GAA8B;AAE/C,UAAM,IAAW,KAAK,MAAM,iBAAiB,IAAQ,OAAO;AAC5D,SAAK,MAAM,eAAe,GAC1B,KAAK,eAAA,GACL,KAAK,kBAAkB,CAAA;AAAA;EAOzB,kBAA0B,GAAgD;AACxE,QAAI,CAAC,KAAK,UAAW;AAErB,UAAM,IAAa,KAAK,MAAM,aACxB,IAAc,KAAK,MAAM;AAE/B,QAAI,IAAO,EAAe,IAAI,IAC1B,IAAM,EAAe,IAAI;AAE7B,UAAM,IAAgB,OAAO,YACvB,IAAiB,OAAO;AAG9B,IAAI,IAAO,IAAa,IAAgB,IACtC,IAAO,EAAe,IAAI,IAAa,KAC9B,IAAO,IAAgB,MAChC,IAAO,EAAe,IAAI,KAIxB,IAAO,MACT,IAAO,IAGL,IAAM,IACR,IAAM,IACG,IAAM,IAAc,IAAiB,MAC9C,IAAM,IAAiB,IAAc,IAGvC,KAAK,UAAU,MAAM,OAAO,GAAG,CAAA,MAC/B,KAAK,UAAU,MAAM,MAAM,GAAG,CAAA;AAAA;EAOhC,UAAkB,GAAqB;AACrC,IAAK,KAAK,cACV,EAAE,eAAA,GAEF,KAAK,aAAa;AAAA,MAChB,GAAG,EAAE,UAAU,KAAK,UAAU;AAAA,MAC9B,GAAG,EAAE,UAAU,KAAK,UAAU;AAAA,OAGhC,KAAK,UAAU,MAAM,SAAS,YAE9B,KAAK,kBAAA,CAAmB,MAAmB,KAAK,WAAW,CAAA,GAC3D,KAAK,iBAAA,MAAuB,KAAK,QAAA,GACjC,SAAS,iBAAiB,aAAa,KAAK,eAAA,GAC5C,SAAS,iBAAiB,WAAW,KAAK,cAAA;AAAA;EAG5C,WAAmB,GAAqB;AACtC,QAAI,CAAC,KAAK,aAAa,CAAC,KAAK,WAAY;AAEzC,QAAI,IAAO,EAAE,UAAU,KAAK,WAAW,GACnC,IAAM,EAAE,UAAU,KAAK,WAAW;AAGtC,UAAM,IAAU,OAAO,aAAa,KAAK,UAAU,cAAc,GAC3D,IAAS,OAAO,cAAc,KAAK,UAAU,eAAe;AAClE,IAAA,IAAO,KAAK,IAAI,GAAkB,KAAK,IAAI,GAAM,CAAA,CAAQ,GACzD,IAAM,KAAK,IAAI,GAAkB,KAAK,IAAI,GAAK,CAAA,CAAO,GAEtD,KAAK,UAAU,MAAM,OAAO,GAAG,CAAA,MAC/B,KAAK,UAAU,MAAM,MAAM,GAAG,CAAA;AAAA;EAGhC,UAAwB;AACtB,IAAI,KAAK,cACP,KAAK,UAAU,MAAM,SAAS,KAEhC,KAAK,aAAa,MAEd,KAAK,oBACP,SAAS,oBAAoB,aAAa,KAAK,eAAA,GAC/C,KAAK,kBAAkB,OAErB,KAAK,mBACP,SAAS,oBAAoB,WAAW,KAAK,cAAA,GAC7C,KAAK,iBAAiB;AAAA;EAQ1B,yBAAuC;AACrC,SAAK,gBAAA,CAAiB,MAAqB;AACzC,MAAI,EAAE,QAAQ,YACZ,KAAK,QAAA;AAAA,OAGT,SAAS,iBAAiB,WAAW,KAAK,aAAA;AAAA;EAG5C,uBAAqC;AACnC,SAAK,QAAA,GAED,KAAK,kBACP,SAAS,oBAAoB,WAAW,KAAK,aAAA,GAC7C,KAAK,gBAAgB;AAAA;;AChT3B,SAAS,GAAkB,GAAmE;AAC5F,MAAI,CAAC,EAAS,QAAO,EAAgB;AACrC,MAAI,MAAY,EAAgB,QAAS,QAAO,EAAgB;;AAQlE,IAAa,KAAb,MAA+C;AAAA,EAC7C,OAA0B;AAAA,EAE1B;AAAA,EAGA,OAA8B;AAAA,EAC9B,kBAAkD;AAAA,EAClD,2BAA2C;AAAA,EAG3C,oBAAsD;AAAA,EACtD,gBAA8C;AAAA,EAC9C,qBAAwD;AAAA,EACxD,cAA0C;AAAA,EAG1C,YAA0C;AAAA,EAG1C,eAAqD;AAAA,EAGrD,eAA2C;AAAA,EAC3C,aAA8B;AAAA,EAG9B,kBAAkD;AAAA,EAMlD,YAAY,GAA+B;AACzC,SAAK,cAAc;AAGnB,UAAM,IAAW,EAAW;AAC5B,IAAI,eAAe,KAAY,OAAQ,EAAiB,aAAc,eACpE,KAAK,eAAe,IAAI,GACtB,GAAA,CACC,MAAc,KAAK,wBAAwB,CAAA,GAAU,MAChD,KAAK,qBAAA,CAAsB,IAKrC,KAAK,oBAAoB,KAAK,kBAAkB,KAAK,IAAA,GACrD,KAAK,kBAAkB,KAAK,gBAAgB,KAAK,IAAA,GACjD,KAAK,yBAAyB,KAAK,uBAAuB,KAAK,IAAA,GAC/D,KAAK,gBAAgB,KAAK,cAAc,KAAK,IAAA,GAC7C,KAAK,iBAAiB,KAAK,eAAe,KAAK,IAAA;AAAA;EAOjD,aAAmB;AAEjB,SAAK,OAAO,QACZ,KAAK,oBAAoB,MACzB,KAAK,gBAAgB,MACrB,KAAK,qBAAqB,MAC1B,KAAK,cAAc,MACnB,KAAK,kBAAkB;AAGvB,UAAM,IAAY,KAAK,YAAY,aAAA;AAEnC,IAAA,EAAU,iBAAiB,eAAe,KAAK,iBAAA,GAC/C,EAAU,iBAAiB,aAAa,KAAK,eAAA,GAC7C,EAAU,iBAAiB,YAAY,KAAK,cAAA,GAC5C,OAAO,iBAAiB,WAAW,KAAK,aAAA;AAAA;EAO1C,eAAqB;AAEnB,SAAK,gBAAA,GAGL,KAAK,oBAAA,GACL,KAAK,cAAc,MAAA;AAEnB,UAAM,IAAY,KAAK,YAAY,aAAA;AAEnC,SAAK,YAAY,IAAI,oBAAoB,KAAK,sBAAA,GAC9C,EAAU,oBAAoB,eAAe,KAAK,iBAAA,GAClD,EAAU,oBAAoB,aAAa,KAAK,eAAA,GAChD,EAAU,oBAAoB,YAAY,KAAK,cAAA,GAC/C,OAAO,oBAAoB,WAAW,KAAK,aAAA,GAG3C,KAAK,OAAO,QACZ,KAAK,oBAAoB,MACzB,KAAK,gBAAgB,MACrB,KAAK,qBAAqB,MAC1B,KAAK,cAAc,MACnB,KAAK,kBAAkB,MACvB,KAAK,aAAa;AAGlB,UAAM,IAAW,KAAK,YAAY,YAAA;AAClC,IAAI,MACF,EAAS,YAAY;AAAA;EAOzB,YAAY,GAAmB;AAC7B,SAAK,cAAc,YAAY,CAAA;AAAA;EAMjC,kBAAwB;AACtB,IAAI,KAAK,SAAS,kBAChB,KAAK,mBAAA,IACI,KAAK,SAAS,cACvB,KAAK,eAAA,IACI,KAAK,SAAS,YACvB,KAAK,aAAA,IACI,KAAK,SAAS,mBACvB,KAAK,oBAAA,IACI,KAAK,SAAS,mBACvB,KAAK,qBAAA;AAAA;EAQT,gBAA4B;AAC1B,UAAM,IAAiB,KAAK,YAAY,kBAAA;AAGxC,QAAI,KAAK,SAAS;AAChB,aAAI,KAAkB,EAAe,SAAS,cAAoB,YAC9D,KAAK,aAAmB,gBACrB,KAAK,kBAAkB,cAAc;AAI9C,QAAI,KAAK,SAAS;AAChB,aAAK,KAAK,kBAAkB,CAAA,IAGrB,cAFE;AAMX,QAAI,KAAK,SAAS,oBAAoB,KAAK,SAAS,eAAe,KAAK,SAAS,UAC/E,QAAO;AAIT,QAAI,GAAgB;AAElB,UAAI,EAAe,SAAS,QAC1B,QAAO;AAIT,YAAM,IAAY,KAAK,YAAY,oBAAA,EAAsB,aAAA;AACzD,UAAI,KAAa,EAAU,SAAS,UAAU,EAAe,OAAO,EAAU,GAC5E,QAAO;AAIT,UAAI,EAAe,SAAS,UAAU,EAAe,SAAS,YAC5D,QAAO;AAAA;AAIX,WAAO;AAAA;EAOT,oBAAsC;AACpC,UAAM,IAA6B,CAAA;AAGnC,WAAI,KAAK,SAAS,mBAAmB,KAAK,mBAAmB,eAC3D,EAAS,KAAK,KAAK,kBAAkB,WAAA,GAInC,KAAK,SAAS,mBAAmB,KAAK,gBACxC,EAAS,KAAK,KAAK,YAAA,GAGd;AAAA;EAWT,kBAA0B,GAAyB;AACjD,QAAI,EAAM,WAAW,EAAG;AACxB,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI,CAAC,EAAS;AACd,UAAM,IAAiB,KAAK,YAAY,kBAAA;AAExC,QAAI,KAAK,SAAS,QAAQ;AAExB,WAAK,EAAM,WAAW,EAAM,YAAY,EAAM,YAAY,GAAgB;AACxE,QAAI,EAAe,SAAS,eAC1B,KAAK,gBAAgB,EAAe,IAAI,CAAA;AAG1C;AAAA;AAIF,WAAK,EAAM,WAAW,EAAM,YAAY,GAAgB;AACtD,QAAI,EAAe,SAAS,UAC1B,KAAK,qBAAqB,EAAe,IAAI,EAAe,QAAA,IACnD,EAAe,SAAS,eACjC,KAAK,YAAY,qBAAqB,EAAe,EAAA;AAIvD;AAAA;AAGF,UAAI,KAAkB,EAAe,SAAS,SAAS;AACrD,cAAM,IAAU,EAAe;AAG/B,YADyB,CAAC,EAAe,SAAS,SAAS,eAGzD,KAAK,mBACL,KAAK,gBAAgB,SAAS,mBAC9B,KAAK,IAAA,IAAQ,KAAK,gBAAgB,KAAK,KACvC;AACA,eAAK,YAAY,GAAS,KAAK,YAAY,0BAAA,CAA2B;AACtE;AAAA;AAIF,aAAK,kBAAkB,CAAA;AACvB;AAAA;AAGF,UAAI,KAAkB,EAAe,SAAS,aAAa;AACzD,cAAM,IAAc,EAAe;AACnC,aAAK,mBAAmB,GAAa,KAAK,YAAY,0BAAA,CAA2B;AACjF;AAAA;AAGF,UAAI,KAAkB,EAAe,SAAS,QAAQ;AACpD,cAAM,IAAS,EAAe,IACxB,IAAY,IAAI,EAAM,QAAQ,EAAM,SAAS,EAAM,OAAA,GACnD,IAAgB,KAAK,YAAY,0BAAA,GAGjC,IAAO,EAAQ,QAAQ,CAAA;AAC7B,YAAI,CAAC,EAAM;AAGX,cAAM,IAAe,KAAK,YAAY,kBAAkB,6BACtD,GACA,CAAA;AAEF,YAAI,GAAc;AAEhB,gBAAM,IAAM,EAAK,sBAAsB,EAAa,UAAA;AACpD,cAAI,GAAK;AACP,kBAAM,IAAW,IAAI,EAAM,QAAQ,EAAI,GAAG,GAAG,CAAC,EAAI,CAAA;AAClD,iBAAK,cAAc,GAAQ,gBAAgB,EAAa,YAAY,CAAA;AACpE;AAAA;;AAIJ,cAAM,IAAc,KAAK,YAAY,kBAAkB,0BACrD,GACA,CAAA;AAEF,aAAK,cAAc,GAAQ,oBAAoB,GAAa,CAAA;AAC5D;AAAA;eAEO,KAAK,SAAS;UACnB,CAAC,GAAgB;AAGnB,aAAK,mBAAA;AACL;AAAA;eAEO,KAAK,SAAS,iBAAiB;AAExC,UAAI,CAAC,KAAkB,KAAK,iBAAiB;AAC3C,YAAI,KAAK,YAAY;AACnB,eAAK,YAAY,KAAK,uBAAuB;AAAA,YAC3C,UAAU,KAAK;AAAA,YACf,MAAM;AAAA,YACN,cAAc;AAAA,WACf;AACD;AAAA;AAEF,aAAK,kBAAA;AAAA;AAEP;AAAA;;EAYJ,gBAAwB,GAAyB;AAO/C,QANI,EAAM,WAAW,KAGjB,KAAK,SAAS,mBAGd,CADY,KAAK,YAAY,WAAA,EACnB;AACd,UAAM,IAAiB,KAAK,YAAY,kBAAA;AAExC,IAAI,KAAK,SAAS,kBAGd,KACA,EAAe,SAAS,WACxB,EAAe,OAAO,KAAK,mBAAmB,gBAE9C,KAAK,mBAAA,IAEL,KAAK,qBAAqB,CAAA,IAEnB,KAAK,SAAS,cAEvB,KAAK,iBAAA,IACI,KAAK,SAAS,YAEvB,KAAK,eAAA,IACI,KAAK,SAAS,oBAEvB,KAAK,sBAAA,GAIP,KAAK,YAAY,IAAI,oBAAoB,KAAK,sBAAA,GAE9C,KAAK,YAAY,YAAA,EAAe,YAAY;AAAA;EAO9C,uBAA+B,GAA+B;AAC5D,YAAQ,KAAK,MAAb;AAAA,MACE,KAAK;AACH,aAAK,mBAAmB,CAAA;AACxB;AAAA,MACF,KAAK;AACH,aAAK,eAAe,CAAA;AACpB;AAAA,MACF,KAAK;AACH,aAAK,oBAAoB,CAAA;AACzB;AAAA,MACF,KAAK;AACH,aAAK,aAAa,CAAA;AAClB;AAAA,MACF,KAAK;AACH,aAAK,0BAA0B,CAAA;AAC/B;AAAA,MACF;AACE;AAAA;;EAQN,cAAsB,GAA4B;AAEhD,QAAI,EAAM,QAAQ,UAAU;AAC1B,MAAI,KAAK,SAAS,kBAChB,KAAK,qBAAA,IACI,KAAK,SAAS,kBACvB,KAAK,mBAAA,IACI,KAAK,SAAS,cACvB,KAAK,eAAA,IACI,KAAK,SAAS,YACvB,KAAK,aAAA,IACI,KAAK,SAAS,oBACvB,KAAK,oBAAA;AAEP;AAAA;AAIF,SAAK,EAAM,WAAW,EAAM,YAAY,EAAM,QAAQ,KAAK;AACzD,YAAM,IAAY,KAAK,YAAY,oBAAA,EAAsB,aAAA;AACzD,MAAI,KAAa,EAAU,SAAS,UAAU,EAAU,SAAS,eAC/D,KAAK,cAAc,EAAU,EAAA;AAE/B;AAAA;AAIF,SAAK,EAAM,WAAW,EAAM,YAAY,EAAM,QAAQ,KAAK;AACzD,MAAI,KAAK,aACP,KAAK,eAAA;AAEP;AAAA;AAIF,UAAM,IAAY,KAAK,YAAY,oBAAA,EAAsB,aAAA;AAEzD,QADI,CAAC,KACD,EAAU,SAAS,QAAS;AAChC,UAAM,IAAgB;AAEtB,QAAI,EAAM,QAAQ,YAAY,EAAM,QAAQ;UAEtC,EAAc,SAAS,aAAa;AACtC,cAAM,IAAc,EAAc;AAClC,aAAK,YAAY,gBAAgB,CAAA;AAAA,iBACxB,EAAc,SAAS,QAAQ;AACxC,cAAM,IAAS,EAAc;AAC7B,aAAK,YAAY,WAAW,CAAA;AAAA,iBACnB,EAAc,SAAS,WAAW,EAAc,SAAS,kBAAkB;AACpF,cAAM,IAAU,EAAc;AAC9B,aAAK,YAAY,qBAAqB,CAAA;AAAA;gBAG/B,EAAM,QAAQ,OAAO,EAAM,QAAQ,QAExC,EAAc,SAAS,aAAa;AACtC,YAAM,IAAc,EAAc;AAClC,WAAK,gBAAgB,CAAA;AAAA;;EAS3B,eAAuB,GAAyB;AAE9C,QADI,EAAM,WAAW,KACjB,EAAM,WAAW,EAAM,QAAS;AAEpC,UAAM,IAAiB,KAAK,YAAY,kBAAA;AAGxC,QAAI,KAAkB,EAAe,SAAS,QAAQ;AACpD,YAAM,IAAS,EAAe,IACxB,IAAe,KAAK,YAAY,0BAAA,GAChC,IAAU,KAAK,2BAA2B,GAAQ,CAAA;AACxD,MAAI,KACF,KAAK,YAAY,oBAAA,EAAsB,UAAU,SAAS,GAAS,EAAE,aAAa,KAAA,CAAM;AAAA,eAInF,KAAkB,EAAe,SAAS,aAAa;AAC9D,YAAM,IAAc,EAAe;AACnC,WAAK,gBAAgB,CAAA;AAAA,eACZ,CAAC,GAAgB;AAG1B,UAAI,KAAK,IAAA,IAAQ,KAAK,2BAA2B,IAAK;AACtD,UAAI,KAAK,gBAAgB,KAAK,SAAS,OACrC,MAAK,sBAAsB,CAAA;AAAA,eAClB,CAAC,KAAK,cAAc;AAE7B,cAAM,IAAe,KAAK,YAAY,0BAAA,GAChC,IAAU,KAAK,+BAA+B,CAAA;AACpD,QAAI,KACF,KAAK,YAAY,oBAAA,EAAsB,UAAU,SAAS,GAAS,EAAE,aAAa,KAAA,CAAM;AAAA;;;EAehG,kBAA0B,GAA2B;AACnD,UAAM,IAAU,KAAK,YAAY,WAAA;AAIjC,QAHI,CAAC,KAGD,CADgB,EAAQ,SAAS,CAAA,EACnB;AAGlB,UAAM,IAAa,KAAK,YAAY,eAAe,IAAI,CAAA;AACvD,QAAI,CAAC,EAAY;AACjB,UAAM,IAAiB,EAAW,SAAS,MAAA;AAC3C,IAAI,EAAW,SAAS,eAGtB,EAAW,iBAAiB,CAAA;AAI9B,UAAM,IAAc,KAAK,YAAY,kBAAkB,kBAAkB,CAAA;AAGzE,SAAK,OAAO,iBACZ,KAAK,oBAAoB;AAAA,MACvB,eAAA;AAAA,MACA,gBAAgB,EAAe,MAAA;AAAA,MAC/B,aAAA;AAAA,MACA,IAAI,KAAK,IAAA;AAAA,OAGX,KAAK,YAAY,YAAA,EAAe,YAAY,IAC5C,KAAK,YAAY,GAAG,oBAAoB,KAAK,sBAAA,GAE7C,KAAK,YAAY,KAAK,wBAAwB;AAAA,MAC5C,UAAU,KAAK;AAAA,MACf,MAAM,KAAK;AAAA,MACX,eAAe,EAAE,eAAA,EAAA;AAAA,KAClB;AAAA;EAQH,mBAA2B,GAA+B;AACxD,SAAK,YAAY,kBAAkB,kBAAkB,CAAA;AAAA;EAMvD,mBAA2B,IAAgB,IAAY;AACrD,IAAI,KAAK,SAAS,oBAClB,KAAK,YAAY,kBAAkB,kBAAA,GAC/B,KACF,KAAK,YAAY,KAAK,0BAA0B;AAAA,MAC9C,UAAU,KAAK;AAAA,MACf,MAAM,KAAK;AAAA,KACZ,GAEH,KAAK,kBAAkB;AAAA,MACrB,MAAM,KAAK;AAAA,MACX,IAAI,KAAK,IAAA;AAAA,OAGX,KAAK,OAAO,QACZ,KAAK,oBAAoB;AAAA;EAU3B,qBAA6B,GAAyD;AAIpF,QAHI,KAAK,SAAS,mBAAmB,CAAC,KAAK,qBAGvC,CADY,KAAK,YAAY,WAAA,EACnB;AACd,UAAM,IAAgB,KAAK,kBAAkB;AAG7C,QAAI;AACF,UAAI,IAAgB,MAChB,IAAc;AAClB,UAAK;YAUM,EAAe,SAAS,QAAQ;AAEzC,gBAAM,IAAe,EAAe,IAC9B,IAAe,KAAK,YAAY,0BAAA;AACtC,UAAA,IAAgB,KAAK,2BAA2B,GAAc,CAAA;AAAA,eACrD,EAAe,SAAS,YACjC,IAAgB,EAAe;AAAA,WAhBZ;AAEnB,cAAM,IAAgB,KAAK,YAAY,0BAAA;AACvC,QAAA,IAAgB,KAAK,+BAA+B,CAAA,GAChD,MACF,KAAK,YACF,oBAAA,EACA,UAAU,SAAS,GAAe,EAAE,aAAa,KAAA,CAAM,GAC1D,IAAc;AAAA;AAWlB,UAAI,CAAC,EACH,OAAM,IAAI,MAAM,kCAAA;AAIlB,YAAM,IAAO,KAAK,YAAY,QAAQ,GAAe,CAAA;AAErD,aAAK,KACH,KAAK,YAAY,oBAAA,EAAsB,UAAU,QAAQ,EAAK,EAAA,GAEhE,KAAK,YAAY,0BAAA,GAEjB,KAAK,YAAY,KAAK,0BAA0B;AAAA,QAC9C,UAAU,KAAK;AAAA,QACf,MAAM,KAAK;AAAA,QACX,eAAe;AAAA,UAAE,QAAQ,EAAK;AAAA,UAAI,eAAA;AAAA,UAAe,eAAA;AAAA;QACjD,aAAa,EAAE,YAAY,CAAC,EAAK,EAAA,EAAG;AAAA,OACrC,GAED,KAAK,2BAA2B,KAAK,IAAA,GACrC,KAAK,mBAAA,GACE,EAAK;AAAA,aACL,GAAO;AACd,WAAK,mBAAA,GACL,KAAK,YAAY,KAAK,uBAAuB;AAAA,QAC3C,UAAU,KAAK;AAAA,QACf,MAAM,KAAK;AAAA,QACX,cAAc,aAAiB,QAAQ,EAAM,UAAU;AAAA,OACxD;AAAA;AAGH,SAAK,OAAO,QACZ,KAAK,oBAAoB;AAAA;EAW3B,cACE,GACA,GACA,GACA,GACM;AACN,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI,CAAC,EAAS;AAEd,UAAM,IAAO,EAAQ,QAAQ,CAAA;AAC7B,QAAI,CAAC,EAAM;AAGX,UAAM,IAAoB,EAAK,sBAAsB,IAAA,CAAK,OAAO,EAAE,GAAG,EAAA,EAAG;AAGzE,QAAI,MAAe,oBAAoB;AACrC,YAAM,IAAc,GACd,IAAU,EAAoB,CAAA;AACpC,MAAA,EAAkB,OAAO,GAAa,GAAG,CAAA,GACzC,IAAa;AAAA;AAGf,SAAK,OAAO,aACZ,KAAK,gBAAgB;AAAA,MACnB,QAAA;AAAA,MACA,YAAA;AAAA,MACA,iBAAiB,EAAc,MAAA;AAAA,MAC/B,mBAAA;AAAA,MACA,YAAA;AAAA,OAIF,KAAK,YAAY,YAAA,EAAe,YAAY,IAC5C,KAAK,YAAY,GAAG,oBAAoB,KAAK,sBAAA,GAE7C,KAAK,YAAY,KAAK,wBAAwB;AAAA,MAC5C,UAAU,KAAK;AAAA,MACf,MAAM,KAAK;AAAA,MACX,eAAe;AAAA,QAAE,QAAA;AAAA,QAAQ,YAAA;AAAA,QAAY,YAAA;AAAA;KACtC;AAAA;EAOH,eAAuB,GAAoC;AACzD,QAAI,KAAK,SAAS,eAAe,CAAC,KAAK,cAAe;AAGtD,UAAM,IAAU,EAAoB,CAAA,GAC9B,IAAe,CAAC,GAAG,KAAK,cAAc,iBAAA;AAC5C,IAAA,EAAa,KAAK,cAAc,UAAA,IAAc,GAI9C,KAAK,YAAY,cAAc,sBAAsB,KAAK,cAAc,QAAQ,CAAA,GAChF,KAAK,YAAY,kBAAkB,eAAe,KAAK,cAAc,MAAA;AAAA;EAMvE,eAAuB,IAAgB,IAAY;AACjD,IAAI,KAAK,SAAS,eAAe,CAAC,KAAK,kBAGvC,KAAK,YAAY,cAAc,sBAC7B,KAAK,cAAc,QACnB,KAAK,cAAc,mBACnB,EAAA,GAEF,KAAK,YAAY,kBAAkB,eAAe,KAAK,cAAc,MAAA,GAEjE,KACF,KAAK,YAAY,KAAK,0BAA0B;AAAA,MAC9C,UAAU,KAAK;AAAA,MACf,MAAM,KAAK;AAAA,KACZ,GAEH,KAAK,kBAAkB;AAAA,MACrB,MAAM,KAAK;AAAA,MACX,IAAI,KAAK,IAAA;AAAA,OAGX,KAAK,OAAO,QACZ,KAAK,gBAAgB;AAAA;EAMvB,mBAAiC;AAC/B,QAAI,KAAK,SAAS,eAAe,CAAC,KAAK,cAAe;AAEtD,UAAM,IAAgB,KAAK;AAE3B,SAAK,OAAO,QACZ,KAAK,gBAAgB,MACrB,KAAK,2BAA2B,KAAK,IAAA;AAErC,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI,CAAC,EAAS;AACd,UAAM,IAAS,EAAc,QACvB,IAAO,EAAQ,QAAQ,CAAA;AAC7B,QAAK;AAEL,UAAI;AAEF,cAAM,IAAiB,KAAK,iBAAiB,CAAA;AAE7C,aAAK,YAAY,cAAc,sBAC7B,EAAc,QACd,GACA,EAAA;AAGF,cAAM,IAAiB,KAAK,YAAY,kBAAA;AAExC,YAAI,KAAkB,EAAe,SAAS,SAAS;AACrD,gBAAM,IAAgB,EAAe,IAC/B,IAAgB,KAAK,YAAY,0BAAA,GACjC,IAAS,KAAK,YAAY,UAAU,GAAQ,GAAe,CAAA;AACjE,eAAK,YAAY,KAAK,0BAA0B;AAAA,YAC9C,UAAU,KAAK;AAAA,YACf,MAAM,KAAK;AAAA,YACX,eAAe;AAAA,cACL,QAAA;AAAA,cACR,uBAAuB;AAAA,cACR,eAAA;AAAA;YAEjB,aAAa;AAAA,cACX,aAAa;AAAA,cACb,SAAS,EAAO,eAAe;AAAA,cAC/B,YAAY,EAAO,MAAM,IAAA,CAAK,MAAM,EAAE,EAAA;AAAA;WAEzC,GACD,KAAK,YACF,oBAAA,EACA,UAAU,SAAS,GAAe,EAAE,aAAa,KAAA,CAAM;AAC1D;AAAA;AAIF,YAAI,KAAkB,EAAe,SAAS,UAAU,EAAe,OAAO,GAAQ;AACpF,gBAAM,IAAe,EAAe,IAC9B,IAAgB,KAAK,YAAY,0BAAA,GACjC,IAAgB,KAAK,2BAA2B,GAAc,CAAA,GAC9D,IAAS,KAAK,YAAY,UAAU,GAAQ,GAAe,CAAA;AACjE,eAAK,YAAY,KAAK,0BAA0B;AAAA,YAC9C,UAAU,KAAK;AAAA,YACf,MAAM,KAAK;AAAA,YACX,eAAe;AAAA,cACL,QAAA;AAAA,cACR,uBAAuB;AAAA,cACT,cAAA;AAAA;YAEhB,aAAa;AAAA,cACX,aAAa;AAAA,cACb,SAAS,EAAO,eAAe;AAAA,cAC/B,YAAY,EAAO,MAAM,IAAA,CAAK,MAAM,EAAE,EAAA;AAAA;WAEzC,GACG,KACF,KAAK,YACF,oBAAA,EACA,UAAU,SAAS,GAAe,EAAE,aAAa,KAAA,CAAM;AAE5D;AAAA;AAIF,aAAK,YAAY,kBAAkB,eAAe,EAAc,MAAA,GAChE,KAAK,YAAY,0BAAA,GACjB,KAAK,YAAY,KAAK,0BAA0B;AAAA,UAC9C,UAAU,KAAK;AAAA,UACf,MAAM,KAAK;AAAA,UACX,eAAe;AAAA,YACL,QAAA;AAAA,YACR,uBAAuB;AAAA;UAEzB,aAAa,EACX,cAAc,CAAC,CAAA,EAAO;AAAA,SAEzB;AAAA,eACM,GAAO;AACd,aAAK,YAAY,KAAK,uBAAuB;AAAA,UAC3C,UAAU,KAAK;AAAA,UACf,MAAM,KAAK;AAAA,UACX,cAAc,+BAAgC,EAAgB,OAAA;AAAA,SAC/D,GACD,KAAK,eAAe,EAAA;AAAA;;EASxB,mBAA2B,GAAmB,GAAoC;AAChF,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,IAAK,KAEa,EAAQ,aAAa,CAAA,MAGvC,KAAK,OAAO,kBACZ,KAAK,qBAAqB;AAAA,MACxB,aAAA;AAAA,MACA,iBAAiB,EAAc,MAAA;AAAA,OAIjC,KAAK,YAAY,YAAA,EAAe,YAAY,IAC5C,KAAK,YAAY,GAAG,oBAAoB,KAAK,sBAAA,GAE7C,KAAK,YAAY,KAAK,wBAAwB;AAAA,MAC5C,UAAU,KAAK;AAAA,MACf,MAAM,KAAK;AAAA,MACX,eAAe,EAAE,aAAA,EAAA;AAAA,KAClB;AAAA;EAOH,oBAA4B,GAAoC;AAC9D,QAAI,KAAK,SAAS,oBAAoB,CAAC,KAAK,mBAAoB;AAEhE,UAAM,IAAS,KAAK,YAAY,YAAY,aAAa,KAAK,mBAAmB,WAAA;AACjF,QAAI,CAAC,EAAQ;AAEb,UAAM,IAAc,GAAyB,CAAA;AAC7C,IAAA,EAAO,SAAS,KAAK,CAAA,GAGrB,KAAK,YAAY,kBAAkB,wBAAwB,KAAK,mBAAmB,WAAA;AAAA;EAMrF,oBAA4B,IAAgB,IAAY;AACtD,QAAI,KAAK,SAAS,oBAAoB,CAAC,KAAK,mBAAoB;AAGhE,UAAM,IAAS,KAAK,YAAY,YAAY,aAAa,KAAK,mBAAmB,WAAA;AACjF,IAAK,MAEL,EAAO,SAAS,KAAK,KAAK,mBAAmB,eAAA,GAE7C,KAAK,YAAY,kBAAkB,wBAAwB,KAAK,mBAAmB,WAAA,GAE/E,KACF,KAAK,YAAY,KAAK,0BAA0B;AAAA,MAC9C,UAAU,KAAK;AAAA,MACf,MAAM;AAAA,KACP,GAEH,KAAK,kBAAkB;AAAA,MACrB,MAAM,KAAK;AAAA,MACX,IAAI,KAAK,IAAA;AAAA,OAGX,KAAK,OAAO,QACZ,KAAK,qBAAqB;AAAA;EAM5B,wBAAsC;AACpC,QAAI,KAAK,SAAS,oBAAoB,CAAC,KAAK,mBAAoB;AAEhE,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI,CAAC,EAAS;AAEd,UAAM,IAAc,KAAK,mBAAmB,aACtC,IAAS,KAAK,YAAY,YAAY,aAAa,CAAA;AACzD,QAAK,GAEL;AAAA,UAAI;AACF,cAAM,IAAY,KAAK,YAAY,cAAc,kBAAkB,GAAa,GAAQ,EAAA;AACxF,mBAAW,KAAiB,EAAQ,oBAAoB,CAAA;AACtD,eAAK,YAAY,cAAc,0BAA0B,EAAc,EAAA,GACvE,KAAK,YAAY,kBAAkB,eAAe,EAAc,EAAA;AAElE,aAAK,YAAY,0BAAA,GACjB,KAAK,YAAY,KAAK,0BAA0B;AAAA,UAC9C,UAAU,KAAK;AAAA,UACf,MAAM;AAAA,UACN,eAAe;AAAA,YACA,aAAA;AAAA,YACb,aAAa,EAAU;AAAA;UAEzB,aAAa,CAAA;AAAA,SACd;AAAA,eACM,GAAO;AACd,aAAK,YAAY,KAAK,uBAAuB;AAAA,UAC3C,UAAU,KAAK;AAAA,UACf,MAAM,KAAK;AAAA,UACX,cAAc,oCAAqC,EAAgB,OAAA;AAAA,SACpE,GACD,KAAK,oBAAoB,EAAA;AAAA;AAI3B,WAAK,OAAO,QACZ,KAAK,qBAAqB,MAC1B,KAAK,2BAA2B,KAAK,IAAA;AAAA;AAAA;EAQvC,YAAoB,GAAe,GAAoC;AACrE,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,IAAK,KAEkB,EAAQ,SAAS,CAAA,MAGxC,KAAK,OAAO,WACZ,KAAK,cAAc;AAAA,MACjB,SAAA;AAAA,MACA,iBAAiB,EAAc,MAAA;AAAA,OAGjC,KAAK,YAAY,YAAA,EAAe,YAAY,IAC5C,KAAK,YAAY,GAAG,oBAAoB,KAAK,sBAAA,GAE7C,KAAK,YAAY,KAAK,wBAAwB;AAAA,MAC5C,UAAU,KAAK;AAAA,MACf,MAAM,KAAK;AAAA,MACX,eAAe,EAAE,SAAA,EAAA;AAAA,KAClB;AAAA;EAOH,aAAqB,GAAoC;AACvD,QAAI,KAAK,SAAS,aAAa,CAAC,KAAK,YAAa;AAElD,UAAM,IAAS,KAAK,YAAY,eAAe,IAAI,KAAK,YAAY,OAAA;AACpE,QAAI,CAAC,EAAQ;AAEb,IAAA,EAAO,SAAS,KAAK,GAAyB,CAAA,CAAc;AAC5D,UAAM,IAAQ,KAAK,YAAY,cAAc,uBAAuB,CAAA;AAGpE,eAAW,KAAmB,EAAM,MAClC,MAAK,YAAY,kBAAkB,eAAe,CAAA;AAAA;EAOtD,aAAqB,IAAgB,IAAY;AAC/C,QAAI,KAAK,SAAS,aAAa,CAAC,KAAK,YAAa;AAElD,UAAM,IAAkB,KAAK,YAAY,iBAEnC,IAAS,KAAK,YAAY,eAAe,IAAI,KAAK,YAAY,OAAA;AACpE,QAAI,CAAC,EAAQ;AACb,IAAA,EAAO,SAAS,KAAK,CAAA;AAErB,UAAM,IAAQ,KAAK,YAAY,cAAc,uBAAuB,CAAA;AAGpE,eAAW,KAAmB,EAAM,MAClC,MAAK,YAAY,kBAAkB,eAAe,CAAA;AAGpD,IAAI,KACF,KAAK,YAAY,KAAK,0BAA0B;AAAA,MAC9C,UAAU,KAAK;AAAA,MACf,MAAM;AAAA,KACP,GAEH,KAAK,kBAAkB;AAAA,MACrB,MAAM,KAAK;AAAA,MACX,IAAI,KAAK,IAAA;AAAA,OAGX,KAAK,OAAO,QACZ,KAAK,cAAc;AAAA;EAMrB,iBAA+B;AAC7B,QAAI,KAAK,SAAS,aAAa,CAAC,KAAK,YAAa;AAElD,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAK,GAEL;AAAA,UAAI;AACF,cAAM,IAAiB,EAAQ,SAAS,KAAK,YAAY,OAAA;AACzD,YAAI,CAAC,EACH,OAAM,IAAI,MAAM,mBAAmB,KAAK,YAAY,OAAA,YAAQ;AAG9D,mBAAW,KAAmB,EAAe;AAC3C,eAAK,YAAY,cAAc,0BAA0B,CAAA,GACzD,KAAK,YAAY,kBAAkB,eAAe,CAAA;AAEpD,aAAK,YAAY,0BAAA,GACjB,KAAK,YAAY,KAAK,0BAA0B;AAAA,UAC9C,UAAU,KAAK;AAAA,UACf,MAAM;AAAA,UACN,eAAe;AAAA,YACb,kBAAkB,KAAK,YAAY;AAAA,YACnC,aAAa,EAAe;AAAA;UAE9B,aAAa,CAAA;AAAA,SACd;AAAA,eACM,GAAO;AACd,aAAK,YAAY,KAAK,uBAAuB;AAAA,UAC3C,UAAU,KAAK;AAAA,UACf,MAAM,KAAK;AAAA,UACX,cAAc,0CAA2C,EAAgB,OAAA;AAAA,SAC1E,GACD,KAAK,aAAa,EAAA;AAClB;AAAA;AAGF,WAAK,OAAO,QACZ,KAAK,cAAc,MACnB,KAAK,2BAA2B,KAAK,IAAA;AAAA;AAAA;EAWvC,+BAAuC,GAAgD;AAErF,QADgB,KAAK,YAAY,WAAA;AAEjC,UAAI;AAEF,cAAM,IAAiB,KAAK,YAAY,kBAAkB,CAAA;AAC1D,oBAAK,YAAY,KAAK,0BAA0B;AAAA,UAC9C,UAAU,KAAK;AAAA,UACf,MAAM;AAAA,UACN,eAAe,EACb,eAAA,EAAA;AAAA,UAEF,aAAa,EACX,SAAS,EAAe,GAAA;AAAA,SAE3B,GACM,EAAe;AAAA,eACf,GAAO;AACd,aAAK,YAAY,KAAK,uBAAuB;AAAA,UAC3C,UAAU,KAAK;AAAA,UACf,MAAM;AAAA,UACN,cAAc,qCAAsC,EAAgB,OAAA;AAAA,SACrE;AAAA;;EAUL,2BAAmC,GAAc,GAAgD;AAE/F,QADgB,KAAK,YAAY,WAAA;AAGjC,UAAI;AACF,cAAM,IAAS,KAAK,YAAY,UAAU,GAAQ,CAAA;AAElD,oBAAK,YAAY,KAAK,0BAA0B;AAAA,UAC9C,UAAU,KAAK;AAAA,UACf,MAAM;AAAA,UACN,eAAe;AAAA,YACb,QAAA;AAAA,YACA,eAAA;AAAA;UAEF,aAAa;AAAA,YACX,aAAa;AAAA,YACb,SAAS,EAAO,eAAe;AAAA,YAC/B,YAAY,EAAO,MAAM,IAAA,CAAK,MAAM,EAAE,EAAA;AAAA;SAEzC,GACM,EAAO,eAAe;AAAA,eACtB,GAAO;AACd,aAAK,YAAY,KAAK,uBAAuB;AAAA,UAC3C,UAAU,KAAK;AAAA,UACf,MAAM;AAAA,UACN,cAAc,qCAAsC,EAAgB,OAAA;AAAA,SACrE;AACD;AAAA;;EASJ,kBAA0B,GAAmE;AAC3F,WAAK,KAAK,oBAEL,IAGD,EAAe,SAAS,UACnB,EAAe,OAAO,KAAK,kBAAkB,gBAIlD,EAAe,SAAS,SARA,KAFQ;AAAA;EAyBtC,gBAAwB,GAAyB;AAC/C,UAAM,IAAS,KAAK,YAAY,YAAY,aAAa,CAAA;AACzD,QAAI,CAAC,EACH;AAGF,UAAM,KADe,EAAO,SAAS,IACJ,KAAK,KAAK,MAAM,KAAK,KAAK;AAC3D,IAAA,EAAO,SAAS,IAAI,GAAG,GAAU,CAAA;AAEjC,QAAI;AACF,YAAM,IAAY,KAAK,YAAY,cAAc,kBAAkB,GAAa,CAAA;AAChF,WAAK,YAAY,kBAAkB,wBAAwB,EAAU,EAAA,GACrE,KAAK,YAAY,KAAK,0BAA0B;AAAA,QAC9C,UAAU,KAAK;AAAA,QACf,MAAM;AAAA,QACN,eAAe;AAAA,UACA,aAAA;AAAA,UACb,aAAa,EAAU;AAAA;QAEzB,aAAa,CAAA;AAAA,OACd;AAAA,aACM,GAAO;AACd,WAAK,YAAY,KAAK,uBAAuB;AAAA,QAC3C,UAAU,KAAK;AAAA,QACf,MAAM,KAAK;AAAA,QACX,cAAc,sCAAuC,EAAgB,OAAA;AAAA,OACtE,GACD,KAAK,oBAAoB,EAAA;AAAA;;EAU7B,iBAAyB,GAAuC;AAC9D,QAAI,CAAC,KAAK,cAAe,QAAO,EAAK;AAErC,UAAM,IAAY,CAAC,GAAG,EAAK,qBAAA;AAE3B,QAAI,EAAU,WAAW,EAAG,QAAO;AACnC,UAAM,IAAe,KAAK,cAAc,YAClC,IAAa,EAAU,CAAA,GAGvB,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI,CAAC,EAAS,QAAO;AAErB,UAAM,IAAQ,EAAQ,SAAS,EAAK,KAAA,GAC9B,IAAQ,EAAQ,SAAS,EAAK,KAAA;AACpC,QAAI,CAAC,KAAS,CAAC,EAAO,QAAO;AAE7B,UAAM,IAAY,EAAM,YAAY,CAAA,GAC9B,IAAY,EAAM,YAAY,CAAA,GAE9B,IAAY;AAgBlB,QAbwB,KAAK,KAC3B,KAAK,IAAI,EAAW,IAAI,EAAU,GAAG,CAAA,IAAK,KAAK,IAAI,EAAW,IAAI,EAAU,GAAG,CAAA,CAAE,IAE7D,KAOE,KAAK,KAC3B,KAAK,IAAI,EAAW,IAAI,EAAU,GAAG,CAAA,IAAK,KAAK,IAAI,EAAW,IAAI,EAAU,GAAG,CAAA,CAAE,IAE7D;AAEpB,aAAA,EAAU,OAAO,GAAc,CAAA,GACxB;AAIT,aAAS,IAAI,GAAG,IAAI,EAAU,QAAQ,KAAK;AACzC,UAAI,MAAM,EAAc;AAExB,YAAM,IAAW,EAAU,CAAA;AAK3B,UAJa,KAAK,KAChB,KAAK,IAAI,EAAW,IAAI,EAAS,GAAG,CAAA,IAAK,KAAK,IAAI,EAAW,IAAI,EAAS,GAAG,CAAA,CAAE,IAGtE;AAET,eAAA,EAAU,OAAO,GAAc,CAAA,GACxB;AAAA;AAIX,WAAO;AAAA;EAOT,cAAsB,GAAyB;AAC7C,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI,CAAC,EAAS;AAEd,UAAM,IAAY,EAAQ,aAAa,CAAA;AACvC,QAAI,CAAC,EAAW;AAEhB,UAAM,IAAU,EAAU,KAAK,IAAA,CAAK,MAAU;AAC5C,YAAM,IAAQ,EAAQ,SAAS,CAAA;AAC/B,aAAO,IAAQ,EAAM,SAAS;AAAA;AAGhC,SAAK,YAAY;AAAA,MACf,eAAe,EAAU;AAAA,MACzB,UAAU,EAAoB,EAAU,QAAA;AAAA,MACxC,YAAY;AAAA,MACZ,QAAQ,IAAI,IAAI,EAAU,MAAA;AAAA;;EAQ9B,iBAA+B;AAI7B,QAHI,CAAC,KAAK,aAGN,CADY,KAAK,YAAY,WAAA,EACnB;AAGd,UAAM,IAAgB,KAAK,YAAY,0BAAA;AAEvC,SAAK,YAAY,aACf,KAAK,UAAU,eACf,GACA,KAAK,UAAU,UACf,KAAK,UAAU,QACf,KAAK,UAAU,UAAA,GAGjB,KAAK,YAAY,0BAAA;AAAA;EASnB,qBAA6B,GAAe,GAA8B;AAExE,QADI,EAAO,SAAS,oBAChB,CAAC,EAAO,OAAQ;AAEpB,UAAM,IAAiB,GAAkB,EAAO,OAAO,SAAS,UAAA;AAChE,SAAK,YAAY,sBAAsB,GAAS,KAAkB,IAAA;AAAA;EASpE,gBAAwB,GAAmB,GAAyB;AAClE,UAAM,IAAiB;AAAA,MAAE,GAAG,EAAM;AAAA,MAAS,GAAG,EAAM;AAAA;AACpD,SAAK,YAAY,gBAAgB,GAAa,CAAA;AAAA;EAWhD,sBAA8B,GAAyB;AACrD,QAAI,CAAC,KAAK,aAAc;AAExB,SAAK,OAAO;AACZ,UAAM,IAAY;AAAA,MAAE,GAAG,EAAM;AAAA,MAAS,GAAG,EAAM;AAAA;AAC/C,SAAK,aAAa,KAAK,CAAA,GAGvB,KAAK,YAAY,GAAG,oBAAoB,KAAK,sBAAA,GAGzC,KAAK,aAAa,qBACpB,KAAK,kBAAkB,KAAK,aAAa,kBACzC,KAAK,mBAAmB,KAAK,eAAA,IAG/B,KAAK,YAAY,KAAK,wBAAwB;AAAA,MAC5C,UAAU,KAAK;AAAA,MACf,MAAM;AAAA,MACN,eAAe,CAAA;AAAA,KAChB;AAAA;EAMH,uBAAqC;AACnC,IAAI,KAAK,SAAS,oBAElB,KAAK,oBAAA,GACL,KAAK,cAAc,MAAA,GACnB,KAAK,YAAY,IAAI,oBAAoB,KAAK,sBAAA,GAC9C,KAAK,aAAa,IAElB,KAAK,YAAY,KAAK,0BAA0B;AAAA,MAC9C,UAAU,KAAK;AAAA,MACf,MAAM;AAAA,KACP,GAED,KAAK,OAAO;AAAA;EAMd,wBAAgC,GAAyC;AACvE,SAAK,kBAAkB,GACvB,KAAK,oBAAA,GACD,KACF,KAAK,mBAAmB,CAAA;AAAA;EAO5B,oBAAkC;AAChC,QAAI,CAAC,KAAK,gBAAiB;AAE3B,UAAM,IAAgB,KAAK,YAAY,0BAAA;AAEvC,QAAI,KAAK,oBAAA,uBAA8C;AACrD,YAAM,IAAU,KAAK,+BAA+B,CAAA;AACpD,MAAI,KACF,KAAK,YAAY,oBAAA,EAAsB,UAAU,SAAS,GAAS,EAAE,aAAa,KAAA,CAAM,GAG1F,KAAK,oBAAA,GACL,KAAK,mBAAmB,KAAK,eAAA;AAC7B;AAAA;AAGF,QAAI;AACF,YAAM,IAAY,KAAK,YAAY,aAAa,KAAK,iBAAiB,GAAe,IAAA;AACrF,WAAK,YAAY,0BAAA,GACjB,KAAK,YAAY,KAAK,0BAA0B;AAAA,QAC9C,UAAU,KAAK;AAAA,QACf,MAAM;AAAA,QACN,eAAe;AAAA,UACb,aAAa,EAAU;AAAA,UACvB,eAAe,KAAK;AAAA,UACpB,UAAU,EAAc,MAAA;AAAA;QAE1B,aAAa,EAAE,iBAAiB,CAAC,EAAU,EAAA,EAAG;AAAA,OAC/C,GAED,KAAK,oBAAA,GACL,KAAK,mBAAmB,KAAK,eAAA;AAAA,aACtB,GAAO;AACd,WAAK,YAAY,KAAK,uBAAuB;AAAA,QAC3C,UAAU,KAAK;AAAA,QACf,MAAM;AAAA,QACN,cAAc,8BAA+B,EAAgB,OAAA;AAAA,OAC9D;AAAA;;EAOL,0BAAkC,GAAoC;AACpE,QAAI,CAAC,KAAK,aAAc;AAExB,UAAM,IAAkB,IAAI,EAAM,QAChC,KAAK,MAAM,EAAc,CAAA,GACzB,GACA,KAAK,MAAM,EAAc,CAAA,CAAE;AAE7B,SAAK,aAAa,SAAS,KAAK,CAAA;AAGhC,UAAM,IAAkB,KAAK;AAC7B,SAAK,aAAa,KAAK,kBAAA,GAEnB,KAAK,cAAc,CAAC,IACtB,KAAK,mBAAmB,KAAK,YAAA,IACpB,CAAC,KAAK,cAAc,KAC7B,KAAK,oBAAoB,KAAK,YAAA;AAAA;EAYlC,mBAA2B,GAAkC;AAC3D,SAAK,oBAAA;AAEL,QAAI;AACF,UAAI;AAEJ,UAAI,MAAA,uBAAwC;AAE1C,cAAM,IAAY,IAAI,GACpB,EAAU,gBACV,QACA,QACA,IAAI,EAAS,GAAG,CAAA,CAAE;AAEpB,QAAA,IAAS,KAAK,YAAY,4BAA4B,aAAa,CAAA;AAAA,aAC9D;AACL,cAAM,IAAU,KAAK,YAAY,gBAAgB,IAAI,CAAA,GAC/C,IAAgB,IAAI,GACxB,GACA,IAAI,EAAS,GAAG,CAAA,GAChB,IAAI,GAAS,CAAA,GACb,CAAA,CAAE;AAEJ,QAAA,IAAS,EAAQ,aAAa,GAAe,KAAK,YAAY,aAAA,GAC9D,EAAO,SAAS,IAAI,GAAG,EAAQ,gBAAA,GAAmB,CAAA;AAAA;AAGpD,UAAI,EAAE,aAAkB,EAAM,QAAQ;AACpC,gBAAQ,KAAK,yCAAyC,CAAA,EAAA;AACtD;AAAA;AAIF,MAAA,EAAO,SAAS,UAAU,IAC1B,EAAO,SAAA,CAAU,MAAU;AACzB,QAAA,EAAM,SAAS,UAAU;AAAA,UAG3B,KAAK,eAAe,GACpB,KAAK,iBAAiB,KAAK,YAAA;AAG3B,YAAM,IAAY,KAAK,YAAY,0BAAA;AACnC,WAAK,aAAa,SAAS,IAAI,KAAK,MAAM,EAAU,CAAA,GAAI,GAAG,KAAK,MAAM,EAAU,CAAA,CAAE,GAElF,KAAK,YAAY,SAAA,EAAW,IAAI,KAAK,YAAA;AAAA,aAC9B,GAAO;AACd,cAAQ,KAAK,sCAAsC,CAAA,KAAc,CAAA,GACjE,KAAK,eAAe;AAAA;;EAOxB,sBAAoC;AAClC,IAAK,KAAK,iBAEV,KAAK,YAAY,SAAA,EAAW,OAAO,KAAK,YAAA,GACxC,KAAK,aAAa,SAAA,CAAU,MAAU;AACpC,MAAI,aAAiB,EAAM,SACrB,EAAM,YAAU,EAAM,SAAS,QAAA,GAC/B,MAAM,QAAQ,EAAM,QAAA,IACtB,EAAM,SAAS,QAAA,CAAS,MAAQ,EAAI,QAAA,CAAS,IACpC,EAAM,YACf,EAAM,SAAS,QAAA;AAAA,QAIrB,KAAK,eAAe,MACpB,KAAK,aAAa;AAAA;EAMpB,iBAAyB,GAA8B;AACrD,IAAA,EAAO,SAAA,CAAU,MAAU;AACzB,MAAI,aAAiB,EAAM,SACrB,MAAM,QAAQ,EAAM,QAAA,KACtB,EAAM,WAAW,EAAM,SAAS,IAAA,CAAK,MAAwB,EAAI,MAAA,CAAO,GACxE,EAAM,SAAS,QAAA,CAAS,MAAwB;AAC9C,QAAA,EAAI,cAAc,IAClB,EAAI,UAAU;AAAA,aAGhB,EAAM,WAAW,EAAM,SAAS,MAAA,GAChC,EAAM,SAAS,cAAc,IAC7B,EAAM,SAAS,UAAU;AAAA;;EASjC,oBAAqC;AAInC,QAHI,CAAC,KAAK,gBAGN,KAAK,oBAAA,sBAA8C,QAAO;AAE9D,UAAM,IAAa,IAAI,EAAM,KAAA,EAAO,cAAc,KAAK,YAAA,GACjD,IAAmB,KAAK,YAAY;AAE1C,eAAW,CAAC,GAAK,CAAA,KAAe,EAE9B,KAAI,EAAW,cAAc,EAAW,QAAA,EACtC,QAAO;AAGX,WAAO;AAAA;EAMT,mBAA2B,GAA8B;AACvD,IAAA,EAAO,SAAA,CAAU,MAAU;AACzB,MAAI,aAAiB,EAAM,QAAQ,EAAM,aACnC,MAAM,QAAQ,EAAM,QAAA,IACtB,EAAM,SAAS,QAAA,CAAS,MAAwB;AAC9C,QAAI,aAAe,EAAM,yBACvB,EAAI,SAAS,OAAO,QAAA,GACpB,EAAI,oBAAoB;AAAA,WAGnB,EAAM,oBAAoB,EAAM,yBACzC,EAAM,SAAS,SAAS,OAAO,QAAA,GAC/B,EAAM,SAAS,oBAAoB;AAAA;;EAS3C,oBAA4B,GAA8B;AACxD,IAAA,EAAO,SAAA,CAAU,MAAU;AACzB,MAAI,aAAiB,EAAM,QAAQ,EAAM,aACnC,MAAM,QAAQ,EAAM,QAAA,IACtB,EAAM,SAAS,QAAA,CAAS,MAAwB;AAC9C,QAAI,aAAe,EAAM,yBACvB,EAAI,SAAS,OAAO,CAAA,GACpB,EAAI,oBAAoB;AAAA,WAGnB,EAAM,oBAAoB,EAAM,yBACzC,EAAM,SAAS,SAAS,OAAO,CAAA,GAC/B,EAAM,SAAS,oBAAoB;AAAA;;GC9wDvC,KAA0B,GAwEnB,KAAb,MAAqD;AAAA,EACnD,OAAgB;AAAA,EAEhB,OAAoC;AAAA,EACpC;AAAA,EAGA,qBAAwD;AAAA,EAGxD,gBAA8C;AAAA,EAG9C,gBAA8C;AAAA,EAE9C,0BAAmD,oBAAI,IAAA;AAAA,EAEvD,sBAAiD,oBAAI,IAAA;AAAA,EAGrD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YAAY,GAA+B;AACzC,SAAK,aAAa,GAGlB,KAAK,oBAAoB,KAAK,mBAAmB,KAAK,IAAA,GACtD,KAAK,oBAAoB,KAAK,mBAAmB,KAAK,IAAA,GACtD,KAAK,kBAAkB,KAAK,iBAAiB,KAAK,IAAA,GAClD,KAAK,gBAAgB,KAAK,eAAe,KAAK,IAAA,GAC9C,KAAK,yBAAyB,KAAK,wBAAwB,KAAK,IAAA;AAAA;EAMlE,UAA+B;AAC7B,WAAO,KAAK;AAAA;EAMd,aAAmB;AACjB,SAAK,OAAO,QACZ,KAAK,qBAAqB,MAC1B,KAAK,gBAAgB;AAGrB,UAAM,IAAY,KAAK,WAAW,aAAA;AAClC,IAAA,EAAU,iBAAiB,eAAe,KAAK,iBAAA,GAC/C,EAAU,iBAAiB,eAAe,KAAK,iBAAA,GAC/C,EAAU,iBAAiB,aAAa,KAAK,eAAA,GAC7C,OAAO,iBAAiB,WAAW,KAAK,aAAA;AAAA;EAM1C,eAAqB;AAEnB,SAAK,gBAAA;AAGL,UAAM,IAAY,KAAK,WAAW,aAAA;AAClC,IAAA,EAAU,oBAAoB,eAAe,KAAK,iBAAA,GAClD,EAAU,oBAAoB,eAAe,KAAK,iBAAA,GAClD,EAAU,oBAAoB,aAAa,KAAK,eAAA,GAChD,OAAO,oBAAoB,WAAW,KAAK,aAAA,GAC3C,KAAK,WAAW,IAAI,oBAAoB,KAAK,sBAAA,GAE7C,KAAK,OAAO,QACZ,KAAK,qBAAqB,MAC1B,KAAK,gBAAgB;AAAA;EAMvB,kBAAwB;AACtB,IAAI,KAAK,SAAS,cAChB,KAAK,qBAAA,IACI,KAAK,SAAS,cACvB,KAAK,gBAAA;AAAA;EAOT,gBAA4B;AAC1B,UAAM,IAAiB,KAAK,WAAW,kBAAA,GACjC,IAAmB,KAAK,WAAW,oBAAA;AAEzC,YAAQ,KAAK,MAAb;AAAA,MACE,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MAET;AAEE,eAAI,IACiB,EAAiB,WAAW,EAAe,MAAM,EAAe,EAAA,IAE1E,SAGF,YAEF;AAAA;;EAOb,oBAAsC;AAEpC,WAAO,CAAA;AAAA;EAaT,mBAA2B,GAA2B;AACpD,QAAI,EAAM,WAAW,EAAG;AAExB,UAAM,IAAiB,KAAK,WAAW,kBAAA,GACjC,IAAmB,KAAK,WAAW,oBAAA,GACnC,IAAY,EAAM;AAExB,QAAI,KAAK,SAAS,QAAQ;AAExB,UAAI,GAAgB;AAClB,cAAM,IAAa,EAAiB,WAAW,EAAe,MAAM,EAAe,EAAA;AAGnF,YAAI,GAAW;AACb,cAAI,EAAe,SAAS,OAAQ;AACpC,cAAI,CAAC,EACH,CAAA,EAAiB,eAAe,EAAe,MAAM,EAAe,EAAA;AAAA,mBAIpE,EAAiB,oBAAoB,EAAe,MAAM,EAAe,EAAA,GACrE,EAAe,SAAS,aAAa;AACvC,kBAAM,IAAmB,KAAK,WAC3B,WAAA,EACA,aAAa,EAAe,EAAA;AAC/B,gBAAI,EAEF,YAAW,KAAS,EAAiB,MAAM;AACzC,cAAA,EAAiB,oBAAoB,SAAS,CAAA;AAC9C,oBAAM,IAAQ,KAAK,WAAW,WAAA,EAAc,SAAS,CAAA;AACrD,kBAAI,EACF,YAAW,KAAU,EAAM,MACzB,CAAA,EAAiB,oBAAoB,QAAQ,CAAA;AAAA;qBAQrD,EAAe,SAAS,WACxB,CAAC,EAAe,SAAS,SAAS,aAClC;AACA,kBAAM,IAAQ,KAAK,WAAW,WAAA,EAAc,SAAS,EAAe,EAAA;AACpE,gBAAI,EACF,YAAW,KAAU,EAAM,MACzB,CAAA,EAAiB,oBAAoB,QAAQ,CAAA;AAAA;AAKrD;AAAA;AAIF,YACE,CAAC,KACD,EAAE,EAAe,SAAS,WAAa,EAAe,SAAS,SAAS,cACxE;AACA,UAAA,EAAiB,UAAU,EAAe,MAAM,EAAe,EAAA;AAC/D;AAAA;AAIF,cAAM,IAAgB,KAAK,WAAW,0BAAA;AACtC,aAAK,eAAe,CAAA;AACpB;AAAA;AAKF,YAAM,IAAgB,KAAK,WAAW,aAAA,EAAe,sBAAA,GAC/C,IAAU,EAAM,UAAU,EAAc,MACxC,IAAU,EAAM,UAAU,EAAc;AAE9C,WAAK,oBAAoB,GAAS,GAAS,CAAA;AAAA;;EAQ/C,mBAA2B,GAA2B;AACpD,QAAI,KAAK,SAAS,eAAe,CAAC,KAAK,mBAAoB;AAE3D,UAAM,IAAgB,KAAK,WAAW,aAAA,EAAe,sBAAA,GAC/C,IAAU,EAAM,UAAU,EAAc,MACxC,IAAU,EAAM,UAAU,EAAc;AAG9C,SAAK,mBAAmB,gBAAgB;AAAA,MAAE,GAAG;AAAA,MAAS,GAAG;AAAA,OAGzD,KAAK,4BAAA,GAGL,KAAK,2BAAA;AAAA;EAOP,iBAAyB,GAA2B;AAClD,QAAI,EAAM,WAAW;AAErB,UAAI,KAAK,SAAS,eAAe,KAAK,oBAAoB;AACxD,cAAM,EAAE,aAAA,GAAa,eAAA,GAAe,WAAA,EAAA,IAAc,KAAK,oBAGjD,IAAQ,KAAK,IAAI,EAAc,IAAI,EAAY,CAAA,GAC/C,IAAS,KAAK,IAAI,EAAc,IAAI,EAAY,CAAA;AAGtD,YAAI,IAAQ,MAA2B,IAAS,IAAyB;AAEvE,UAAK,KACH,KAAK,WAAW,oBAAA,EAAsB,SAAA,GAExC,KAAK,qBAAA;AACL;AAAA;AAIF,aAAK,qBAAA;AAAA,aACI,KAAK,SAAS,cAAc,KAAK,iBAE1C,KAAK,gBAAA;AAAA;EAQT,wBAAgC,GAA+B;AAC7D,IAAI,KAAK,SAAS,cAAc,KAAK,iBACnC,KAAK,gBAAgB,CAAA;AAAA;EAYzB,eAAuB,GAA4B;AAEjD,QAAI,EAAM,QAAQ;UACZ,KAAK,SAAS,aAAa;AAC7B,aAAK,qBAAA;AACL;AAAA,iBACS,KAAK,SAAS,YAAY;AACnC,aAAK,gBAAA;AACL;AAAA;;AAKJ,QAAI,EAAM,QAAQ,YAAY,EAAM,QAAQ,aAAa;AAEvD,MAAI,KAAK,SAAS,UAChB,KAAK,gBAAA;AAEP;AAAA;AAMF,QAFkB,EAAM,WAAW,EAAM,SAKzC;AAAA,UAAI,EAAM,QAAQ,OAAO,EAAM,QAAQ,KAAK;AAC1C,QAAI,KAAK,SAAS,UAChB,KAAK,cAAA;AAEP;AAAA;AAIF,UAAI,EAAM,QAAQ,OAAO,EAAM,QAAQ,KAAK;AAC1C,QAAI,KAAK,SAAS,UAChB,KAAK,cAAA;AAEP;AAAA;AAIF,UAAI,EAAM,QAAQ,OAAO,EAAM,QAAQ,KAAK;AAC1C,QAAI,KAAK,SAAS,UAChB,KAAK,aAAA;AAEP;AAAA;;;EAWJ,oBAA4B,GAAiB,GAAiB,GAA0B;AAEtF,UAAM,IAAiB,SAAS,cAAc,KAAA;AAC9C,IAAA,EAAe,MAAM,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAO/B,KAAK,WAAW,aAAA,EAAe,YAAY,CAAA,GAG3C,KAAK,qBAAqB;AAAA,MACxB,aAAa;AAAA,QAAE,GAAG;AAAA,QAAS,GAAG;AAAA;MAC9B,eAAe;AAAA,QAAE,GAAG;AAAA,QAAS,GAAG;AAAA;MAChC,gBAAA;AAAA,MACA,WAAA;AAAA,MACA,mBAAmB,oBAAI,IAAA;AAAA,OAGzB,KAAK,OAAO;AAGZ,UAAM,IAAW,KAAK,WAAW,YAAA;AACjC,IAAI,MACF,EAAS,YAAY,KAIvB,KAAK,WAAW,KAAK,wBAAwB;AAAA,MAC3C,UAAU,KAAK;AAAA,MACf,MAAM;AAAA,MACN,eAAe,EAAE,aAAa;AAAA,QAAE,GAAG;AAAA,QAAS,GAAG;AAAA,QAAS;AAAA,KACzD;AAAA;EAMH,8BAA4C;AAC1C,QAAI,CAAC,KAAK,mBAAoB;AAE9B,UAAM,EAAE,aAAA,GAAa,eAAA,GAAe,gBAAA,EAAA,IAAmB,KAAK,oBAEtD,IAAO,KAAK,IAAI,EAAY,GAAG,EAAc,CAAA,GAC7C,IAAM,KAAK,IAAI,EAAY,GAAG,EAAc,CAAA,GAC5C,IAAQ,KAAK,IAAI,EAAc,IAAI,EAAY,CAAA,GAC/C,IAAS,KAAK,IAAI,EAAc,IAAI,EAAY,CAAA;AAEtD,IAAA,EAAe,MAAM,OAAO,GAAG,CAAA,MAC/B,EAAe,MAAM,MAAM,GAAG,CAAA,MAC9B,EAAe,MAAM,QAAQ,GAAG,CAAA,MAChC,EAAe,MAAM,SAAS,GAAG,CAAA;AAAA;EAMnC,6BAA2C;AACzC,QAAI,CAAC,KAAK,mBAAoB;AAE9B,UAAM,IAAiB,KAAK,4BAAA,GACtB,IAAgB,oBAAI,IAAA;AAG1B,eAAW,KAAM,EAAe,WAC9B,CAAA,EAAc,IAAI,CAAA;AAEpB,eAAW,KAAM,EAAe,OAC9B,CAAA,EAAc,IAAI,CAAA;AAEpB,eAAW,KAAM,EAAe,MAC9B,CAAA,EAAc,IAAI,CAAA;AAIpB,SAAK,mBAAmB,oBAAoB;AAAA;EAW9C,8BAIE;AACA,UAAM,IAAS;AAAA,MACb,YAAY,CAAA;AAAA,MACZ,QAAQ,CAAA;AAAA,MACR,OAAO,CAAA;AAAA;AAGT,QAAI,CAAC,KAAK,mBAAoB,QAAO;AAErC,UAAM,IAAU,KAAK,WAAW,WAAA;AAChC,QAAI,CAAC,EAAS,QAAO;AAErB,UAAM,IAAS,KAAK,WAAW,UAAA,GACzB,IAAY,KAAK,WAAW,aAAA,GAC5B,IAAQ,EAAU,aAClB,IAAS,EAAU,cAGnB,EAAE,aAAA,GAAa,eAAA,EAAA,IAAkB,KAAK,oBACtC,IAAa;AAAA,MACjB,MAAM,KAAK,IAAI,EAAY,GAAG,EAAc,CAAA;AAAA,MAC5C,MAAM,KAAK,IAAI,EAAY,GAAG,EAAc,CAAA;AAAA,MAC5C,MAAM,KAAK,IAAI,EAAY,GAAG,EAAc,CAAA;AAAA,MAC5C,MAAM,KAAK,IAAI,EAAY,GAAG,EAAc,CAAA;AAAA,OAIxC,IAAmB,oBAAI,IAAA,GAEvB,IAA0B,oBAAI,IAAA,GAG9B,IAAqB,KAAK,WAAW;AAC3C,eAAW,CAAC,GAAa,CAAA,KAAa,GAAoB;AACxD,YAAM,IAAW,IAAI,EAAM,QAAA;AAG3B,UAFA,EAAS,iBAAiB,CAAA,GAEtB,GAAoB,GAAU,GAAQ,GAAO,GAAQ,CAAA,GAAa;AACpE,QAAA,EAAO,WAAW,KAAK,CAAA;AAGvB,cAAM,IAAY,EAAQ,aAAa,CAAA;AACvC,YAAI,EACF,YAAW,KAAS,EAAU,MAAM;AAClC,UAAA,EAAiB,IAAI,CAAA;AACrB,gBAAM,IAAQ,EAAQ,SAAS,CAAA;AAC/B,cAAK;AACL,uBAAW,KAAU,EAAM,OAAO;AAChC,oBAAM,IAAe,EAAwB,IAAI,CAAA,KAAW;AAC5D,cAAA,EAAwB,IAAI,GAAQ,IAAe,CAAA;AAAA;;;;AAQ7D,UAAM,IAAiB,KAAK,WAAW;AACvC,eAAW,CAAC,GAAS,CAAA,KAAa,GAAgB;AAKhD,UAHI,EAAS,SAAS,eAGlB,EAAiB,IAAI,CAAA,EAAU;AAEnC,YAAM,IAAW,IAAI,EAAM,QAAA;AAG3B,UAFA,EAAS,iBAAiB,CAAA,GAEtB,GAAoB,GAAU,GAAQ,GAAO,GAAQ,CAAA,GAAa;AACpE,QAAA,EAAO,OAAO,KAAK,CAAA,GACnB,EAAiB,IAAI,CAAA;AACrB,cAAM,IAAQ,EAAQ,SAAS,CAAA;AAC/B,YAAI,CAAC,EAAO;AACZ,mBAAW,KAAU,EAAM,OAAO;AAChC,gBAAM,IAAe,EAAwB,IAAI,CAAA,KAAW;AAC5D,UAAA,EAAwB,IAAI,GAAQ,IAAe,CAAA;AAAA;;;AAKzD,eAAW,CAAC,GAAQ,CAAA,KAAU,EAE5B,CAAI,KAAS,KACX,EAAO,MAAM,KAAK,CAAA;AAItB,WAAO;AAAA;EAMT,uBAAqC;AACnC,QAAI,CAAC,KAAK,mBAAoB;AAE9B,UAAM,IAAiB,KAAK,4BAAA,GACtB,IAAmB,KAAK,WAAW,oBAAA,GAGnC,IAAa,oBAAI,IAAA,GACjB,IAAS,oBAAI,IAAA,GACb,IAAQ,oBAAI,IAAA;AAElB,eAAW,KAAM,EAAe,WAC9B,CAAA,EAAW,IAAI,GAAI,IAAA;AAErB,eAAW,KAAM,EAAe,OAC9B,CAAA,EAAO,IAAI,GAAI,IAAA;AAEjB,eAAW,KAAM,EAAe,MAC9B,CAAA,EAAM,IAAI,GAAI,IAAA;AAIhB,QAAI,KAAK,mBAAmB,WAAW;AAErC,YAAM,IAAW,EAAiB,eAAA;AAClC,iBAAW,KAAM,EAAS,WACxB,CAAA,EAAW,IAAI,GAAI,IAAA;AAErB,iBAAW,KAAM,EAAS,OACxB,CAAA,EAAO,IAAI,GAAI,IAAA;AAEjB,iBAAW,KAAM,EAAS,MACxB,CAAA,EAAM,IAAI,GAAI,IAAA;AAAA;AAKlB,IAAA,EAAiB,eAAe,GAAY,GAAQ,CAAA;AAGpD,UAAM,IAAa,EAAW,OAAO,EAAO,OAAO,EAAM;AACzD,SAAK,WAAW,KAAK,0BAA0B;AAAA,MAC7C,UAAU,KAAK;AAAA,MACf,MAAM;AAAA,MACN,eAAe;AAAA,QACb,eAAe;AAAA,QACf,UAAU,KAAK,mBAAmB;AAAA;MAEpC,aAAa,CAAA;AAAA,KACd,GAGD,KAAK,sBAAA,GACL,KAAK,OAAO;AAGZ,UAAM,IAAW,KAAK,WAAW,YAAA;AACjC,IAAI,MACF,EAAS,YAAY;AAAA;EAOzB,uBAAqC;AACnC,QAAI,CAAC,KAAK,mBAAoB;AAG9B,SAAK,WAAW,KAAK,0BAA0B;AAAA,MAC7C,UAAU,KAAK;AAAA,MACf,MAAM;AAAA,KACP,GAGD,KAAK,sBAAA,GACL,KAAK,OAAO;AAGZ,UAAM,IAAW,KAAK,WAAW,YAAA;AACjC,IAAI,MACF,EAAS,YAAY;AAAA;EAOzB,wBAAsC;AACpC,IAAI,KAAK,oBAAoB,kBAC3B,KAAK,mBAAmB,eAAe,OAAA,GAEzC,KAAK,qBAAqB;AAAA;EAU5B,eAAuB,GAAoC;AACzD,UAAM,IAAU,KAAK,WAAW,WAAA;AAChC,QAAI,CAAC,EAAS;AAGd,UAAM,IADmB,KAAK,WAAW,oBAAA,EACJ,eAAA,GAG/B,IAAmB,oBAAI,IAAA;AAG7B,eAAW,KAAe,EAAY,YAAY;AAChD,YAAM,IAAW,KAAK,WAAW,mBAAmB,IAAI,CAAA;AACxD,MAAI,KACF,EAAiB,IAAI,GAAa,EAAS,SAAS,MAAA,CAAO;AAAA;AAK/D,eAAW,KAAW,EAAY,QAAQ;AACxC,YAAM,IAAW,KAAK,WAAW,eAAe,IAAI,CAAA;AACpD,MAAI,KAAY,CAAC,EAAS,SAAS,eAEjC,EAAiB,IAAI,GAAS,EAAS,SAAS,MAAA,CAAO;AAAA;AAK3D,UAAM,IAAkB,oBAAI,IAAA;AAG5B,eAAW,KAAU,EAAY,MAC/B,CAAA,EAAgB,IAAI,CAAA;AAItB,eAAW,KAAe,EAAY,YAAY;AAChD,YAAM,IAAY,EAAQ,aAAa,CAAA;AACvC,UAAI,EACF,YAAW,KAAS,EAAU,MAAM;AAClC,cAAM,IAAQ,EAAQ,SAAS,CAAA;AAC/B,YAAI,EACF,YAAW,KAAU,EAAM,MACzB,CAAA,EAAgB,IAAI,CAAA;AAAA;;AAO9B,eAAW,KAAW,EAAY,QAAQ;AACxC,YAAM,IAAQ,EAAQ,SAAS,CAAA;AAC/B,UAAI,EACF,YAAW,KAAU,EAAM,MACzB,CAAA,EAAgB,IAAI,CAAA;AAAA;AAM1B,UAAM,IAAmC,oBAAI,IAAA;AAC7C,eAAW,KAAU,EAAY,OAAO;AACtC,YAAM,IAAO,EAAQ,QAAQ,CAAA;AAC7B,UAAI,KAAQ,EAAK,sBAAsB,SAAS,GAAG;AAEjD,cAAM,IAAY,EAAK,sBAAsB,IAAA,CAAK,MAAQ,EAAoB,CAAA,CAAI;AAClF,QAAA,EAAiC,IAAI,GAAQ,CAAA;AAAA;;AAKjD,SAAK,gBAAgB;AAAA,MACnB,gBAAgB,EAAc,MAAA;AAAA,MAC9B,kBAAA;AAAA,MACA,iBAAA;AAAA,MACA,kCAAA;AAAA,OAGF,KAAK,OAAO;AAGZ,UAAM,IAAW,KAAK,WAAW,YAAA;AACjC,IAAI,MACF,EAAS,YAAY,KAIvB,KAAK,WAAW,GAAG,oBAAoB,KAAK,sBAAA,GAG5C,KAAK,WAAW,KAAK,wBAAwB;AAAA,MAC3C,UAAU,KAAK;AAAA,MACf,MAAM;AAAA,MACN,eAAe,EAAE,cAAc,EAAiB,KAAA;AAAA,KACjD;AAAA;EAMH,gBAAwB,GAAoC;AAC1D,QAAI,CAAC,KAAK,cAAe;AAEzB,UAAM,EAAE,gBAAA,GAAgB,kBAAA,GAAkB,iBAAA,GAAiB,kCAAA,EAAA,IACzD,KAAK;AAGP,IAAA,IAAgB,KAAK,WAAW,0BAAA;AAGhC,UAAM,IAAQ,IAAI,EAAM,QAAA,EAAU,WAAW,GAAe,CAAA;AAG5D,eAAW,CAAC,GAAW,CAAA,KAAe,GAAkB;AAEtD,YAAM,IAAkB,GADJ,IAAI,EAAM,QAAA,EAAU,WAAW,GAAY,CAAA,CAAM,GAI/D,IAAoB,KAAK,WAAW,mBAAmB,IAAI,CAAA;AACjE,UAAI,GAAmB;AACrB,QAAA,EAAkB,SAAS,KAAK,CAAA,GAGhC,KAAK,WAAW,cAAc,kBAAkB,GAAW,CAAA;AAC3D;AAAA;AAIF,YAAM,IAAgB,KAAK,WAAW,eAAe,IAAI,CAAA;AACzD,MAAI,MACF,EAAc,SAAS,KAAK,CAAA,GAG5B,KAAK,WAAW,cAAc,uBAAuB,CAAA;AAAA;AAMzD,eAAW,CAAC,GAAQ,CAAA,KAAiC,GAAkC;AAErF,YAAM,IAAmB,EAA6B,IAAA,CAAK,MAElD,EADQ,IAAI,EAAM,QAAA,EAAU,WAAW,GAAK,CAAA,CAAM;AAK3D,WAAK,WAAW,cAAc,sBAAsB,GAAQ,GAAkB,EAAA;AAAA;AAIhF,UAAM,IAAoB,KAAK,WAAW;AAC1C,eAAW,KAAU,EACnB,CAAA,EAAkB,eAAe,CAAA;AAAA;EAOrC,kBAAgC;AAI9B,QAHI,CAAC,KAAK,iBAGN,CADY,KAAK,WAAW,WAAA,EAClB;AAEd,UAAM,EAAE,gBAAA,GAAgB,kBAAA,EAAA,IAAqB,KAAK,eAC5C,IAAkB,KAAK,WAAW,0BAAA,GAIlC,IAAY,EADJ,IAAI,EAAM,QAAA,EAAU,WAAW,GAAiB,CAAA,CAAe;AAI7E,SAAK,WAAW,KAAK,0BAA0B;AAAA,MAC7C,UAAU,KAAK;AAAA,MACf,MAAM;AAAA,MACN,eAAe;AAAA,QACb,cAAc,EAAiB;AAAA,QAC/B,OAAO;AAAA,UAAE,GAAG,EAAU;AAAA,UAAG,GAAG,EAAU;AAAA;;MAExC,aAAa,CAAA;AAAA,KACd,GAGD,KAAK,WAAW,0BAAA,GAGhB,KAAK,OAAO,QACZ,KAAK,gBAAgB,MAGrB,KAAK,WAAW,IAAI,oBAAoB,KAAK,sBAAA;AAG7C,UAAM,IAAW,KAAK,WAAW,YAAA;AACjC,IAAI,MACF,EAAS,YAAY;AAAA;EAOzB,kBAAgC;AAC9B,QAAI,CAAC,KAAK,cAAe;AAEzB,UAAM,EAAE,kBAAA,GAAkB,iBAAA,GAAiB,kCAAA,EAAA,IACzC,KAAK;AAGP,eAAW,CAAC,GAAW,CAAA,KAAe,GAAkB;AACtD,YAAM,IAAoB,KAAK,WAAW,mBAAmB,IAAI,CAAA;AACjE,UAAI,GAAmB;AACrB,QAAA,EAAkB,SAAS,KAAK,CAAA,GAChC,KAAK,WAAW,cAAc,kBAAkB,GAAW,CAAA;AAC3D;AAAA;AAGF,YAAM,IAAgB,KAAK,WAAW,eAAe,IAAI,CAAA;AACzD,MAAI,MACF,EAAc,SAAS,KAAK,CAAA,GAC5B,KAAK,WAAW,cAAc,uBAAuB,CAAA;AAAA;AAKzD,eAAW,CAAC,GAAQ,CAAA,KAAiC,GAAkC;AACrF,YAAM,IAAY,EAA6B,IAAA,CAAK,MAAQ,EAAoB,CAAA,CAAI;AACpF,WAAK,WAAW,cAAc,sBAAsB,GAAQ,GAAW,EAAA;AAAA;AAIzE,UAAM,IAAoB,KAAK,WAAW;AAC1C,eAAW,KAAU,EACnB,CAAA,EAAkB,eAAe,CAAA;AAInC,SAAK,WAAW,KAAK,0BAA0B;AAAA,MAC7C,UAAU,KAAK;AAAA,MACf,MAAM;AAAA,KACP,GAGD,KAAK,OAAO,QACZ,KAAK,gBAAgB,MAGrB,KAAK,WAAW,IAAI,oBAAoB,KAAK,sBAAA;AAG7C,UAAM,IAAW,KAAK,WAAW,YAAA;AACjC,IAAI,MACF,EAAS,YAAY;AAAA;EAWzB,sBAA+B;AAC7B,WAAO,KAAK,kBAAkB;AAAA;EAOhC,gBAAyB;AACvB,UAAM,IAAU,KAAK,WAAW,WAAA;AAChC,QAAI,CAAC,EAAS,QAAO;AAGrB,UAAM,IADmB,KAAK,WAAW,oBAAA,EACJ,eAAA;AAGrC,QACE,EAAY,WAAW,WAAW,KAClC,EAAY,OAAO,WAAW,KAC9B,EAAY,MAAM,WAAW,EAE7B,QAAO;AAIT,UAAM,IAAS;AAAA,MACb,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA;AAIR,eAAW,KAAe,EAAY,YAAY;AAChD,YAAM,IAAY,EAAQ,aAAa,CAAA;AACvC,UAAI,GAAW;AACb,cAAM,IAAM,EAAU;AACtB,QAAA,EAAO,OAAO,KAAK,IAAI,EAAO,MAAM,EAAI,CAAA,GACxC,EAAO,OAAO,KAAK,IAAI,EAAO,MAAM,EAAI,CAAA,GACxC,EAAO,OAAO,KAAK,IAAI,EAAO,MAAM,EAAI,CAAA,GACxC,EAAO,OAAO,KAAK,IAAI,EAAO,MAAM,EAAI,CAAA;AAAA;;AAK5C,eAAW,KAAW,EAAY,QAAQ;AACxC,YAAM,IAAQ,EAAQ,SAAS,CAAA;AAC/B,UAAI,KAAS,EAAM,UAAU;AAC3B,cAAM,IAAM,EAAM;AAClB,QAAA,EAAO,OAAO,KAAK,IAAI,EAAO,MAAM,EAAI,CAAA,GACxC,EAAO,OAAO,KAAK,IAAI,EAAO,MAAM,EAAI,CAAA,GACxC,EAAO,OAAO,KAAK,IAAI,EAAO,MAAM,EAAI,CAAA,GACxC,EAAO,OAAO,KAAK,IAAI,EAAO,MAAM,EAAI,CAAA;AAAA;;AAI5C,UAAM,IAAS;AAAA,MACb,IAAI,EAAO,OAAO,EAAO,QAAQ;AAAA,MACjC,IAAI,EAAO,OAAO,EAAO,QAAQ;AAAA,OAI7B,IAA4C,CAAA;AAClD,eAAW,KAAe,EAAY,YAAY;AAChD,YAAM,IAAY,EAAQ,aAAa,CAAA;AACvC,UAAI,GAAW;AACb,cAAM,IAAM,EAAU,UAChB,IAAU,EAAU,KAAK,IAAA,CAAK,MAAU;AAC5C,gBAAM,IAAQ,EAAQ,SAAS,CAAA;AAC/B,iBAAO,IAAQ,EAAM,SAAS;AAAA;AAEhC,QAAA,EAAoB,KAAK;AAAA,UACvB,MAAM,EAAU;AAAA,UAChB,kBAAkB;AAAA,YAChB,GAAG,EAAI,IAAI,EAAO;AAAA,YAClB,GAAG,EAAI,IAAI,EAAO;AAAA;UAEpB,UAAU,EAAU,SAAS;AAAA,UAC7B,YAAY;AAAA,UACZ,YAAY;AAAA,UACZ,QAAQ,IAAI,IAAI,EAAU,MAAA;AAAA,SAC3B;AAAA;;AAKL,UAAM,IAAsD,CAAA;AAC5D,eAAW,KAAW,EAAY,QAAQ;AACxC,YAAM,IAAQ,EAAQ,SAAS,CAAA;AAC/B,UAAI,KAAS,EAAM,UAAU;AAC3B,cAAM,IAAM,EAAM;AAClB,QAAA,EAAyB,KAAK;AAAA,UAC5B,kBAAkB;AAAA,YAChB,GAAG,EAAI,IAAI,EAAO;AAAA,YAClB,GAAG,EAAI,IAAI,EAAO;AAAA;UAEpB,YAAY;AAAA,UACZ,QAAQ,EAAM;AAAA,SACf;AAAA;;AAML,UAAM,IAAsB,oBAAI,IAAA;AAGhC,SAAK,wBAAwB,MAAA,GAC7B,KAAK,oBAAoB,MAAA;AAGzB,eAAW,KAAe,EAAY,YAAY;AAChD,YAAM,IAAY,EAAQ,aAAa,CAAA;AACvC,UAAI,EACF,UAAS,IAAI,GAAG,IAAI,EAAU,KAAK,QAAQ,KAAK;AAC9C,cAAM,IAAQ,EAAU,KAAK,CAAA;AAC7B,QAAI,MACF,EAAoB,IAAI,CAAA,GACxB,KAAK,wBAAwB,IAAI,GAAO,CAAA,GACxC,KAAK,oBAAoB,IAAI,GAAO,CAAA;AAAA;;AAO5C,eAAW,KAAW,EAAY,OAChC,CAAA,EAAoB,IAAI,CAAA;AAG1B,UAAM,IAAkC,CAAA;AACxC,eAAW,KAAU,EAAY,OAAO;AACtC,YAAM,IAAO,EAAQ,QAAQ,CAAA;AAC7B,UAAK,KAGD,EAAoB,IAAI,EAAK,KAAA,KAAU,EAAoB,IAAI,EAAK,KAAA,GAAQ;AAE9E,cAAM,IAAgC,EAAK,sBAAsB,IAAA,CAAK,OAAS;AAAA,UAC7E,GAAG,EAAI,IAAI,EAAO;AAAA,UAClB,GAAG,EAAI,IAAI,EAAO;AAAA,UACnB;AAED,QAAA,EAAe,KAAK;AAAA,UAClB,iBAAiB,EAAK;AAAA,UACtB,iBAAiB,EAAK;AAAA,UACtB,+BAAA;AAAA,SACD;AAAA;;AAKL,gBAAK,gBAAgB;AAAA,MACnB,QAAA;AAAA,MACA,YAAY;AAAA,MACZ,iBAAiB;AAAA,MACjB,OAAO;AAAA,OAIT,KAAK,WAAW,KAAK,0BAA0B;AAAA,MAC7C,UAAU,KAAK;AAAA,MACf,MAAM;AAAA,MACN,eAAe;AAAA,QACb,gBAAgB,EAAoB;AAAA,QACpC,qBAAqB,EAAyB;AAAA,QAC9C,WAAW,EAAe;AAAA;MAE5B,aAAa,CAAA;AAAA,KACd,GAEM;AAAA;EAOT,gBAAyB;AAIvB,QAHI,CAAC,KAAK,iBAGN,CADY,KAAK,WAAW,WAAA,EAClB,QAAO;AAGrB,UAAM,IAAa,EADI,KAAK,WAAW,0BAAA,CAA2B,GAI5D,IAAU,oBAAI,IAAA,GAGd,IAA8B,CAAA;AACpC,eAAW,KAAiB,KAAK,cAAc,YAAY;AAMzD,YAAM,IAAW,EALE,IAAI,EACrB,KAAK,MAAM,EAAW,IAAI,EAAc,iBAAiB,CAAA,GACzD,KAAK,MAAM,EAAW,IAAI,EAAc,iBAAiB,CAAA,CAAE,CAC5D,GAGK,IAAW,EAAoB,IAAI,GAAS,EAAc,QAAA,CAAS;AAEzE,UAAI;AACF,cAAM,IAAe,KAAK,WAAW,aACnC,EAAc,MACd,GACA,GACA,EAAc,QACd,EAAc,UAAA;AAGhB,QAAA,EAAoB,KAAK,EAAa,EAAA,GAGtC,EAAQ,IAAI,EAAc,YAAY,EAAa,EAAA;AAInD,mBAAW,CAAC,GAAe,CAAA,KAAwB,KAAK,wBACtD,KAAI,MAAwB,EAAc,YAAY;AACpD,gBAAM,IAAW,KAAK,oBAAoB,IAAI,CAAA;AAC9C,cAAI,MAAa,UAAa,IAAW,EAAa,KAAK,QAAQ;AACjE,kBAAM,IAAW,EAAa,KAAK,CAAA;AACnC,YAAI,KACF,EAAQ,IAAI,GAAe,CAAA;AAAA;;eAK5B,GAAO;AACd,gBAAQ,MAAM,8BAA8B,CAAA;AAAA;;AAKhD,UAAM,IAAmC,CAAA;AACzC,eAAW,KAAU,KAAK,cAAc,iBAAiB;AAMvD,YAAM,IAAW,EALE,IAAI,EACrB,KAAK,MAAM,EAAW,IAAI,EAAO,iBAAiB,CAAA,GAClD,KAAK,MAAM,EAAW,IAAI,EAAO,iBAAiB,CAAA,CAAE,CACrD;AAID,UAAI;AACF,cAAM,IAAW,KAAK,WAAW,kBAAkB,GAAU,EAAO,MAAA;AACpE,QAAA,EAAyB,KAAK,EAAS,EAAA,GAGvC,EAAQ,IAAI,EAAO,YAAY,EAAS,EAAA;AAAA,eACjC,GAAO;AACd,gBAAQ,MAAM,oCAAoC,CAAA;AAAA;;AAKtD,UAAM,IAAyB,CAAA;AAC/B,eAAW,KAAY,KAAK,cAAc,OAAO;AAC/C,YAAM,IAAW,EAAQ,IAAI,EAAS,eAAA,GAChC,IAAW,EAAQ,IAAI,EAAS,eAAA;AAGtC,UAAI,KAAY,EACd,KAAI;AACF,cAAM,IAAU,KAAK,WAAW,QAAQ,GAAU,CAAA;AAIlD,YAHA,EAAe,KAAK,EAAQ,EAAA,GAGxB,EAAS,8BAA8B,SAAS,GAAG;AACrD,gBAAM,IAAoB,EAAS,8BAA8B,IAAA,CAAK,OAAY;AAAA,YAChF,GAAG,KAAK,MAAM,EAAW,IAAI,EAAO,CAAA;AAAA,YACpC,GAAG,KAAK,MAAM,EAAW,IAAI,EAAO,CAAA;AAAA,YACrC;AAED,eAAK,WAAW,cAAc,sBAC5B,EAAQ,IACR,GACA,EAAA,GAEF,KAAK,WAAW,kBAAkB,eAAe,EAAQ,EAAA;AAAA;eAEpD,GAAO;AACd,gBAAQ,MAAM,yBAAyB,CAAA;AAAA;;AAM7C,UAAM,IAAmB,KAAK,WAAW,oBAAA,GACnC,IAAgB,oBAAI,IAAA,GACpB,IAAY,oBAAI,IAAA,GAChB,IAAW,oBAAI,IAAA;AAErB,eAAW,KAAM,EACf,CAAA,EAAc,IAAI,GAAI,IAAA;AAExB,eAAW,KAAM,EACf,CAAA,EAAU,IAAI,GAAI,IAAA;AAEpB,eAAW,KAAM,EACf,CAAA,EAAS,IAAI,GAAI,IAAA;AAGnB,WAAA,EAAiB,eAAe,GAAe,GAAW,CAAA,GAG1D,KAAK,WAAW,KAAK,0BAA0B;AAAA,MAC7C,UAAU,KAAK;AAAA,MACf,MAAM;AAAA,MACN,eAAe;AAAA,QACb,gBAAgB,EAAoB;AAAA,QACpC,qBAAqB,EAAyB;AAAA,QAC9C,WAAW,EAAe;AAAA,QAC1B,UAAU;AAAA,UAAE,GAAG,EAAW;AAAA,UAAG,GAAG,EAAW;AAAA;;MAE7C,aAAa,CAAA;AAAA,KACd,GAGD,KAAK,WAAW,0BAAA,GAET;AAAA;EAWT,eAAwB;AAGtB,WADoB,KAAK,cAAA,KAIzB,KAAK,gBAAA,GACE,MAJkB;AAAA;EAmB3B,kBAA2B;AACzB,UAAM,IAAmB,KAAK,WAAW,oBAAA,GACnC,IAAc,EAAiB,eAAA;AAMrC,QAHE,EAAY,WAAW,SAAS,EAAY,OAAO,SAAS,EAAY,MAAM,WAG7D,EACjB,QAAO;AAMT,eAAW,KAAU,EAAY,MAC/B,MAAK,WAAW,WAAW,CAAA;AAI7B,eAAW,KAAe,EAAY,WACpC,MAAK,WAAW,gBAAgB,CAAA;AAIlC,eAAW,KAAW,EAAY,OAChC,MAAK,WAAW,qBAAqB,CAAA;AAIvC,gBAAK,WAAW,KAAK,0BAA0B;AAAA,MAC7C,UAAU,KAAK;AAAA,MACf,MAAM;AAAA,MACN,eAAe;AAAA,QACb,gBAAgB,EAAY,WAAW;AAAA,QACvC,qBAAqB,EAAY,OAAO;AAAA,QACxC,WAAW,EAAY,MAAM;AAAA;MAE/B,aAAa,CAAA;AAAA,KACd,GAGD,KAAK,WAAW,0BAAA,GAGhB,EAAiB,SAAA,GAEV;AAAA;GCl2CE,KAAb,MAA8B;AAAA,EAE5B,YAA0C;AAAA,EAG1C,aAAoC;AAAA,EAGpC,YAA4C,oBAAI,IAAA;AAAA,EAKhD,cAAc;AAAA,EAAA;AAAA,EAOd,eAAqC;AACnC,WAAO,KAAK;AAAA;EAQd,gBAA+B;AAC7B,WAAO,KAAK;AAAA;EAUd,WAAW,GAAqB,GAAyB;AACvD,QAAI,CAAC,KAAK,UACR,QAAO;AAET,QAAI,KAAK,UAAU,SAAS,OAC1B,QAAO,KAAK,UAAU,SAAS,KAAQ,KAAK,UAAU,OAAO;AAE/D,QAAI,KAAK,UAAU,SAAS,SAAS;AACnC,UAAI,MAAS,YACX,QAAO,KAAK,UAAU,YAAY,IAAI,CAAA,KAAa;AAErD,UAAI,MAAS,QACX,QAAO,KAAK,UAAU,QAAQ,IAAI,CAAA,KAAa;AAEjD,UAAI,MAAS,OACX,QAAO,KAAK,UAAU,OAAO,IAAI,CAAA,KAAa;AAAA;AAGlD,WAAO;AAAA;EAQT,eAAwB;AACtB,WAAO,KAAK,cAAc;AAAA;EAG5B,iBAAyB,GAAyB,GAAkC;AAClF,QAAI,MAAM,EACR,QAAO;AAKT,QAHI,MAAM,QAAQ,MAAM,QAGpB,EAAE,SAAS,EAAE,KACf,QAAO;AAET,QAAI,EAAE,SAAS,UAAU,EAAE,SAAS,OAClC,QAAO,EAAE,OAAO,EAAE;AAEpB,QAAI,EAAE,SAAS,WAAW,EAAE,SAAS,SAAS;AAC5C,YAAM,IAAc,EAAE,cAAc,oBAAI,IAAA,GAClC,IAAc,EAAE,cAAc,oBAAI,IAAA,GAClC,IAAU,EAAE,UAAU,oBAAI,IAAA,GAC1B,IAAU,EAAE,UAAU,oBAAI,IAAA,GAC1B,IAAS,EAAE,SAAS,oBAAI,IAAA,GACxB,IAAS,EAAE,SAAS,oBAAI,IAAA;AAE9B,UACE,EAAY,SAAS,EAAY,QACjC,EAAQ,SAAS,EAAQ,QACzB,EAAO,SAAS,EAAO,KAEvB,QAAO;AAGT,iBAAW,KAAM,EAAY,KAAA,EAC3B,KAAI,CAAC,EAAY,IAAI,CAAA,EACnB,QAAO;AAGX,iBAAW,KAAM,EAAQ,KAAA,EACvB,KAAI,CAAC,EAAQ,IAAI,CAAA,EACf,QAAO;AAGX,iBAAW,KAAM,EAAO,KAAA,EACtB,KAAI,CAAC,EAAO,IAAI,CAAA,EACd,QAAO;AAAA;AAIb,WAAO;AAAA;EAYT,UAAU,GAAqB,GAAgB,GAAqC;AAClF,UAAM,IAAoB,KAAK,WACzB,IAA8B;AAAA,MAAE,MAAM;AAAA,MAAc,MAAA;AAAA,MAAM,IAAI;AAAA,MAAU,MAAM;AAAA;AAGpF,IAAI,KAAK,iBAAiB,GAAc,CAAA,MAIpC,MAAS,WAAa,MAExB,EAAa,OAAQ,EAAS,cAA4C,EAAU,MAArC,EAAU,iBAI3D,KAAK,YAAY,GACjB,KAAK,aAAa,KAAK,IAAA,GAGvB,KAAK,gBAAgB,GAAc,CAAA;AAAA;EAMrC,WAAiB;AACf,UAAM,IAAoB,KAAK;AAE/B,IAAK,MAIL,KAAK,YAAY,MACjB,KAAK,aAAa,MAGlB,KAAK,gBAAgB,MAAM,CAAA;AAAA;EAa7B,eACE,GACA,GACA,GACM;AACN,UAAM,IAAoB,KAAK;AAK/B,SAFoB,GAAY,QAAQ,MAAM,GAAQ,QAAQ,MAAM,GAAO,QAAQ,OAEhE,GAAG;AACpB,WAAK,SAAA;AACL;AAAA;AAGF,UAAM,IAAmC;AAAA,MACvC,MAAM;AAAA,MACN,GAAI,KAAc,EAAW,OAAO,IAAI,EAAE,YAAA,EAAA,IAAe,CAAA;AAAA,MACzD,GAAI,KAAU,EAAO,OAAO,IAAI,EAAE,QAAA,EAAA,IAAW,CAAA;AAAA,MAC7C,GAAI,KAAS,EAAM,OAAO,IAAI,EAAE,OAAA,EAAA,IAAU,CAAA;AAAA;AAI5C,IAAI,KAAK,iBAAiB,GAAc,CAAA,MAKxC,KAAK,YAAY,GACjB,KAAK,aAAa,KAAK,IAAA,GAGvB,KAAK,gBAAgB,GAAc,CAAA;AAAA;EAgBrC,eAAe,GAAqB,GAAgB,GAAyB;AAE3E,QAAI,KAAK,WAAW,GAAM,CAAA,EACxB;AAGF,UAAM,IAAoB,KAAK;AAG/B,QAAI,CAAC,GAAmB;AACtB,WAAK,UAAU,GAAM,GAAU,CAAA;AAC/B;AAAA;AAIF,QAAI,EAAkB,SAAS,QAAQ;AACrC,YAAM,IAAa,oBAAI,IAAA,GACjB,IAAS,oBAAI,IAAA,GACb,IAAQ,oBAAI,IAAA;AAGlB,MAAI,EAAkB,SAAS,cAC7B,EAAW,IAAI,EAAkB,IAAI,EAAkB,QAAQ,IAAA,IACtD,EAAkB,SAAS,UACpC,EAAO,IAAI,EAAkB,IAAI,EAAkB,QAAQ,IAAA,IAClD,EAAkB,SAAS,UACpC,EAAM,IAAI,EAAkB,IAAI,EAAkB,QAAQ,IAAA,GAIxD,MAAS,cACX,EAAW,IAAI,GAAU,IAAA,IAChB,MAAS,UAClB,EAAO,IAAI,GAAU,IAAA,IACZ,MAAS,UAClB,EAAM,IAAI,GAAU,IAAA,GAGtB,KAAK,eAAe,GAAY,GAAQ,CAAA;AACxC;AAAA;AAIF,QAAI,EAAkB,SAAS,SAAS;AACtC,YAAM,IAAa,IAAI,IAAI,EAAkB,cAAc,oBAAI,IAAA,CAAK,GAC9D,IAAS,IAAI,IAAI,EAAkB,UAAU,oBAAI,IAAA,CAAK,GACtD,IAAQ,IAAI,IAAI,EAAkB,SAAS,oBAAI,IAAA,CAAK;AAE1D,MAAI,MAAS,cACX,EAAW,IAAI,GAAU,IAAA,IAChB,MAAS,UAClB,EAAO,IAAI,GAAU,IAAA,IACZ,MAAS,UAClB,EAAM,IAAI,GAAU,IAAA,GAGtB,KAAK,eAAe,GAAY,GAAQ,CAAA;AAAA;;EAgB5C,oBAAoB,GAAqB,GAAsB;AAE7D,QAAI,CAAC,KAAK,WAAW,GAAM,CAAA,EACzB;AAGF,UAAM,IAAoB,KAAK;AAE/B,QAAK,GAKL;AAAA,UAAI,EAAkB,SAAS,QAAQ;AACrC,aAAK,SAAA;AACL;AAAA;AAIF,UAAI,EAAkB,SAAS,SAAS;AACtC,cAAM,IAAa,IAAI,IAAI,EAAkB,cAAc,oBAAI,IAAA,CAAK,GAC9D,IAAS,IAAI,IAAI,EAAkB,UAAU,oBAAI,IAAA,CAAK,GACtD,IAAQ,IAAI,IAAI,EAAkB,SAAS,oBAAI,IAAA,CAAK;AAa1D,YAXI,MAAS,cACX,EAAW,OAAO,CAAA,IACT,MAAS,UAClB,EAAO,OAAO,CAAA,IACL,MAAS,UAClB,EAAM,OAAO,CAAA,GAGI,EAAW,OAAO,EAAO,OAAO,EAAM,SAGtC,GAAG;AACpB,cAAI,EAAW,SAAS,EACtB,YAAW,CAAC,GAAI,CAAA,KAAS,EAAW,QAAA,GAAW;AAC7C,iBAAK,UAAU,aAAa,GAAI,IAAO,EAAE,MAAA,EAAA,IAAS,MAAA;AAClD;AAAA;mBAEO,EAAO,SAAS,EACzB,YAAW,CAAC,GAAI,CAAA,KAAS,EAAO,QAAA,GAAW;AACzC,iBAAK,UAAU,SAAS,GAAI,IAAO,EAAE,MAAA,EAAA,IAAS,MAAA;AAC9C;AAAA;mBAEO,EAAM,SAAS,EACxB,YAAW,CAAC,GAAI,CAAA,KAAS,EAAM,QAAA,GAAW;AACxC,iBAAK,UAAU,QAAQ,GAAI,IAAO,EAAE,MAAA,EAAA,IAAS,MAAA;AAC7C;AAAA;AAGJ;AAAA;AAIF,aAAK,eAAe,GAAY,GAAQ,CAAA;AAAA;;;EAS5C,oBAA4B;AAC1B,WAAK,KAAK,YAIN,KAAK,UAAU,SAAS,SACnB,IAGL,KAAK,UAAU,SAAS,WAEvB,KAAK,UAAU,YAAY,QAAQ,MACnC,KAAK,UAAU,QAAQ,QAAQ,MAC/B,KAAK,UAAU,OAAO,QAAQ,KAI5B,IAfE;AAAA;EA0BX,iBAIE;AACA,QAAI,CAAC,KAAK,UACR,QAAO;AAAA,MAAE,YAAY,CAAA;AAAA,MAAI,QAAQ,CAAA;AAAA,MAAI,OAAO,CAAA;AAAA;AAG9C,QAAI,KAAK,UAAU,SAAS,QAAQ;AAClC,UAAI,KAAK,UAAU,SAAS,YAC1B,QAAO;AAAA,QAAE,YAAY,CAAC,KAAK,UAAU,EAAA;AAAA,QAAK,QAAQ,CAAA;AAAA,QAAI,OAAO,CAAA;AAAA;AAE/D,UAAI,KAAK,UAAU,SAAS,QAC1B,QAAO;AAAA,QAAE,YAAY,CAAA;AAAA,QAAI,QAAQ,CAAC,KAAK,UAAU,EAAA;AAAA,QAAK,OAAO,CAAA;AAAA;AAE/D,UAAI,KAAK,UAAU,SAAS,OAC1B,QAAO;AAAA,QAAE,YAAY,CAAA;AAAA,QAAI,QAAQ,CAAA;AAAA,QAAI,OAAO,CAAC,KAAK,UAAU,EAAA;AAAA;;AAIhE,WAAI,KAAK,UAAU,SAAS,UACnB;AAAA,MACL,YAAY,MAAM,KAAK,KAAK,UAAU,YAAY,KAAA,KAAU,CAAA,CAAE;AAAA,MAC9D,QAAQ,MAAM,KAAK,KAAK,UAAU,QAAQ,KAAA,KAAU,CAAA,CAAE;AAAA,MACtD,OAAO,MAAM,KAAK,KAAK,UAAU,OAAO,KAAA,KAAU,CAAA,CAAE;AAAA,QAIjD;AAAA,MAAE,YAAY,CAAA;AAAA,MAAI,QAAQ,CAAA;AAAA,MAAI,OAAO,CAAA;AAAA;;EAS9C,kBAAkB,GAAyC;AACzD,gBAAK,UAAU,IAAI,CAAA,GAEnB,MAAa;AACX,WAAK,UAAU,OAAO,CAAA;AAAA;;EAU1B,gBACE,GACA,GACM;AACN,eAAW,KAAY,KAAK,UAC1B,KAAI;AACF,MAAA,EAAS,GAAc,CAAA;AAAA,aAChB,GAAO;AACd,cAAQ,MAAM,6BAA6B,CAAA;AAAA;;EAQjD,UAAgB;AACd,SAAK,YAAY,MACjB,KAAK,aAAa,MAClB,KAAK,UAAU,MAAA;AAAA;GCveN,KAAb,MAA2B;AAAA,EACzB;AAAA,EAEA,YAAY,GAA+B;AACzC,SAAK,cAAc;AAAA;EAUrB,sBAAsB,GAAmB,GAA0C;AACjF,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI;AACF,UAAI,CAAC,EACH,OAAM,IAAI,MAAM,+CAAA;AAGlB,YAAM,IAAgB,EAAoB,CAAA,GACpC,IAAe,EAAQ,kBAAkB,GAAe,CAAA,GAExD,IAAoD;AAAA,QACxD,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,IAAI,EAAa;AAAA,QACjB,OAAO;AAAA,QACP,MAAM;AAAA,UACJ,UAAU;AAAA,UACE,YAAA;AAAA;;AAGhB,kBAAK,YAAY,KAAK,wBAAwB,CAAA,GACvC;AAAA,aACA,GAAO;AACd,YAAM,IAAoD;AAAA,QACxD,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,IAAI;AAAA,QACG,OAAA;AAAA,QACP,MAAM;AAAA;AAER,iBAAK,YAAY,KAAK,wBAAwB,CAAA,GACxC,IAAI,MAAO,EAAgB,OAAA;AAAA;;EAWrC,uBAAuB,GAA0B,IAAgB,IAAO;AACtE,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI;AACF,UAAI,CAAC,EACH,OAAM,IAAI,MAAM,+CAAA;AAElB,YAAM,IAAe,EAAQ,SAAS,EAAe,SAAS,OAAA;AAC9D,UAAI,CAAC,EACH,OAAM,IAAI,MACR,oBAAoB,EAAe,SAAS,OAAA,wBAAQ;AAIxD,YAAM,IAAgB,EAAoB,EAAe,QAAA,GACnD,IAAa,EAAe,SAAS;AAC3C,aAAA,EAAa,YAAY,CAAA,GACzB,EAAa,cAAc,CAAA,GAEvB,KACF,KAAK,YAAY,KAAK,wBAAwB;AAAA,QAC5C,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,IAAI,EAAa;AAAA,QACjB,OAAO;AAAA,QACP,MAAM;AAAA,UACJ,UAAU;AAAA,UACE,YAAA;AAAA;OAEf,GAEI;AAAA,aACA,GAAO;AACd,YAAM,IAAoD;AAAA,QACxD,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,IAAI,EAAe,SAAS;AAAA,QACrB,OAAA;AAAA,QACP,MAAM;AAAA;AAER,iBAAK,YAAY,KAAK,wBAAwB,CAAA,GACxC,IAAI,MAAO,EAAgB,OAAA;AAAA;;EAUrC,yBAAyB,GAIvB;AACA,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI;AACF,UAAI,CAAC,EACH,OAAM,IAAI,MAAM,+CAAA;AAElB,YAAM,IAAS,EAAQ,qBAAqB,CAAA,GAEtC,IAAoD;AAAA,QACxD,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,MAAM,EACJ,GAAG,EAAA;AAAA;AAGP,kBAAK,YAAY,KAAK,wBAAwB,CAAA,GACvC;AAAA,aACA,GAAO;AACd,YAAM,IAAoD;AAAA,QACxD,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,IAAI;AAAA,QACG,OAAA;AAAA,QACP,MAAM;AAAA;AAER,iBAAK,YAAY,KAAK,wBAAwB,CAAA,GACxC,IAAI,MAAO,EAAgB,OAAA;AAAA;;EAWrC,YAAY,GAAqB,GAAqB;AACpD,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI;AACF,UAAI,CAAC,EACH,OAAM,IAAI,MAAM,+CAAA;AAElB,YAAM,IAAY,EAAQ,QAAQ,GAAe,CAAA;AACjD,UAAI,aAAqB,MACvB,OAAM,IAAI,MAAM,EAAU,OAAA;AAE5B,YAAM,IAAO,GACP,IAAoD;AAAA,QACxD,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,IAAI,EAAK;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,UACJ,OAAO,EAAK;AAAA,UACZ,OAAO,EAAK;AAAA;;AAGhB,kBAAK,YAAY,KAAK,wBAAwB,CAAA,GACvC;AAAA,aACA,GAAO;AACd,YAAM,IAAoD;AAAA,QACxD,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,IAAI;AAAA,QACG,OAAA;AAAA,QACP,MAAM;AAAA;AAER,iBAAK,YAAY,KAAK,wBAAwB,CAAA,GACxC,IAAI,MAAO,EAAgB,OAAA;AAAA;;EAarC,cACE,GACA,GACA,IAA6B,MACa;AAC1C,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI,CAAC,EACH,OAAM,IAAI,MAAM,+CAAA;AAGlB,UAAM,IAAe,EAAoB,CAAA,GACnC,IAAS,EAAQ,UAAU,GAAQ,GAAc,CAAA;AAEvD,SAAK,YAAY,KAAK,wBAAwB;AAAA,MAC5C,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,IAAI;AAAA,KACL,GACI,KACH,KAAK,YAAY,KAAK,wBAAwB;AAAA,MAC5C,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,IAAI,EAAO,eAAe;AAAA,KAC3B;AAEH,eAAW,KAAQ,EAAO,MACxB,MAAK,YAAY,KAAK,wBAAwB;AAAA,MAC5C,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,IAAI,EAAK;AAAA,KACV;AAEH,WAAO;AAAA;EAST,eAAe,GAAoB;AACjC,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI;AACF,UAAI,CAAC,EACH,OAAM,IAAI,MAAM,+CAAA;AAElB,MAAA,EAAQ,WAAW,CAAA;AACnB,YAAM,IAAoD;AAAA,QACxD,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,MAAM;AAAA;AAER,WAAK,YAAY,KAAK,wBAAwB,CAAA;AAC9C;AAAA,aACO,GAAO;AACd,YAAM,IAAoD;AAAA,QACxD,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,IAAI;AAAA,QACG,OAAA;AAAA,QACP,MAAM;AAAA;AAER,WAAK,YAAY,KAAK,wBAAwB,CAAA;AAAA;;EAYlD,sBACE,GACA,GACA,IAAgB,IACE;AAClB,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI,CAAC,EAAS;AACd,UAAM,IAAO,EAAQ,QAAQ,CAAA;AAC7B,QAAI,CAAC,EAAM;AAEX,UAAM,IAAkB,EAAU,IAAA,CAAK,MAAM,IAAI,EAAS,EAAE,GAAG,EAAE,CAAA,CAAE;AACnE,WAAA,EAAQ,gCAAgC,GAAQ,CAAA,GAE5C,MACF,EAAQ,kCAAkC,CAAA,GAC1C,KAAK,YAAY,KAAK,wBAAwB;AAAA,MAC5C,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,MAAM,EACJ,uBAAuB,CAAC,GAAG,EAAK,qBAAA,EAAsB;AAAA,KAEzD,IAEI;AAAA;EAST,0BAA0B,GAAgC;AACxD,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI,CAAC,EAAS;AACd,UAAM,IAAO,EAAQ,QAAQ,CAAA;AAC7B,QAAK;AAEL,aAAA,EAAQ,kCAAkC,CAAA,GAC1C,KAAK,YAAY,KAAK,wBAAwB;AAAA,QAC5C,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,MAAM,EACJ,uBAAuB,CAAC,GAAG,EAAK,qBAAA,EAAsB;AAAA,OAEzD,GACM;AAAA;EAQT,wBAAwB,GAAe,GAA0C;AAC/E,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI,CAAC,EACH,OAAM,IAAI,MAAM,+CAAA;AAGlB,IAAA,EAAQ,sBAAsB,GAAS,CAAA,GAEvC,KAAK,YAAY,KAAK,wBAAwB;AAAA,MAC5C,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,MAAM,EACQ,YAAA,EAAA;AAAA,KAEf;AAAA;EAcH,iBACE,GACA,GACA,GACA,GACA,GACW;AACX,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI;AACF,UAAI,CAAC,EACH,OAAM,IAAI,MAAM,+CAAA;AAGlB,YAAM,IAAgB,EAAoB,CAAA,GACpC,IAAgB,GAAoB,CAAA,GACpC,IAAY,EAAQ,aAAa,GAAM,GAAe,GAAe,CAAA;AAE3E,UAAI,EACF,YAAW,KAAU,EAAU,MAAM;AACnC,YAAI,CAAC,EAAW,CAAA,EAAS;AACzB,cAAM,IAAe,EAAW,CAAA,GAE1B,IAAW,EAAU,KAAK,CAAA;AAChC,YAAI,CAAC,EAAU;AACf,cAAM,IAAM,EAAQ,SAAS,CAAA;AAC7B,QAAK,KACL,EAAI,cAAc,CAAA;AAAA;AAItB,YAAM,IAAoD;AAAA,QACxD,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,IAAI,EAAU;AAAA,QACd,OAAO;AAAA,QACP,MAAM;AAAA,UACJ,aAAa,EAAU;AAAA,UACvB,eAAe;AAAA,UACf,UAAU;AAAA,UACV,UAAU;AAAA,UACF,QAAA;AAAA,UACI,YAAA;AAAA;;AAGhB,kBAAK,YAAY,KAAK,wBAAwB,CAAA,GACvC;AAAA,aACA,GAAO;AACd,YAAM,IAAoD;AAAA,QACxD,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,IAAI;AAAA,QACG,OAAA;AAAA,QACP,MAAM;AAAA;AAER,iBAAK,YAAY,KAAK,wBAAwB,CAAA,GACxC,IAAI,MAAO,EAAgB,OAAA;AAAA;;EAUrC,kBAAkB,GAAmB,GAAkB,IAAgB,IAAkB;AAEvF,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI,CAAC,EACH,OAAM,IAAI,MAAM,+CAAA;AAElB,UAAM,IAAY,EAAQ,aAAa,CAAA;AACvC,QAAI,CAAC,EACH,OAAM,IAAI,MAAM,qBAAqB,CAAA,4BAAY;AAGnD,UAAM,IAAgB,EAAoB,EAAO,QAAA,GAC3C,IAAgB,GAAoB,EAAO,QAAA;AACjD,WAAA,EAAU,YAAY,CAAA,GACtB,EAAU,YAAY,CAAA,GAElB,KACF,KAAK,YAAY,KAAK,wBAAwB;AAAA,MAC5C,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,MAAM;AAAA,QACJ,UAAU;AAAA,QACV,UAAU;AAAA;KAEb,GAGI;AAAA;EAQT,wBAAwB,GAAmB,GAA4C;AAErF,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI,CAAC,EACH,OAAM,IAAI,MAAM,+CAAA;AAElB,UAAM,IAAY,EAAQ,aAAa,CAAA;AACvC,QAAI,CAAC,EACH,OAAM,IAAI,MAAM,qBAAqB,CAAA,4BAAY;AAGnD,WAAA,EAAU,SAAS,IAAI,IAAI,CAAC,GAAG,EAAU,QAAQ,GAAG,CAAA,CAAW,GAC/D,EAAQ,sBAAsB,CAAA,GAC9B,KAAK,YAAY,KAAK,wBAAwB;AAAA,MAC5C,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,MAAM,EACJ,QAAQ,EAAU,OAAA;AAAA,KAErB,GACM;AAAA;EAWT,qBAAqB,GAAkE;AAErF,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI,CAAC,EACH,OAAM,IAAI,MAAM,+CAAA;AAElB,UAAM,IAAY,EAAQ,aAAa,CAAA;AACvC,QAAI,CAAC,EACH,OAAM,IAAI,MAAM,qBAAqB,CAAA,4BAAY;AAEnD,UAAM,IAAS,EAAU;AACzB,YAAQ,EAAU,MAAlB;AAAA,MACE,KAAK,EAAc;AACjB,eAAA,EAAO,IAAI,gBAAgB,EAAO,IAAI,cAAA,MAAoB,SAAS,WAAW,MAAA,GAC9E,KAAK,wBAAwB,EAAU,IAAI,CAAA,GACpC;AAAA,UAAE,YAAY;AAAA,UAAiB,WAAA;AAAA;MACxC,KAAK,EAAc;AACjB,eAAA,EAAO,IAAI,gBAAgB,EAAO,IAAI,cAAA,MAAoB,WAAW,WAAW,QAAA,GAChF,KAAK,wBAAwB,EAAU,IAAI,CAAA,GACpC;AAAA,UAAE,YAAY;AAAA,UAAiB,WAAA;AAAA;MACxC,KAAK,EAAc;AACjB,eAAA,EAAO,IAAI,aAAa,EAAO,IAAI,WAAA,KAAgB,SAAS,UAAU,MAAA,GACtE,KAAK,wBAAwB,EAAU,IAAI,CAAA,GACpC;AAAA,UAAE,YAAY;AAAA,UAAiB,WAAA;AAAA;MACxC;AACE,eAAK,EAAO,IAAI,iBAAA,KAGhB,EAAO,IACL,mBACA,EAAO,IAAI,iBAAA,MAAuB,aAAa,aAAa,UAAA,GAE9D,EAAQ,sBAAsB,CAAA,GAC9B,KAAK,wBAAwB,EAAU,IAAI,CAAA,GACpC;AAAA,UAAE,YAAY;AAAA,UAAiB,WAAA;AAAA,aAR7B;AAAA,UAAE,YAAY;AAAA,UAAkB,WAAA;AAAA;;;EAkB/C,oBAAoB,GAGlB;AACA,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI;AACF,UAAI,CAAC,EACH,OAAM,IAAI,MAAM,+CAAA;AAIlB,UAAI,CADc,EAAQ,aAAa,CAAA,EAErC,OAAM,IAAI,MAAM,qBAAqB,CAAA,4BAAY;AAInD,YAAM,IACJ,EAAQ,gBAAgB,CAAA,GAEpB,IAAoD;AAAA,QACxD,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,MAAM,EAAE,GAAG,EAAA;AAAA;AAEb,kBAAK,YAAY,KAAK,wBAAwB,CAAA,GAEvC;AAAA,aACA,GAAO;AACd,YAAM,IAAoD;AAAA,QACxD,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,IAAI;AAAA,QACG,OAAA;AAAA,QACP,MAAM;AAAA;AAER,iBAAK,YAAY,KAAK,wBAAwB,CAAA,GACxC,IAAI,MAAO,EAAgB,OAAA;AAAA;;EAQrC,4BAAqC;AACnC,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI,CAAC,EACH,OAAM,IAAI,MAAM,+CAAA;AAGlB,UAAM,IAAU,KAAK,IAAI,IAAI,EAAQ,iBAAiB,CAAA,CAAE;AACxD,QAAI,EAAQ,SAAS,SAAS,EAC5B,QAAO;AAGT,UAAM,IAAY,GAAwB,CAAA;AAE1C,WAAA,EAAQ,SAAS,OAAO,GACxB,EAAQ,SAAS,YAAY,GAE7B,KAAK,YAAY,KAAK,0BAA0B;AAAA,MAC9C,aAAa,EAAQ;AAAA,MACrB,MAAM;AAAA,QACJ,MAAM;AAAA,QACK,WAAA;AAAA;KAEd,GACM;AAAA;EAMT,oBAA0B;AACxB,UAAM,IAAU,KAAK,YAAY,WAAA;AACjC,QAAI,CAAC,EACH,OAAM,IAAI,MAAM,+CAAA;AAElB,UAAM,IAAS,KAAK,YAAY,UAAA;AAChC,QAAI,CAAC,EACH,OAAM,IAAI,MAAM,8CAAA;AAElB,UAAM,IAAW,KAAK,YAAY,YAAA;AAClC,QAAI,CAAC,EACH,OAAM,IAAI,MAAM,gDAAA;AAGlB,UAAM,IAAU,IAAI,GAClB,IAAI,GAAW,EAAO,SAAS,GAAG,EAAO,SAAS,GAAG,EAAO,SAAS,CAAA,GACrE,IAAI,GAAW,EAAS,OAAO,GAAG,EAAS,OAAO,GAAG,EAAS,OAAO,CAAA,GACrE,EAAO,KACP,EAAO,MACP,EAAO,GAAA;AAET,IAAA,EAAQ,SAAS,gBAAgB,GAEjC,KAAK,YAAY,KAAK,0BAA0B;AAAA,MAC9C,aAAa,EAAQ;AAAA,MACrB,MAAM,EACJ,eAAe,EAAA;AAAA,KAElB;AAAA;;ACnoBL,SAAgB,GACd,IAAiB,GACjB,IAAyB,IAAI,GAAA,GACJ;AACzB,QAAM,IAAS,IAAI,EAAM,kBAAkB,EAAQ,KAAK,GAAQ,EAAQ,MAAM,EAAQ,GAAA,GAEhF,IAAS,EAAQ;AACvB,EAAA,EAAO,SAAS,IAAI,EAAO,GAAG,EAAO,GAAG,EAAO,CAAA;AAC/C,QAAM,IAAY,EAAQ;AAC1B,SAAA,EAAO,OAAO,EAAU,GAAG,EAAU,GAAG,EAAU,CAAA,GAC3C;;AAST,SAAgB,GAAa,GAAiC,GAA8B;AAC1F,EAAA,EAAO,MAAM,EAAQ,KACrB,EAAO,OAAO,EAAQ,MACtB,EAAO,MAAM,EAAQ;AAErB,QAAM,IAAS,EAAQ;AACvB,EAAA,EAAO,SAAS,IAAI,EAAO,GAAG,EAAO,GAAG,EAAO,CAAA;AAG/C,QAAM,IAAY,EAAQ;AAC1B,EAAA,EAAO,OAAO,EAAU,GAAG,EAAU,GAAG,EAAU,CAAA,GAClD,EAAO,uBAAA;;AC/BT,SAAgB,GACd,IAAgB,UAChB,IAAoB,KACA;AACpB,SAAO,IAAI,EAAM,aAAa,GAAO,CAAA;;AAWvC,SAAgB,GACd,IAAgB,UAChB,IAAoB,KACpB,IAA0B,IAAI,EAAM,QAAQ,IAAI,IAAI,EAAA,GAC5B;AACxB,QAAM,IAAQ,IAAI,EAAM,iBAAiB,GAAO,CAAA;AAChD,SAAA,EAAM,SAAS,KAAK,CAAA,GACb;;AAST,SAAgB,GAAiB,GAAmC;AAClE,QAAM,IAAwB,CAAA,GAGxB,IAAU,GAAmB,UAAU,GAAA;AAC7C,EAAA,EAAM,IAAI,CAAA,GACV,EAAO,KAAK,CAAA;AAGZ,QAAM,IAAO,GAAuB,UAAU,KAAK,IAAI,EAAM,QAAQ,IAAI,IAAI,EAAA,CAAG;AAChF,EAAA,EAAM,IAAI,CAAA,GACV,EAAO,KAAK,CAAA;AAGZ,QAAM,IAAO,GAAuB,UAAU,KAAK,IAAI,EAAM,QAAQ,KAAK,IAAI,GAAA,CAAI;AAClF,SAAA,EAAM,IAAI,CAAA,GACV,EAAO,KAAK,CAAA,GAEL;;ACnCT,IAAa,IAAe;AAAA,EAE1B,SAAS;AAAA,EAET,OAAO;AAAA,EAEP,WAAW;AAAA,EAEX,MAAM;GCZK,KAAb,MAA0B;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,sBAAuC,IAAI,EAAM,QAAA;AAAA,EACjD,mBAAkD;AAAA,EAClD,YAAwC,oBAAI,IAAA;AAAA,EAC5C,UAA2B;AAAA,EAC3B,cAA+B;AAAA,EAC/B,aAA6B;AAAA,EAC7B,aAA6B;AAAA,EAC7B,iBAAiC;AAAA,EACjC,aAA6B;AAAA,EAQ7B,YAAY,GAAoB,GAAsB;AACpD,SAAK,QAAQ,GACb,KAAK,SAAS,GACd,KAAK,YAAY,IAAI,EAAM,UAAA,GAE3B,KAAK,UAAU,OAAO,QAAQ,EAAE,WAAW,GAAA,GAE3C,KAAK,cAAc,IAAI,EAAM,MAAM,IAAI,EAAM,QAAQ,GAAG,GAAG,CAAA,GAAI,CAAA;AAAA;EAQjE,yBAAwC;AACtC,WAAO,KAAK;AAAA;EAad,gBAAgB,GAAqB,GAA2B;AAC9D,QAAI,CAAC,KAAK,QACR;AAIF,UAAM,IAAM,YAAY,IAAA;AACxB,QAAI,IAAM,KAAK,iBAAiB,KAAK,WACnC;AAEF,SAAK,iBAAiB,GAGtB,KAAK,aAAa,GAClB,KAAK,aAAa,GAGlB,KAAK,UAAU,cAAc,IAAI,EAAM,QAAQ,GAAa,CAAA,GAAc,KAAK,MAAA,GAG/E,KAAK,UAAU,IAAI,eAAe,KAAK,aAAa,KAAK,mBAAA;AAGzD,QAAI,IAAoC;AAOxC,QAJA,IAAa,KAAK,cAAc,EAAa,OAAO,SAAS,aAAA,GAIzD,KAAc,EAAW,SAAS,SAAS,aAAa;AAC1D,YAAM,IAAe,KAAK,cACxB,EAAa,WACb,aACA,iBAAA;AAEF,MAAI,KAAgB,EAAa,OAAO,EAAW,SAAS,SAAS,gBACnE,IAAa;AAAA;AAKjB,IAAK,MACH,IAAa,KAAK,cAAc,EAAa,WAAW,aAAa,iBAAA,IAIlE,MACH,IAAa,KAAK,cAAc,EAAa,MAAM,QAAQ,MAAA,IAG7D,KAAK,kBAAkB,CAAA;AAAA;EASzB,UAAgB;AACd,IAAI,KAAK,WACP,KAAK,gBAAgB,KAAK,YAAY,KAAK,UAAA;AAAA;EAU/C,QAAc;AACZ,SAAK,kBAAkB,IAAA;AAAA;EAQzB,oBAA2C;AACzC,WAAO,KAAK;AAAA;EAad,cAAc,GAA+B;AAC3C,SAAK,UAAU,IAAI,CAAA;AAAA;EAQrB,eAAe,GAA+B;AAC5C,SAAK,UAAU,OAAO,CAAA;AAAA;EAWxB,WAAW,GAAwB;AACjC,SAAK,UAAU,GACV,KAEH,KAAK,MAAA;AAAA;EAST,YAAqB;AACnB,WAAO,KAAK;AAAA;EAOd,eAAe,GAA4B;AACzC,SAAK,cAAc;AAAA;EAMrB,gBAAyB;AACvB,WAAO,KAAK;AAAA;EASd,UAAgB;AACd,SAAK,MAAA,GACL,KAAK,UAAU,MAAA,GACf,KAAK,cAAc;AAAA;EAerB,cACE,GACA,GACA,GACuB;AAEvB,SAAK,UAAU,OAAO,IAAI,CAAA;AAG1B,UAAM,IAAgB,KAAK,UAAU,iBAAiB,KAAK,MAAM,UAAU,EAAA;AAG3E,eAAW,KAAgB,GAAe;AACxC,YAAM,IAAM,EAAa,QACnB,IAAW,EAAI;AAGrB,UAAI,KAAY,EAAS,SAAS,GAAY;AAC5C,YAAI,EAAS,eAAe,SAAA,EAC1B;AAGF,YAAI;AACJ,YAAI,EAAS,SAAS,cAEpB,CAAA,IAAY,EAAS;AAAA,iBACZ,EAAS,SAAS,kBAC3B,CAAA,IAAY,EAAS;AAAA,iBACZ,EAAS,SAAS,OAC3B,CAAA,IAAY,EAAS;AAAA,YAErB;AAGF,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,MAAM;AAAA,UACM,YAAA;AAAA,UACZ,UAAU;AAAA;;;AAKhB,WAAO;AAAA;EAUT,kBAA0B,GAAqC;AAI7D,QAFmB,CAAC,KAAK,aAAa,KAAK,kBAAkB,CAAA,GAE7C;AACd,YAAM,IAAc,KAAK;AACzB,WAAK,mBAAmB;AAGxB,iBAAW,KAAY,KAAK,UAC1B,CAAA,EAAS,GAAQ,CAAA;AAAA;;EAYvB,aAAqB,GAA0B,GAAmC;AAEhF,WAAI,MAAM,QAAQ,MAAM,OACf,KAIL,MAAM,QAAQ,MAAM,OACf,KAIF,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE;AAAA;GCvT5B,KAAb,MAAa,EAA4B;AAAA,EAEvC,OAAwB,SAAS;AAAA,IAC/B,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS;AAAA;EAGX,OAAwB,sBAAsB;AAAA,EAC9C,OAAwB,iBAAiB;AAAA,EACzC,OAAwB,oBAAoB;AAAA,EAG5C,OAAwB,cAAc;AAAA,EACtC,OAAwB,cAAc;AAAA,EACtC,OAAwB,gBAAgB;AAAA,EAGxC,OAAwB,gBAAgB;AAAA,EAOxC,aAAa,GAA2B;AACtC,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,OAAO,EAAU,gBAEvB,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa;AAAA,MACb,SAAS,EAAM;AAAA,MACf,OAAO,EAAU;AAAA;AAInB,UAAM,IAAa,IAAI,EAAM,YAC3B,EAA4B,eAC5B,EAA4B,eAC5B,EAA4B,aAAA,GAExB,IAAS,IAAI,EAAM,KACvB,GACA,IAAI,EAAM,qBAAqB;AAAA,MAC7B,OAAO,EAA4B;AAAA,MACnC,aAAa;AAAA,MACb,SAAS;AAAA,KACV,CAAC;AAEJ,IAAA,EAAO,WAAW;AAAA,MAChB,MAAM;AAAA,MACN,aAAa;AAAA,MACb,SAAS,EAAM;AAAA,MACf,OAAO,EAAU;AAAA,OAEnB,EAAO,OAAO,IAAI,EAAa,KAAA,GAC/B,EAAM,IAAI,CAAA;AAGV,UAAM,IAAe,IAAI,EAAM,aAC7B,EAA4B,aAC5B,EAA4B,aAC5B,EAA4B,aAAA,GAGxB,IAAQ,KAAK,sBAAsB,EAAM,MAAA,GAEzC,IAAe,IAAI,EAAM,qBAAqB;AAAA,MAClD,OAAA;AAAA,MACA,UAAU;AAAA,MACV,WAAW;AAAA,MACX,WAAW;AAAA,KACZ,GAGK,IAAS,IAAI,EAAM,KAAK,GAAc,CAAA;AAC5C,WAAA,EAAO,WAAW;AAAA,MAChB,MAAM;AAAA,MACN,aAAa;AAAA,MACb,SAAS,EAAM;AAAA,MACf,OAAO,EAAU;AAAA,OAEnB,EAAO,SAAS,IAAI,GAAG,KAAK,CAAA,GAC5B,EAAM,IAAI,CAAA,GAEH;AAAA;EAQT,iBAAiB,GAA0B,GAA0C;AACnF,IAAA,EAAS,SAAS,aAAa;AAC/B,UAAM,IAAS,EAAS,SAAS,KAAA,CAAM,MAAU,EAAM,SAAS,SAAS,OAAA;AAIzE,QAAI,KAAU,EAAO,oBAAoB,EAAM,sBAAsB;AACnE,YAAM,IAAW,KAAK,sBAAsB,CAAA;AAC5C,MAAA,EAAO,SAAS,MAAM,OAAO,CAAA;AAAA;;EAIjC,wBAAkC,GAAsD;AACtF,YAAQ,GAAR;AAAA,MACE,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MAET;AACE,eAAO;AAAA;;EAQb,WAAW,GAAgC;AACzC,QAAI,EAAS,SAAS,WAEpB;AAGF,UAAM,IAAS,EAAS,SAAS,KAAA,CAAM,MAAU,EAAM,SAAS,SAAS,OAAA;AAIzE,IAAI,KAAU,EAAO,oBAAoB,EAAM,wBAC7C,EAAO,SAAS,SAAS,OAAO,EAA4B,cAAA;AAAA;EAQhE,YAAY,GAAgC;AAC1C,QAAI,EAAS,SAAS,WAEpB;AAGF,QAAI,IAAmB;AACvB,IAAI,EAAS,SAAS,oBACpB,IAAmB,KAAK,wBAAwB,EAAS,SAAS,eAAA;AAGpE,UAAM,IAAS,EAAS,SAAS,KAAA,CAAM,MAAU,EAAM,SAAS,SAAS,OAAA;AAIzE,IAAI,KAAU,EAAO,oBAAoB,EAAM,wBAC7C,EAAO,SAAS,SAAS,OAAO,CAAA;AAAA;EAQpC,eAAe,GAAgC;AAC7C,UAAM,IAAS,EAAS,SAAS,KAAA,CAAM,MAAU,EAAM,SAAS,SAAS,OAAA;AAIzE,IAAI,KAAU,EAAO,oBAAoB,EAAM,wBAC7C,EAAO,SAAS,SAAS,OAAO,EAA4B,iBAAA,GAE9D,EAAS,SAAS,aAAa;AAAA;EAOjC,gBAAgB,GAAgC;AAC9C,UAAM,IAAS,EAAS,SAAS,KAAA,CAAM,MAAU,EAAM,SAAS,SAAS,OAAA;AAIzE,IAAI,KAAU,EAAO,oBAAoB,EAAM,wBAC7C,EAAO,SAAS,SAAS,OAAO,CAAA,GAElC,EAAS,SAAS,aAAa;AAAA;EAQjC,sBAA8B,GAAwD;AACpF,WAAK,IAGE,EAA4B,OAAO,CAAA,IAFjC,EAA4B,OAAO;AAAA;;ACzJhD,SAAgB,EAAoB,IAAgB,UAAU,IAAoB,GAAiB;AACjG,SAAO,IAAI,GAAa;AAAA,IACtB,OAAA;AAAA,IACA,WAAA;AAAA,GACD;;ACrBH,IAAa,KAAb,MAA+B;AAAA,EAC7B,iBAAiC;AAAA,EACjC,kBAAkC;AAAA,EAElC,SAAqC;AAAA,EACrC,UAAuC;AAAA,EACvC;AAAA,EACA;AAAA,EACA,aAAyC;AAAA,EACzC,WAAmC;AAAA,EAInC,gBAA8D,oBAAI,IAAA;AAAA,EAElE,cAAoC;AAAA,EAEpC,YAAY,GAA+C,GAAiC;AAC1F,SAAK,sBAAsB,GAC3B,KAAK,iBAAiB,GAEtB,KAAK,gBAAgB,oBAAI,IAAI;AAAA,MAC3B,CAAC,QAAQ,EAAoB,UAAU,CAAA,CAAE;AAAA,MACzC,CAAC,WAAW,EAAoB,SAAU,CAAA,CAAE;AAAA,MAC5C,CAAC,YAAY,EAAoB,UAAU,CAAA,CAAE;AAAA,MAC7C,CAAC,WAAW,EAAoB,UAAU,CAAA,CAAE;AAAA,MAC5C,CAAC,WAAW,EAAoB,KAAU,CAAA,CAAE;AAAA,MAC5C,CAAC,MAAM,EAAoB,UAAU,CAAA,CAAE;AAAA,KACxC;AAAA;EAGH,aAAa,GAAqC;AAChD,SAAK,aAAa;AAAA;EAGpB,kBAAkB,GAAoB,GAA4B;AAChE,SAAK,SAAS,GACd,KAAK,UAAU;AAAA;EAGjB,WAAW,GAA+B;AACxC,SAAK,WAAW;AAAA;EAqBlB,cAAc,GAAe,GAAsB;AACjD,SAAK,iBAAiB,GACtB,KAAK,kBAAkB;AACvB,eAAW,KAAY,KAAK,cAAc,OAAA,EACxC,CAAA,EAAS,WAAW,IAAI,GAAO,CAAA;AAAA;EAUnC,YAAY,GAAiC;AAC3C,WAAO,KAAK,gBAAgB,IAAI,CAAA;AAAA;EASlC,QAAQ,GAAuB;AAC7B,WAAO,KAAK,gBAAgB,IAAI,CAAA,KAAW;AAAA;EAQ7C,aAAqB;AACnB,WAAK,KAAK,iBAGH,MAAM,KAAK,KAAK,eAAe,KAAA,CAAM,IAFnC,CAAA;AAAA;EAWX,mBAAmB,GAAmB;AACpC,QAAI,CAAC,KAAK,OACR,OAAM,IAAI,MAAM,oCAAA;AAGlB,UAAM,IAAW,KAAK,gBAAgB,CAAA;AAEtC,QAAI,IAAO,KAAK,eAAe,IAAI,EAAK,EAAA;AAExC,QAAI,GAAM;AAER,YAAM,IAAW,IAAI,GAAA;AACrB,MAAA,EAAS,cAAc,EAAS,MAAA,GAChC,EAAK,SAAS,QAAA,GACd,EAAK,WAAW;AAAA,WACX;AAEL,YAAM,IAAW,IAAI,GAAA;AACrB,MAAA,EAAS,cAAc,EAAS,MAAA,GAChC,IAAO,IAAI,GAAM,GAAU,KAAK,cAAc,IAAI,MAAA,CAAO,GACzD,EAAK,WAAW;AAAA,QACd,MAAM;AAAA,QACN,QAAQ,EAAK;AAAA,QACb,iBAAiB;AAAA,SAGnB,EAAK,OAAO,OAAO,EAAa,IAAA,GAChC,KAAK,eAAe,IAAI,EAAK,IAAI,CAAA,GAEjC,KAAK,OAAO,IAAI,CAAA;AAAA;AAElB,WAAO;AAAA;EAQT,eAAe,GAAoB;AAGjC,UAAM,IAFU,KAAK,SAEA,QAAQ,CAAA;AAC7B,IAAI,KACF,KAAK,mBAAmB,CAAA;AAAA;EAW5B,wBAAwB,GAAyB;AAC/C,UAAM,IAAU,KAAK,UAEf,IAAY,EAAQ,aAAa,CAAA;AACvC,QAAI,CAAC,EAAW;AAGhB,UAAM,IAAkB,oBAAI,IAAA;AAE5B,eAAW,KAAS,EAAU,MAAM;AAClC,YAAM,IAAQ,EAAQ,SAAS,CAAA;AAC/B,UAAI,EACF,YAAW,KAAU,EAAM,MACzB,CAAA,EAAgB,IAAI,CAAA;AAAA;AAM1B,eAAW,KAAU,EACnB,MAAK,eAAe,CAAA;AAAA;EAYxB,mBAAmB,GAAoB;AACrC,UAAM,IAAO,KAAK,eAAe,IAAI,CAAA;AACrC,IAAK,KACD,EAAK,aAAa,KAAK,cAAc,IAAI,UAAA,MAE7C,EAAK,WAAW,KAAK,cAAc,IAAI,SAAA;AAAA;EAGzC,oBAAoB,GAAoB;AACtC,UAAM,IAAO,KAAK,eAAe,IAAI,CAAA;AACrC,IAAK,KACD,EAAK,aAAa,KAAK,cAAc,IAAI,SAAA,MAE7C,EAAK,WAAW,KAAK,cAAc,IAAI,EAAK,SAAS,mBAAmB,MAAA;AAAA;EAG1E,oBAAoB,GAAoB;AACtC,UAAM,IAAO,KAAK,eAAe,IAAI,CAAA;AACrC,IAAK,MACL,EAAK,WAAW,KAAK,cAAc,IAAI,UAAA;AAAA;EAGzC,qBAAqB,GAAoB;AACvC,UAAM,IAAO,KAAK,eAAe,IAAI,CAAA;AACrC,IAAK,KACD,EAAK,aAAa,KAAK,cAAc,IAAI,UAAA,MAE7C,EAAK,WAAW,KAAK,cAAc,IAAI,EAAK,SAAS,mBAAmB,MAAA;AAAA;EAU1E,qBAAqB,GAAc,GAAoD;AACrF,UAAM,IAAO,KAAK,eAAe,IAAI,CAAA;AACrC,QAAI,CAAC,EAAM;AAGX,IAAA,EAAK,SAAS,kBAAkB;AAEhC,UAAM,IAAkB,EAAK;AAC7B,IAAI,MAAoB,KAAK,cAAc,IAAI,UAAA,KAC3C,MAAoB,KAAK,cAAc,IAAI,SAAA,MAE/C,EAAK,WAAW,KAAK,cAAc,IAAI,CAAA;AAAA;EAYzC,WAAW,GAAoB;AAC7B,QAAI,CAAC,KAAK,OACR,OAAM,IAAI,MAAM,oCAAA;AAGlB,UAAM,IAAY,KAAK,gBACjB,IAAQ,KAAK,QAEb,IAAO,EAAU,IAAI,CAAA;AAC3B,IAAI,MACF,EAAM,OAAO,CAAA,GACb,EAAK,SAAS,QAAA,GAEd,EAAU,OAAO,CAAA;AAAA;EAOrB,UAAgB;AAEd,QAAI,CAAC,KAAK,eACR;AAGF,UAAM,IAAY,KAAK,gBACjB,IAAQ,KAAK;AACnB,eAAW,CAAC,GAAS,CAAA,KAAS;AAC5B,MAAI,KAAO,EAAM,OAAO,CAAA,GACxB,EAAK,SAAS,QAAA;AAGhB,IAAA,EAAU,MAAA,GAGV,KAAK,kBAAA;AAGL,eAAW,KAAY,KAAK,cAAc,OAAA,EACxC,CAAA,EAAS,QAAA;AAEX,SAAK,cAAc,MAAA,GAEnB,KAAK,SAAS,MACd,KAAK,UAAU,MACf,KAAK,aAAa,MAClB,KAAK,WAAW,MAEhB,KAAK,sBAAsB,MAE3B,KAAK,iBAAiB;AAAA;EAYxB,kBAAkB,GAAqC;AACrD,QAAI,CAAC,KAAK,OACR,OAAM,IAAI,MAAM,oCAAA;AAIlB,SAAK,kBAAA;AAGL,UAAM,IAAW,IAAI,GAAA;AACrB,IAAA,EAAS,cAAc,CACrB,EAAc,MAAA,GACd,EAAc,MAAA,CAAO,CAEtB;AAGD,UAAM,IAAW,EAAoB,UAAU,CAAA;AAC/C,IAAA,EAAS,UAAU,KACnB,EAAS,SAAS,IAClB,EAAS,WAAW,GACpB,EAAS,UAAU,KACnB,EAAS,cAAc;AAEvB,UAAM,IAAc,IAAI,GAAM,GAAU,CAAA;AACxC,WAAA,EAAY,cAAc,KAE1B,KAAK,cAAc,GACnB,KAAK,YAAY,WAAW,EAC1B,eAAe,EAAc,MAAA,EAAO,GAGtC,KAAK,OAAO,IAAI,CAAA,GAET;AAAA;EAOT,kBAAkB,GAAkC;AAClD,QAAI,CAAC,KAAK,YACR;AAGF,UAAM,IAAW,KAAK,YAAY,UAE5B,IAAgB,KAAK,YAAY,SAAS;AAEhD,IAAA,EAAS,cAAc,CAAC,EAAc,MAAA,GAAS,EAAY,MAAA,CAAO,CAAC;AAAA;EAMrE,oBAA0B;AACxB,IAAI,KAAK,UAAU,KAAK,gBACtB,KAAK,OAAO,OAAO,KAAK,WAAA,GACxB,KAAK,YAAY,SAAS,QAAA,GACzB,KAAK,YAAY,SAA0B,QAAA,GAC5C,KAAK,cAAc;AAAA;EAevB,6BACE,GACA,GACsB;AACtB,UAAM,IAAS,IAAI,EAAM,QAAA;AACzB,QAAI,IAAQ;AAEZ,WAAA,EAAe,SAAA,CAAU,MAAU;AACjC,MAAI,MAIF,EAAM,SAAS,YAAY,KAC1B,EAAM,SAAS,SAAS,gBAAgB,EAAM,SAAS,YAAY,OAEpE,EAAM,iBAAiB,CAAA,GACvB,IAAQ;AAAA,QAIL,IAAQ,IAAS;AAAA;EAS1B,0BAA0B,GAAc,GAAsC;AAC5E,UAAM,IAAU,KAAK;AACrB,QAAI,CAAC,EAAS,QAAO;AAErB,UAAM,IAAO,EAAQ,QAAQ,CAAA;AAC7B,QAAI,CAAC,EAAM,QAAO;AAIlB,UAAM,IADW,KAAK,gBAAgB,CAAA,EACd;AAGxB,QAAI,IAAc,OACd,IAAc;AAElB,aAAS,IAAI,GAAG,IAAI,EAAO,SAAS,GAAG,KAAK;AAC1C,YAAM,IAAe,EAAO,CAAA,GACtB,IAAa,EAAO,IAAI,CAAA;AAE9B,UAAI,CAAC,KAAgB,CAAC,EAAY;AAGlC,YAAM,IAAa,IAAI,EAAM,QAAA,EAAU,WAAW,GAAY,CAAA,GACxD,IAAgB,EAAW,OAAA;AAEjC,UAAI,MAAkB,EAAG;AAEzB,MAAA,EAAW,UAAA;AAEX,YAAM,IADU,IAAI,EAAM,QAAA,EAAU,WAAW,GAAe,CAAA,EACnC,IAAI,CAAA,GACzB,IAAoB,KAAK,IAAI,GAAG,KAAK,IAAI,GAAe,CAAA,CAAW,GAEnE,IAAe,EAAa,MAAA,EAAQ,gBAAgB,GAAY,CAAA,GAChE,IAAW,EAAc,WAAW,CAAA;AAE1C,MAAI,IAAW,MACb,IAAc,GACd,IAAc;AAAA;AAIlB,WAAO;AAAA;EAUT,6BACE,GACA,GACA,IAAsB,IAC2B;AACjD,UAAM,IAAU,KAAK;AACrB,QAAI,CAAC,EAAS,QAAO;AAErB,UAAM,IAAO,EAAQ,QAAQ,CAAA;AAC7B,QAAI,CAAC,EAAM,QAAO;AAGlB,UAAM,IAAuB,KAAK,wBAAwB,CAAA;AAE1D,QAAI,IAAe,IACf,IAAkB;AAGtB,aAAS,IAAI,GAAG,IAAI,EAAK,sBAAsB,QAAQ,KAAK;AAC1D,YAAM,IAAM,EAAK,sBAAsB,CAAA;AACvC,UAAI,CAAC,EAAK;AAEV,YAAM,IAAW,EAAoB,CAAA,GAC/B,IAAiB,KAAK,cAAc,CAAA,GACpC,IAAW,KAAK,eAAe,GAAsB,CAAA;AAE3D,MAAI,IAAW,KAAe,IAAW,MACvC,IAAe,GACf,IAAkB;AAAA;AAItB,WAAI,KAAgB,IACX;AAAA,MAAE,YAAY;AAAA,MAAc,UAAU;AAAA,QAGxC;AAAA;EAST,gBAAgB,GAAsB;AACpC,UAAM,IAAU,KAAK,UACf,IAAqB,KAAK,qBAE1B,IAAQ,EAAQ,SAAS,EAAK,KAAA,GAC9B,IAAQ,EAAQ,SAAS,EAAK,KAAA;AAEpC,QAAI,CAAC,KAAS,CAAC,EACb,OAAM,IAAI,MAAM,QAAQ,EAAK,EAAA,8BAAG;AAIlC,UAAM,IAAW,KAAK,uBACpB,EAAM,IACN,EAAM,MACN,EAAM,WACN,GACA,CAAA,GAII,IAAS,KAAK,uBAClB,EAAM,IACN,EAAM,MACN,EAAM,WACN,GACA,CAAA,GAII,IAA0B,CAAC,CAAA;AAGjC,eAAW,KAAO,EAAK,sBACrB,CAAA,EAAO,KAAK,EAAoB,CAAA,CAAI;AAGtC,WAAA,EAAO,KAAK,CAAA,GAEL;AAAA,MAAE,QAAQ,EAAK;AAAA,MAAI,QAAA;AAAA;;EAoB5B,uBACE,GACA,GACA,GACA,GACA,GACe;AACf,QAAI,MAAc,EAAU,OAAO,GAAa;AAC9C,YAAM,IAAiB,EAAgB,IAAI,CAAA;AAC3C,UAAI,GAAgB;AAClB,cAAM,IAAc,KAAK,6BAA6B,GAAS,CAAA;AAC/D,YAAI,EACF,QAAO;AAAA;AAKX,YAAM,IAAQ,EAAQ,SAAS,CAAA;AAC/B,UAAI,EACF,QAAO,EAAoB,EAAM,YAAY,CAAA,CAAQ;AAAA;AAKzD,UAAM,IAAQ,EAAQ,SAAS,CAAA;AAC/B,QAAI,CAAC,EACH,OAAM,IAAI,MAAM,SAAS,CAAA,YAAQ;AAEnC,WAAO,EAAoB,EAAM,YAAY,CAAA,CAAQ;AAAA;EAQvD,cAAsB,GAA6C;AACjE,QAAI,CAAC,KAAK,QACR,OAAM,IAAI,MAAM,qCAAA;AAGlB,UAAM,IAAS,KAAK,SAEd,IAAS,EAAc,MAAA;AAC7B,IAAA,EAAO,QAAQ,CAAA;AAEf,UAAM,IAAY,KAAK,iBAAiB,GAClC,IAAa,KAAK,kBAAkB;AAE1C,WAAO,IAAI,EAAM,QACf,EAAO,IAAI,IAAY,GACvB,EAAE,EAAO,IAAI,KAAc,CAAA;AAAA;EAU/B,eAAuB,GAA2B,GAAmC;AACnF,WAAO,EAAW,WAAW,CAAA;AAAA;EAQ/B,wBAAgC,GAAyC;AACvE,QAAI,CAAC,KAAK,WACR,OAAM,IAAI,MAAM,wCAAA;AAGlB,UAAM,IADY,KAAK,WACA,sBAAA;AAEvB,WAAO,IAAI,EAAM,QAAQ,EAAU,IAAI,EAAK,MAAM,EAAU,IAAI,EAAK,GAAA;AAAA;;AC1rBzE,SAAgB,GACd,IAA0C,QACtB;AACpB,QAAM,IAAqC;AAAA,IACzC,WAAW;AAAA,IACX,oBAAoB;AAAA,IACpB,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,eAAe;AAAA,IACf,eAAe;AAAA,IACf,aAAa;AAAA,IACb,aAAa;AAAA,IACb,UAAU;AAAA,IACV,WAAW;AAAA,IACX,aAAa;AAAA;AAGf,SAAK,IACE;AAAA,IAAE,GAAG;AAAA,IAAgB,GAAG;AAAA,MADV;;AAQvB,SAAgB,EACd,IAAyC,QACtB;AACnB,QAAM,IAAoC;AAAA,IACxC,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,aAAa;AAAA,IACb,aAAa,GAAA;AAAA,IACb,iBAAiB;AAAA,IACjB,oBAAoB;AAAA;AAGtB,SAAK,KACL,EAAQ,cAAc,GAAmB,EAAQ,WAAA,GAC1C;AAAA,IAAE,GAAG;AAAA,IAAgB,GAAG;AAAA,OAFV;;AASvB,SAAgB,GAAc,IAAqC,QAA0B;AAC3F,QAAM,IAAgC;AAAA,IACpC,aAAa;AAAA,IACb,mBAAmB,EAAA;AAAA,IACnB,eAAe;AAAA,MAAE,eAAe;AAAA,MAAO,cAAc;AAAA;;AAGvD,SAAK,KACL,EAAQ,oBAAoB,EAAkB,EAAQ,iBAAA,GAC/C;AAAA,IAAE,GAAG;AAAA,IAAgB,GAAG;AAAA,OAFV;;ACvDvB,SAAgB,GACd,GACA,GACA,GACa;AACb,QAAM,IAAW,IAAI,GAAY,GAAQ,CAAA;AACzC,SAAA,IAAU,GAAmB,CAAA,GAE7B,EAAS,YAAY,EAAQ,WAC7B,EAAS,qBAAqB,EAAQ,oBACtC,EAAS,aAAa,EAAQ,YAC9B,EAAS,eAAe,EAAQ,cAChC,EAAS,gBAAgB,EAAQ,eACjC,EAAS,gBAAgB,EAAQ,eACjC,EAAS,cAAc,EAAQ,aAC/B,EAAS,cAAc,EAAQ,aAC/B,EAAS,WAAW,EAAQ,UAC5B,EAAS,YAAY,EAAQ,WAC7B,EAAS,cAAc,EAAQ,aAExB;;ACoBT,IAAsB,KAAtB,cAAwD,GAAiC;AAAA,EAEvF,aAA2C;AAAA,EAC3C,SAAuC;AAAA,EACvC,QAA2C;AAAA,EAC3C,UAAoD;AAAA,EACpD,eAA6C;AAAA,EAG7C,WAAqC;AAAA,EAGrC;AAAA,EAGA,eAAkC;AAAA,EAClC;AAAA,EAEA,UAA6B;AAAA,EAC7B,YAA+B;AAAA,EAC/B,gBAAkC;AAAA,EAGlC;AAAA,EACA;AAAA,EAGA,sBAA2D,oBAAI,IAAA;AAAA,EAC/D,kBAAuD,oBAAI,IAAA;AAAA,EAC3D,iBAA6C,oBAAI,IAAA;AAAA,EAGjD,gBAA+C;AAAA,EAC/C,oBAAoE;AAAA,EACpE,qBAAqE;AAAA,EACrE,4BAA2D;AAAA,EAG3D,mBAAqD;AAAA,EACrD,sBAAyC;AAAA,EASzC,YAAY,GAAmC,GAAmC;AAEhF,QADA,MAAA,GACI,CAAC,EACH,OAAM,IAAI,UAAU,6BAAA;AAEtB,SAAK,WAAW,EAAA,GAGZ,KACF,KAAK,mBAAmB,GACxB,KAAK,sBAAsB,IAC3B,KAAK,kBAAkB,EAAgB,iBACvC,KAAK,8BAA8B,EAAgB,6BACnD,KAAK,oBAAoB,EAAgB,sBAEzC,KAAK,sBAAsB,IAC3B,KAAK,kBAAkB,GACvB,KAAK,8BAA8B,IAAI,GAAA,GACvC,KAAK,oBAAoB,IAAI,GAAkB,KAAK,qBAAqB,KAAK,cAAA;AAAA;EAIlF,IAAI,qBAAgD;AAClD,WAAO,KAAK;AAAA;EAGd,IAAI,iBAA4C;AAC9C,WAAO,KAAK;AAAA;EAGd,IAAI,gBAAkC;AACpC,WAAO,KAAK;AAAA;EAQd,IAAI,gBAA+B;AACjC,WAAO,EACL,UAAA,CAAW,MAAa,KAAK,UAAU,SAAS,CAAA,EAAG;AAAA;EAIvD,IAAc,OAAgC;AAC5C,WAAI,KAAK,sBAA4B,KAAK,kBAAkB,QAAQ,OAC7D,KAAK;AAAA;EAGd,IAAc,KAAK,GAAwB;AACzC,IAAI,KAAK,sBACH,KAAK,qBACP,KAAK,iBAAiB,OAAO,KAG/B,KAAK,QAAQ;AAAA;EAoBjB,WAAW,GAAwB,GAAmC;AACpE,QAAI,MAAK,cAMT;AAAA,UAHA,IAAU,EAAkB,CAAA,GAC5B,KAAK,WAAW,GAEZ,CAAC,KAAa,EAAE,aAAqB,cAAc;AACrD,cAAM,IAAQ,oBAAI,UAAU,uCAAA;AAC5B,mBAAK,UAAU,CAAA,GACT;AAAA;AAGR,UAAI;AACF,aAAK,aAAa,GAEd,KAAK,uBAAuB,KAAK,oBAEnC,KAAK,sBAAsB,KAAK,iBAAiB,oBACjD,KAAK,kBAAkB,KAAK,iBAAiB,gBAC7C,KAAK,iBAAiB,KAAK,iBAAiB,eAE5C,KAAK,SAAS,KAAK,iBAAiB,OACpC,KAAK,UAAU,KAAK,iBAAiB,QACrC,KAAK,eAAe,KAAK,iBAAiB,aAC1C,KAAK,gBAAgB,KAAK,iBAAiB,cAI3C,KAAK,kBAAkB,cACrB,KAAK,WAAY,aACjB,KAAK,WAAY,YAAA,GAId,KAAK,cAAc,cAAA,KACtB,KAAK,wBAAA,GAGP,KAAK,qBAAA,MAIL,KAAK,SAAS,IAAI,EAAM,MAAA,GACxB,KAAK,OAAO,aAAa,IAAI,EAAM,MAAM,EAAQ,eAAA,GAEjD,KAAK,QAAQ,EAAiB,IAAI,IAAI,EAAQ,iBAAkB,EAAQ,SAAA,GACxE,KAAK,OAAO,IAAI,KAAK,KAAA,GAErB,GAAiB,KAAK,MAAA,GAItB,KAAK,UAAU,GADA,EAAU,cAAc,EAAU,gBAAgB,CAAA,GAEjE,KAAK,QAAQ,OAAO,IAAI,CAAA,GACxB,KAAK,QAAQ,OAAO,OAAO,CAAA,GAC3B,KAAK,QAAQ,OAAO,OAAO,CAAA,GAE3B,KAAK,eAAe,GAAkB,KAAK,SAAS,KAAK,YAAY,EAAQ,WAAA,GAG7E,KAAK,kBAAkB,aAAa,KAAK,UAAA,GACzC,KAAK,kBAAkB,cACrB,KAAK,WAAY,aACjB,KAAK,WAAY,YAAA,GAEnB,KAAK,kBAAkB,kBAAkB,KAAK,QAAQ,KAAK,OAAA,GAG3D,KAAK,gBAAgB,IAAI,GAAa,KAAK,QAAQ,KAAK,OAAA,GAExD,KAAK,wBAAA,GAEL,KAAK,qBAAA,GAEL,KAAK,UAAU,KAIjB,KAAK,aAAa,CAAA,GAElB,KAAK,eAAe,IAGpB,KAAK,UAAA;AAAA,eACE,GAAO;AACd,cAAM,IAAM;AACZ,mBAAK,UAAU,CAAA,GACT;AAAA;;;EAoBV,UAAoB,GAAoB;AACrC,SAA0C,KAAK,SAAS;AAAA,MACvD,SAAS,EAAM;AAAA,MACf,OAAA;AAAA,KACD;AAAA;EAOH,oBAAoC;AAClC,QAAI,CAAC,KAAK,aACR,OAAM,IAAI,MAAM,sDAAA;AAElB,QAAI,KAAK,UACP,OAAM,IAAI,MAAM,8BAAA;AAAA;EAWpB,UAAgB;AACd,SAAK,kBAAA;AAEL,QAAI;AAEF,WAAK,UAAA,GAGD,KAAK,eACH,KAAK,sBACP,KAAK,WAAW,oBAAoB,aAAa,KAAK,iBAAA,GACtD,KAAK,oBAAoB,OAEvB,KAAK,uBACP,KAAK,WAAW,oBAAoB,cAAc,KAAK,kBAAA,GACvD,KAAK,qBAAqB,QAKzB,KAAK,uBAqCR,KAAK,gBAAgB,MACrB,KAAK,SAAS,MACd,KAAK,UAAU,MACf,KAAK,eAAe,MACpB,KAAK,QAAQ,SAvCT,KAAK,kBACP,KAAK,cAAc,QAAA,GACnB,KAAK,gBAAgB,OAIvB,KAAK,kBAAA,GAED,KAAK,SACP,KAAK,OAAQ,OAAO,KAAK,IAAA,GACzB,KAAK,KAAK,SAAS,QAAA,GACnB,KAAK,KAAK,QAAA,GACV,KAAK,QAAQ,OAIf,KAAK,oBAAoB,MAAA,GACzB,KAAK,gBAAgB,MAAA,GACrB,KAAK,eAAe,MAAA,GAGpB,KAAK,kBAAkB,QAAA,GAGnB,KAAK,iBACH,KAAK,8BACP,KAAK,aAAa,oBAAoB,UAAU,KAAK,yBAAA,GACrD,KAAK,4BAA4B,OAEnC,KAAK,aAAa,QAAA,GAClB,KAAK,eAAe,QAcxB,KAAK,mBAAA,GAEL,KAAK,YAAY,IACjB,KAAK,eAAe;AAAA,aACb,GAAO;AACd,YAAM,IAAM;AACZ,iBAAK,UAAU,CAAA,GACT;AAAA;;EAeV,UAAiB,GAAuB;AACtC,SAAK,UAAU,GACf,KAAK,YAAY,CAAA;AAAA;EAcnB,aAA6B;AAC3B,WAAO,KAAK;AAAA;EASd,YAAY,GAAoB;AAAA,EAAA;AAAA,EAQhC,YAAsB,GAA+B;AAEnD,QADA,KAAK,kBAAA,GACD,MAAY,KAAK,UAErB;AAAA,UAAM,KAAK,UAAU;AAEnB,aAAK,kBAAA;AACL,cAAM,IACJ,KAAK,SAAS,YAAY,KAAK,SAAS,SAAS,UAC7C,KAAK,SAAS,SAAS,QAAQ,OAC/B;AACN,aAAK,WAAW,MAChB,KAAK,kBAAkB,WAAW,IAAA,GAClC,KAAK,KAAK,kBAAkB,EAAE,MAAM,EAAA,CAAgB;AAAA;AAUtD,UAPI,CAAC,KAAK,uBAAuB,KAAK,UACpC,KAAK,MAAM,SAAS,QAAA,GACpB,KAAK,MAAM,QAAA,GACX,KAAK,OAAQ,OAAO,KAAK,KAAA,GACzB,KAAK,QAAQ,OAGX,MAAY,MAAM;AACpB,cAAM,IACJ,EAAQ,YAAY,EAAQ,SAAS,UACjC,EAAQ,SAAS,QAAQ,OACzB,mBACA,IAAU,KAAK,YAAY,EAAA;AAOjC,YALA,KAAK,WAAW,GAChB,KAAK,OAAQ,OAAO,GACpB,KAAK,kBAAkB,WAAW,CAAA,GAClC,KAAK,gBAAgB,KAAK,KAAK,EAAQ,SAAS,OAAO,CAAA,GAEnD,CAAC,KAAK,wBACR,KAAK,QAAQ,EACX,EAAQ,SAAS,MACjB,EAAQ,SAAS,WACjB,EAAQ,iBACR,EAAQ,SAAA,GAEV,KAAK,OAAQ,IAAI,KAAK,KAAA,GAElB,KAAK,WACP,GAAa,KAAK,SAAS,EAAQ,SAAS,aAAA,GAG1C,KAAK,eAAc;AACrB,gBAAM,IAAW,KAAK,cAChB,IAAS,EAAQ,SAAS,cAAc;AAC9C,UAAA,EAAS,OAAO,IAAI,EAAO,GAAG,EAAO,GAAG,EAAO,CAAA;AAAA;AAGnD,aAAK,aAAA,GACL,KAAK,KAAK,iBAAiB,EAAE,MAAM,EAAA,CAAe;AAAA;;;EAStD,YAAY,GAAqB,GAAsC;AACrE,YAAQ,GAAR;AAAA,MACE,KAAK;AACH,eAAO,KAAK,oBAAoB,IAAI,CAAA;AAAA,MACtC,KAAK;AACH,eAAO,KAAK,gBAAgB,IAAI,CAAA;AAAA,MAClC,KAAK;AACH,eAAO,KAAK,eAAe,IAAI,CAAA;AAAA,MACjC;AACE;AAAA;;EAON,cAAkC;AAChC,WAAO,KAAK;AAAA;EAYd,0BAA0B,IAAiB,IAAsB;AAC/D,UAAM,IAAS,KAAK,cAAe,uBAAA,EAAyB,MAAA;AAC5D,WAAI,KACF,EAAO,IACL,KAAK,IAAI,KAAK,IAAI,EAAO,GAAG,CAAC,KAAK,aAAA,GAAgB,KAAK,aAAA,GACvD,GACA,KAAK,IAAI,KAAK,IAAI,EAAO,GAAG,CAAC,KAAK,aAAA,GAAgB,KAAK,aAAA,CAAc,GAGlE;AAAA;EAQT,0BAAwC;AACtC,QAAI,CAAC,KAAK,cACR,OAAM,IAAI,MAAM,wDAAA;AAElB,QAAI,CAAC,KAAK,WACR,OAAM,IAAI,MAAM,sDAAA;AAUlB,UAAM,IAAA,CAA0B,MAA4B;AAC1D,UAAI,EAAQ,eAAe,eAAe;AACxC,cAAM,IAAW,EAAQ,SAAS,UAC5B,IAAU,EAAS;AACzB,YAAI,CAAC,GAAS;AACZ,kBAAQ,KAAK,kDAAA;AACb;AAAA;AAEF,cAAM,IAAa,KAAK,gBAAgB,IAAI,CAAA;AAC5C,YAAI,CAAC,GAAY;AACf,kBAAQ,KAAK,uDAAA;AACb;AAAA;AAEF,YAAI;AAEF,UAAK,EAAS,cAGZ,KAAK,gBAAgB,mBAAA,EAAqB,eAAe,CAAA,IAFzD,KAAK,4BAA4B,YAAY,CAAA;AAAA,iBAIxC,GAAO;AACd,kBAAQ,KAAK,mCAAmC,CAAA;AAAA;AAElD;AAAA,iBACS,EAAQ,eAAe,mBAAmB;AACnD,cAAM,IAAW,EAAQ,SAAS,UAC5B,IAAc,EAAS;AAC7B,YAAI,CAAC,GAAa;AAChB,kBAAQ,KAAK,sDAAA;AACb;AAAA;AAEF,cAAM,IAAiB,KAAK,oBAAoB,IAAI,CAAA;AACpD,YAAI,CAAC,GAAgB;AACnB,kBAAQ,KAAK,2DAAA;AACb;AAAA;AAEF,YAAI;AACc,eAAK,gBAAgB,IAAI,EAAS,aAAA,EAC1C,YAAY,CAAA;AAAA,iBACb,GAAO;AACd,kBAAQ,KAAK,kCAAkC,CAAA;AAAA;AAEjD;AAAA,iBACS,EAAQ,eAAe,QAAQ;AAExC,cAAM,IADW,EAAQ,SAAS,SACV;AACxB,YAAI,CAAC,GAAQ;AACX,kBAAQ,KAAK,iDAAA;AACb;AAAA;AAGF,YAAI,CADS,KAAK,eAAe,IAAI,CAAA,GAC1B;AACT,kBAAQ,KAAK,iDAAA;AACb;AAAA;AAEF,aAAK,kBAAkB,oBAAoB,CAAA;AAAA;OAIzC,IAAA,CAAgB,MAA4B;AAChD,UAAI,EAAQ,eAAe,eAAe;AACxC,cAAM,IAAW,EAAQ,SAAS,UAC5B,IAAU,EAAS;AACzB,YAAI,CAAC,GAAS;AACZ,kBAAQ,KAAK,gDAAA;AACb;AAAA;AAEF,cAAM,IAAa,KAAK,gBAAgB,IAAI,CAAA;AAC5C,YAAI,CAAC,GAAY;AACf,kBAAQ,KAAK,qDAAA;AACb;AAAA;AAEF,YAAI;AAEF,UAAK,EAAS,cAGZ,KAAK,gBAAgB,mBAAA,EAAqB,cAAc,CAAA,IAFxD,KAAK,4BAA4B,WAAW,CAAA;AAAA,iBAIvC,GAAO;AACd,kBAAQ,KAAK,iCAAiC,CAAA;AAAA;AAEhD;AAAA,iBACS,EAAQ,eAAe,mBAAmB;AACnD,cAAM,IAAW,EAAQ,SAAS,UAC5B,IAAc,EAAS;AAC7B,YAAI,CAAC,GAAa;AAChB,kBAAQ,KAAK,oDAAA;AACb;AAAA;AAEF,cAAM,IAAiB,KAAK,oBAAoB,IAAI,CAAA;AACpD,YAAI,CAAC,GAAgB;AACnB,kBAAQ,KAAK,yDAAA;AACb;AAAA;AAEF,YAAI;AACc,eAAK,gBAAgB,IAAI,EAAS,aAAA,EAC1C,WAAW,CAAA;AAAA,iBACZ,GAAO;AACd,kBAAQ,KAAK,iCAAiC,CAAA;AAAA;AAEhD;AAAA,iBACS,EAAQ,eAAe,QAAQ;AAExC,cAAM,IADW,EAAQ,SAAS,SACV;AACxB,YAAI,CAAC,GAAQ;AACX,kBAAQ,KAAK,+CAAA;AACb;AAAA;AAGF,YAAI,CADS,KAAK,eAAe,IAAI,CAAA,GAC1B;AACT,kBAAQ,KAAK,+CAAA;AACb;AAAA;AAEF,aAAK,kBAAkB,mBAAmB,CAAA;AAAA;;AAK9C,SAAK,cAAc,cAAA,CAAe,GAAS,MAAoB;AAE7D,MAAI,MAAoB,CAAC,KAAW,EAAQ,OAAO,EAAgB,QACjE,EAAuB,CAAA,GACvB,KAAK,KAAK,WAAW;AAAA,QACnB,UAAU,EAAgB;AAAA,QAC1B,YAAY,EAAgB;AAAA,QAC5B,UAAU,EAAgB,SAAS;AAAA,OACpC,GACD,IAAkB,OAIhB,MACF,EAAa,CAAA,GACb,KAAK,KAAK,SAAS;AAAA,QACjB,UAAU,EAAQ;AAAA,QAClB,YAAY,EAAQ;AAAA,QACpB,UAAU,EAAQ,SAAS;AAAA,OAC5B;AAAA,QAKD,KAAK,iBACP,KAAK,4BAAA,MAAkC;AACrC,MAAI,KAAK,iBACP,KAAK,cAAc,QAAA;AAAA,OAGvB,KAAK,aAAa,iBAAiB,UAAU,KAAK,yBAAA,IAEpD,KAAK,cAAc,eAAe,EAAA;AAAA;EAGpC,uBAAuC;AACrC,QAAI,CAAC,KAAK,cACR,OAAM,IAAI,MAAM,oEAAA;AAElB,QAAI,CAAC,KAAK,WACR,OAAM,IAAI,MAAM,oDAAA;AAIlB,SAAK,oBAAA,CAAqB,MAAsB;AAC9C,UAAI,CAAC,KAAK,WAAW,CAAC,KAAK,cAAc,CAAC,KAAK,cAAe;AAC9D,YAAM,IAAO,KAAK,WAAW,sBAAA,GACvB,KAAM,EAAM,UAAU,EAAK,QAAQ,EAAK,QAAS,IAAI,GACrD,IAAI,GAAG,EAAM,UAAU,EAAK,OAAO,EAAK,UAAU,IAAI,GACtD,IAAc,KAAK,0BAAA;AACzB,WAAK,cAAc,gBAAgB,GAAG,CAAA;AACtC,YAAM,IAAc,KAAK,0BAAA;AACzB,MAAK,EAAY,OAAO,CAAA,KAEtB,KAAK,KAAK,oBAAoB,CAAA;AAAA,OAGlC,KAAK,WAAW,iBAAiB,aAAa,KAAK,iBAAA,GAGnD,KAAK,qBAAA,CAAsB,MAAuB;AAChD,MAAI,KAAK,iBACP,KAAK,cAAc,MAAA;AAAA,OAGvB,KAAK,WAAW,iBAAiB,cAAc,KAAK,kBAAA;AAAA;EAMtD,oBAA2C;AACzC,WAAO,KAAK,eAAe,kBAAA,KAAuB;AAAA;EAMpD,gBAAgB,GAAwB;AACtC,SAAK,eAAe,WAAW,CAAA;AAAA;EAMjC,iBAA0B;AACxB,WAAO,KAAK,eAAe,UAAA,KAAe;AAAA;EAW5C,WAAwB;AACtB,gBAAK,kBAAA,GACE,KAAK;AAAA;EAOd,YAAqC;AACnC,gBAAK,kBAAA,GACE,KAAK;AAAA;EAOd,eAA4B;AAE1B,QADA,KAAK,kBAAA,GACD,CAAC,KAAK,WACR,OAAM,IAAI,MAAM,2BAAA;AAElB,WAAO,KAAK;AAAA;EAMd,IAAI,gBAAyB;AAC3B,WAAO,KAAK;AAAA;EAMd,IAAI,aAAsB;AACxB,WAAO,KAAK;AAAA;EAiBd,kBAAkB,GAAgB,GAAuB;AACvD,QAAK,KAAK,YAEV;AAAA,UAAI,MAAU,UAAa,MAAW,QAAW;AAC/C,cAAM,IAAO,KAAK,WAAW,sBAAA;AAC7B,QAAA,IAAQ,EAAK,OACb,IAAS,EAAK;AAAA;AAGhB,MAAI,KAAK,YACP,KAAK,QAAQ,SAAS,IAAQ,GAC9B,KAAK,QAAQ,uBAAA,IAEf,KAAK,kBAAkB,cAAc,GAAO,CAAA,GAG5C,KAAK,SAAS,GAAO,CAAA;AAAA;AAAA;EAMvB,SAAmB,GAAgB,GAAuB;AAAA,EAAA;GCvzB/C,KAAb,MAA+B;AAAA,EAC7B;AAAA,EACA;AAAA,EAGA,UAA2B;AAAA,EAC3B,sBAA2C;AAAA,EAC3C,oBAA8C;AAAA,EAC9C,kBAA0D;AAAA,EAC1D,MAA0B;AAAA,EAC1B,YAA2C;AAAA,EAC3C,iBAA8C,CAAA;AAAA,EAG9C,sBAAoE;AAAA,EACpE,gBAAiE;AAAA,EAUjE,YACE,GACA,GACA,GACA,GACA;AACA,SAAK,kBAAkB,GACvB,KAAK,sBAAsB;AAAA;EAM7B,IAAI,SAAkB;AACpB,WAAO,KAAK;AAAA;EAMd,IAAI,qBAAkC;AACpC,WAAO,KAAK;AAAA;EAUd,KAAK,GAAsB,GAAmD;AAE5E,IAAI,KAAK,WACP,KAAK,MAAA;AAGP,UAAM,IAAU,KAAK,gBAAgB,IAAI,EAAU,IAAA,GAC7C,IAAU,EAAQ,wBAAwB,EAAU,MAAA;AAE1D,WAAI,CAAC,KAAW,EAAQ,OAAO,WAAW,IACjC,MAIT,KAAK,YAAY,SAAS,cAAc,KAAA,GACxC,KAAK,UAAU,MAAM,WAAW,YAChC,KAAK,UAAU,MAAM,SAAS,QAC9B,SAAS,KAAK,YAAY,KAAK,SAAA,GAG/B,KAAK,kBAAkB,CAAA,GAEvB,KAAK,oBAAoB,GACzB,KAAK,kBAAkB,GAGvB,KAAK,SAAS,GAAS,GAAW,CAAA,GAGlC,KAAK,oBAAA,GAEL,KAAK,UAAU,IACf,KAAK,sBAAsB,EAAU,IAE9B;AAAA;EAMT,QAAc;AACZ,IAAK,KAAK,YAKN,KAAK,QACP,KAAK,IAAI,QAAA,GACT,KAAK,MAAM,OAIT,KAAK,cACP,SAAS,KAAK,YAAY,KAAK,SAAA,GAC/B,KAAK,YAAY,OAInB,KAAK,qBAAA,GAEL,KAAK,UAAU,IACf,KAAK,sBAAsB,MAC3B,KAAK,oBAAoB,MACzB,KAAK,kBAAkB,MACvB,KAAK,iBAAiB,CAAA;AAAA;EAMxB,UAAgB;AACd,SAAK,MAAA;AAAA;EAMP,kBAA0B,GAAgD;AACxE,QAAI,CAAC,KAAK,UAAW;AAErB,UAAM,IAAc,KACd,IAAW,IACX,IAAmB;AAGzB,QAAI,IAAO,EAAe,IAAI,GAC1B,IAAM,EAAe;AAGzB,UAAM,IAAgB,OAAO,YACvB,IAAiB,OAAO;AAG9B,IAAI,IAAO,IAAc,IAAgB,MACvC,IAAO,EAAe,IAAI,IAAc,IAItC,IAAO,MACT,IAAO,IAIL,IAAM,IACR,IAAM,IACG,IAAM,IAAiB,MAChC,IAAM,IAAiB,IAAmB,MAG5C,KAAK,UAAU,MAAM,OAAO,GAAG,CAAA,MAC/B,KAAK,UAAU,MAAM,MAAM,GAAG,CAAA;AAAA;EAMhC,SAAiB,GAA+B,GAAgB,GAAoB;AAClF,QAAI,CAAC,KAAK,UAAW;AAGrB,SAAK,MAAM,IAAI,GAAI;AAAA,MAAE,WAAW,KAAK;AAAA,MAAW,OAAO;AAAA,KAAK;AAC5D,UAAM,IAAW,EAAK,cAAc,EAAU,IAAA,SAAa,EAAE,cAAc,EAAU,KAAA,CAAM;AAC3F,SAAK,IAAI,MAAM,EAAK,gBAAgB;AAAA,MAAE,MAAM;AAAA,MAAU,cAAc,WAAW,CAAA;AAAA,KAAY,CAAC;AAG5F,UAAM,IAAW,EAAQ,oBAAoB,EAAU,MAAA;AAGvD,SAAK,iBAAiB,CAAA;AACtB,eAAW,CAAC,GAAK,CAAA,KAAU,EAAS,QAAA,EAClC,MAAK,eAAe,CAAA,IAAO;AAI7B,eAAW,KAAS,EAAQ,QAAQ;AAClC,UAAI,IAA4C;AAEhD,cAAQ,EAAM,MAAd;AAAA,QACE,KAAK;AACH,UAAA,IAAa,KAAK,IACf,IAAI,KAAK,gBAAgB,EAAM,GAAA,EAC/B,KAAK,KAAK,mBAAmB,EAAU,MAAM,CAAA,CAAM,EACnD,SAAA,CAAU,MAAe,KAAK,cAAc,EAAM,KAAK,GAAO,GAAW,CAAA,CAAQ;AACpF;AAAA,QAEF,KAAK;AACH,UAAI,EAAM,YACR,IAAa,KAAK,IACf,IAAI,KAAK,gBAAgB,EAAM,KAAK,EAAM,OAAA,EAC1C,KAAK,KAAK,mBAAmB,EAAU,MAAM,CAAA,CAAM,EACnD,SAAA,CAAU,MAAe,KAAK,cAAc,EAAM,KAAK,GAAO,GAAW,CAAA,CAAQ;AAEtF;AAAA,QAEF,KAAK;AACH,UAAA,IAAa,KAAK,IACf,IAAI,KAAK,gBAAgB,EAAM,GAAA,EAC/B,KAAK,KAAK,mBAAmB,EAAU,MAAM,CAAA,CAAM,EACnD,SAAA,CAAU,MAAe,KAAK,cAAc,EAAM,KAAK,GAAO,GAAW,CAAA,CAAQ,GAChF,EAAM,QAAQ,UAAW,EAAW,IAAI,EAAM,GAAA,GAC9C,EAAM,QAAQ,UAAW,EAAW,IAAI,EAAM,GAAA,GAC9C,EAAM,SAAS,UAAW,EAAW,KAAK,EAAM,IAAA;AACpD;AAAA,QAEF,KAAK;AACH,UAAA,IAAa,KAAK,IACf,IAAI,KAAK,gBAAgB,EAAM,GAAA,EAC/B,KAAK,KAAK,mBAAmB,EAAU,MAAM,CAAA,CAAM,EACnD,SAAA,CAAU,MAAe,KAAK,cAAc,EAAM,KAAK,GAAO,GAAW,CAAA,CAAQ;AACpF;AAAA,QAEF,KAAK;AACH,UAAA,IAAa,KAAK,IACf,SAAS,KAAK,gBAAgB,EAAM,GAAA,EACpC,KAAK,KAAK,mBAAmB,EAAU,MAAM,CAAA,CAAM,EACnD,SAAA,CAAU,MAAe,KAAK,cAAc,EAAM,KAAK,GAAO,GAAW,CAAA,CAAQ;AACpF;AAAA;AAGJ,MAAI,KAAc,EAAM,YACtB,EAAW,QAAQ,EAAA;AAAA;;EAgBzB,cAAsB,GAAoB,GAAa,GAAgB,GAAoB;AAEzF,UAAM,IAAc,oBAAI,IAAA;AACxB,eAAW,CAAC,GAAK,CAAA,KAAU,OAAO,QAAQ,KAAK,cAAA,EAC7C,CAAA,EAAY,IAAI,GAAK,CAAA;AAGvB,UAAM,IAAa,EAAQ,oBAAoB,CAAA;AAE/C,IAAI,KAAK,uBACP,KAAK,oBAAoB,KAAK,qBAAqB,CAAA,IAIjD,MAAe,wBAAwB,MAAe,sBACxD,KAAK,WAAW,GAAW,CAAA;AAAA;EAU/B,mBAA2B,GAAuB,GAAsC;AACtF,UAAM,IAAW,EAAM,IACpB,QAAQ,YAAY,KAAA,EACpB,QAAQ,MAAA,CAAO,MAAM,EAAE,YAAA,CAAa,EACpC,KAAA;AACH,WAAO,EAAK,cAAc,CAAA,kBAA+B,EAAM,GAAA,SAAY,EACzE,cAAc,EAAA,CACf;AAAA;EAOH,YAAY,GAAoB;AAC9B,IAAI,CAAC,KAAK,WAAW,CAAC,KAAK,qBAAqB,CAAC,KAAK,mBACtD,KAAK,WAAW,KAAK,mBAAmB,KAAK,eAAA;AAAA;EAO/C,WAAmB,GAAgB,GAAoB;AACrD,QAAI,CAAC,KAAK,OAAO,CAAC,KAAK,UAAW;AAGlC,SAAK,IAAI,QAAA,GACT,KAAK,MAAM;AAGX,UAAM,IAAU,EAAQ,wBAAwB,EAAU,MAAA;AAC1D,IAAK,KAEL,KAAK,SAAS,GAAS,GAAW,CAAA;AAAA;EAMpC,sBAAoC;AAElC,SAAK,sBAAA,CAAuB,MAAsB;AAChD,UAAI,CAAC,KAAK,UAAW;AAErB,YAAM,IAAS,EAAM;AAErB,MAAK,KAAK,UAAU,SAAS,CAAA,KAC3B,KAAK,MAAA;AAAA,OAKT,WAAA,MAAiB;AACf,MAAI,KAAK,uBACP,SAAS,iBAAiB,eAAe,KAAK,mBAAA;AAAA,OAE/C,GAAA,GAGH,KAAK,gBAAA,CAAiB,MAAyB;AAC7C,MAAI,EAAM,QAAQ,YAChB,KAAK,MAAA;AAAA,OAGT,SAAS,iBAAiB,WAAW,KAAK,aAAA;AAAA;EAM5C,uBAAqC;AACnC,IAAI,KAAK,wBACP,SAAS,oBAAoB,eAAe,KAAK,mBAAA,GACjD,KAAK,sBAAsB,OAEzB,KAAK,kBACP,SAAS,oBAAoB,WAAW,KAAK,aAAA,GAC7C,KAAK,gBAAgB;AAAA;GC3Wd,KAAb,MAA8B;AAAA,EAC5B,WAA0C;AAAA,EAC1C,wBAAsD;AAAA,EACtD,gBAA0D;AAAA,EAC1D;AAAA,EAEA,YAAY,GAAyD;AACnE,SAAK,mBAAmB;AAAA;EAO1B,KAAK,GAAkB,GAA8B,GAAiB,GAAuB;AAC3F,QAAI,KAAK,YAAY,KAAK,0BAA0B,GAAe;AACjE,WAAK,UAAU,GAAS,CAAA;AACxB;AAAA;AAEF,SAAK,KAAA,GACL,KAAK,wBAAwB;AAE7B,UAAM,IAAgB,EAAK,cAAc,CAAA,SAAsB,EAAE,cAAc,EAAA,CAAe;AAE9F,SAAK,WAAW,SAAS,cAAc,KAAA,GACvC,KAAK,SAAS,cAAc,GAAG,CAAA,KAAa,CAAA,KAC5C,OAAO,OAAO,KAAK,SAAS,OAAO;AAAA,MACjC,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,OAAO;AAAA,MACP,SAAS;AAAA,MACT,cAAc;AAAA,MACd,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,KACT,GAED,KAAK,gBAAA,CAAiB,MAAkB;AACtC,MAAA,EAAE,gBAAA,GACE,KAAK,0BAA0B,QACjC,KAAK,iBAAiB,KAAK,qBAAA;AAAA,OAG/B,KAAK,SAAS,iBAAiB,SAAS,KAAK,aAAA,GAE7C,SAAS,KAAK,YAAY,KAAK,QAAA,GAC/B,KAAK,UAAU,GAAS,CAAA;AAAA;EAI1B,OAAa;AACX,IAAK,KAAK,aACN,KAAK,kBACP,KAAK,SAAS,oBAAoB,SAAS,KAAK,aAAA,GAChD,KAAK,gBAAgB,OAEvB,SAAS,KAAK,YAAY,KAAK,QAAA,GAC/B,KAAK,WAAW,MAChB,KAAK,wBAAwB;AAAA;EAI/B,eAAe,GAAiB,GAAuB;AACrD,IAAI,KAAK,YACP,KAAK,UAAU,GAAS,CAAA;AAAA;EAI5B,IAAI,YAAqB;AACvB,WAAO,KAAK,aAAa;AAAA;EAO3B,YAAY,GAAoB;AAC9B,SAAK,KAAA;AAAA;EAGP,UAAgB;AACd,SAAK,KAAA;AAAA;EAGP,UAAkB,GAAiB,GAAuB;AACxD,QAAI,CAAC,KAAK,SAAU;AACpB,UAAM,IAAW,IACX,IAAW;AACjB,QAAI,IAAO,IAAU,GACjB,IAAM,IAAU;AAEpB,UAAM,IAAe,KAAK,SAAS,eAAe,KAC5C,IAAgB,KAAK,SAAS,gBAAgB,IAC9C,IAAK,OAAO;AAElB,IAAI,IAAO,IAAe,IAAK,MAAG,IAAO,IAAK,IAAe,IACzD,IAAO,MAAG,IAAO,IACjB,IAAM,MAAG,IAAM,IAAU,IAAgB,IAE7C,KAAK,SAAS,MAAM,OAAO,GAAG,CAAA,MAC9B,KAAK,SAAS,MAAM,MAAM,GAAG,CAAA;AAAA;GC3EpB,KAAb,cAAuC,GAA0B;AAAA,EAE/D,YAA6B;AAAA,EAE7B;AAAA,EAEA,oBAAqD;AAAA,EAErD,sBAAwD;AAAA,EAExD,oBAAqD;AAAA,EACrD,eAA+B;AAAA,EAC/B,eAA+B;AAAA,EAC/B,2BAAqE;AAAA,EAErE,SAA8C,oBAAI,IAAA;AAAA,EAClD,cAAuC;AAAA,EAavC,YAAY,GAAmC,GAAmC;AAChF,UAAM,GAAiB,CAAA,GAEvB,KAAK,gBAAgB,IAAI,GAAc,IAAA,GAEvC,KAAK,oBAAoB,KAAK,kBAAkB,KAAK,IAAA,GACrD,KAAK,oBAAoB,KAAK,kBAAkB,KAAK,IAAA;AAAA;EAGvD,YAAqB,GAAmB;AACtC,SAAK,mBAAmB,YAAY,CAAA,GACpC,KAAK,qBAAqB,YAAY,CAAA,GACrC,KAAK,OAAO,IAAI,OAAA,GAAoC,YAAY,CAAA;AAAA;EAOnE,aAAuB,GAA8B;AAEnD,SAAK,iBAAA,GAEL,KAAK,4BAAA,GAEL,KAAK,8BAAA,GAEL,KAAK,sBAAA,GAEL,KAAK,eAAe,IAEf,KAAK,oBACR,KAAK,UAAU,EAAA;AAAA;EAInB,YAAsB;AACpB,SAAK,KAAK,SAAS,EAAE,gBAAgB,SAAA,CAAU;AAAA;EASjD,YAAY,GAAwB;AAGlC,QAFA,KAAK,kBAAA,GAED,KAAK,cAAc,MACvB,KAAK,YAAY,GAEb,CAAC,KAEC,KAAK,gBAAgB,OAAM;AAC7B,YAAM,IAAe,KAAK,aACpB,IAAO,KAAK,OAAO,IAAI,CAAA;AAE7B,MAAI,KACF,EAAK,aAAA,GAEP,KAAK,cAAc,MAEnB,KAAK,KAAK,mBAAmB,EAAE,UAAU,EAAA,CAAc;AAAA;;EAQ7D,YAA4B;AAC1B,SAAK,YAAY,EAAA,GAGb,KAAK,wBACP,KAAK,oBAAoB,QAAA,GACzB,KAAK,sBAAsB,OAGzB,KAAK,sBACP,KAAK,kBAAkB,QAAA,GACvB,KAAK,oBAAoB,OAG3B,KAAK,mBAAA,GAEL,KAAK,kBAAkB,QAAA;AAAA;EAGzB,YAAY,GAAuB;AACjC,IAAK,IAMC,KAAK,gBAAgB,KAAK,YAAY,KAAK,SAAS,gBACtD,KAAK,YAAY,EAAA,GACjB,KAAK,cAAc,KAAK,SAAS,WAAA,MANnC,KAAK,YAAY,EAAA,GACjB,KAAK,mBAAmB,SAAA,GACxB,KAAK,mBAAmB,KAAA;AAAA;EAS5B,WAAW,GAA+B;AACxC,SAAK,YAAY,CAAA;AAAA;EAOnB,eAAyB;AACvB,SAAK,YAAA;AAAA;EAOP,sBAAwC;AAEtC,QADA,KAAK,kBAAA,GACD,CAAC,KAAK,kBACR,OAAM,IAAI,MAAM,kCAAA;AAElB,WAAO,KAAK;AAAA;EAGd,kBAA0B,GAAyB;AAEjD,QAAI,EAAM,WAAW;AAKrB,UAAI,KAAK,eAAe,kBAAA,GAAqB;AAE3C,cAAM,IAAU,KAAK,cAAc,kBAAA;AAEnC,QADwB,KAAK,kBAAmB,WAAW,EAAQ,MAAM,EAAQ,EAAA,MAE/E,KAAK,mBAAmB,UAAU,EAAQ,MAAM,EAAQ,IAAI,EAAQ,SAAS,QAAA,GAC7E,KAAK,KAAK,UAAU,KAAK,kBAAmB,aAAA,CAAc;AAAA,iBAIvC,KAAK,mBAAmB,aAAA,GAC3B;AAChB,cAAM,IAAY,KAAK,kBAAmB,aAAA;AAC1C,aAAK,mBAAmB,SAAA,GACxB,KAAK,KAAK,YAAY,CAAA;AAAA;;;EAK5B,sBAA8B,GAA0B,GAAyB;AAC/E,QAAI,IAA8C,MAC9C,IAA0C,MAC1C,IAAyC;AAC7C,QAAI,EAAU,SAAS,OACrB,SAAQ,EAAU,MAAlB;AAAA,MACE,KAAK;AACH,QAAA,IAAa,oBAAI,IAAA,GACjB,EAAW,IAAI,EAAU,IAAI,EAAU,QAAQ,IAAA;AAC/C;AAAA,MACF,KAAK;AACH,QAAA,IAAS,oBAAI,IAAA,GACb,EAAO,IAAI,EAAU,IAAI,EAAU,QAAQ,IAAA;AAC3C;AAAA,MACF,KAAK;AACH,QAAA,IAAQ,oBAAI,IAAA,GACZ,EAAM,IAAI,EAAU,IAAI,EAAU,QAAQ,IAAA;AAC1C;AAAA,MACF;AACE;AAAA;;AAGJ,MAAA,IAAa,EAAU,cAAc,MACrC,IAAS,EAAU,UAAU,MAC7B,IAAQ,EAAU,SAAS;AAG7B,QAAI,EACF,YAAW,CAAC,GAAI,CAAA,KAAU,GAAY;AACpC,YAAM,IAAW,KAAK,oBAAoB,IAAI,CAAA;AAC9C,UAAK;AAGL,YAAI;AACF,gBAAM,IAAgB,EAAS,SAAS,eAClC,IAAU,KAAK,gBAAgB,IAAI,CAAA;AACzC,UAAI,IACF,EAAQ,eAAe,CAAA,IAEvB,EAAQ,gBAAgB,CAAA;AAAA,iBAEnB,GAAO;AACd,kBAAQ,KACN,aAAa,IAAW,UAAU,QAAA,gCAClC,CAAA;AAAA;;AAKR,QAAI,EACF,YAAW,CAAC,GAAI,CAAA,KAAU,GAAQ;AAChC,YAAM,IAAW,KAAK,gBAAgB,IAAI,CAAA;AAC1C,MAAK,MAGC,EAAS,SAAS,gBACpB,IACF,KAAK,4BAA4B,eAAe,CAAA,IAEhD,KAAK,4BAA4B,gBAAgB,CAAA;AAAA;AAIvD,QAAI,EACF,YAAW,CAAC,GAAI,CAAA,KAAU,EACxB,CAAI,IACF,KAAK,kBAAkB,oBAAoB,CAAA,IAE3C,KAAK,kBAAkB,qBAAqB,CAAA;AAAA;EAMpD,8BAA4C;AAC1C,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,WAAW,CAAC,KAAK,WACzC,OAAM,IAAI,MAAM,0EAAA;AAIlB,SAAK,oBAAoB,IAAI,GAAA,GAG7B,KAAK,kBAAkB,kBAAA,CAAmB,GAAc,MAAsB;AAE5E,MAAI,KACF,KAAK,sBAAsB,GAAmB,EAAA,GAG5C,KACF,KAAK,sBAAsB,GAAc,EAAA,GAI3C,KAAK,KAAK,mBAAmB;AAAA,QAC3B,cAAc;AAAA,QACd,mBAAmB;AAAA,OACpB;AAAA,QAGH,KAAK,WAAW,iBAAiB,eAAe,KAAK,iBAAA;AAAA;EAOvD,gCAA8C;AAC5C,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,WAAW,CAAC,KAAK,WACzC,OAAM,IAAI,MAAM,2EAAA;AAIlB,SAAK,sBAAsB,IAAI,GAC7B,KAAK,iBACL,KAAK,oBAAoB,KAAK,IAAA,GAC9B,KAAK,SACL,KAAK,UAAA;AAAA;EAIT,wBAAsC;AACpC,SAAK,oBAAoB,IAAI,GAAA,CAAkB,MAAkB;AAC/D,WAAK,KAAK,0BAA0B,EAAE,eAAA,EAAA,CAAe;AAAA,QAGvD,KAAK,2BAAA,CAA4B,MAAkB;AACjD,WAAK,eAAe,EAAE,SACtB,KAAK,eAAe,EAAE,SACtB,KAAK,mBAAmB,eAAe,EAAE,SAAS,EAAE,OAAA;AAAA,OAEtD,KAAK,WAAY,iBAAiB,aAAa,KAAK,wBAAA,GAEpD,KAAK,GAAG,SAAA,CAAU,MAAY;AAC5B,UACE,EAAQ,eAAe,iBACvB,EAAQ,UAAU,SAAS,iBAC3B,EAAQ,SAAS,gBAAgB,MACjC;AACA,cAAM,EAAE,OAAA,GAAO,eAAA,EAAA,IAAkB,EAAQ;AACzC,YAAI,KAAS,GAAe;AAC1B,eAAK,mBAAmB,KAAK,GAAO,GAAe,KAAK,cAAc,KAAK,YAAA;AAC3E;AAAA;;AAGJ,WAAK,mBAAmB,KAAA;AAAA,QAG1B,KAAK,GAAG,WAAA,MAAiB,KAAK,mBAAmB,KAAA,CAAM;AAAA;EAGzD,qBAAmC;AACjC,IAAI,KAAK,4BAA4B,KAAK,eACxC,KAAK,WAAW,oBAAoB,aAAa,KAAK,wBAAA,GACtD,KAAK,2BAA2B,OAE9B,KAAK,sBACP,KAAK,kBAAkB,QAAA,GACvB,KAAK,oBAAoB;AAAA;EAW7B,gBAAgB,GAAmB,GAAmD;AAEpF,QADA,KAAK,kBAAA,GACD,CAAC,KAAK,oBACR,OAAM,IAAI,MAAM,oCAAA;AAElB,QAAI,CAAC,KAAK,SACR,QAAO;AAET,UAAM,IAAY,KAAK,SAAS,aAAa,CAAA;AAC7C,WAAK,IAIE,KAAK,oBAAoB,KAAK,GAAW,CAAA,KAH9C,QAAQ,KAAK,sCAAsC,CAAA,YAAY,GACxD;AAAA;EAaX,WAAW,GAA0B;AACnC,UAAM,IAAe,KAAK;AAK1B,IAHI,MAAiB,QACnB,KAAK,eAAe,CAAA,GAElB,MAAiB,KAGrB,KAAK,cAAc,CAAA;AAAA;EAGrB,eAAe,GAA0B;AAIvC,QAHI,KAAK,gBAAgB,QAGrB,KAAK,gBAAgB,EACvB;AAGF,UAAM,IAAe,KAAK,aACpB,IAAO,KAAK,OAAO,IAAI,CAAA;AAE7B,IAAI,KACF,EAAK,aAAA,GAGP,KAAK,cAAc,MAEnB,KAAK,KAAK,mBAAmB,EAAE,UAAU,EAAA,CAAc;AAAA;EAQzD,gBAAiC;AAC/B,WAAO,KAAK;AAAA;EAWd,cAAc,GAA0B;AAGtC,QAFA,KAAK,kBAAA,GAED,CAAC,KAAK,UACR,OAAM,IAAI,MAAM,6CAAA;AAGlB,QAAI,KAAK,gBAAgB,EACvB;IACS,KAAK,gBAAgB,QAE9B,KAAK,eAAe,KAAK,WAAA,GAI3B,KAAK,cAAc;AACnB,UAAM,IAAO,KAAK,OAAO,IAAI,CAAA;AAE7B,QAAI,GAAM;AACR,MAAA,EAAK,WAAA,GAGL,KAAK,KAAK,iBAAiB,EAAE,UAAA,EAAA,CAAU;AAGvC,YAAM,IAAa,EAAK,cAAA;AACxB,WAAK,KAAK,yBAAyB,EAAE,YAAA,EAAA,CAAY;AAAA;;EAQrD,mBAAiC;AAE/B,SAAK,OAAO,IAAI,SAAS,IAAI,GAAU,IAAA,CAAK,GAC5C,KAAK,OAAO,IAAI,eAAe,IAAI,GAAgB,IAAA,CAAK;AAAA;EAQ1D,cAA4B;AAG1B,QAFA,KAAK,kBAAA,GAED,CAAC,KAAK,SAAU;AAGpB,UAAM,IAAa,KAAK,SAAS,iBAAA,GAC3B,IAAQ,KAAK,SAAS,YAAA,GACtB,IAAS,KAAK,SAAS,aAAA;AAE7B,eAAW,KAAa,EACtB,MAAK,yBAAyB,CAAA;AAEhC,eAAW,KAAS,EAClB,MAAK,qBAAqB,CAAA;AAE5B,eAAW,KAAQ,EACjB,MAAK,oBAAoB,CAAA;AAAA;EAI7B,yBAAiC,GAA4B;AAC3D,QAAI;AAGF,YAAM,IAFU,KAAK,gBAAgB,IAAI,EAAU,IAAA,EAE9B,aAAa,GAAW,KAAK,aAAA;AAGlD,MAAA,EAAK,SAAS,KAAK,EAAoB,EAAU,QAAA,CAAS,GAC1D,EAAK,SAAS,KAAK,EAAoB,EAAU,QAAA,CAAS,GAG1D,EAAK,SAAS,cAAc,EAAU,IACtC,EAAK,SAAS,gBAAgB,EAAU,MAExC,KAAK,OAAQ,IAAI,CAAA,GACjB,KAAK,wBAAwB,EAAU,IAAI,CAAA;AAG3C,iBAAW,KAAS,EAAU,MAAM;AAClC,cAAM,IAAQ,KAAK,SAAU,SAAS,CAAA;AACtC,YAAI,CAAC,KAAS,CAAC,EAAM,OAAQ;AAC7B,cAAM,IAAW,KAAK,gBAAgB,IAAI,EAAM,EAAA;AAChD,QAAK,KACL,KAAK,gBAAgB,mBAAA,EAAqB,oBAAoB,GAAU,EAAM,MAAA;AAAA;aAEzE,GAAO;AACd,YAAM,IAAM;AACZ,cAAQ,KAAK,uCAAuC,EAAU,EAAA,KAAO,EAAI,OAAA,GACzE,KAAK,KAAK,SAAS;AAAA,QAAE,SAAS,+BAA+B,EAAI,OAAA;AAAA,QAAW,OAAO;AAAA,OAAK;AAAA;;EAU5F,wBAAgC,GAAqB,GAAgC;AACnF,SAAK,oBAAoB,IAAI,GAAa,CAAA,GAC1C,EAAS,SAAA,CAAU,MAAQ;AACzB,UAAI,EAAI,YAAY,EAAI,SAAS,SAAS,cAAc;AACtD,cAAM,IAAU,EAAI,SAAS;AAC7B,QAAI,KACF,KAAK,gBAAgB,IAAI,GAAS,CAAA;AAAA;;;EAM1C,yBAAiC,GAAkB;AACjD,UAAM,IAAQ,KAAK,oBAAoB,IAAI,CAAA;AAC3C,IAAK,MAIL,KAAK,OAAQ,OAAO,CAAA,GAEpB,EAAM,SAAA,CAAU,MAAQ;AACtB,MAAI,EAAI,YAAY,EAAI,SAAS,SAAS,eACxC,KAAK,qBAAqB,EAAI,SAAS,OAAA,IAC9B,aAAe,EAAM,SAC1B,EAAI,YACN,EAAI,SAAS,QAAA,GAEX,EAAI,aACF,MAAM,QAAQ,EAAI,QAAA,IACpB,EAAI,SAAS,QAAA,CAAS,MAAQ,EAAI,QAAA,CAAS,IAE3C,EAAI,SAAS,QAAA;AAAA,QAKrB,KAAK,oBAAoB,OAAO,CAAA;AAAA;EAUlC,qBAA6B,GAAoB;AAE/C,QAAI,EAAM,SAAS,EAAU,IAAK;AAGlC,UAAM,IAAQ,KAAK,4BAA4B,aAAa,CAAA;AAG5D,IAAA,EAAM,SAAS,KAAK,EAAoB,EAAM,YAAY,KAAK,QAAA,CAAU,CAAC,GAE1E,KAAK,OAAQ,IAAI,CAAA,GACjB,KAAK,gBAAgB,IAAI,EAAM,IAAI,CAAA;AAAA;EAGrC,qBAA6B,GAAkB;AAC7C,UAAM,IAAQ,KAAK,gBAAgB,IAAI,CAAA;AACvC,IAAK,MACL,GAAO,SAAA,CAAU,MAAQ;AACvB,MAAI,aAAe,EAAM,SACnB,EAAI,YACN,EAAI,SAAS,QAAA,GAEX,EAAI,aACF,MAAM,QAAQ,EAAI,QAAA,IACpB,EAAI,SAAS,QAAA,CAAS,MAAQ,EAAI,QAAA,CAAS,IAE3C,EAAI,SAAS,QAAA;AAAA,QAKrB,KAAK,OAAQ,OAAO,CAAA,GACpB,KAAK,gBAAgB,OAAO,CAAA;AAAA;EAG9B,kBAAkB,GAA8B,GAAiD;AAC/F,UAAM,IAAiB,KAAK,cAAc,sBAAsB,GAAe,CAAA;AAE/E,gBAAK,qBAAqB,CAAA,GACnB;AAAA;EAWT,UACE,GACA,GACA,IAA6B,MACa;AAE1C,UAAM,IAAS,KAAK,cAAc,cAAc,GAAQ,GAAe,CAAA;AAEvE,SAAK,kBAAkB,WAAW,CAAA,GAE7B,KACH,KAAK,qBAAqB,EAAO,cAAA;AAInC,eAAW,KAAQ,EAAO,MACxB,MAAK,kBAAkB,mBAAmB,CAAA;AAG5C,WAAO;AAAA;EAOT,qBAAqB,GAAe;AAClC,UAAM,IAAS,KAAK,cAAc,yBAAyB,CAAA;AAC3D,QAAK,GAGL;AAAA,UAFA,KAAK,qBAAqB,CAAA,GAC1B,KAAK,gBAAgB,OAAO,CAAA,GACxB,EAAO,cAAc;AACvB,mBAAW,KAAU,EAAO,aAC1B,MAAK,oBAAoB,CAAA;AAE3B,aAAK,0BAAA;AAAA;AAEP,UAAI,EAAO,YACT,YAAW,KAAU,EAAO,YAC1B,MAAK,oBAAoB,CAAA;AAG7B,MAAI,EAAO,YACT,KAAK,oBAAoB,EAAO,OAAA,GAChC,KAAK,0BAAA;AAAA;AAAA;EAIT,oBAA4B,GAAkB;AAC5C,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,UAAU;AAClC,cAAQ,KAAK,sBAAsB,EAAK,EAAA,oCAAG;AAC3C;AAAA;AAEF,QAAI;AAEF,WAAK,kBAAkB,mBAAmB,CAAA;AAAA,aACnC,GAAO;AACd,YAAM,IAAM;AACZ,cAAQ,KAAK,mCAAmC,EAAK,EAAA,KAAO,EAAI,OAAA;AAAA;;EASpE,QAAQ,GAAqB,GAA2B;AACtD,UAAM,IAAO,KAAK,cAAc,YAAY,GAAe,CAAA;AAC3D,gBAAK,kBAAkB,mBAAmB,CAAA,GACnC;AAAA;EAaT,aACE,GACA,GACA,GACA,GACA,GACW;AACX,QAAI,CAAC,GAAU;AACb,YAAM,IAAU,KAAK,gBAAgB,IAAI,CAAA,GACnC,IAAkB,IAAU,EAAQ,gBAAA,IAAoB;AAC9D,MAAA,IAAW,IAAI,EAAM,MAAM,GAAG,GAAiB,CAAA;AAAA;AAIjD,UAAM,IAAY,KAAK,cAAc,iBACnC,GACA,GACA,GACA,GACA,CAAA;AAGF,gBAAK,yBAAyB,CAAA,GACvB;AAAA;EAST,oBAAoB,GAAmB,GAAgC;AACrE,UAAM,IAAY,KAAK,cAAc,wBAAwB,GAAa,CAAA;AAC1E,QAAI,CAAC,EAAW;AAEhB,UAAM,IAAW,KAAK,oBAAoB,IAAI,CAAA;AAC9C,IAAK,MAEW,KAAK,gBAAgB,IAAI,EAAU,IAAA,EAC3C,wBAAwB,GAAU,EAAU,MAAA,GAGpD,KAAK,kBAAkB,wBAAwB,EAAU,EAAA;AAAA;EAY3D,qBAAqB,GAA4B;AAC/C,UAAM,IAAS,KAAK,cAAc,qBAAqB,CAAA;AACvD,QAAI,CAAC,EAAO,WACV,QAAO;AAET,UAAM,IAAW,KAAK,oBAAoB,IAAI,CAAA;AAC9C,WAAK,KAEW,KAAK,gBAAgB,IAAI,EAAO,UAAU,IAAA,EAClD,wBAAwB,GAAU,EAAO,UAAU,MAAA,GACpD,MAJe;AAAA;EAYxB,gBAAgB,GAAyB;AAEvC,UAAM,IAAS,KAAK,cAAc,oBAAoB,CAAA;AAEtD,eAAW,KAAU,EAAO,aAC1B,MAAK,oBAAoB,CAAA;AAG3B,SAAK,yBAAyB,CAAA,GAC9B,KAAK,0BAAA;AAAA;EAQP,sBAAsB,GAAe,GAA0C;AAC7E,UAAM,IAAW,KAAK,gBAAgB,IAAI,CAAA;AAC1C,IAAK,MACD,EAAS,SAAS,qBAEtB,KAAK,cAAc,wBAAwB,GAAS,CAAA,GAEhD,EAAS,SAAS,cACpB,KAAK,gBAAgB,mBAAA,EAAqB,oBAAoB,GAAU,KAAc,IAAA,IAEtF,KAAK,4BAA4B,iBAAiB,GAAU,KAAc,IAAA;AAAA;EAQ9E,WAAW,GAAc;AACvB,SAAK,cAAc,eAAe,CAAA,GAClC,KAAK,oBAAoB,CAAA,GACzB,KAAK,0BAAA;AAAA;EAMP,4BAA4B;AAE1B,QADA,KAAK,kBAAA,GACD,EAAC,KAAK,YACN,KAAK,cAAc,0BAAA,GAA6B;AAElD,WAAK,gBAAgB,KAAK,KAAK,KAAK,SAAS,SAAS,OAAO,CAAA,GAEzD,KAAK,SACP,KAAK,OAAQ,OAAO,KAAK,IAAA,GACzB,KAAK,KAAK,SAAS,QAAA;AAErB,YAAM,IAAU,KAAK,YAAY,EAAA;AACjC,WAAK,OAAO,EACV,KAAK,SAAS,SAAS,MACvB,KAAK,SAAS,SAAS,WACvB,EAAQ,iBACR,EAAQ,SAAA,GAEV,KAAK,OAAQ,IAAI,KAAK,IAAA;AAAA;;EAQ1B,eAA4B;AAC1B,QAAI,GAAC,KAAK,YAAY,CAAC,KAAK,WAAW,CAAC,KAAK;AAC7C,UAAI;AACF,aAAK,cAAc,kBAAA;AAAA,eACZ,GAAO;AACd,gBAAQ,KAAK,CAAA;AAAA;;EAIjB,oBAA4B,GAAkB;AAC5C,IAAI,KAAK,eAAe,IAAI,CAAA,KAE1B,KAAK,kBAAkB,WAAW,CAAA;AAAA;EAItC,oBAAoC;AAElC,eAAW,KAAM,MAAM,KAAK,KAAK,eAAe,KAAA,CAAM,EACpD,MAAK,oBAAoB,CAAA;AAG3B,eAAW,KAAM,MAAM,KAAK,KAAK,gBAAgB,KAAA,CAAM,EACrD,MAAK,qBAAqB,CAAA;AAG5B,eAAW,KAAM,MAAM,KAAK,KAAK,oBAAoB,KAAA,CAAM,EACzD,MAAK,yBAAyB,CAAA;AAGhC,IAAI,KAAK,SACP,KAAK,OAAQ,OAAO,KAAK,IAAA,GACzB,KAAK,KAAK,QAAA,GACV,KAAK,QAAQ;AAAA;GC33BN,KAAb,cAA6C,GAA0B;AAAA,EACrE,UAAwC;AAAA,EACxC;AAAA,EAGA,oBAAqD;AAAA,EAGrD,YAAoB;AAAA,EACpB,aAA8B;AAAA,EAC9B,kBAAkC,EAAiB;AAAA,EACnD,oBAA2C;AAAA,EAC3C,gBAA8D;AAAA,EAU9D,YACE,GACA,GACA,GACA;AAEA,QADA,MAAM,GAAiB,CAAA,GACnB,CAAC,EACH,OAAM,IAAI,UAAU,8BAAA;AAEtB,SAAK,oBAAoB;AAAA;EAO3B,IAAI,YAAqB;AACvB,WAAO,KAAK;AAAA;EAOd,IAAI,eAAuB;AACzB,WAAO,KAAK;AAAA;EAUd,IAAI,aAAa,GAAe;AAC9B,QAAI,IAAQ,MAAM,IAAQ,IACxB,OAAM,IAAI,WAAW,6CAAA;AAEvB,SAAK,kBAAkB,GAGnB,KAAK,eACP,KAAK,MAAA,GACL,KAAK,KAAA;AAAA;EAQT,IAAI,kBAA0B;AAC5B,WAAO,KAAK,MAAM,MAAO,KAAK,eAAA;AAAA;EAWhC,IAAI,gBAAgB,GAAa;AAC/B,UAAM,IAAgB,KAAK,iBACrB,IAAa,KAAK,IAAI,EAAiB,SAAS,KAAK,IAAI,EAAiB,SAAS,CAAA,CAAI;AAG7F,IAAI,MAAe,MAKnB,KAAK,kBAAkB,KAAK,MAAM,MAAO,CAAA,GAGzC,KAAK,2BAA2B,CAAA,GAG5B,KAAK,sBACP,KAAK,kBAAkB,iBAAiB,IAItC,KAAK,eACP,KAAK,MAAA,GACL,KAAK,KAAA,IAIP,KAAK,KAAK,0BAA0B;AAAA,MAClC,eAAA;AAAA,MACA,UAAU;AAAA,KACX;AAAA;EAMH,IAAI,qBAA6B;AAC/B,WAAO,EAAiB;AAAA;EAM1B,IAAI,qBAA6B;AAC/B,WAAO,EAAiB;AAAA;EAU1B,iBAAiB,GAAsC;AACrD,UAAM,IAAY,KAAK,KAAM,IAAuB,KAAK,kBAAmB,GAAA;AAC5E,WAAO,KAAK,IAAI,GAAG,CAAA;AAAA;EAQrB,uBAA+B,GAAqC;AAClE,UAAM,IAAQ,SAAS,EAAO,IAAI,oBAAA,KAAyB,IAAI,EAAA;AAC/D,WAAI,MAAM,CAAA,KAAU,IAAQ,IACnB,GAAoB,0BAEtB;AAAA;EAOT,IAAI,cAAsB;AACxB,WAAO,KAAK,SAAS,eAAA,KAAoB;AAAA;EAS3C,aAAuB,GAA6B;AAClD,IAAI,MACE,EAAQ,oBAAiB,KAAK,kBAAkB,EAAQ,kBACxD,OAAO,EAAQ,sBAAsB,cACvC,KAAK,YAAY,EAAQ,sBAG7B,KAAK,gBAAgB,KAAK,aAAa,KAAK,IAAA,GAC5C,KAAK,WAAY,iBAAiB,SAAS,KAAK,aAAA,GAG3C,KAAK,oBACR,KAAK,UAAU,EAAA;AAAA;EAInB,YAAsB;AACpB,SAAK,KAAK,SAAS,EAAE,gBAAgB,aAAA,CAAc;AAAA;EAMrD,YAA4B;AAE1B,IAAI,KAAK,cACP,KAAK,MAAA,GAIH,KAAK,iBAAiB,KAAK,eAC7B,KAAK,WAAW,oBAAoB,SAAS,KAAK,aAAA,GAClD,KAAK,gBAAgB,OAIvB,KAAK,UAAU;AAAA;EAGjB,YAAY,GAAuB;AACjC,QAAI,CAAC;AACH,WAAK,KAAA,GACL,KAAK,UAAU,MACf,KAAK,8BAAA,GACL,KAAK,gBAAgB,oBAAoB,IAAA,GACzC,KAAK,oBAAoB;AAAA,SACpB;AACL,UAAI,CAAC,KAAK,SAAU;AAEpB,WAAK,UAAU,IAAI,EAAc,KAAK,UAAU,KAAK,iBAAA,GAErD,KAAK,oBAAoB;AAAA,QACvB,gBAAgB,KAAK;AAAA,QACrB,kBAAkB;AAAA,SAEpB,KAAK,gBAAgB,oBAAoB,KAAK,iBAAA,GAE9C,KAAK,YAAA,GAED,KAAK,aAAW,KAAK,KAAA;AAAA;;EAI7B,WAAW,GAA+B;AAExC,QADA,KAAK,kBAAA,GACD,MAAY,KAAK,UAUrB;AAAA,UAPI,KAAK,cACP,KAAK,KAAA,GAEP,KAAK,UAAU,MAIX,KAAK,qBAAqB;AAG5B,YAFA,KAAK,WAAW,GAChB,KAAK,kBAAkB,WAAW,CAAA,GAC9B,GAAS;AAEX,cADA,KAAK,gBAAgB,KAAK,KAAK,EAAQ,SAAS,OAAO,CAAA,GACnD,CAAC,KAAK,QAAS;AAEnB,eAAK,UAAU,IAAI,EAAc,GAAS,KAAK,iBAAA,GAE/C,KAAK,oBAAoB;AAAA,YACvB,gBAAgB,KAAK;AAAA,YACrB,kBAAkB;AAAA,aAEpB,KAAK,gBAAgB,oBAAoB,KAAK,iBAAA,GAE9C,KAAK,YAAA,GAED,KAAK,aAAW,KAAK,KAAA;AAAA;AAE3B;AAAA;AAIF,WAAK,YAAY,CAAA;AAAA;AAAA;EAOnB,eAAyB;AACvB,IAAK,KAAK,aACV,KAAK,UAAU,IAAI,EAAc,KAAK,UAAU,KAAK,iBAAA,GAChD,KAAK,uBAER,KAAK,UAAU,EAAA;AAAA;EAYnB,OAAa;AACX,QAAI,CAAC,KAAK,SAAS;AACjB,cAAQ,KAAK,uCAAA;AACb;AAAA;AAGF,IAAI,KAAK,eAGT,KAAK,aAAa,IACd,KAAK,sBACP,KAAK,kBAAkB,mBAAmB,YAG5C,KAAK,iCAAA,GACL,KAAK,KAAK,oBAAoB,EAAE,MAAM,KAAK,QAAQ,eAAA,EAAgB,CAAE,GAGrE,KAAK,oBAAoB,OAAO,YAAA,MAAkB;AAChD,WAAK,aAAA;AAAA,OACJ,KAAK,eAAA;AAAA;EASV,QAAc;AACZ,IAAK,KAAK,eAIV,KAAK,aAAa,IACd,KAAK,sBACP,KAAK,kBAAkB,mBAAmB,WAIxC,KAAK,sBAAsB,SAC7B,OAAO,cAAc,KAAK,iBAAA,GAC1B,KAAK,oBAAoB,OAG3B,KAAK,KAAK,oBAAoB,EAAE,MAAM,KAAK,SAAS,eAAA,KAAoB,EAAA,CAAG;AAAA;EAW7E,OAAa;AACX,QAAI,CAAC,KAAK,SAAS;AACjB,cAAQ,KAAK,uCAAA;AACb;AAAA;AAIF,IAAI,KAAK,cACP,KAAK,MAAA;AAIP,UAAM,IAAS,KAAK,aAAA;AAEpB,SAAK,KAAK,qBAAqB;AAAA,MAAE,MAAM,KAAK,QAAQ,eAAA;AAAA,MAAkB,QAAA;AAAA,KAAQ;AAAA;EAWhF,OAAa;AACX,QAAI,CAAC,KAAK,SAAS;AACjB,cAAQ,KAAK,uCAAA;AACb;AAAA;AAGF,IAAI,KAAK,cACP,KAAK,MAAA,GAEH,KAAK,sBACP,KAAK,kBAAkB,mBAAmB,YAE5C,KAAK,QAAQ,MAAA,GAEb,KAAK,iCAAA;AACL,UAAM,IAAS,KAAK,QAAQ,eAAA;AAE5B,SAAK,KAAK,qBAAqB,EAAE,MAAM,EAAA,CAAQ;AAAA;EAOjD,eAAgC;AAC9B,QAAI,CAAC,KAAK,QACR,QAAO;AAIT,UAAM,IAAS,KAAK,QAAQ,KAAA,GAGtB,IAAQ,KAAK,QAAQ,aAAa,iBAAA;AAGxC,gBAAK,KAAK,kBAAkB;AAAA,MAAE,MAAM,KAAK,QAAQ,eAAA;AAAA,MAAkB,OAAA;AAAA,KAAO,GAG1E,KAAK,uBAAuB,CAAA,GAC5B,KAAK,kBAAkB,CAAA,GACvB,KAAK,mBAAmB,CAAA,GAEjB;AAAA;EAOT,uBAA+B,GAAkD;AAC/E,QAAK,KAAK;AAGV,iBAAW,KAAe,EAAM,YAAY;AAC1C,cAAM,IAAW,KAAK,oBAAoB,IAAI,CAAA;AAC9C,YAAI,CAAC,EAAU;AAGf,cAAM,IAAY,KAAK,UAAU,aAAa,CAAA;AAC9C,YAAI,CAAC,EAAW;AAEhB,cAAM,IAAQ,KAAK,QAAQ,kBAAkB,CAAA;AAC7C,QAAK,KAGW,KAAK,gBAAgB,IAAI,EAAU,IAAA,EAC3C,gBAAgB,GAAU,CAAA;AAAA;;EAUtC,iBAAiB,GAAqB;AACpC,QAAI,GAAC,KAAK,qBAAqB,KAAK,kBAAkB,qBAAqB;AAE3E,iBAAW,KAAY,KAAK,oBAAoB,OAAA,GAAU;AACxD,cAAM,IAAQ,EAAS,SAAS;AAChC,QAAK,KACL,EAAM,OAAO,CAAA;AAAA;;EAQjB,2BAAmC,GAAsB;AACvD,UAAM,IAAc,KAAK,mBAAmB,kBAAkB;AAE9D,eAAW,KAAY,KAAK,oBAAoB,OAAA,GAAU;AAExD,UAAI,CADU,EAAS,SAAS,MACpB;AAGZ,YAAM,IAAS,EAAS,SAAS;AACjC,MAAK,MAGL,EAAO,YAAY,IAAS;AAAA;;EAQhC,kBAA0B,GAA6C;AACrE,QAAK,KAAK;AAGV,iBAAW,KAAU,EAAM,OAAO;AAEhC,cAAM,IAAY,KAAK,QAAQ,aAAa,CAAA;AAC5C,YAAI,CAAC,EAAW;AAIhB,YAAI;AACJ,QAAI,EAAU,cAAc,EAAU,aACpC,IAAgB,OACP,EAAU,aACnB,IAAgB,YACP,EAAU,aACnB,IAAgB,YAEhB,IAAgB,QAIlB,KAAK,kBAAkB,qBAAqB,GAAQ,CAAA;AAAA;;EASxD,mBAA2B,GAA8C;AACvE,QAAK,KAAK;AAGV,iBAAW,KAAW,EAAM,QAAQ;AAElC,cAAM,IAAa,KAAK,QAAQ,cAAc,CAAA;AAC9C,YAAI,CAAC,EAAY;AAGjB,cAAM,IAAc,KAAK,gBAAgB,IAAI,CAAA;AAC7C,YAAI,CAAC,EAAa;AAElB,QAAA,EAAY;AAIZ,YAAI;AACJ,QAAI,EAAW,cAAc,EAAW,cACtC,EAAY,SAAS,kBAAkB,MACvC,IAAgB,YACP,EAAW,cACpB,EAAY,SAAS,kBAAkB,WACvC,IAAgB,OACP,EAAW,cACpB,EAAY,SAAS,kBAAkB,WACvC,IAAgB,YAEhB,EAAY,SAAS,kBAAkB,QAIzC,EAAY,SAAA,CAAU,MAAQ;AAC5B,cAAI,aAAe,EAAM,QAAQ,EAAI,UAAU;AAC7C,kBAAM,IAAW,EAAI;AACrB,YAAI,EAAS,aACX,EAAS,SAAS,OAAO,CAAA,GACzB,EAAS,oBAAoB,MAAkB,IAAW,IAAI;AAAA;;;;EAUxE,gCAAsC;AACpC,eAAW,KAAU,KAAK,eAAe,KAAA,EACvC,MAAK,kBAAkB,qBAAqB,GAAQ,MAAA;AAEtD,eAAW,KAAW,KAAK,gBAAgB,KAAA,GAAQ;AACjD,YAAM,IAAc,KAAK,gBAAgB,IAAI,CAAA;AAC7C,MAAK,MACL,EAAY,SAAS,kBAAkB,QACvC,EAAY,SAAA,CAAU,MAAQ;AAC5B,YAAI,aAAe,EAAM,QAAQ,EAAI,UAAU;AAC7C,gBAAM,IAAW,EAAI;AACrB,UAAI,EAAS,aACX,EAAS,SAAS,OAAO,CAAA,GACzB,EAAS,oBAAoB;AAAA;;;AAKrC,eAAW,KAAe,KAAK,oBAAoB,KAAA,GAAQ;AACzD,YAAM,IAAkB,KAAK,oBAAoB,IAAI,CAAA;AACrD,UAAI,CAAC,EAAiB;AAEtB,YAAM,IAAY,KAAK,UAAU,aAAa,CAAA;AAC9C,MAAK,KACW,KAAK,gBAAgB,IAAI,EAAU,IAAA,EAC3C,gBAAgB,GAAiB,IAAA;AAAA;;EAS7C,aAAqB,GAAyB;AAG5C,QAFI,CAAC,KAAK,WAEN,EAAM,WAAW,EAAG;AACxB,UAAM,IAAiB,KAAK,kBAAA;AAC5B,IAAK,MACD,EAAM,WAAW,EAAM,UACzB,KAAK,iBAAiB,CAAA,IAEtB,KAAK,oBAAoB,CAAA;AAAA;EAS7B,oBAA4B,GAAgC;AAE1D,QADI,CAAC,KAAK,WACN,EAAe,SAAS,OAAQ;AACpC,QAAI,IAAiB;AACrB,QAAI,EAAe,SAAS,YAC1B,CAAA,IAAiB,EAAe,SAAS;AAAA,aAChC,EAAe,SAAS,SAAS;AAE1C,YAAM,IAAQ,KAAK,UAAU,SAAS,EAAe,EAAA;AAErD,UADI,CAAC,KACD,CAAC,EAAM,UAAW;AACtB,MAAA,IAAiB,KAAK,oBAAoB,IAAI,EAAM,SAAA;AAAA;AAEtD,QAAI,CAAC,EAAgB;AACrB,UAAM,IAAgB,EAAe,SAAS,eACxC,IAAc,EAAe,SAAS;AAC5C,YAAQ,GAAR;AAAA,MACE,KAAK,EAAc;AAAA,MACnB,KAAK,EAAc,QAAQ;AAEzB,cAAM,IAAY,KAAK,UAAU,aAAa,CAAA;AAC9C,YAAI,CAAC,EAAW;AAGhB,cAAM,IAAqB,KAAK,uBAAuB,EAAU,MAAA,GAC3D,IAAY,KAAK,iBAAiB,CAAA,GAElC,IAAwB;AAAA,UAC5B,MAAM;AAAA,UACN,UAAU;AAAA,UACV,iBAAiB,KAAK,QAAQ,eAAA;AAAA,UAC9B,YAAY,oBAAI,IAAoB,CAAC,CAAC,aAAa,OAAO,CAAA,CAAU,CAAC,CAAC;AAAA;AAGxE,aAAK,QAAQ,cAAc,CAAA,GAC3B,KAAK,KAAK,yBAAyB,CAAA;AACnC;AAAA;MAEF;AACE;AAAA;;EAIN,iBAAyB,GAAiC;AAExD,YAAQ,KAAK,qCAAA;AAAA;EAWf,cAA4B;AAG1B,QAFA,KAAK,kBAAA,GAED,EAAC,KAAK,UAKV;AAAA,UAAI,CAAC,KAAK,qBAAqB;AAE7B,cAAM,IAAa,KAAK,SAAS,iBAAA,GAC3B,IAAQ,KAAK,SAAS,YAAA,GACtB,IAAS,KAAK,SAAS,aAAA;AAE7B,mBAAW,KAAa,EACtB,MAAK,yBAAyB,CAAA;AAEhC,mBAAW,KAAS,EAClB,MAAK,qBAAqB,CAAA;AAE5B,mBAAW,KAAQ,EACjB,MAAK,oBAAoB,CAAA;AAAA;AAK7B,WAAK,iCAAA;AAAA;AAAA;EAOP,mCAAiD;AAC/C,QAAI,CAAC,KAAK,YAAY,CAAC,KAAK,QAAS;AAErC,UAAM,IAAa,KAAK,SAAS,iBAAA,GAC3B,IAAQ,KAAK,SAAS,YAAA,GACtB,IAAS,KAAK,SAAS,aAAA;AAE7B,SAAK,uBAAuB,EAAE,YAAY,IAAI,IAAI,EAAW,IAAA,CAAK,MAAM,EAAE,EAAA,CAAG,EAAC,CAAE,GAChF,KAAK,mBAAmB,EAAE,QAAQ,IAAI,IAAI,EAAO,IAAA,CAAK,MAAM,EAAE,EAAA,CAAG,EAAC,CAAE,GACpE,KAAK,kBAAkB,EAAE,OAAO,IAAI,IAAI,EAAM,IAAA,CAAK,MAAM,EAAE,EAAA,CAAG,EAAC,CAAE;AAAA;EAGnE,yBAAiC,GAA4B;AAC3D,QAAI;AAGF,YAAM,IAFU,KAAK,gBAAgB,IAAI,EAAU,IAAA,EAE9B,aAAa,GAAW,KAAK,aAAA;AAGlD,MAAA,EAAK,SAAS,KAAK,EAAoB,EAAU,QAAA,CAAS,GAC1D,EAAK,SAAS,KAAK,EAAoB,EAAU,QAAA,CAAS,GAG1D,EAAK,SAAS,cAAc,EAAU,IACtC,EAAK,SAAS,gBAAgB,EAAU,MAExC,KAAK,OAAQ,IAAI,CAAA,GACjB,KAAK,wBAAwB,EAAU,IAAI,CAAA;AAG3C,iBAAW,KAAS,EAAU,MAAM;AAClC,cAAM,IAAQ,KAAK,SAAU,SAAS,CAAA;AACtC,YAAI,CAAC,KAAS,CAAC,EAAM,OAAQ;AAC7B,cAAM,IAAW,KAAK,gBAAgB,IAAI,EAAM,EAAA;AAChD,QAAK,KACL,KAAK,gBAAgB,mBAAA,EAAqB,oBAAoB,GAAU,EAAM,MAAA;AAAA;aAEzE,GAAO;AACd,YAAM,IAAM;AACZ,cAAQ,KAAK,uCAAuC,EAAU,EAAA,KAAO,EAAI,OAAA,GACzE,KAAK,KAAK,SAAS;AAAA,QAAE,SAAS,+BAA+B,EAAI,OAAA;AAAA,QAAW,OAAO;AAAA,OAAK;AAAA;;EAU5F,wBAAgC,GAAqB,GAAgC;AACnF,SAAK,oBAAoB,IAAI,GAAa,CAAA,GAC1C,EAAS,SAAA,CAAU,MAAQ;AACzB,UAAI,EAAI,YAAY,EAAI,SAAS,SAAS,cAAc;AACtD,cAAM,IAAU,EAAI,SAAS;AAC7B,QAAI,KACF,KAAK,gBAAgB,IAAI,GAAS,CAAA;AAAA;;;EAa1C,qBAA6B,GAAoB;AAE/C,QAAI,EAAM,SAAS,EAAU,IAAK;AAGlC,UAAM,IAAQ,KAAK,4BAA4B,aAAa,CAAA;AAG5D,IAAA,EAAM,SAAS,KAAK,EAAoB,EAAM,YAAY,KAAK,QAAA,CAAU,CAAC,GAE1E,KAAK,OAAQ,IAAI,CAAA,GACjB,KAAK,gBAAgB,IAAI,EAAM,IAAI,CAAA;AAAA;EAGrC,oBAA4B,GAAkB;AAC5C,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,UAAU;AAClC,cAAQ,KAAK,sBAAsB,EAAK,EAAA,oCAAG;AAC3C;AAAA;AAEF,QAAI;AAEF,WAAK,kBAAkB,mBAAmB,CAAA;AAAA,aACnC,GAAO;AACd,YAAM,IAAM;AACZ,cAAQ,KAAK,mCAAmC,EAAK,EAAA,KAAO,EAAI,OAAA;AAAA;;EAIpE,yBAAiC,GAAkB;AACjD,UAAM,IAAQ,KAAK,oBAAoB,IAAI,CAAA;AAC3C,QAAI,CAAC,EACH;AAIF,UAAM,IAAQ,EAAM,SAAS;AAC7B,IAAI,MACF,EAAM,cAAA,GACN,EAAM,YAAY,CAAA,IAGpB,KAAK,OAAQ,OAAO,CAAA,GAEpB,EAAM,SAAA,CAAU,MAAQ;AACtB,MAAI,EAAI,YAAY,EAAI,SAAS,SAAS,eACxC,KAAK,qBAAqB,EAAI,SAAS,OAAA,IAC9B,aAAe,EAAM,SAC1B,EAAI,YACN,EAAI,SAAS,QAAA,GAEX,EAAI,aACF,MAAM,QAAQ,EAAI,QAAA,IACpB,EAAI,SAAS,QAAA,CAAS,MAAQ,EAAI,QAAA,CAAS,IAE3C,EAAI,SAAS,QAAA;AAAA,QAKrB,KAAK,oBAAoB,OAAO,CAAA;AAAA;EAGlC,qBAA6B,GAAkB;AAC7C,UAAM,IAAQ,KAAK,gBAAgB,IAAI,CAAA;AACvC,IAAK,MAEL,GAAO,SAAA,CAAU,MAAQ;AACvB,MAAI,aAAe,EAAM,SACnB,EAAI,YACN,EAAI,SAAS,QAAA,GAEX,EAAI,aACF,MAAM,QAAQ,EAAI,QAAA,IACpB,EAAI,SAAS,QAAA,CAAS,MAAQ,EAAI,QAAA,CAAS,IAE3C,EAAI,SAAS,QAAA;AAAA,QAKrB,KAAK,OAAQ,OAAO,CAAA,GACpB,KAAK,gBAAgB,OAAO,CAAA;AAAA;EAG9B,oBAA4B,GAAkB;AAC5C,IAAI,KAAK,eAAe,IAAI,CAAA,KAE1B,KAAK,kBAAkB,WAAW,CAAA;AAAA;EAItC,oBAAoC;AAElC,eAAW,KAAM,MAAM,KAAK,KAAK,eAAe,KAAA,CAAM,EACpD,MAAK,oBAAoB,CAAA;AAG3B,eAAW,KAAM,MAAM,KAAK,KAAK,gBAAgB,KAAA,CAAM,EACrD,MAAK,qBAAqB,CAAA;AAG5B,eAAW,KAAM,MAAM,KAAK,KAAK,oBAAoB,KAAA,CAAM,EACzD,MAAK,yBAAyB,CAAA;AAAA;GCx1BvB,KAAb,cAAmC,GAAoC;AAAA,EAErE;AAAA,EACA;AAAA,EAGA,mBAAmD;AAAA,EACnD,aAAyC;AAAA,EAGzC,kBAAoD;AAAA,EACpD,wBAAgE;AAAA,EAGhE,QAA4B;AAAA,EAC5B,eAAgC;AAAA,EAChC,WAAyC;AAAA,EACzC,YAA6B;AAAA,EAG7B,yBAAsD;AAAA,EACtD,+BAA4D;AAAA,EAS5D,YAAY,GAAmC,GAAoC;AAGjF,QAFA,MAAA,GAEI,CAAC,EACH,OAAM,IAAI,UAAU,6BAAA;AAEtB,QAAI,CAAC,EACH,OAAM,IAAI,UAAU,8BAAA;AAGtB,SAAK,mBAAmB,GACxB,KAAK,oBAAoB;AAAA;EAU3B,IAAI,gBAAyB;AAC3B,WAAO,KAAK;AAAA;EAMd,IAAI,aAAsB;AACxB,WAAO,KAAK;AAAA;EAYd,WAAW,GAAwB,GAA+B;AAChE,QAAI,KAAK,aACP,OAAM,IAAI,MAAM,sCAAA;AAElB,QAAI,KAAK,UACP,OAAM,IAAI,MAAM,iCAAA;AAElB,QAAI,CAAC,KAAa,EAAE,aAAqB,aACvC,OAAM,IAAI,UAAU,uCAAA;AAGtB,IAAA,IAAU,GAAc,CAAA,GACxB,KAAK,WAAW,GAEhB,KAAK,aAAa,GAGlB,KAAK,mBAAmB,KAAK,uBAAuB,GAAW,CAAA,GAG/D,KAAK,kBAAkB,IAAI,GAAkB,KAAK,kBAAkB,KAAK,gBAAA,GACzE,KAAK,wBAAwB,IAAI,GAC/B,KAAK,kBACL,KAAK,mBACL,KAAK,gBAAA,GAGP,KAAK,gBAAgB,WAAW,GAAW,EAAQ,iBAAA,GACnD,KAAK,sBAAsB,WAAW,GAAW,EAAQ,iBAAA,GAGzD,KAAK,sBAAA,GAGL,KAAK,QAAQ,GAAS,eAAe,QACjC,KAAK,UAAU,SACjB,KAAK,gBAAgB,UAAU,EAAA,IAE/B,KAAK,sBAAsB,UAAU,EAAA,GAGvC,KAAK,eAAe,IAGpB,KAAK,KAAK,SAAS,EAAE,gBAAgB,SAAA,CAAU;AAC/C,UAAM,IAAc,KAAK;AACzB,SAAK,KAAK,eAAe,EAAE,MAAM,EAAA,CAAa;AAAA;EAMhD,uBAA+B,GAAwB,GAAyC;AAC9F,UAAM,IAAoB,EAAQ,mBAE5B,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,aAAa,IAAI,EAAM,MAAM,EAAkB,eAAA;AAErD,UAAM,IAAO,EACX,IACA,IACA,EAAkB,iBAClB,EAAkB,SAAA;AAEpB,IAAA,EAAM,IAAI,CAAA,GACV,GAAiB,CAAA;AAIjB,UAAM,IAAS,GADA,EAAU,cAAc,EAAU,gBAAgB,CAAA;AAEjE,IAAA,EAAO,OAAO,IAAI,CAAA,GAClB,EAAO,OAAO,OAAO,CAAA,GACrB,EAAO,OAAO,OAAO,CAAA;AAGrB,UAAM,IAAc,GAAkB,GAAQ,GAAW,EAAkB,WAAA,GAGrE,IAAe,IAAI,GAAa,GAAO,CAAA,GACvC,IAA8B,IAAI,GAAA,GAGlC,IAAqB,oBAAI,IAAA,GACzB,IAAiB,oBAAI,IAAA,GACrB,IAAgB,oBAAI,IAAA,GAEpB,IAAoB,IAAI,GAAkB,GAAoB,CAAA;AACpE,WAAA,EAAkB,aAAa,CAAA,GAC/B,EAAkB,cAAc,EAAU,aAAa,EAAU,YAAA,GACjE,EAAkB,kBAAkB,GAAO,CAAA,GAEpC;AAAA,MACL,OAAA;AAAA,MACA,QAAA;AAAA,MACA,aAAA;AAAA,MACM,MAAA;AAAA,MACN,iBAAiB,KAAK;AAAA,MACtB,6BAAA;AAAA,MACA,mBAAA;AAAA,MACA,cAAA;AAAA,MACA,oBAAA;AAAA,MACA,gBAAA;AAAA,MACA,eAAA;AAAA;;EAOJ,wBAAsC;AACpC,IAAI,KAAK,oBACP,KAAK,yBAAyB,KAAK,gBAAgB,MAAA,CAAO,GAAO,MAAY;AAE3E,WAAK,KAAK,GAAsC,CAAA;AAAA,SAIhD,KAAK,0BACP,KAAK,+BAA+B,KAAK,sBAAsB,MAAA,CAAO,GAAO,MAAY;AAEvF,WAAK,KAAK,GAAsC,CAAA;AAAA;;EAQtD,2BAAyC;AACvC,IAAI,KAAK,2BACP,KAAK,uBAAA,GACL,KAAK,yBAAyB,OAE5B,KAAK,iCACP,KAAK,6BAAA,GACL,KAAK,+BAA+B;AAAA;EASxC,UAAgB;AACd,SAAK,kBAAA,GAGD,KAAK,UAAU,gBAAgB,KAAK,uBAAuB,aAC7D,KAAK,sBAAsB,MAAA,GAI7B,KAAK,yBAAA,GAGD,KAAK,oBACP,KAAK,gBAAgB,QAAA,GACrB,KAAK,kBAAkB,OAErB,KAAK,0BACP,KAAK,sBAAsB,QAAA,GAC3B,KAAK,wBAAwB,OAI3B,KAAK,qBACP,KAAK,iBAAiB,aAAa,QAAA,GACnC,KAAK,iBAAiB,kBAAkB,QAAA,GACxC,KAAK,iBAAiB,YAAY,QAAA,GAGlC,KAAK,iBAAiB,mBAAmB,MAAA,GACzC,KAAK,iBAAiB,eAAe,MAAA,GACrC,KAAK,iBAAiB,cAAc,MAAA,GAEpC,KAAK,mBAAmB,OAO1B,KAAK,mBAAA,GAEL,KAAK,YAAY,IACjB,KAAK,eAAe;AAAA;EAUtB,IAAI,OAAmB;AACrB,WAAO,KAAK;AAAA;EAed,YAAY,GAAmB;AAC7B,YAAQ,IAAI,CAAA,GACZ,KAAK,kBAAA,GACL,KAAK,iBAAiB,YAAY,CAAA,GAClC,KAAK,uBAAuB,YAAY,CAAA;AAAA;EAU1C,QAAQ,GAAwB;AAI9B,QAHA,KAAK,kBAAA,GAGD,KAAK,UAAU,EACjB;AAGF,UAAM,IAAe,KAAK;AAE1B,IAAI,MAAS,eACX,KAAK,wBAAA,IAEL,KAAK,kBAAA,GAGP,KAAK,QAAQ,GAGb,KAAK,KAAK,eAAe;AAAA,MAAE,MAAA;AAAA,MAAM,cAAA;AAAA,KAAc;AAAA;EAMjD,0BAAwC;AAGtC,QAAI,CADY,KAAK,iBAAiB,WAAA,EAEpC,OAAM,IAAI,MAAM,qDAAA;AAIlB,IAAI,KAAK,mBACP,KAAK,gBAAgB,UAAU,EAAA,GAI7B,KAAK,yBACP,KAAK,sBAAsB,UAAU,EAAA;AAAA;EAOzC,oBAAkC;AAEhC,IAAI,KAAK,yBACP,KAAK,sBAAsB,UAAU,EAAA,GAKnC,KAAK,mBACP,KAAK,gBAAgB,UAAU,EAAA;AAAA;EAcnC,WAAW,GAA+B;AACxC,SAAK,kBAAA;AACL,UAAM,IAAU,KAAK,YAAY,GAAA;AAEjC,IAAI,KAAK,kBAAkB,SACzB,KAAK,kBAAkB,MAAM,QAAA,GAC7B,KAAK,kBAAkB,MAAM,OAAO,KAAK,kBAAkB,IAAA,IAIzD,KAAK,oBACP,KAAK,gBAAgB,WAAW,CAAA,GAChC,KAAK,uBAAuB,WAAW,CAAA;AAGzC,UAAM,IAAW,IAAU,EAAQ,SAAS,OAAO,IAC7C,IAAgB,IAAU,EAAQ,SAAS,YAAY;AAY7D,QAXA,KAAK,iBAAkB,OAAO,EAC5B,GACA,GACA,EAAQ,kBAAmB,iBAC3B,EAAQ,kBAAmB,SAAA,GAE7B,KAAK,kBAAkB,MAAM,IAAI,KAAK,iBAAkB,IAAA,GAEpD,KAAW,KAAK,kBAAkB,UACpC,GAAa,KAAK,kBAAkB,QAAQ,EAAQ,SAAS,aAAA,GAE3D,KAAW,KAAK,kBAAkB,aAAa;AACjD,YAAM,IAAW,KAAK,kBAAkB,aAClC,IAAS,EAAQ,SAAS,cAAc;AAC9C,MAAA,EAAS,OAAO,IAAI,EAAO,GAAG,EAAO,GAAG,EAAO,CAAA;AAAA;;EAOnD,aAA6B;AAC3B,gBAAK,kBAAA,GACE,KAAK,iBAAiB,WAAA,KAAgB;AAAA;EAY/C,oBAAuC;AACrC,gBAAK,kBAAA,GACE,KAAK;AAAA;EAQd,0BAAmD;AACjD,gBAAK,kBAAA,GACE,KAAK;AAAA;EAad,cAAc,GAA0B;AACtC,SAAK,eAAA,GACL,KAAK,gBAAiB,cAAc,CAAA;AAAA;EAQtC,gBAAiC;AAC/B,gBAAK,eAAA,GACE,KAAK,gBAAiB,cAAA;AAAA;EAS/B,mBAAmB,GAAwB;AACzC,SAAK,eAAA,GACL,KAAK,gBAAiB,YAAY,CAAA;AAAA;EAYpC,OAAa;AACX,SAAK,qBAAA,GACL,KAAK,sBAAuB,KAAA;AAAA;EAQ9B,QAAc;AACZ,SAAK,qBAAA,GACL,KAAK,sBAAuB,MAAA;AAAA;EAQ9B,OAAa;AACX,SAAK,qBAAA,GACL,KAAK,sBAAuB,KAAA;AAAA;EAQ9B,OAAa;AACX,SAAK,qBAAA,GACL,KAAK,sBAAuB,KAAA;AAAA;EAM9B,IAAI,YAAqB;AACvB,WAAI,KAAK,UAAU,eACV,KAEF,KAAK,uBAAuB,aAAa;AAAA;EAMlD,IAAI,cAAsB;AACxB,WAAI,KAAK,UAAU,eACV,IAEF,KAAK,uBAAuB,eAAe;AAAA;EAMpD,IAAI,eAAuB;AACzB,WAAO,KAAK,uBAAuB,gBAAgB;AAAA;EAGrD,IAAI,aAAa,GAAe;AAC9B,IAAI,KAAK,0BACP,KAAK,sBAAsB,eAAe;AAAA;EAQ9C,IAAI,kBAA0B;AAC5B,WAAO,KAAK,uBAAuB,mBAAmB;AAAA;EAGxD,IAAI,gBAAgB,GAAa;AAC/B,IAAI,KAAK,0BACP,KAAK,sBAAsB,kBAAkB;AAAA;EAOjD,IAAI,qBAA6B;AAC/B,WAAO,KAAK,uBAAuB,sBAAsB;AAAA;EAM3D,IAAI,qBAA6B;AAC/B,WAAO,KAAK,uBAAuB,sBAAsB;AAAA;EAa3D,OAAO,GAAqB;AAC1B,IAAI,CAAC,KAAK,gBAAgB,KAAK,aAAa,KAAK,UAAU,gBAC3D,KAAK,sBAAuB,iBAAiB,CAAA;AAAA;EAY/C,WAAwB;AACtB,gBAAK,kBAAA,GACE,KAAK,iBAAkB;AAAA;EAQhC,YAAqC;AACnC,gBAAK,kBAAA,GACE,KAAK,iBAAkB;AAAA;EAQhC,cAA2B;AACzB,gBAAK,kBAAA,GACE,KAAK,iBAAkB;AAAA;EAOhC,eAA4B;AAC1B,IAAK,KAAK,mBACV,KAAK,gBAAgB,aAAA;AAAA;EASvB,kBAAkB,GAAgB,GAAuB;AACvD,SAAK,kBAAA;AAEL,UAAM,IAAI,KAAS,KAAK,WAAY,aAC9B,IAAI,KAAU,KAAK,WAAY,cAG/B,IAAS,KAAK,iBAAkB;AACtC,IAAA,EAAO,SAAS,IAAI,GACpB,EAAO,uBAAA,GAGP,KAAK,iBAAkB,kBAAkB,cAAc,GAAG,CAAA,GAGtD,KAAK,UAAU,UAAU,KAAK,kBAChC,KAAK,gBAAgB,kBAAkB,GAAG,CAAA,IACjC,KAAK,UAAU,gBAAgB,KAAK,yBAC7C,KAAK,sBAAsB,kBAAkB,GAAG,CAAA;AAAA;EAapD,oBAAkC;AAChC,QAAI,KAAK,UACP,OAAM,IAAI,MAAM,iCAAA;AAElB,QAAI,CAAC,KAAK,aACR,OAAM,IAAI,MAAM,kCAAA;AAAA;EASpB,iBAA+B;AAE7B,QADA,KAAK,kBAAA,GACD,KAAK,UAAU,OACjB,OAAM,IAAI,MAAM,2CAAA;AAAA;EASpB,uBAAqC;AAEnC,QADA,KAAK,kBAAA,GACD,KAAK,UAAU,aACjB,OAAM,IAAI,MAAM,iDAAA;AAAA;GCzuBJ,IAAX,0BAAA,GAAA;AACL,SAAA,EAAA,QAAA,SACA,EAAA,eAAA,gBACA,EAAA,QAAA,SACA,EAAA,YAAA;QAIgB,IAAX,0BAAA,GAAA;AACL,SAAA,EAAA,SAAA,UACA,EAAA,UAAA,WACA,EAAA,WAAA;QAYgB,IAAX,0BAAA,GAAA;AACL,SAAA,EAAA,EAAA,SAAA,CAAA,IAAA,UACA,EAAA,EAAA,UAAA,CAAA,IAAA,WACA,EAAA,EAAA,UAAA,CAAA,IAAA,WACA,EAAA,EAAA,kBAAA,CAAA,IAAA;QAQI,IAAc,SAEd,IAAkB,UAQX,IAET;AAAA,GACD,EAAe,KAAA,GAAQ;AAAA,KACrB,EAAc,MAAA,GAAS,IAAI,EAAM,oBAAoB;AAAA,MACpD,OAAO;AAAA,MACP,UAAU;AAAA,QAAE,SAAS,EAAW;AAAA,QAAQ,QAAQ,EAAe;AAAA;KAChE;AAAA,KACA,EAAc,OAAA,GAAU,IAAI,EAAM,oBAAoB;AAAA,MACrD,OAAO;AAAA,MACP,UAAU;AAAA,MACV,mBAAmB;AAAA,MACnB,UAAU;AAAA,QAAE,SAAS,EAAW;AAAA,QAAQ,QAAQ,EAAe;AAAA;KAChE;AAAA,KACA,EAAc,QAAA,GAAW,IAAI,EAAM,oBAAoB;AAAA,MACtD,OAAO;AAAA,MACP,UAAU;AAAA,MACV,mBAAmB;AAAA,MACnB,UAAU;AAAA,QAAE,SAAS,EAAW;AAAA,QAAQ,QAAQ,EAAe;AAAA;KAChE;AAAA;GAEF,EAAe,YAAA,GAAe;AAAA,KAC5B,EAAc,MAAA,GAAS,IAAI,EAAM,oBAAoB;AAAA,MACpD,OAAO;AAAA,MACP,UAAU;AAAA,MACV,mBAAmB;AAAA,MACnB,UAAU;AAAA,QAAE,SAAS,EAAW;AAAA,QAAQ,QAAQ,EAAe;AAAA;KAChE;AAAA,KACA,EAAc,OAAA,GAAU,IAAI,EAAM,oBAAoB;AAAA,MACrD,OAAO;AAAA,MACP,UAAU;AAAA,MACV,mBAAmB;AAAA,MACnB,UAAU;AAAA,QAAE,SAAS,EAAW;AAAA,QAAQ,QAAQ,EAAe;AAAA;KAChE;AAAA,KACA,EAAc,QAAA,GAAW,IAAI,EAAM,oBAAoB;AAAA,MACtD,OAAO;AAAA,MACP,UAAU;AAAA,MACV,mBAAmB;AAAA,MACnB,UAAU;AAAA,QAAE,SAAS,EAAW;AAAA,QAAQ,QAAQ,EAAe;AAAA;KAChE;AAAA;GAEF,EAAe,KAAA,GAAQ;AAAA,KACrB,EAAc,MAAA,GAAS,IAAI,EAAM,oBAAoB;AAAA,MACpD,OAAO;AAAA,MACP,aAAa;AAAA,MACb,SAAS;AAAA,MACT,UAAU;AAAA,QAAE,SAAS,EAAW;AAAA,QAAQ,QAAQ,EAAe;AAAA;KAChE;AAAA,KACA,EAAc,OAAA,GAAU,IAAI,EAAM,oBAAoB;AAAA,MACrD,OAAO;AAAA,MACP,aAAa;AAAA,MACb,SAAS;AAAA,MACT,UAAU;AAAA,MACV,mBAAmB;AAAA,MACnB,UAAU;AAAA,QAAE,SAAS,EAAW;AAAA,QAAQ,QAAQ,EAAe;AAAA;KAChE;AAAA,KACA,EAAc,QAAA,GAAW,IAAI,EAAM,oBAAoB;AAAA,MACtD,OAAO;AAAA,MACP,aAAa;AAAA,MACb,SAAS;AAAA,MACT,UAAU;AAAA,MACV,mBAAmB;AAAA,MACnB,UAAU;AAAA,QAAE,SAAS,EAAW;AAAA,QAAQ,QAAQ,EAAe;AAAA;KAChE;AAAA;GAEF,EAAe,SAAA,GAAY;AAAA,KACzB,EAAc,MAAA,GAAS,IAAI,EAAM,oBAAoB;AAAA,MACpD,OAAO;AAAA,MACP,UAAU;AAAA,QAAE,SAAS,EAAW;AAAA,QAAQ,QAAQ,EAAe;AAAA;KAChE;AAAA,KACA,EAAc,OAAA,GAAU,IAAI,EAAM,oBAAoB;AAAA,MACrD,OAAO;AAAA,MACP,UAAU;AAAA,MACV,mBAAmB;AAAA,MACnB,UAAU;AAAA,QAAE,SAAS,EAAW;AAAA,QAAQ,QAAQ,EAAe;AAAA;KAChE;AAAA,KACA,EAAc,QAAA,GAAW,IAAI,EAAM,oBAAoB;AAAA,MACtD,OAAO;AAAA,MACP,UAAU;AAAA,MACV,mBAAmB;AAAA,MACnB,UAAU;AAAA,QAAE,SAAS,EAAW;AAAA,QAAQ,QAAQ,EAAe;AAAA;KAChE;AAAA;GC9GQ,KAAb,MAAyD;AAAA,EACvD,YAAiE,oBAAI,IAAA;AAAA,EACrE;AAAA,EAQA,YAAY,GAA0C;AACpD,QAAI,CAAC,EACH,OAAM,IAAI,UAAU,mDAAA;AAEtB,SAAK,kBAAkB;AAAA;EAWzB,SAAS,GAAqB,GAAmD;AAC/E,QAAI,OAAO,KAAS,YAAY,EAAK,KAAA,MAAW,GAC9C,OAAM,IAAI,UAAU,2CAAA;AAEtB,QAAI,CAAC,EACH,OAAM,IAAI,UAAU,iDAAiD,CAAA,EAAA;AAGvE,QAAI,OAAO,KAAY,YAAY,OAAQ,EAAgB,gBAAiB,WAC1E,OAAM,IAAI,UAAU,kEAAkE,CAAA,EAAA;AAExF,gBAAK,UAAU,IAAI,GAAM,CAAA,GAClB;AAAA;EAaT,IAAI,GAA8C;AAChD,WAAO,KAAK,UAAU,IAAI,CAAA,KAAS,KAAK;AAAA;EAS1C,IAAI,GAA8B;AAChC,WAAO,KAAK,UAAU,IAAI,CAAA;AAAA;EAM5B,qBAA8C;AAC5C,WAAO,KAAK;AAAA;EAYd,WAAW,GAA8B;AACvC,WAAO,KAAK,UAAU,OAAO,CAAA;AAAA;EAQ/B,qBAAsC;AACpC,WAAO,MAAM,KAAK,KAAK,UAAU,KAAA,CAAM;AAAA;EAMzC,oBAAoB,GAAoC;AACtD,eAAW,KAAW,KAAK,UAAU,OAAA,EACnC,CAAA,EAAQ,oBAAoB,CAAA;AAE9B,SAAK,gBAAgB,oBAAoB,CAAA;AAAA;GC2GvB,IAAtB,MAAsB,GAA8D;AAAA,EAElF,oBAAuD;AAAA,EAGvD,iBAAiD;AAAA,EAGjD,OAA0B,sBAAsB;AAAA,EAGhD,OAA0B,0BAA0B;AAAA,EAEpD,OACE,GACA,IAAyB,EAAc,QAClB;AACrB,UAAM,IAAS,EAAc,CAAA;AAC7B,WAAK,KAGE,EAAO,CAAA,KAAY,EAAc,EAAe,KAAA,EAAO,EAAc,MAAA;AAAA;EAG9E,kBAAkB;AAChB,WAAO;AAAA;EAYT,wBAAwB,GAA2B,GAA8B;AAAA,EAAA;AAAA,EAajF,WAAW,GAAgC;AACzC,IAAI,EAAS,SAAS,cAKtB,EAAS,SAAA,CAAU,MAAU;AAC3B,UAAI,EAAM,SAAS,SAAS,iBAAiB,EAAM,SAAS,SAAS,QACnE;AAEF,UAAI,EAAM,SAAS,SAAS,cAAc;AACxC,aAAK,cAAc,CAAA;AACnB;AAAA;AAEF,UAAI,EAAE,aAAiB,EAAM,MAAO;AAEpC,YAAM,IAAW,EAAM;AACvB,UAAI,EAAS,YAAY,MAAS,EAAS,UAAU,YAAY,EAAW,OAAQ;AAEpF,YAAM,IAAc,EAAc,EAAS,SAAS,MAAA;AACpD,MAAK,MACL,EAAM,WAAW,EAAY,EAAc,OAAA;AAAA;;EAO/C,YAAY,GAAgC;AAC1C,IAAI,EAAS,SAAS,cAItB,EAAS,SAAA,CAAU,MAAU;AAC3B,UAAI,EAAM,SAAS,SAAS,iBAAiB,EAAM,SAAS,SAAS,QACnE;AAEF,UAAI,EAAM,SAAS,SAAS,cAAc;AACxC,aAAK,eAAe,CAAA;AACpB;AAAA;AAEF,UAAI,EAAE,aAAiB,EAAM,MAAO;AAEpC,YAAM,IAAW,EAAM;AACvB,UAAI,EAAS,YAAY,MAAS,EAAS,UAAU,YAAY,EAAW,OAAQ;AAEpF,YAAM,IAAc,EAAc,EAAS,SAAS,MAAA;AACpD,MAAK,MACL,EAAM,WAAW,EAAY,EAAc,MAAA;AAAA;;EAY/C,eAAe,GAAgC;AAC7C,IAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,UAAI,EAAM,SAAS,SAAS,iBAAiB,EAAM,SAAS,SAAS,QACnE;AAEF,UAAI,EAAM,SAAS,SAAS,cAAc;AACxC,aAAK,eAAe,CAAA;AACpB;AAAA;AAEF,UAAI,EAAE,aAAiB,EAAM,MAAO;AAEpC,YAAM,IAAW,EAAM;AACvB,UAAI,EAAS,YAAY,MAAS,EAAS,UAAU,YAAY,EAAW,OAAQ;AAEpF,YAAM,IAAc,EAAc,EAAS,SAAS,MAAA;AACpD,MAAK,MACL,EAAM,WAAW,EAAY,EAAc,QAAA;AAAA,QAG7C,EAAS,SAAS,aAAa;AAAA;EAWjC,gBAAgB,GAAgC;AAC9C,IAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,UAAI,EAAM,SAAS,SAAS,iBAAiB,EAAM,SAAS,SAAS,QACnE;AAEF,UAAI,EAAM,SAAS,SAAS,cAAc;AACxC,aAAK,eAAe,CAAA;AACpB;AAAA;AAEF,UAAI,EAAE,aAAiB,EAAM,MAAO;AAEpC,YAAM,IAAW,EAAM;AACvB,UAAI,EAAS,YAAY,MAAS,EAAS,UAAU,YAAY,EAAW,OAAQ;AAEpF,YAAM,IAAc,EAAc,EAAS,SAAS,MAAA;AACpD,MAAK,MACL,EAAM,WAAW,EAAY,EAAc,MAAA;AAAA,QAE7C,EAAS,SAAS,aAAa;AAAA;EAMjC,oBAA4B,GAAoB,GAAwB;AACtE,YAAQ,GAAR;AAAA,MACE,KAAK;AACH,QAAA,EAAM,QAAQ,CAAC,KAAK,KAAK,CAAA,GACzB,EAAM,QAAQ,KAAK,EAAA;AACnB;AAAA,MACF,KAAK;AACH,QAAA,EAAM,QAAQ,KAAK,KAAK,CAAA;AACxB;AAAA,MACF,KAAK;AACH,QAAA,EAAM,QAAQ,KAAK,KAAK,CAAA,GACxB,EAAM,QAAQ,KAAK,EAAA;AACnB;AAAA,MACF,KAAK;AACH,QAAA,EAAM,QAAQ,CAAC,KAAK,KAAK,CAAA;AACzB;AAAA;;EAiBN,eACE,GACA,IAAwB,SACxB,IAAqC,MACxB;AACb,QAAI,CAAC,EAAK,UACR,OAAM,IAAI,MAAM,kDAAA;AAIlB,UAAM,IAAiB;AAAA,MAAC;AAAA,MAAW;AAAA,MAAO;AAAA,MAAW;AAAA,MAAO;AAAA,OAEtD,IAAgB,CAAC,OAAO,KAAA,GAExB,IAAY;AAAA,MAChB,aAAa,EAAK;AAAA,MAClB,SAAS,EAAK;AAAA,MACd,OAAO,EAAK;AAAA,MACZ,SAAS,EAAK;AAAA,MACd,kBAAkB,EAAe,SAAS,EAAK,OAAA;AAAA,OAG3C,IAAW,IAAI,EAAM,MAAA;AAC3B,IAAA,EAAS,WAAW;AAAA,MAAE,GAAG;AAAA,MAAW,MAAM;AAAA,MAAc,YAAY,EAAK,UAAU;AAAA;AAEnF,QAAI,IAAe,KACf,IAAe;AAEnB,IAAI,EAAc,SAAS,EAAK,OAAA,MAC9B,IAAe,MACf,IAAe;AAIjB,UAAM,IAAa,IAAI,EAAM,eAC3B,GACA,IACA,GACA,GACA,KAAK,KAAK,GACV,GACA,KAAK,KAAK,CAAA,GAEN,IAAS,IAAI,EAAM,KACvB,GACA,IAAI,EAAM,kBAAkB;AAAA,MAC1B,OAAO,GAA2B;AAAA,MAClC,aAAa;AAAA,MACb,SAAS;AAAA,KACV,CAAC;AAEJ,IAAA,EAAO,WAAW;AAAA,MAAE,GAAG;AAAA,MAAW,MAAM;AAAA,MAAe,eAAe,KAAK;AAAA,OAC3E,EAAO,OAAO,IAAI,EAAa,KAAA,GAC/B,EAAS,IAAI,CAAA;AAGb,UAAM,IAAS,IAAI,EAAM,KACvB,IAAI,EAAM,eAAe,GAAc,IAAI,GAAG,GAAG,KAAK,KAAK,GAAG,GAAG,KAAK,KAAK,CAAA,GAC3E,IAAI,EAAM,oBAAoB;AAAA,MAC5B,OAAO,KAAK,sBAAsB,EAAK,UAAU,IAAA;AAAA,MACjD,UAAU,KAAK,sBAAsB,EAAK,UAAU,IAAA;AAAA,MACpD,mBAAmB;AAAA,KACpB,CAAC;AAEJ,WAAA,EAAO,WAAW;AAAA,MAChB,GAAG;AAAA,MACH,MAAM;AAAA,MACN,YAAY,EAAK,UAAU;AAAA,MAC3B,QAAQ;AAAA,OAEV,EAAS,IAAI,CAAA,GACP,KACJ,EAAO,qBAAqB,CAAA,GAG9B,KAAK,oBAAoB,GAAU,CAAA,GAC5B;AAAA;EAST,qBACE,GACA,GACmB;AACnB,UAAM,IAAY,KAAK,uBAAuB,CAAA;AAC9C,QAAI,CAAC,EACH,QAAO;AAGT,UAAM,IAAS,IAAI,EAAM,KACvB,IAAI,EAAM,eAAe,EAAU,SAAS,QAAQ,IAAI,GAAG,GAAG,KAAK,KAAK,GAAG,GAAG,KAAK,KAAK,CAAA,GACxF,CAAA;AAEF,IAAA,EAAO,WAAW;AAAA,MAChB,MAAM;AAAA,MACN,aAAa,EAAU,SAAS;AAAA,MAChC,MAAM;AAAA,OAER,EAAO,SAAS,KAAK,EAAS,QAAA;AAE9B,UAAM,IAAoB,EAAU,SAAS,MAAA;AAE7C,WAAA,EAAO,SAAS,KACd,IAAI,EAAM,MACR,CAAC,EAAS,SAAS,GACnB,CAAC,EAAS,SAAS,GACnB,CAAC,EAAS,SAAS,GACnB,EAAS,SAAS,KAAA,CACnB,GAEH,EAAO,QAAQ,CAAC,EAAkB,CAAA,GAClC,EAAO,QAAQ,CAAC,EAAkB,CAAA,GAClC,EAAO,QAAQ,EAAkB,CAAA,GAE1B;AAAA;EAQT,aAAa,GAA0B,GAAmC;AACxE,QAAI,IAA+B;AACnC,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MACE,EAAM,SAAS,SAAS,gBACxB,EAAM,SAAS,UAAU,KACzB,aAAiB,EAAM,UAEvB,IAAW;AAAA,QAGR;AAAA;EAOT,uBAAuB,GAA0C;AAC/D,QAAI;AACJ,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,EAAM,SAAS,SAAS,WAAW,aAAiB,EAAM,SAC5D,IAAY;AAAA,QAIT;AAAA;EAQT,cAAc,GAA0B,GAAkC;AACxE,QAAI,IAA+B,KAAK,aAAa,GAAU,CAAA;AAC/D,WAAK,IAGE,KAAK,uBAAuB,CAAA,IAF1B;AAAA;EAiBX,sBACE,GACA,GACA,GACA,GACA,GACY;AACZ,UAAM,IAAW,IAAI,EAAM,YAAY,GAAO,GAAQ,CAAA,GAChD,IAAW,IAAI,EAAM,kBAAkB;AAAA,MAC3C,OAAO;AAAA,MACP,aAAa;AAAA,MACb,SAAS;AAAA,MACT,SAAS;AAAA,KACV,GACK,IAAS,IAAI,EAAM,KAAK,GAAU,CAAA;AACxC,WAAA,EAAO,WAAW;AAAA,MAChB,MAAM;AAAA,MACO,aAAA;AAAA,MACJ,SAAA;AAAA,OAEX,EAAO,OAAO,IAAI,EAAa,SAAA,GACxB;AAAA;EAGT,WAAqB,GAA6C;AAChE,QAAI,IAA4B;AAChC,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,EAAM,SAAS,SAAS,qBAAqB,aAAiB,EAAM,SACtE,IAAS;AAAA,QAGN;AAAA;EAGT,sBAAgC,GAA4C;AAC1E,WAAK,IAGD,MAAe,EAAgB,UAC1B,WACE,MAAe,EAAgB,UACjC,MAEF,WAPE;AAAA;EAUX,2BAAqC,GAAsD;AACzF,YAAQ,GAAR;AAAA,MACE,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MAET;AACE,eAAO;AAAA;;EAqBb,oBAAoB,GAA0B,GAA0C;AACtF,QAAM,EAAS,SAAS,iBAAkB;AAC1C,IAAA,EAAS,SAAS,aAAa;AAE/B,UAAM,IAAS,EAAS,SAAS,KAAA,CAAM,MAAU,EAAM,SAAS,SAAS,OAAA;AAIzE,QAAI,CAAC,EACH;AAEF,IAAA,EAAO,SAAS,aAAa;AAC7B,UAAM,IAAQ,KAAK,sBAAsB,CAAA,GACnC,IAAW,EAAO;AACxB,IAAA,EAAS,MAAM,OAAO,CAAA,GACtB,EAAS,SAAS,OAAO,CAAA;AAAA;EAM3B,cAAc,GAAgC;AAC5C,IAAI,EAAS,SAAS,cAGtB,EAAS,SAAS,YAAY,IAE9B,EAAS,SAAA,CAAU,MAAU;AAC3B,UAAI,aAAiB,EAAM,MAAM;AAC/B,cAAM,IAAW,EAAM;AAEvB,QAAI,EAAM,SAAS,SAAS,gBAC1B,EAAS,UAAU,MACV,EAAM,SAAS,SAAS,YACjC,EAAS,MAAM,OAAO,KAAA,GAEtB,EAAS,oBAAoB;AAAA;;;EASrC,eAAe,GAAgC;AAC7C,QAAI,CAAC,EAAS,SAAS,UACrB;AAEF,IAAA,EAAS,SAAS,YAAY;AAE9B,UAAM,IAAiC,EAAS,SAAS,cAAc;AAEvE,IAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,UAAI,aAAiB,EAAM,MAAM;AAC/B,cAAM,IAAW,EAAM;AAEvB,YAAI,EAAM,SAAS,SAAS,cAC1B,CAAA,EAAS,UAAU;AAAA,iBACV,EAAM,SAAS,SAAS,YACjC,EAAS,MAAM,OAAO,KAAK,sBAAsB,CAAA,CAAO,GACxD,EAAS,oBAAoB,IAAS,IAAI,GACtC,CAAC,KAAU,EAAS,SAAS,kBAAiB;AAChD,gBAAM,IAAgB,KAAK,2BACzB,EAAS,SAAS,eAAA;AAEpB,UAAA,EAAS,SAAS,OAAO,CAAA,GACzB,EAAS,oBAAoB,MAAkB,IAAW,IAAI;AAAA;;;;EAaxE,gBAAgB,GAA2B,GAAqC;AAAA,EAAA;AAAA,EAUhF,wBAAwB,GAA4D;AAClF,WAAO;AAAA;EAQT,oBAAoB,GAA+C;AACjE,WAAO,IAAI,IAAI,CAAA;AAAA;EAQjB,oBAAoB,GAAiD;AACnE,UAAM,IAAS,oBAAI,IAAA;AACnB,eAAW,CAAC,GAAK,CAAA,KAAU,EAAS,QAAA,EAClC,CAAA,EAAO,IAAI,GAAK,OAAO,CAAA,CAAM;AAE/B,WAAO;AAAA;EAMT,oBAAoB,GAAoC;AACtD,SAAK,oBAAoB;AAAA;GCpyBhB,KAAkD;AAAA,EAC7D,KAAK;AAAA,EACL,OAAO;AAAA,EACP,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,OAAO;;AAQT,SAAgB,GAAW,GAAwB;AACjD,SAAO,oBAAoB,KAAK,CAAA;;AAQlC,SAAgB,EAAiB,GAAqB;AACpD,QAAM,IAAW,EAAI,YAAA;AACrB,aAAW,CAAC,GAAM,CAAA,KAAc,OAAO,QAAQ,EAAA,EAC7C,KAAI,EAAU,YAAA,MAAkB,EAC9B,QAAO;AAGX,SAAO;;AAQT,SAAgB,EAAiB,GAAuB;AACtD,SAAI,GAAW,CAAA,IACN,IAEF,GAAc,CAAA,KAAU;;ACvCjC,IAAa,KAAb,cAA0C,EAA2B;AAAA,EACnE,aAAa,GAAsC;AACjD,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA,MACzB,eAAe;AAAA;AAIjB,UAAM,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,GAAG,GAAG,CAAA;AACxE,IAAA,EAAM,IAAI,CAAA;AAGV,UAAM,IAAc,IAAI,EAAM,YAAY,KAAK,KAAK,KAAK,GAAG,GAAG,CAAA,GACzD,IAAc,IAAI,EAAM,qBAAqB,EAAE,OAAO,SAAA,CAAU,GAChE,IAAM,IAAI,EAAM,KAAK,GAAa,CAAA;AACxC,WAAA,EAAI,WAAW;AAAA,MACb,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,OAEzB,EAAM,IAAI,CAAA,GAEH;AAAA;EAST,0BAAgE;AAE9D,WAAO,EACL,QAAQ,CAAC;AAAA,MAAE,KAAK;AAAA,MAAS,MAAM;AAAA,KAAS,EAAC;AAAA;EAW7C,oBAA6B,GAA+C;AAC1E,UAAM,IAAW,oBAAI,IAAA,GACf,IAAQ,EAAO,IAAI,OAAA,KAAY;AAGrC,WAAA,EAAS,IAAI,SAAS,EAAiB,CAAA,CAAM,GAEtC;AAAA;EAUT,oBAA6B,GAAiD;AAC5E,UAAM,IAAS,oBAAI,IAAA,GACb,IAAQ,EAAS,IAAI,OAAA;AAG3B,WAAI,KACF,EAAO,IAAI,SAAS,EAAiB,CAAA,CAAM,GAGtC;AAAA;EAcT,wBAAiC,GAA0B,GAAmC;AAC5F,UAAM,IAAQ,EAAO,IAAI,OAAA;AACzB,IAAK,KAGL,EAAS,SAAA,CAAU,MAAU;AAC3B,UAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,eACrD,EAAM,oBAAoB,EAAM,sBAAsB;AAExD,cAAM,IAAW,EAAiB,CAAA,GAC5B,IAAW,SAAS,EAAS,QAAQ,KAAK,EAAA,GAAK,EAAA;AACrD,QAAA,EAAM,SAAS,MAAM,OAAO,CAAA;AAAA;;;GC3ChC,KAAN,MAA8D;AAAA,EAC5D,YACE,GACA,GACA,GACA,GACA;AAJiB,SAAA,eAAA,GACA,KAAA,aAAA,GACA,KAAA,iBAAA,GACA,KAAA,UAAA;AAAA;EAGnB,IAAI,GAAqB,GAA0D;AACjF,QAAI,OAAO,KAAS,YAAY,EAAK,KAAA,MAAW,GAC9C,OAAM,IAAI,UAAU,2CAAA;AAEtB,QAAI,CAAC,EACH,OAAM,IAAI,UAAU,iDAAiD,CAAA,EAAA;AAEvE,QAAI,OAAO,KAAY,YAAY,OAAQ,EAAgB,gBAAiB,WAC1E,OAAM,IAAI,UAAU,gEAAgE,CAAA,EAAA;AAItF,UAAM,IAAkB,KAAK,eAAe,IAAI,CAAA;AAChD,QAAI,MAAoB,UAAa,MAAoB,KAAK,aAAa,IAAI;AAC7E,YAAM,IAAW,KAAK,QAAQ,IAAI,CAAA;AAClC,MAAI,MACF,EAAS,QAAQ,EAAS,MAAM,OAAA,CAAQ,MAAM,MAAM,CAAA;AAAA;AAIxD,gBAAK,WAAW,IAAI,GAAM,CAAA,GAC1B,KAAK,eAAe,IAAI,GAAM,KAAK,aAAa,EAAA,GAE3C,KAAK,aAAa,MAAM,SAAS,CAAA,KACpC,KAAK,aAAa,MAAM,KAAK,CAAA,GAGxB;AAAA;GA+BE,KAAb,MAAyF;AAAA,EACvF,aAA2E,oBAAI,IAAA;AAAA,EAC/E;AAAA,EACA,UAAqD,oBAAI,IAAA;AAAA,EACzD,iBAA8D,oBAAI,IAAA;AAAA,EAQlE,YAAY,GAA0C;AACpD,QAAI,CAAC,EACH,OAAM,IAAI,UAAU,0DAAA;AAEtB,SAAK,mBAAmB;AAAA;EAe1B,SACE,GACA,GACA,GACM;AACN,QAAI,OAAO,KAAO,YAAY,EAAG,KAAA,MAAW,GAC1C,OAAM,IAAI,UAAU,qCAAA;AAEtB,QAAI,OAAO,KAAU,YAAY,EAAM,KAAA,MAAW,GAChD,OAAM,IAAI,UAAU,wCAAA;AAGtB,QAAI,IAAc,KAAK,QAAQ,IAAI,CAAA;AACnC,WAAK,MACH,IAAc;AAAA,MAAE,IAAA;AAAA,MAAI,OAAA;AAAA,MAAO,OAAO,CAAA;AAAA,OAClC,KAAK,QAAQ,IAAI,GAAI,CAAA,IAUvB,EANgB,IAAI,GAClB,GACA,KAAK,YACL,KAAK,gBACL,KAAK,OAAA,CACN,GAGM;AAAA;EAST,IAAI,GAA8C;AAChD,WAAO,KAAK,WAAW,IAAI,CAAA,KAAS,KAAK;AAAA;EAQ3C,IAAI,GAA8B;AAChC,WAAO,KAAK,WAAW,IAAI,CAAA;AAAA;EAM7B,qBAA8C;AAC5C,WAAO,KAAK;AAAA;EAQd,YAA8B;AAC5B,WAAO,MAAM,KAAK,KAAK,QAAQ,OAAA,CAAQ,EAAE,IAAA,CAAK,OAAO;AAAA,MAAE,IAAI,EAAE;AAAA,MAAI,OAAO,EAAE;AAAA,MAAO;AAAA;EASnF,WAAW,GAAiD;AAC1D,UAAM,IAAU,KAAK,eAAe,IAAI,CAAA;AACxC,QAAI,CAAC,EAAS;AACd,UAAM,IAAQ,KAAK,QAAQ,IAAI,CAAA;AAC/B,QAAK;AACL,aAAO;AAAA,QAAE,IAAI,EAAM;AAAA,QAAI,OAAO,EAAM;AAAA;;EAUtC,mBAAmB,GAAmC;AACpD,QAAI,MAAY,OACd,QAAO,MAAM,KAAK,KAAK,WAAW,KAAA,CAAM;AAE1C,UAAM,IAAQ,KAAK,QAAQ,IAAI,CAAA;AAC/B,WAAK,IACE,CAAC,GAAG,EAAM,KAAA,IADE,CAAA;AAAA;EAarB,WAAW,GAA8B;AACvC,QAAI,CAAC,KAAK,WAAW,IAAI,CAAA,EAAO,QAAO;AAEvC,SAAK,WAAW,OAAO,CAAA;AAEvB,UAAM,IAAU,KAAK,eAAe,IAAI,CAAA;AACxC,QAAI,GAAS;AACX,YAAM,IAAQ,KAAK,QAAQ,IAAI,CAAA;AAC/B,MAAI,MACF,EAAM,QAAQ,EAAM,MAAM,OAAA,CAAQ,MAAM,MAAM,CAAA,IAEhD,KAAK,eAAe,OAAO,CAAA;AAAA;AAG7B,WAAO;AAAA;EAMT,oBAAoB,GAAoC;AACtD,eAAW,KAAW,KAAK,WAAW,OAAA,EACpC,CAAA,EAAQ,oBAAoB,CAAA;AAE9B,SAAK,iBAAiB,oBAAoB,CAAA;AAAA;EAQ5C,SAAS,GAAsB,GAAqD;AAClF,UAAM,IAAI,MACR,4GAAA;AAAA;GCxSO,KAAb,cAA0C,EAA2B;AAAA,EACnE,mBAAoC,IAAI,EAAM,iBAAiB,KAAK,KAAK,GAAG,EAAA;AAAA,EAE5E,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,aAAa,GAAsB,GAAwC;AACzE,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAG9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAI3B,UAAM,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,GAAG,GAAG,CAAA;AACxE,IAAA,EAAM,IAAI,CAAA;AAGV,UAAM,IAAW,IAAI,EAAM,KAAK,KAAK,kBAAkB,KAAK,OAAO,EAAe,KAAA,CAAM;AACxF,WAAA,EAAS,WAAW;AAAA,MAClB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,OAEzB,EAAS,QAAQ,KAAK,KAAK,CAAA,GAC3B,EAAM,IAAI,CAAA,GAGN,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAGrC;AAAA;EAGT,iBAAyB,GAAsB,GAAwB,GAAoB;AACzF,UAAM,IAAc,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACpD,QAAI,GAAa;AACf,YAAM,IAAe,KAAK,eAAe,GAAa,KAAA;AACtD,MAAA,EAAa,SAAS,IAAI,GAAG,GAAG,EAAA,GAChC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAY,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAClD,QAAI,GAAW;AACb,YAAM,IAAa,KAAK,eAAe,GAAW,QAAA;AAClD,MAAA,EAAW,SAAS,IAAI,GAAG,GAAG,CAAA,GAC9B,EAAM,IAAI,CAAA;AAAA;;GCzBH,KAAb,MAAa,UAA2B,EAA2B;AAAA,EAEjE,OAAwB,kBAAkB;AAAA,EAG1C,OAAwB,cAAc;AAAA,EAGtC,OAAwB,aAAa;AAAA,EAGrC,OAAwB,cAAc;AAAA,EAGtC,OAAwB,kBAAkB;AAAA,EAG1C,OAAwB,iBAAiB;AAAA,EAGzC,OAAwB,UAAU;AAAA,EAElC,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,aAAa,GAAsB,GAAyC;AAC1E,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAE9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAI3B,UAAM,IAAO,EAAU,OAAO,IAAI,MAAA,KAAW,SAGvC,IAAW,KAAK,eAAe,CAAA;AAErC,IAAA,EAAS,SAAS,OAAO,aACzB,EAAS,SAAS,cAAc,EAAU,IAC1C,EAAS,SAAS,OAAO,QACzB,EAAM,IAAI,CAAA;AAGV,UAAM,IAAe,EAAS,UACxB,IAAQ,EAAa,WAAW,OAChC,IAAS,EAAa,WAAW,QACjC,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,GAAO,GAAQ,GAAA;AACjF,WAAA,EAAO,SAAS,gBAAgB,EAAU,MAC1C,EAAM,IAAI,CAAA,GAGV,KAAK,wBAAwB,GAAO,EAAU,MAAA,GAEvC;AAAA;EAST,iBAAyB,GAAiC;AACxD,UAAM,IAAS,SAAS,cAAc,QAAA,GAChC,IAAM,EAAO,WAAW,IAAA,GAGxB,IAAa,KAAK,IAAI,OAAO,oBAAoB,GAAG,CAAA,GACpD,IAAiB,EAAmB,iBAAiB,GACrD,IAAgB,EAAmB,UAAU,GAG7C,IAAc,EAAK,MAAM,GAAG,EAAmB,eAAA,KAAoB;AAGzE,IAAA,EAAI,OAAO,QAAQ,CAAA,MAAoB,EAAmB,WAAA;AAC1D,UAAM,IAAU,EAAI,YAAY,CAAA,GAG1B,IAAY,KAAK,KAAK,EAAQ,KAAA,GAC9B,IAAa,KAAK,KAAK,IAAiB,GAAA;AAE9C,WAAA,EAAO,QAAQ,IAAY,IAAgB,GAC3C,EAAO,SAAS,IAAa,IAAgB,GAG7C,EAAI,OAAO,QAAQ,CAAA,MAAoB,EAAmB,WAAA,IAC1D,EAAI,YAAY,EAAmB,YACnC,EAAI,YAAY,UAChB,EAAI,eAAe,UAGnB,EAAI,SAAS,GAAa,EAAO,QAAQ,GAAG,EAAO,SAAS,CAAA,GAErD;AAAA;EAST,eAAuB,GAA0B;AAC/C,UAAM,IAAc,KAAK,qBAAqB,CAAA,GAExC,IAAS,KAAK,iBAAiB,CAAA,GAC/B,IAAU,IAAI,EAAM,cAAc,CAAA;AACxC,IAAA,EAAQ,YAAY,EAAM,cAC1B,EAAQ,YAAY,EAAM;AAE1B,UAAM,IAAW,IAAI,EAAM,kBAAkB;AAAA,MAC3C,KAAK;AAAA,MACL,aAAa;AAAA,MACb,MAAM,EAAM;AAAA,MACZ,YAAY;AAAA,KACb,GAGK,IAAa,KAAK,IAAI,OAAO,oBAAoB,GAAG,CAAA,GACpD,IAAa,EAAO,QAAQ,IAAa,IACzC,IAAc,EAAO,SAAS,IAAa,IAE3C,IAAW,IAAI,EAAM,cAAc,GAAY,CAAA,GAC/C,IAAO,IAAI,EAAM,KAAK,GAAU,CAAA;AAGtC,WAAA,EAAK,SAAS,SAAS,GACvB,EAAK,SAAS,UAAU,GACxB,EAAK,SAAS,OAAO,GAGrB,EAAK,SAAS,IAAI,GAAG,MAAM,CAAA,GAC3B,EAAK,SAAS,IAAI,CAAC,KAAK,KAAK,GAEtB;AAAA;EAST,aAAqB,GAA6C;AAChE,QAAI,IAA8B;AAClC,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,WACzD,IAAW;AAAA,QAGR;AAAA;EAQT,qBAA6B,GAAsB;AAEjD,WADoB,EAAK,MAAM,GAAG,EAAmB,eAAA,KAAoB;AAAA;EAa3E,eAAuB,GAAkB,GAAc,GAA6B;AAClF,UAAM,IAAc,KAAK,qBAAqB,CAAA;AAC9C,QAAI,MAAgB,EAAK,SAAS,KAAM;AAExC,IAAA,EAAK,SAAS,QAAA;AAEd,UAAM,IAAS,KAAK,iBAAiB,CAAA,GAC/B,IAAU,IAAI,EAAM,cAAc,CAAA;AACxC,IAAA,EAAQ,YAAY,EAAM,cAC1B,EAAQ,YAAY,EAAM;AAE1B,UAAM,IAAW,IAAI,EAAM,kBAAkB;AAAA,MAC3C,KAAK;AAAA,MACL,aAAa;AAAA,MACb,MAAM,EAAM;AAAA,MACZ,YAAY;AAAA,KACb,GAGK,IAAa,KAAK,IAAI,OAAO,oBAAoB,GAAG,CAAA,GACpD,IAAa,EAAO,QAAQ,IAAa,IACzC,IAAc,EAAO,SAAS,IAAa;AAEjD,IAAA,EAAK,WAAW,IAAI,EAAM,cAAc,GAAY,CAAA,GACpD,EAAK,WAAW;AAGhB,UAAM,IAAS,KAAK,WAAW,CAAA;AAC/B,IAAI,MACF,EAAO,SAAS,QAAA,GAChB,EAAO,WAAW,IAAI,GAAY,GAAY,KAAK,CAAA,IAIrD,EAAK,SAAS,SAAS,GACvB,EAAK,SAAS,UAAU,GACxB,EAAK,SAAS,OAAO;AAAA;EASvB,wBAAiC,GAA0B,GAAmC;AAE5F,UAAM,IAAW,KAAK,aAAa,CAAA;AACnC,QAAI,GAAU;AACZ,YAAM,IAAU,EAAO,IAAI,MAAA,KAAW;AACtC,MAAI,EAAS,SAAS,SAAS,KAC7B,KAAK,eAAe,GAAU,GAAS,CAAA;AAAA;AAK3C,UAAM,IAAO,WAAW,EAAO,IAAI,MAAA,KAAW,GAAA,GACxC,IAAc,KAAK,IAAI,GAAG,CAAA;AAChC,IAAA,EAAS,MAAM,IAAI,GAAa,GAAa,CAAA;AAAA;EAQ/C,0BAAyD;AACvD,WAAO,EACL,QAAQ,CACN;AAAA,MAAE,KAAK;AAAA,MAAQ,MAAM;AAAA,OACrB;AAAA,MAAE,KAAK;AAAA,MAAQ,MAAM;AAAA,MAAU,KAAK;AAAA,MAAG,KAAK;AAAA,MAAI,MAAM;AAAA,KAAG,EAC1D;AAAA;EAUL,oBAA6B,GAA+C;AAC1E,UAAM,IAAW,oBAAI,IAAA;AACrB,WAAA,EAAS,IAAI,QAAQ,EAAO,IAAI,MAAA,KAAW,OAAA,GAC3C,EAAS,IAAI,QAAQ,WAAW,EAAO,IAAI,MAAA,KAAW,GAAA,CAAI,GACnD;AAAA;EAST,oBAA6B,GAAiD;AAC5E,UAAM,IAAS,oBAAI,IAAA;AAGnB,QAAI,IAAO,OAAO,EAAS,IAAI,MAAA,KAAW,OAAA;AAC1C,IAAA,IAAO,EAAK,MAAM,GAAG,EAAmB,eAAA,GACnC,EAAK,KAAA,MACR,IAAO,UAET,EAAO,IAAI,QAAQ,CAAA;AAGnB,UAAM,IAAO,KAAK,IAAI,GAAG,OAAO,EAAS,IAAI,MAAA,CAAO,KAAK,CAAA;AACzD,WAAA,EAAO,IAAI,QAAQ,OAAO,CAAA,CAAK,GAExB;AAAA;EAUT,WAAoB,GAAgC;AAGlD,IAFA,EAAS,SAAS,YAAY,IAE1B,CAAA,EAAS,SAAS,cACtB,KAAK,oBAAoB,GAAU,EAAmB,WAAA;AAAA;EAUxD,YAAqB,GAAgC;AAGnD,IAFA,EAAS,SAAS,YAAY,IAE1B,CAAA,EAAS,SAAS,cACtB,KAAK,oBAAoB,GAAU,EAAmB,UAAA;AAAA;EAUxD,eAAwB,GAAgC;AACtD,IAAA,EAAS,SAAS,aAAa,IAC/B,KAAK,oBAAoB,GAAU,EAAmB,eAAA;AAAA;EAUxD,gBAAyB,GAAgC;AACvD,IAAA,EAAS,SAAS,aAAa,IAE3B,EAAS,SAAS,YACpB,KAAK,oBAAoB,GAAU,EAAmB,WAAA,IAEtD,KAAK,oBAAoB,GAAU,EAAmB,UAAA;AAAA;EAU1D,oBAA4B,GAA0B,GAAqB;AACzE,UAAM,IAAW,KAAK,aAAa,CAAA;AACnC,QAAI,CAAC,EACH;AAGF,UAAM,IAAS,EAAS,SAAS,QAC3B,IAAU,EAAS,SAAS,SAC5B,IAAc,EAAS,SAAS;AAEtC,QAAI,CAAC,KAAU,CAAC,KAAW,CAAC,EAC1B;AAGF,UAAM,IAAM,EAAO,WAAW,IAAA,GACxB,IAAa,KAAK,IAAI,OAAO,oBAAoB,GAAG,CAAA,GACpD,IAAiB,EAAmB,iBAAiB;AAG3D,IAAA,EAAI,UAAU,GAAG,GAAG,EAAO,OAAO,EAAO,MAAA,GACzC,EAAI,OAAO,QAAQ,CAAA,MAAoB,EAAmB,WAAA,IAC1D,EAAI,YAAY,GAChB,EAAI,YAAY,UAChB,EAAI,eAAe,UACnB,EAAI,SAAS,GAAa,EAAO,QAAQ,GAAG,EAAO,SAAS,CAAA,GAE5D,EAAQ,cAAc;AAAA;GCpZb,KAAb,cAA4C,EAA2B;AAAA,EAErE,iBAAkC;AAAA,EAElC,qBAAsC;AAAA,EAEtC,qBAAsC;AAAA,EAEtC,gBAAiC,IAAI,EAAM,iBACzC,KACA,MACA,KACA,IACA,GACA,IACA,GACA,KAAK,KAAK,CAAA;AAAA,EAEZ,gBAAiC,IAAI,EAAM,eAAe,KAAK,IAAI,EAAA;AAAA,EAGnE,UAA2B;AAAA,IAAC;AAAA,IAAG;AAAA,IAAG;AAAA;EAElC,YAA6B;AAAA,IAAC;AAAA,IAAG;AAAA,IAAG;AAAA;EAEpC,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,aAAa,GAAsB,GAAwC;AACzE,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAG9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAI3B,UAAM,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,GAAG,GAAG,CAAA;AACxE,IAAA,EAAM,IAAI,CAAA;AAGV,UAAM,IAAO,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,OAAO,EAAe,KAAA,CAAM;AACjF,IAAA,EAAK,WAAW;AAAA,MACd,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAK,SAAS,IAAI,GAAG,KAAK,CAAA,GAC1B,EAAM,IAAI,CAAA;AAEV,UAAM,IAAO,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,OAAO,EAAe,KAAA,CAAM;AACjF,WAAA,EAAK,OAAO,QACZ,EAAK,WAAW;AAAA,MACd,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAK,SAAS,IAAI,GAAG,KAAK,CAAA,GAC1B,EAAM,IAAI,CAAA,GACV,EAAK,cAAc,IAGf,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAI5C,KAAK,wBAAwB,GAAO,EAAU,MAAA,GACvC;AAAA;EAGT,iBAAyB,GAAsB,GAAwB,GAAoB;AACzF,UAAM,IAAW,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACjD,QAAI,GAAU;AACZ,YAAM,IAAY,KAAK,eAAe,GAAU,MAAA;AAChD,MAAA,EAAU,SAAS,IAAI,OAAO,GAAG,CAAA,GACjC,EAAM,IAAI,CAAA;AAEV,YAAM,IAAkB,KAAK,qBAC3B,GACA,KAAK,OAAO,EAAe,KAAA,CAAM;AAEnC,MAAM,KACJ,EAAM,IAAI,CAAA;AAAA;AAId,UAAM,IAAW,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACjD,QAAI,GAAU;AACZ,YAAM,IAAY,KAAK,eAAe,GAAU,OAAA;AAChD,MAAA,EAAU,SAAS,IAAI,MAAM,GAAG,CAAA,GAChC,EAAM,IAAI,CAAA;AAEV,YAAM,IAAkB,KAAK,qBAC3B,GACA,KAAK,OAAO,EAAe,KAAA,CAAM;AAEnC,MAAM,KACJ,EAAM,IAAI,CAAA;AAAA;;EAUhB,0BAAgE;AAC9D,WAAO,EACL,QAAQ,CACN;AAAA,MAAE,KAAK;AAAA,MAAkB,MAAM;AAAA,MAAU,KAAK;AAAA,MAAG,MAAM;AAAA,OACvD;AAAA,MAAE,KAAK;AAAA,MAAQ,MAAM;AAAA,MAAU,KAAK;AAAA,MAAG,KAAK;AAAA,MAAI,MAAM;AAAA,KAAG,EAC1D;AAAA;EAIL,oBAA6B,GAA+C;AAC1E,UAAM,IAAW,oBAAI,IAAA;AACrB,WAAA,EAAS,IAAI,kBAAkB,WAAW,EAAO,IAAI,gBAAA,KAAqB,GAAA,CAAI,GAC9E,EAAS,IAAI,QAAQ,WAAW,EAAO,IAAI,MAAA,KAAW,GAAA,CAAI,GACnD;AAAA;EAST,oBAA6B,GAAiD;AAC5E,UAAM,IAAS,oBAAI,IAAA;AACnB,WAAA,EAAO,IAAI,kBAAkB,EAAS,IAAI,gBAAA,EAAkB,SAAA,CAAU,GACtE,EAAO,IAAI,QAAQ,EAAS,IAAI,MAAA,EAAQ,SAAA,CAAU,GAC3C;AAAA;EAGT,wBAAiC,GAA0B,GAA6B;AACtF,UAAM,IAAQ,WAAW,EAAO,IAAI,MAAA,KAAW,GAAA;AAC/C,IAAA,EAAS,MAAM,IAAI,GAAO,GAAO,CAAA,GACjC,KAAK,gBAAgB,GAAU,IAAA;AAAA;EAWjC,gBAAyB,GAA0B,GAAoC;AACrF,UAAM,IAAW,KAAK,aAAa,CAAA;AACnC,QAAI,CAAC,EAAU;AAGf,QAAI,CAAC,KAAS,CAAC,KAAK,mBAAmB;AACrC,WAAK,cAAc,CAAA,GACnB,KAAK,uBAAuB,CAAA;AAC5B;AAAA;AAGF,UAAM,IAAiB;AAGvB,QAAI,KAAK,kBAAkB,qBAAqB,WAAW;AACzD,WAAK,cAAc,CAAA,GACnB,KAAK,aAAa,GAAU,EAAe,KAAA;AAC3C;AAAA;AAIF,QAAI,EAAM,eAAe;AACvB,WAAK,aAAa,GAAU,GAAU,CAAA;AACtC;AAAA;AAIF,SAAK,cAAc,CAAA,GACnB,KAAK,aAAa,GAAU,EAAe,KAAA;AAAA;EAW7C,sBAA8B,GAA4B;AACxD,UAAM,IAAM,EAAS;AACrB,IAAI,EAAI,SAAS,YAAY,EAAW,oBACxC,EAAS,WAAW,EAAI,MAAA,GACvB,EAAS,SAAuC,SAAS,UAAU,EAAW;AAAA;EAOjF,uBAA+B,GAA4B;AACzD,UAAM,IAAM,EAAS;AACrB,IAAI,EAAI,SAAS,YAAY,EAAW,oBACxC,EAAI,QAAA,GACJ,EAAS,WAAW,KAAK,OAAO,EAAe,KAAA;AAAA;EAOjD,aAAqB,GAAsB,GAAsB;AAC/D,SAAK,sBAAsB,CAAA;AAC3B,UAAM,IAAM,EAAS;AAErB,IAAI,KACF,EAAI,SAAS,OAAO,KAAK,cAAA,GACzB,EAAI,oBAAoB,KAAK,oBAC7B,EAAI,UAAU,MAEd,EAAI,SAAS,OAAO,CAAA,GACpB,EAAI,oBAAoB,GACxB,EAAI,UAAU,KAAK;AAAA;EAWvB,aACE,GACA,GACA,GACM;AAEN,QAAI,EAAS,SAAS,uBAAuB,EAAM,UAAW;AAE9D,SAAK,sBAAsB,CAAA;AAE3B,UAAM,IAAM,KAAK,kBAAmB,gBAE9B,KADO,EAAM,iBAAiB,EAAM,aACX;AAG/B,QAAI,IAA8B,EAAS,SAAS;AACpD,IAAK,MACH,IAAQ,IAAI,EAAM,eAAe,CAAA,GACjC,EAAS,SAAS,QAAQ;AAK5B,UAAM,IAAM,EAAS,UACf,IAAmB,EAAI,mBACvB,IAAiB,EAAI,SACrB,IAAa;AAAA,MAAC,EAAI,SAAS;AAAA,MAAG,EAAI,SAAS;AAAA,MAAG,EAAI,SAAS;AAAA;AAGjE,IAAI,EAAS,SAAS,iBACnB,EAAS,SAAS,cAAwC,KAAA,GAEzD,EAAS,SAAS,eACpB,EAAM,YAAY,EAAS,SAAS,WAAA;AAItC,UAAM,IAAY,EAAM,UAAU,WAC5B,IAAe,IAAY,KAAK,qBAAqB,GACrD,IAAa,IAAY,IAAI,KAAK,oBAClC,IAAS,IAAY,KAAK,UAAU,KAAK,WAEzC,IAAS;AAAA,MACb,IAAI,EAAM,oBACR,mCACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAkB,CAAA,CAAa;AAAA,MAElC,IAAI,EAAM,oBACR,yBACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAgB,CAAA,CAAW;AAAA,MAE9B,IAAI,EAAM,mBACR,0BACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAG,GAAY,GAAG,CAAA,CAAO;AAAA,OAIxB,IAAO,IAAI,EAAM,cAAc,YAAY,GAAiB,CAAA,GAC5D,IAAS,EAAM,WAAW,CAAA;AAChC,IAAA,EAAO,OAAO,EAAM,UACpB,EAAO,oBAAoB,IAC3B,EAAO,KAAA,GAGP,EAAS,SAAS,qBAAqB,EAAM,WAC7C,EAAS,SAAS,gBAAgB,GAClC,EAAS,SAAS,cAAc;AAAA;EAMlC,cAAsB,GAAgC;AACpD,UAAM,IAAQ,EAAS,SAAS;AAChC,IAAI,MACF,EAAM,cAAA,GACN,EAAM,YAAY,CAAA,GAClB,OAAO,EAAS,SAAS,QAE3B,OAAO,EAAS,SAAS,eACzB,OAAO,EAAS,SAAS,aACzB,OAAO,EAAS,SAAS;AAAA;EAO3B,aAAqB,GAA6C;AAChE,QAAI,IAA8B;AAElC,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,WACzD,IAAW;AAAA,QAIR;AAAA;GC7UE,KAAb,cAA+C,EAA2B;AAAA,EAExE,gBAAiC;AAAA,EAGjC,oBAAqC;AAAA,EAGrC,YAA6B;AAAA,IAAC;AAAA,IAAG;AAAA,IAAG;AAAA;EAEpC,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,aAAa,GAAsB,GAAwC;AACzE,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAG9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAI3B,UAAM,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,GAAG,GAAG,CAAA;AACxE,IAAA,EAAM,IAAI,CAAA;AAGV,UAAM,IAAc,IAAI,EAAM,YAAY,GAAG,GAAG,CAAA,GAC1C,IAAS,KAAK,OAAO,EAAe,KAAA,EAAO,MAAA;AACjD,IAAA,EAAO,SAAS,UAAU,EAAW;AACrC,UAAM,IAAM,IAAI,EAAM,KAAK,GAAa,CAAA;AACxC,WAAA,EAAI,OAAO,OACX,EAAI,WAAW;AAAA,MACb,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,MACN,cAAc;AAAA,MACd,gBAAgB,KAAK;AAAA,OAEvB,EAAI,SAAS,IAAI,GAAG,MAAM,CAAA,GAC1B,EAAM,IAAI,CAAA,GAGN,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAG5C,KAAK,wBAAwB,GAAO,EAAU,MAAA,GACvC;AAAA;EAGT,iBAAyB,GAAsB,GAAwB,GAAoB;AACzF,UAAM,IAAY,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAClD,QAAI,GAAW;AACb,YAAM,IAAY,KAAK,eAAe,GAAW,MAAA;AACjD,MAAA,EAAU,SAAS,IAAI,MAAM,GAAG,CAAA,GAChC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAiB,KAAK,eAAe,GAAY,OAAA;AACvD,MAAA,EAAe,SAAS,IAAI,KAAK,GAAG,CAAA,GACpC,EAAM,IAAI,CAAA;AAAA;;EASd,0BAAgE;AAC9D,WAAO,EACL,QAAQ;AAAA,MACN;AAAA,QAAE,KAAK;AAAA,QAAkB,MAAM;AAAA,QAAU,KAAK;AAAA,QAAG,MAAM;AAAA;MACvD;AAAA,QAAE,KAAK;AAAA,QAAa,MAAM;AAAA;MAC1B;AAAA,QAAE,KAAK;AAAA,QAAe,MAAM;AAAA;MAC5B;AAAA,QAAE,KAAK;AAAA,QAAQ,MAAM;AAAA,QAAU,KAAK;AAAA,QAAG,KAAK;AAAA,QAAI,MAAM;AAAA;MACtD;AAAA,QAAE,KAAK;AAAA,QAAW,MAAM;AAAA;MACxB;AAAA,QAAE,KAAK;AAAA,QAAW,MAAM;AAAA;MACzB;AAAA;EAWL,oBAA6B,GAA+C;AAC1E,UAAM,IAAW,oBAAI,IAAA;AACrB,IAAA,EAAS,IAAI,kBAAkB,WAAW,EAAO,IAAI,gBAAA,KAAqB,GAAA,CAAI;AAC9E,UAAM,IAAY,EAAO,IAAI,WAAA,KAAgB,WACvC,IAAc,EAAO,IAAI,aAAA,KAAkB;AAGjD,WAAA,EAAS,IAAI,aAAa,EAAiB,CAAA,CAAU,GACrD,EAAS,IAAI,eAAe,EAAiB,CAAA,CAAY,GACzD,EAAS,IAAI,QAAQ,WAAW,EAAO,IAAI,MAAA,KAAW,GAAA,CAAI,GAC1D,EAAS,IAAI,WAAW,WAAW,EAAO,IAAI,SAAA,KAAc,GAAA,CAAI,GAChE,EAAS,IAAI,WAAW,WAAW,EAAO,IAAI,SAAA,KAAc,GAAA,CAAI,GAEzD;AAAA;EAUT,oBAA6B,GAAiD;AAC5E,UAAM,IAAS,oBAAI,IAAA;AACnB,IAAA,EAAO,IAAI,kBAAkB,EAAS,IAAI,gBAAA,EAAkB,SAAA,CAAU;AACtE,UAAM,IAAc,EAAS,IAAI,aAAA,GAC3B,IAAY,EAAS,IAAI,WAAA;AAG/B,WAAI,KACF,EAAO,IAAI,eAAe,EAAiB,CAAA,CAAY,GAErD,KACF,EAAO,IAAI,aAAa,EAAiB,CAAA,CAAU,GAGrD,EAAO,IAAI,QAAQ,EAAS,IAAI,MAAA,EAAQ,SAAA,CAAU,GAClD,EAAO,IAAI,WAAW,EAAS,IAAI,SAAA,EAAW,SAAA,CAAU,GACxD,EAAO,IAAI,WAAW,EAAS,IAAI,SAAA,EAAW,SAAA,CAAU,GACjD;AAAA;EAOT,wBAAiC,GAA0B,GAAmC;AAC5F,UAAM,IAAU,KAAK,YAAY,CAAA,GAC3B,IAAO,KAAK,aAAa,GAAU,MAAA,GACnC,IAAO,KAAK,aAAa,GAAU,MAAA,GACnC,IAAS,KAAK,WAAW,CAAA;AAE/B,QAAI,CAAC,KAAW,CAAC,KAAQ,CAAC,KAAQ,CAAC,EAAQ;AAE3C,UAAM,IAAS,EAAQ,UAGjB,IAAY,EAAO,IAAI,WAAA;AAC7B,QAAI,GAAW;AAEb,YAAM,IAAe,SAAS,EAAiB,CAAA,EAAW,QAAQ,KAAK,EAAA,GAAK,EAAA;AAC5E,MAAA,EAAO,MAAM,OAAO,CAAA,GACpB,EAAQ,SAAS,eAAe;AAAA;AAElC,UAAM,IAAc,EAAO,IAAI,aAAA;AAC/B,IAAI,MAEF,EAAQ,SAAS,iBAAiB,SAChC,EAAiB,CAAA,EAAa,QAAQ,KAAK,EAAA,GAC3C,EAAA;AAIJ,UAAM,IAAU,WAAW,EAAO,IAAI,SAAA,KAAc,GAAA,GAC9C,IAAU,WAAW,EAAO,IAAI,SAAA,KAAc,GAAA;AACpD,IAAA,EAAQ,SAAS,QAAA,GACjB,EAAQ,WAAW,IAAI,EAAM,YAAY,GAAG,GAAS,CAAA,GACrD,EAAQ,SAAS,IAAI,GAAG,OAAO,GAAS,CAAA,GACxC,EAAO,SAAS,QAAA,GAChB,EAAO,WAAW,IAAI,EAAM,YAAY,GAAG,MAAM,GAAS,CAAA;AAE1D,UAAM,IAAW,KAAW,MAAM,IAAI,IAAU;AAChD,IAAA,EAAK,MAAM,IAAI,GAAU,GAAU,CAAA,GACnC,EAAK,MAAM,IAAI,GAAU,GAAU,CAAA;AAGnC,UAAM,IAAQ,WAAW,EAAO,IAAI,MAAA,KAAW,GAAA;AAC/C,IAAA,EAAS,MAAM,IAAI,GAAO,GAAO,CAAA,GAEjC,KAAK,gBAAgB,GAAU,IAAA;AAAA;EAWjC,gBAAyB,GAA0B,GAAoC;AACrF,UAAM,IAAU,KAAK,YAAY,CAAA;AACjC,QAAI,CAAC,EAAS;AAGd,QAAI,CAAC,KAAS,CAAC,KAAK,mBAAmB;AACrC,WAAK,cAAc,CAAA,GACnB,KAAK,wBAAwB,CAAA;AAC7B;AAAA;AAGF,UAAM,IAAW;AAGjB,QAAI,KAAK,kBAAkB,qBAAqB,WAAW;AACzD,WAAK,cAAc,CAAA,GACnB,KAAK,aAAa,GAAS,EAAS,KAAA;AACpC;AAAA;AAIF,QAAI,EAAM,eAAe;AACvB,WAAK,YAAY,GAAU,GAAS,CAAA;AACpC;AAAA;AAIF,SAAK,cAAc,CAAA,GACnB,KAAK,aAAa,GAAS,EAAS,KAAA;AAAA;EAYtC,sBAA8B,GAA2B;AACvD,UAAM,IAAM,EAAQ;AACpB,IAAI,EAAI,SAAS,YAAY,EAAW,oBACxC,EAAQ,SAAS,aAAa,GAC9B,EAAQ,WAAW,EAAI,MAAA,GACtB,EAAQ,SAAuC,SAAS,UAAU,EAAW;AAAA;EAOhF,wBAAgC,GAA2B;AACzD,UAAM,IAAM,EAAQ;AACpB,IAAI,EAAI,SAAS,YAAY,EAAW,oBACxC,EAAI,QAAA,GACA,EAAQ,SAAS,eACnB,EAAQ,WAAW,EAAQ,SAAS,YACpC,OAAO,EAAQ,SAAS;AAAA;EAQ5B,aAAqB,GAAqB,GAAsB;AAC9D,SAAK,sBAAsB,CAAA;AAC3B,UAAM,IAAM,EAAQ;AAEpB,IAAI,KACF,EAAI,SAAS,OAAO,EAAQ,SAAS,cAAA,GACrC,EAAI,oBAAoB,KAAK,sBAE7B,EAAI,SAAS,OAAO,CAAA,GACpB,EAAI,oBAAoB;AAAA;EAW5B,YAAoB,GAA0B,GAAqB,GAA6B;AAE9F,QAAI,EAAS,SAAS,uBAAuB,EAAM,UAAW;AAE9D,SAAK,sBAAsB,CAAA;AAE3B,UAAM,IAAM,KAAK,kBAAmB,gBAE9B,KADO,EAAM,iBAAiB,EAAM,aACX;AAG/B,QAAI,IAA8B,EAAS,SAAS;AACpD,IAAK,MACH,IAAQ,IAAI,EAAM,eAAe,CAAA,GACjC,EAAS,SAAS,QAAQ;AAK5B,UAAM,IAAM,EAAQ,UACd,IAAmB,EAAI,mBACvB,IAAa;AAAA,MAAC,EAAI,SAAS;AAAA,MAAG,EAAI,SAAS;AAAA,MAAG,EAAI,SAAS;AAAA;AAGjE,IAAI,EAAS,SAAS,iBACnB,EAAS,SAAS,cAAwC,KAAA,GAEzD,EAAS,SAAS,eACpB,EAAM,YAAY,EAAS,SAAS,WAAA;AAItC,UAAM,IAAY,EAAM,UAAU,WAC5B,IAAe,IAAY,KAAK,oBAAoB,GACpD,IAAY,EAAQ,SAAS,gBAC7B,IAAc,IAAI,EAAM,MAAM,CAAA,GAC9B,IAAS,IAAY;AAAA,MAAC,EAAY;AAAA,MAAG,EAAY;AAAA,MAAG,EAAY;AAAA,QAAK,KAAK,WAE1E,IAAS,CACb,IAAI,EAAM,oBACR,kCACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAkB,CAAA,CAAa,GAElC,IAAI,EAAM,mBACR,yBACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAG,GAAY,GAAG,CAAA,CAAO,CAC3B,GAGG,IAAO,IAAI,EAAM,cAAc,WAAW,GAAiB,CAAA,GAC3D,IAAS,EAAM,WAAW,CAAA;AAChC,IAAA,EAAO,OAAO,EAAM,UACpB,EAAO,oBAAoB,IAC3B,EAAO,KAAA,GAGP,EAAS,SAAS,qBAAqB,EAAM,WAC7C,EAAS,SAAS,gBAAgB,GAClC,EAAS,SAAS,cAAc;AAAA;EAMlC,cAAsB,GAAgC;AACpD,UAAM,IAAQ,EAAS,SAAS;AAChC,IAAI,MACF,EAAM,cAAA,GACN,EAAM,YAAY,CAAA,GAClB,OAAO,EAAS,SAAS,QAE3B,OAAO,EAAS,SAAS,eACzB,OAAO,EAAS,SAAS,aACzB,OAAO,EAAS,SAAS;AAAA;EAO3B,YAAoB,GAA6C;AAC/D,QAAI,IAA6B;AAEjC,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,UACzD,IAAU;AAAA,QAIP;AAAA;GC1WE,KAAb,cAAwC,EAA2B;AAAA,EAKjE,YAA6B,GAAa,MAAM,KAAK,KAAK,EAAA;AAAA,EAC1D,gBAAiC,IAAI,EAAM,YAAY,KAAK,KAAK,KAAK,CAAA;AAAA,EACtE,gBAAiC,IAAI,EAAM,YAAY,KAAK,KAAK,MAAM,CAAA;AAAA,EACvE,iBAAkC,GAAU,MAAM,MAAM,KAAK,KAAK,IAAO,KAAK,KAAK,EAAA;AAAA,EACnF,0BAA2C,GAAU,MAAM,MAAM,KAAK,KAAK,IAAO,KAAK,KAAK,EAAA;AAAA,EAM5F,kBAAmC;AAAA,EACnC,oBAAqC;AAAA,EAMrC,gBAAiC,IAAI,EAAM,MAAM,CAAC,KAAK,KAAK,GAAG,GAAG,CAAC,KAAK,EAAA;AAAA,EACxE,kBAAmC,IAAI,EAAM,MAAM,CAAC,KAAK,KAAK,GAAG,GAAG,CAAC,KAAK,KAAK,IAAA;AAAA,EAO/E,kBAAmC,IAAI,EAAM,MAAM,QAAA;AAAA,EACnD,qBAAsC,IAAI,EAAM,MAAM,QAAA;AAAA,EACtD,qBAAsC,IAAI,EAAM,MAAM,OAAA;AAAA,EACtD,kBAAmC,IAAI,EAAM,MAAM,QAAA;AAAA,EAGnD,iBAAkC,IAAI,EAAM,MAAM,QAAA;AAAA,EAClD,qBAAsC;AAAA,EAGtC,mBAAoC,IAAI,EAAM,MAAM,QAAA;AAAA,EACpD,sBAAuC,IAAI,EAAM,MAAM,QAAA;AAAA,EACvD,sBAAuC,IAAI,EAAM,MAAM,OAAA;AAAA,EACvD,mBAAoC,IAAI,EAAM,MAAM,QAAA;AAAA,EAEpD,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAOtC,aAAa,GAAsB,GAAwC;AACzE,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAE9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAG3B,UAAM,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,GAAG,GAAG,CAAA;AACxE,IAAA,EAAM,IAAI,CAAA;AAEV,UAAM,IAAY,KAAK,iBAAiB,CAAA;AACxC,IAAA,EAAM,IAAI,CAAA,GACV,EAAU,SAAS,IAAI,MAAM,GAAG,CAAA;AAEhC,UAAM,IAAiB,KAAK,sBAAsB,EAAU,IAAI,EAAU,MAAA;AAC1E,IAAA,EAAM,IAAI,CAAA,GACV,EAAe,SAAS,IAAI,GAAG,GAAG,IAAA,GAClC,EAAe,SAAS,KAAK,KAAK,aAAA;AAElC,UAAM,IAAa,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,OAAO,EAAe,KAAA,CAAM;AACvF,WAAA,EAAW,OAAO,cAClB,EAAW,WAAW,EAAE,MAAM,aAAA,GAC9B,EAAW,QAAQ,CAAC,KAAK,KAAK,CAAA,GAC9B,EAAW,SAAS,IAAI,MAAM,GAAG,IAAA,GACjC,EAAM,IAAI,CAAA,GAEN,EAAU,KAAK,SAAS,KAC1B,KAAK,kBAAkB,GAAW,GAAS,CAAA,GAG7C,KAAK,wBAAwB,GAAO,EAAU,MAAA,GACvC;AAAA;EAGT,kBACE,GACA,GACA,GACM;AACN,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,MAAA;AACpD,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,GAAA,GAClC,EAAM,IAAI,CAAA;AACV,YAAM,IAAc,KAAK,qBAAqB,GAAa,KAAK,OAAO,EAAe,KAAA,CAAM;AAC5F,MAAI,KAAa,EAAM,IAAI,CAAA;AAAA;AAG7B,UAAM,IAAc,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACpD,QAAI,GAAa;AACf,YAAM,IAAe,KAAK,eAAe,GAAa,OAAA;AACtD,MAAA,EAAa,SAAS,IAAI,KAAK,GAAG,IAAA,GAClC,EAAa,cAAc,GAC3B,EAAa,SAAS,QAAA,CAAS,MAAU;AACvC,QAAA,EAAM,cAAc;AAAA,UAEtB,EAAM,IAAI,CAAA;AACV,YAAM,IAAc,KAAK,qBACvB,GACA,KAAK,OAAO,EAAe,KAAA,CAAM;AAEnC,MAAI,KAAa,EAAM,IAAI,CAAA;AAAA;AAG7B,UAAM,IAAY,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAClD,QAAI,GAAW;AACb,YAAM,IAAa,KAAK,eAAe,GAAW,MAAA;AAClD,MAAA,EAAW,SAAS,IAAI,MAAM,GAAG,IAAA,GACjC,EAAM,IAAI,CAAA;AACV,YAAM,IAAc,KAAK,qBAAqB,GAAY,KAAK,OAAO,EAAe,KAAA,CAAM;AAC3F,MAAI,KAAa,EAAM,IAAI,CAAA;AAAA;AAG7B,UAAM,IAAe,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACrD,QAAI,GAAc;AAChB,YAAM,IAAgB,KAAK,eAAe,GAAc,OAAA;AACxD,MAAA,EAAc,SAAS,IAAI,KAAK,GAAG,GAAA,GACnC,EAAM,IAAI,CAAA;AACV,YAAM,IAAc,KAAK,qBACvB,GACA,KAAK,OAAO,EAAe,KAAA,CAAM;AAEnC,MAAI,KAAa,EAAM,IAAI,CAAA;AAAA;;EAI/B,iBAAyB,GAAmC;AAC1D,UAAM,IAAY,IAAI,EAAM,MAAA;AAC5B,IAAA,EAAU,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA;AAGR,UAAM,IAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA;AAGR,QAAI,IAAY;AAChB,UAAM,IAAA,CAAW,GAAW,MAAe;AACzC,YAAM,IAAO,IAAI,EAAM,KAAK,KAAK,WAAW,KAAK,OAAO,EAAe,KAAA,CAAM;AAC7E,MAAA,EAAK,OAAO,OAAO,CAAA,IACnB,EAAK,WAAW,EAAE,GAAG,EAAA,GACrB,EAAK,SAAS,IAAI,GAAG,GAAG,CAAA,GACxB,EAAK,QAAQ,CAAA,GACb,EAAU,IAAI,CAAA,GACd;AAAA;AAGF,IAAA,EAAQ,KAAK,KAAA,GACb,EAAQ,KAAK,IAAA,GACb,EAAQ,KAAK,KAAA,GACb,EAAQ,GAAG,IAAA,GACX,EAAQ,MAAM,KAAA,GACd,EAAQ,MAAM,IAAA,GACd,EAAQ,MAAM,KAAA;AAEd,UAAM,IAAM,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,OAAO,EAAe,YAAA,CAAa;AACvF,WAAA,EAAI,OAAO,WACX,EAAI,WAAW;AAAA,MAAE,GAAG;AAAA,MAAU,MAAM;AAAA,OACpC,EAAU,IAAI,CAAA,GAEP;AAAA;EAGT,sBAA8B,GAAqB,GAA0C;AAC3F,UAAM,IAAiB,IAAI,EAAM,MAAA;AACjC,IAAA,EAAe,OAAO,kBACtB,EAAe,WAAW;AAAA,MACxB,MAAM;AAAA,MACO,aAAA;AAAA,MACb,MAAM;AAAA,MACN,SAAS,EAAO,IAAI,iBAAA;AAAA;AAGtB,UAAM,IAAkB,EAAO,IAAI,iBAAA,GAC7B,IACJ,MAAoB,aAAa,KAAK,0BAA0B,KAAK,gBAEjE,IAAY,IAAI,EAAM,KAAK,GAAM,KAAK,OAAO,EAAe,KAAA,CAAM;AACxE,WAAA,EAAU,OAAO,aACjB,EAAU,WAAW;AAAA,MACnB,MAAM;AAAA,MACO,aAAA;AAAA,MACb,MAAM;AAAA,MACN,SAAS;AAAA,OAEX,EAAU,WAAW,IAAA,GACrB,EAAe,IAAI,CAAA,GAEZ;AAAA;EAGT,uBAA+B,GAAgC;AAC7D,UAAM,IAAiB,KAAK,oBAAoB,CAAA;AAChD,IAAK,KACL,EAAS,OAAO,CAAA;AAAA;EAOlB,0BAAgE;AAC9D,WAAO,EACL,QAAQ;AAAA,MACN;AAAA,QAAE,KAAK;AAAA,QAAmB,MAAM;AAAA;MAChC;AAAA,QAAE,KAAK;AAAA,QAAkB,MAAM;AAAA;MAC/B;AAAA,QAAE,KAAK;AAAA,QAAQ,MAAM;AAAA,QAAU,KAAK;AAAA,QAAG,KAAK;AAAA,QAAI,MAAM;AAAA;MACtD;AAAA,QAAE,KAAK;AAAA,QAAuB,MAAM;AAAA;MACrC;AAAA;EAIL,oBAA6B,GAA+C;AAC1E,UAAM,IAAW,oBAAI,IAAA;AACrB,WAAA,EAAS,IAAI,mBAAmB,EAAO,IAAI,iBAAA,MAAuB,UAAA,GAClE,EAAS,IAAI,kBAAkB,WAAW,EAAO,IAAI,gBAAA,KAAqB,GAAA,CAAI,GAC9E,EAAS,IAAI,QAAQ,WAAW,EAAO,IAAI,MAAA,KAAW,GAAA,CAAI,GAC1D,EAAS,IAAI,uBAAuB,WAAW,EAAO,IAAI,qBAAA,KAA0B,GAAA,CAAI,GACjF;AAAA;EAGT,oBAA6B,GAAiD;AAC5E,UAAM,IAAS,oBAAI,IAAA;AACnB,WAAA,EAAO,IAAI,mBAAmB,EAAS,IAAI,iBAAA,IAAqB,aAAa,UAAA,GAC7E,EAAO,IAAI,kBAAkB,EAAS,IAAI,gBAAA,EAAkB,SAAA,CAAU,GACtE,EAAO,IAAI,QAAQ,EAAS,IAAI,MAAA,EAAQ,SAAA,CAAU,GAClD,EAAO,IAAI,uBAAuB,EAAS,IAAI,qBAAA,EAAuB,SAAA,KAAc,GAAA,GAC7E;AAAA;EAGT,wBAAiC,GAA0B,GAAmC;AAC5F,UAAM,IAAiB,KAAK,oBAAoB,CAAA;AAChD,QAAI,KACqB,EAAe,SAAS,YAC5B,EAAO,IAAI,iBAAA,GACK;AACjC,WAAK,uBAAuB,CAAA;AAC5B,YAAM,IAAW,KAAK,sBAAsB,EAAS,SAAS,aAAa,CAAA;AAC3E,MAAA,EAAS,IAAI,CAAA,GACb,EAAS,SAAS,IAAI,GAAG,GAAG,IAAA,GAC5B,EAAS,SAAS,KAAK,KAAK,aAAA;AAAA;AAIhC,IAAA,EAAS,SAAS,sBAAsB,KAAK,IAC3C,GACA,SAAS,EAAO,IAAI,gBAAA,KAAqB,KAAK,EAAA,KAAO,CAAA;AAGvD,UAAM,IAAQ,WAAW,EAAO,IAAI,MAAA,KAAW,GAAA;AAC/C,IAAA,EAAS,MAAM,IAAI,GAAO,GAAO,CAAA,GACjC,KAAK,gBAAgB,GAAU,IAAA;AAAA;EAejC,gBAAyB,GAA0B,GAAoC;AACrF,UAAM,IAAQ,KAAK,WAAW,CAAA;AAC9B,QAAI,CAAC,EAAO;AAEZ,UAAM,IAAU,EAAM,eAAe,SAAS;AAG9C,QAAI,CAAC,KAAS,CAAC,KAAK,mBAAmB;AACrC,WAAK,cAAc,CAAA,GACnB,KAAK,qBAAqB,CAAA,GAC1B,KAAK,gBAAgB,CAAA,GACrB,OAAO,EAAS,SAAS,cACzB,OAAO,EAAS,SAAS;AACzB;AAAA;AAIF,QAAI,KAAK,kBAAkB,qBAAqB,WAAW;AACzD,WAAK,cAAc,CAAA,GACnB,KAAK,gBAAgB,GAAO,EAAM,OAAO,CAAA,GACzC,KAAK,eAAe,GAAO,CAAA,GAC3B,KAAK,eAAe,GAAO,EAAM,OAAO,CAAA,GACxC,KAAK,gBAAgB,GAAO,CAAA,GAC5B,EAAS,SAAS,eAAe,KAAK,kBAAkB,CAAA,EAAO,aAAA,GAC/D,EAAS,SAAS,gBAAgB,KAAK,mBAAmB,CAAA,EAAO,aAAA;AACjE;AAAA;AAQF,QAJA,KAAK,qBAAqB,GAAU,GAAO,CAAA,GAC3C,KAAK,sBAAsB,GAAU,GAAO,CAAA,GAGxC,EAAM,eAAe;AACvB,WAAK,mBAAmB,GAAU,GAAO,GAAO,CAAA;AAChD;AAAA;AAIF,SAAK,uBAAuB,CAAA,GAC5B,KAAK,gBAAgB,GAAO,EAAM,OAAO,CAAA,GACzC,KAAK,eAAe,GAAO,EAAM,OAAO,CAAA;AAAA;EAQ1C,aAAqB,GAAmB,GAAsC;AAC5E,WAAI,MAAY,aACP,MAAc,UAAU,MAAc,YAExC,MAAc,YAAY,MAAc;AAAA;EAIjD,eAAuB,GAAmB,GAAsC;AAC9E,WAAI,MAAY,aAAmB,MAAc,YAC1C,MAAc;AAAA;EAOvB,kBAA0B,GAAoC;AAC5D,UAAM,IAAW,EAAM,UAAU,IAAI,gBAAA;AACrC,WAAK,IACD,EAAS,cAAc,EAAS,aAAmB,KAAK,kBACxD,EAAS,aAAmB,KAAK,qBACjC,EAAS,aAAmB,KAAK,qBAC9B,KAAK,kBAJU,KAAK;AAAA;EAO7B,mBAA2B,GAAoC;AAC7D,UAAM,IAAU,EAAM,UAAU,IAAI,UAAA,GAC9B,IAAW,EAAM,UAAU,IAAI,WAAA,GAC/B,IAAa,EAAM,UAAU,IAAI,oBAAA;AACvC,WAAI,CAAC,KAAW,CAAC,KAAY,CAAC,IAAmB,KAAK,mBAGlD,EAAM,UAAU,YAAY,EAAM,UAAU,YAC1C,EAAW,cAAc,EAAW,aAAmB,KAAK,mBAC5D,EAAS,aAAmB,KAAK,sBACjC,EAAS,aAAmB,KAAK,sBAC9B,KAAK,mBAGV,EAAQ,cAAc,EAAQ,aAAmB,KAAK,mBACtD,EAAQ,aAAmB,KAAK,sBAChC,EAAQ,aAAmB,KAAK,sBAC7B,KAAK;AAAA;EAGd,0BAAkC,GAAkC;AAClE,UAAM,IAAM,KAAK,kBAAmB,gBAC9B,IAAa,EAAS,SAAS,uBAAkC;AACvE,WAAO,KAAK,IAAI,IAAY,GAAK,IAAA;AAAA;EAOnC,gBAAwB,GAAmB,GAAmB,GAAmC;AAE/F,QADuB,MAAc,aAAa,MAAc,WAC5C;AAElB,YAAM,IAAc,KAAK,eAAe,GAAW,CAAA;AACnD,MAAA,EAAM,eAAe,SAAS,KAAK,IAAc,KAAK,gBAAgB,KAAK,eAAA,GAC3E,EAAM,QAAQ,SAAS,IAAI,IAAc,KAAK,kBAAkB,KAAK;AACrE;AAAA;AAEF,UAAM,IAAS,KAAK,aAAa,GAAW,CAAA;AAC5C,IAAA,EAAM,eAAe,SAAS,KAAK,IAAS,KAAK,kBAAkB,KAAK,aAAA,GACxE,EAAM,QAAQ,SAAS,IAAI,IAAS,KAAK,oBAAoB,KAAK;AAAA;EAGpE,eAAuB,GAAmB,GAA6B;AACrE,SAAK,0BAA0B,CAAA,GACnB,EAAM,UAAU,SACxB,MAAM,KAAK,KAAK,kBAAkB,CAAA,CAAM;AAAA;EAG9C,eAAuB,GAAmB,GAAmB,GAAmC;AAC9F,SAAK,yBAAyB,CAAA;AAC9B,UAAM,IAAM,EAAM,QAAQ,UAIpB,IADiB,MAAc,aAAa,MAAc,YAE5D,CAAC,KAAK,eAAe,GAAW,CAAA,IAChC,KAAK,aAAa,GAAW,CAAA,GAE3B,IAAgB,KAAK,OAAO,EAAe,YAAA;AAEjD,IAAA,EAAI,SAAS,KAAK,IAAS,KAAK,iBAAiB,EAAc,QAAA,GAC/D,EAAI,oBAAoB,IAAS,KAAK,qBAAqB,EAAc;AAAA;EAG3E,gBAAwB,GAAmB,GAA6B;AACtE,SAAK,2BAA2B,CAAA,GACpB,EAAM,UAAU,SACxB,MAAM,KAAK,KAAK,mBAAmB,CAAA,CAAM;AAAA;EAQ/C,0BAAkC,GAAyB;AAEzD,QADY,EAAM,UAAU,SACpB,SAAS,YAAY,EAAW,gBAAiB;AAEzD,UAAM,IAAQ,KAAK,OAAO,EAAe,KAAA,EAAO,MAAA;AAChD,IAAA,EAAM,SAAS,UAAU,EAAW;AACpC,eAAW,KAAQ,EAAM,SACvB,CAAA,EAAK,WAAW;AAAA;EAKpB,yBAAiC,GAAyB;AAExD,IADY,EAAM,QAAQ,SAClB,SAAS,YAAY,EAAW,oBAExC,EAAM,QAAQ,WAAW,KAAK,OAAO,EAAe,YAAA,EAAc,MAAA,GACjE,EAAM,QAAQ,SAAuC,SAAS,UAC7D,EAAW;AAAA;EAIf,2BAAmC,GAAyB;AAE1D,QADY,EAAM,UAAU,SACpB,SAAS,YAAY,EAAW,gBAAiB;AAEzD,UAAM,IAAQ,KAAK,OAAO,EAAe,KAAA,EAAO,MAAA;AAChD,IAAA,EAAM,SAAS,UAAU,EAAW,iBACpC,EAAM,UAAU,WAAW,GAC3B,EAAM,WAAW,WAAW;AAAA;EAI9B,qBAA6B,GAAyB;AAEpD,UAAM,IAAU,EAAM,UAAU;AAChC,QAAI,EAAQ,SAAS,YAAY,EAAW,iBAAiB;AAC3D,MAAA,EAAQ,QAAA;AACR,YAAM,IAAS,KAAK,OAAO,EAAe,KAAA;AAC1C,iBAAW,KAAQ,EAAM,SACvB,CAAA,EAAK,WAAW;AAAA;AAKpB,UAAM,IAAS,EAAM,QAAQ;AAC7B,IAAI,EAAO,SAAS,YAAY,EAAW,oBACzC,EAAO,QAAA,GACP,EAAM,QAAQ,WAAW,KAAK,OAAO,EAAe,YAAA;AAItD,UAAM,IAAW,EAAM,UAAU;AACjC,QAAI,EAAS,SAAS,YAAY,EAAW,iBAAiB;AAC5D,MAAA,EAAS,QAAA;AACT,YAAM,IAAS,KAAK,OAAO,EAAe,KAAA;AAC1C,MAAA,EAAM,UAAU,WAAW,GAC3B,EAAM,WAAW,WAAW;AAAA;;EAQhC,mBACE,GACA,GACA,GACA,GACM;AACN,QAAI,EAAS,SAAS,cAAc,EAAM,UAAW;AAErD,SAAK,yBAAyB,CAAA;AAE9B,UAAM,IAAM,KAAK,kBAAmB,gBAC9B,KAAgB,EAAM,iBAAiB,EAAM,aAAa;AAEhE,QAAI,IAAQ,EAAS,SAAS;AAC9B,IAAK,MACH,IAAQ,IAAI,EAAM,eAAe,CAAA,GACjC,EAAS,SAAS,QAAQ;AAI5B,UAAM,IAAW,EAAM,eAAe,SAAS,GACzC,IAAc,EAAM,QAAQ,SAAS,GACrC,IAAS,EAAM,QAAQ,UACvB,IAAW;AAAA,MAAC,EAAO,SAAS;AAAA,MAAG,EAAO,SAAS;AAAA,MAAG,EAAO,SAAS;AAAA,OAClE,IAAW,EAAO;AAGxB,IAAI,EAAS,SAAS,cACnB,EAAS,SAAS,WAAqC,KAAA,GAEtD,EAAS,SAAS,YACpB,EAAM,YAAY,EAAS,SAAS,QAAA;AAItC,UAAM,IAAc,KAAK,eAAe,EAAM,OAAO,CAAA,GAC/C,IAAa,IAAc,KAAK,gBAAgB,IAAI,KAAK,cAAc,GACvE,IAAa,IAAc,KAAK,oBAAoB,KAAK,iBAEzD,IAAgB,KAAK,OAAO,EAAe,YAAA,GAC3C,IAAa,IAAc,KAAK,iBAAiB,EAAc,UAC/D,IAAgB,IAAc,KAAK,qBAAqB,EAAc,mBAEtE,IAAS;AAAA,MACb,IAAI,EAAM,oBACR,8BACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAU,CAAA,CAAW;AAAA,MAExB,IAAI,EAAM,oBACR,uBACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAa,CAAA,CAAW;AAAA,MAE3B,IAAI,EAAM,mBACR,6BACA,CAAC,GAAG,CAAA,GACJ;AAAA,QAAC,GAAG;AAAA,QAAU,EAAW;AAAA,QAAG,EAAW;AAAA,QAAG,EAAW;AAAA,OAAE;AAAA,MAEzD,IAAI,EAAM,oBACR,sCACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAU,CAAA,CAAc;AAAA,OAIvB,IAAO,IAAI,EAAM,cAAc,mBAAmB,GAAc,CAAA,GAChE,IAAS,EAAM,WAAW,CAAA;AAChC,IAAA,EAAO,OAAO,EAAM,UACpB,EAAO,oBAAoB,IAC3B,EAAO,KAAA,GAEP,EAAS,SAAS,YAAY,EAAM,WACpC,EAAS,SAAS,aAAa,GAC/B,EAAS,SAAS,WAAW;AAAA;EAO/B,qBACE,GACA,GACA,GACM;AACN,UAAM,IAAS,KAAK,kBAAkB,CAAA,GAChC,IAAM,EAAO,aAAA;AACnB,QAAI,EAAS,SAAS,iBAAiB,EAAK;AAE5C,SAAK,0BAA0B,CAAA;AAC/B,UAAM,IAAe,KAAK,0BAA0B,CAAA;AAEpD,QAAI,IAAQ,EAAS,SAAS;AAC9B,IAAK,MACH,IAAQ,IAAI,EAAM,eAAe,CAAA,GACjC,EAAS,SAAS,QAAQ;AAG5B,UAAM,IAAM,EAAM,UAAU,UACtB,IAAS;AAAA,MAAC,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA;AAGpD,IAAI,EAAS,SAAS,mBACnB,EAAS,SAAS,gBAA0C,KAAA,GAE3D,EAAS,SAAS,iBACpB,EAAM,YAAY,EAAS,SAAS,aAAA;AAGtC,UAAM,IAAO,IAAI,EAAM,cAAc,kBAAkB,GAAc,CACnE,IAAI,EAAM,mBACR,wBACA,CAAC,GAAG,CAAA,GACJ;AAAA,MAAC,GAAG;AAAA,MAAQ,EAAO;AAAA,MAAG,EAAO;AAAA,MAAG,EAAO;AAAA,KAAE,CAC1C,CACF,GACK,IAAS,EAAM,WAAW,CAAA;AAChC,IAAA,EAAO,OAAO,EAAM,UACpB,EAAO,oBAAoB,IAC3B,EAAO,KAAA,GAEP,EAAS,SAAS,eAAe,GACjC,EAAS,SAAS,kBAAkB,GACpC,EAAS,SAAS,gBAAgB;AAAA;EAOpC,sBACE,GACA,GACA,GACM;AACN,UAAM,IAAS,KAAK,mBAAmB,CAAA,GACjC,IAAM,EAAO,aAAA;AACnB,QAAI,EAAS,SAAS,kBAAkB,EAAK;AAE7C,SAAK,2BAA2B,CAAA;AAChC,UAAM,IAAe,KAAK,0BAA0B,CAAA;AAEpD,QAAI,IAAQ,EAAS,SAAS;AAC9B,IAAK,MACH,IAAQ,IAAI,EAAM,eAAe,CAAA,GACjC,EAAS,SAAS,QAAQ;AAG5B,UAAM,IAAM,EAAM,UAAU,UACtB,IAAS;AAAA,MAAC,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA;AAGpD,IAAI,EAAS,SAAS,oBACnB,EAAS,SAAS,iBAA2C,KAAA,GAE5D,EAAS,SAAS,kBACpB,EAAM,YAAY,EAAS,SAAS,cAAA;AAGtC,UAAM,IAAO,IAAI,EAAM,cAAc,mBAAmB,GAAc,CACpE,IAAI,EAAM,mBACR,4BACA,CAAC,GAAG,CAAA,GACJ;AAAA,MAAC,GAAG;AAAA,MAAQ,EAAO;AAAA,MAAG,EAAO;AAAA,MAAG,EAAO;AAAA,KAAE,CAC1C,CACF,GACK,IAAS,EAAM,WAAW,CAAA;AAChC,IAAA,EAAO,OAAO,EAAM,UACpB,EAAO,oBAAoB,IAC3B,EAAO,KAAA,GAEP,EAAS,SAAS,gBAAgB,GAClC,EAAS,SAAS,mBAAmB,GACrC,EAAS,SAAS,iBAAiB;AAAA;EAQrC,uBAA+B,GAAgC;AAC7D,UAAM,IAAQ,EAAS,SAAS;AAChC,IAAI,EAAS,SAAS,eACnB,EAAS,SAAS,WAAqC,KAAA,GACpD,KAAS,EAAS,SAAS,YAC7B,EAAM,YAAY,EAAS,SAAS,QAAA,IAGxC,OAAO,EAAS,SAAS,YACzB,OAAO,EAAS,SAAS,UACzB,OAAO,EAAS,SAAS;AAAA;EAI3B,cAAsB,GAAgC;AACpD,UAAM,IAAQ,EAAS,SAAS;AAChC,IAAI,MACF,EAAM,cAAA,GACN,EAAM,YAAY,CAAA,GAClB,OAAO,EAAS,SAAS,QAE3B,OAAO,EAAS,SAAS,YACzB,OAAO,EAAS,SAAS,UACzB,OAAO,EAAS,SAAS,WACzB,OAAO,EAAS,SAAS,iBACzB,OAAO,EAAS,SAAS,eACzB,OAAO,EAAS,SAAS,kBACzB,OAAO,EAAS,SAAS;AAAA;EAI3B,gBAAwB,GAAyB;AAC/C,IAAA,EAAM,eAAe,SAAS,KAAK,KAAK,aAAA,GACxC,EAAM,QAAQ,SAAS,IAAI,KAAK;AAAA;EAQlC,WAAmB,GAA6C;AAC9D,QAAI,IAAqC,MACrC,IAA+B,MAC/B,IAA6B,MAC7B,IAA+B;AACnC,UAAM,IAAyB,CAAA;AAC/B,QAAI,IAAgC;AAkBpC,WAhBA,EAAS,SAAA,CAAU,MAAU;AAC3B,YAAM,IAAO,EAAM,SAAS;AAC5B,MAAI,MAAS,oBAAoB,aAAiB,EAAM,QACtD,IAAiB,IACR,MAAS,eAAe,aAAiB,EAAM,OACxD,IAAY,IACH,MAAS,aAAa,aAAiB,EAAM,OACtD,IAAU,IACD,MAAS,UAAU,aAAiB,EAAM,QACnD,EAAS,KAAK,CAAA,GACT,MAAW,IAAY,MACnB,MAAS,gBAAgB,aAAiB,EAAM,SACzD,IAAa;AAAA,QAIb,CAAC,KAAkB,CAAC,KAAa,CAAC,KAAW,CAAC,KAAa,CAAC,IAAmB,OAC5E;AAAA,MAAE,gBAAA;AAAA,MAAgB,WAAA;AAAA,MAAW,SAAA;AAAA,MAAS,WAAA;AAAA,MAAW,UAAA;AAAA,MAAU,YAAA;AAAA;;EAGpE,oBAA4B,GAAiD;AAC3E,QAAI,IAA+B;AACnC,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,SAAS,EAAM,SAAS,SAAS,qBAC1D,IAAQ;AAAA,QAGL;AAAA;GCxwBE,KAAb,cAA2C,EAA2B;AAAA,EAEpE,gBAAiC;AAAA,EAEjC,oBAAqC;AAAA,EAGrC,YAA6B;AAAA,IAAC;AAAA,IAAG;AAAA,IAAG;AAAA;EAEpC,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,aAAa,GAAsB,GAAwC;AACzE,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAG9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAI3B,UAAM,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,GAAG,GAAG,CAAA;AACxE,IAAA,EAAM,IAAI,CAAA;AAGV,UAAM,IAAc,IAAI,EAAM,iBAAiB,MAAM,MAAM,GAAG,IAAI,GAAG,IAAO,GAAG,KAAK,KAAK,CAAA,GACnF,IAAS,KAAK,OAAO,EAAe,KAAA,EAAO,MAAA;AACjD,IAAA,EAAO,SAAS,UAAU,EAAW;AAErC,UAAM,IAAM,IAAI,EAAM,KAAK,GAAa,CAAA;AACxC,WAAA,EAAI,OAAO,OACX,EAAI,WAAW;AAAA,MACb,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,MACN,cAAc;AAAA,MACd,gBAAgB,KAAK;AAAA,OAEvB,EAAI,SAAS,IAAI,GAAG,MAAM,CAAA,GAC1B,EAAM,IAAI,CAAA,GAGN,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAG5C,KAAK,wBAAwB,GAAO,EAAU,MAAA,GACvC;AAAA;EAGT,iBAAyB,GAAsB,GAAwB,GAAoB;AACzF,UAAM,IAAY,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAClD,QAAI,GAAW;AACb,YAAM,IAAY,KAAK,eAAe,GAAW,MAAA;AACjD,MAAA,EAAU,SAAS,IAAI,OAAO,GAAG,CAAA,GACjC,EAAM,IAAI,CAAA;AAEV,YAAM,IAAkB,KAAK,qBAC3B,GACA,KAAK,OAAO,EAAe,KAAA,CAAM;AAEnC,MAAM,KACJ,EAAM,IAAI,CAAA;AAAA;AAId,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAY,KAAK,eAAe,GAAY,OAAA;AAClD,MAAA,EAAU,SAAS,IAAI,MAAM,GAAG,CAAA,GAChC,EAAM,IAAI,CAAA;AAEV,YAAM,IAAkB,KAAK,qBAC3B,GACA,KAAK,OAAO,EAAe,KAAA,CAAM;AAEnC,MAAM,KACJ,EAAM,IAAI,CAAA;AAAA;;EAUhB,0BAAgE;AAC9D,WAAO,EACL,QAAQ;AAAA,MACN;AAAA,QAAE,KAAK;AAAA,QAAkB,MAAM;AAAA,QAAU,KAAK;AAAA,QAAG,MAAM;AAAA;MACvD;AAAA,QAAE,KAAK;AAAA,QAAa,MAAM;AAAA;MAC1B;AAAA,QAAE,KAAK;AAAA,QAAe,MAAM;AAAA;MAC5B;AAAA,QAAE,KAAK;AAAA,QAAQ,MAAM;AAAA,QAAU,KAAK;AAAA,QAAG,KAAK;AAAA,QAAI,MAAM;AAAA;MACtD;AAAA,QAAE,KAAK;AAAA,QAAW,MAAM;AAAA;MACzB;AAAA;EAWL,oBAA6B,GAA+C;AAC1E,UAAM,IAAW,oBAAI,IAAA;AACrB,IAAA,EAAS,IAAI,kBAAkB,WAAW,EAAO,IAAI,gBAAA,KAAqB,GAAA,CAAI;AAC9E,UAAM,IAAc,EAAO,IAAI,aAAA,KAAkB,WAC3C,IAAY,EAAO,IAAI,WAAA,KAAgB;AAG7C,WAAA,EAAS,IAAI,aAAa,EAAiB,CAAA,CAAU,GACrD,EAAS,IAAI,eAAe,EAAiB,CAAA,CAAY,GACzD,EAAS,IAAI,QAAQ,WAAW,EAAO,IAAI,MAAA,KAAW,GAAA,CAAI,GAC1D,EAAS,IAAI,WAAW,WAAW,EAAO,IAAI,SAAA,KAAc,GAAA,CAAI,GAEzD;AAAA;EAUT,oBAA6B,GAAiD;AAC5E,UAAM,IAAS,oBAAI,IAAA;AACnB,IAAA,EAAO,IAAI,kBAAkB,EAAS,IAAI,gBAAA,EAAkB,SAAA,CAAU;AACtE,UAAM,IAAc,EAAS,IAAI,aAAA,GAC3B,IAAY,EAAS,IAAI,WAAA;AAG/B,WAAI,KACF,EAAO,IAAI,eAAe,EAAiB,CAAA,CAAY,GAErD,KACF,EAAO,IAAI,aAAa,EAAiB,CAAA,CAAU,GAGrD,EAAO,IAAI,QAAQ,EAAS,IAAI,MAAA,EAAQ,SAAA,CAAU,GAClD,EAAO,IAAI,WAAW,EAAS,IAAI,SAAA,EAAW,SAAA,CAAU,GACjD;AAAA;EAOT,wBAAiC,GAA0B,GAAmC;AAC5F,UAAM,IAAU,KAAK,YAAY,CAAA,GAC3B,IAAS,KAAK,WAAW,CAAA;AAC/B,QAAI,CAAC,KAAW,CAAC,EAAQ;AAEzB,UAAM,IAAS,EAAQ,UAGjB,IAAY,EAAO,IAAI,WAAA;AAC7B,QAAI,GAAW;AAEb,YAAM,IAAe,SAAS,EAAiB,CAAA,EAAW,QAAQ,KAAK,EAAA,GAAK,EAAA;AAC5E,MAAA,EAAO,MAAM,OAAO,CAAA,GACpB,EAAQ,SAAS,eAAe;AAAA;AAElC,UAAM,IAAc,EAAO,IAAI,aAAA;AAC/B,IAAI,MAEF,EAAQ,SAAS,iBAAiB,SAChC,EAAiB,CAAA,EAAa,QAAQ,KAAK,EAAA,GAC3C,EAAA;AAIJ,UAAM,IAAU,WAAW,EAAO,IAAI,SAAA,KAAc,GAAA;AACpD,IAAA,EAAQ,SAAS,QAAA,GACjB,EAAQ,WAAW,IAAI,EAAM,iBAC3B,MACA,MACA,GACA,IACA,GACA,IACA,GACA,KAAK,KAAK,CAAA,GAEZ,EAAQ,SAAS,IAAI,GAAG,OAAO,GAAS,CAAA,GACxC,EAAO,SAAS,QAAA,GAChB,EAAO,WAAW,IAAI,EAAM,YAAY,GAAG,MAAM,GAAS,CAAA;AAE1D,UAAM,IAAQ,WAAW,EAAO,IAAI,MAAA,KAAW,GAAA;AAC/C,IAAA,EAAS,MAAM,IAAI,GAAO,GAAO,CAAA,GAEjC,KAAK,gBAAgB,GAAU,IAAA;AAAA;EAWjC,gBAAyB,GAA0B,GAAoC;AACrF,UAAM,IAAU,KAAK,YAAY,CAAA;AACjC,QAAI,CAAC,EAAS;AAGd,QAAI,CAAC,KAAS,CAAC,KAAK,mBAAmB;AACrC,WAAK,cAAc,CAAA,GACnB,KAAK,wBAAwB,CAAA;AAC7B;AAAA;AAGF,UAAM,IAAW;AAGjB,QAAI,KAAK,kBAAkB,qBAAqB,WAAW;AACzD,WAAK,cAAc,CAAA,GACnB,KAAK,aAAa,GAAS,EAAS,KAAA;AACpC;AAAA;AAIF,QAAI,EAAM,eAAe;AACvB,WAAK,YAAY,GAAU,GAAS,CAAA;AACpC;AAAA;AAIF,SAAK,cAAc,CAAA,GACnB,KAAK,aAAa,GAAS,EAAS,KAAA;AAAA;EAYtC,sBAA8B,GAA2B;AACvD,UAAM,IAAM,EAAQ;AACpB,IAAI,EAAI,SAAS,YAAY,EAAW,oBACxC,EAAQ,SAAS,aAAa,GAC9B,EAAQ,WAAW,EAAI,MAAA,GACtB,EAAQ,SAAuC,SAAS,UAAU,EAAW;AAAA;EAOhF,wBAAgC,GAA2B;AACzD,UAAM,IAAM,EAAQ;AACpB,IAAI,EAAI,SAAS,YAAY,EAAW,oBACxC,EAAI,QAAA,GACA,EAAQ,SAAS,eACnB,EAAQ,WAAW,EAAQ,SAAS,YACpC,OAAO,EAAQ,SAAS;AAAA;EAQ5B,aAAqB,GAAqB,GAAsB;AAC9D,SAAK,sBAAsB,CAAA;AAC3B,UAAM,IAAM,EAAQ;AAEpB,IAAI,KACF,EAAI,SAAS,OAAO,EAAQ,SAAS,cAAA,GACrC,EAAI,oBAAoB,KAAK,sBAE7B,EAAI,SAAS,OAAO,CAAA,GACpB,EAAI,oBAAoB;AAAA;EAW5B,YAAoB,GAA0B,GAAqB,GAA6B;AAE9F,QAAI,EAAS,SAAS,uBAAuB,EAAM,UAAW;AAE9D,SAAK,sBAAsB,CAAA;AAE3B,UAAM,IAAM,KAAK,kBAAmB,gBAE9B,KADO,EAAM,iBAAiB,EAAM,aACX;AAG/B,QAAI,IAA8B,EAAS,SAAS;AACpD,IAAK,MACH,IAAQ,IAAI,EAAM,eAAe,CAAA,GACjC,EAAS,SAAS,QAAQ;AAK5B,UAAM,IAAM,EAAQ,UACd,IAAmB,EAAI,mBACvB,IAAa;AAAA,MAAC,EAAI,SAAS;AAAA,MAAG,EAAI,SAAS;AAAA,MAAG,EAAI,SAAS;AAAA;AAGjE,IAAI,EAAS,SAAS,iBACnB,EAAS,SAAS,cAAwC,KAAA,GAEzD,EAAS,SAAS,eACpB,EAAM,YAAY,EAAS,SAAS,WAAA;AAItC,UAAM,IAAY,EAAM,UAAU,WAC5B,IAAe,IAAY,KAAK,oBAAoB,GACpD,IAAY,EAAQ,SAAS,gBAC7B,IAAc,IAAI,EAAM,MAAM,CAAA,GAC9B,IAAS,IAAY;AAAA,MAAC,EAAY;AAAA,MAAG,EAAY;AAAA,MAAG,EAAY;AAAA,QAAK,KAAK,WAE1E,IAAS,CACb,IAAI,EAAM,oBACR,kCACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAkB,CAAA,CAAa,GAElC,IAAI,EAAM,mBACR,yBACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAG,GAAY,GAAG,CAAA,CAAO,CAC3B,GAGG,IAAO,IAAI,EAAM,cAAc,WAAW,GAAiB,CAAA,GAC3D,IAAS,EAAM,WAAW,CAAA;AAChC,IAAA,EAAO,OAAO,EAAM,UACpB,EAAO,oBAAoB,IAC3B,EAAO,KAAA,GAGP,EAAS,SAAS,qBAAqB,EAAM,WAC7C,EAAS,SAAS,gBAAgB,GAClC,EAAS,SAAS,cAAc;AAAA;EAMlC,cAAsB,GAAgC;AACpD,UAAM,IAAQ,EAAS,SAAS;AAChC,IAAI,MACF,EAAM,cAAA,GACN,EAAM,YAAY,CAAA,GAClB,OAAO,EAAS,SAAS,QAE3B,OAAO,EAAS,SAAS,eACzB,OAAO,EAAS,SAAS,aACzB,OAAO,EAAS,SAAS;AAAA;EAO3B,YAAoB,GAA6C;AAC/D,QAAI,IAA6B;AAEjC,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,UACzD,IAAU;AAAA,QAIP;AAAA;GCtYE,KAAb,cAAyC,EAA2B;AAAA,EAElE,kBAAmC,IAAI,EAAM,MAAM,CAAC,KAAK,KAAK,GAAG,GAAG,CAAA;AAAA,EAEpE,gBAAiC,IAAI,EAAM,MAAM,CAAC,KAAK,KAAK,GAAG,CAAC,KAAK,KAAK,GAAG,CAAA;AAAA,EAE7E,qBAAsC,IAAI,EAAM,YAAY,KAAK,KAAK,GAAA;AAAA,EAGtE,wBAAyC,IAAI,EAAM,MAAM,QAAA;AAAA,EAEzD,gBAAiC,IAAI,EAAM,MAAM,OAAA;AAAA,EAEjD,gBAAiC,IAAI,EAAM,MAAM,QAAA;AAAA,EAEjD,aAA8B,IAAI,EAAM,MAAM,QAAA;AAAA,EAE9C,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,aAAa,GAAsB,GAAwC;AACzE,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAG9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAI3B,UAAM,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,KAAK,GAAG,GAAA;AAC1E,IAAA,EAAM,IAAI,CAAA,GACV,EAAO,SAAS,IAAI,MAAM,GAAG,CAAA;AAG7B,UAAM,IAAiB,IAAI,EAAM,MAAA;AACjC,IAAA,EAAe,OAAO,kBACtB,EAAe,WAAW;AAAA,MACxB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,MACN,cAAc;AAAA,OAEhB,EAAe,SAAS,IAAI,KAAK,GAAG,CAAA,GACpC,EAAe,SAAS,KAAK,KAAK,aAAA,GAClC,EAAM,IAAI,CAAA;AAEV,UAAM,IAAY,IAAI,EAAM,KAAK,KAAK,oBAAoB,KAAK,OAAO,EAAe,KAAA,CAAM;AAC3F,WAAA,EAAU,OAAO,aACjB,EAAU,WAAW,EAAE,MAAM,YAAA,GAC7B,EAAU,SAAS,IAAI,OAAO,GAAG,CAAA,GAEjC,EAAe,IAAI,CAAA,GAGf,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAG5C,KAAK,wBAAwB,GAAO,EAAU,MAAA,GACvC;AAAA;EAGT,iBAAyB,GAAsB,GAAwB,GAAoB;AACzF,UAAM,IAAY,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAClD,QAAI,GAAW;AACb,YAAM,IAAgB,KAAK,eAAe,GAAW,MAAA;AACrD,MAAA,EAAc,SAAS,IAAI,IAAI,GAAG,CAAA,GAClC,EAAM,IAAI,CAAA;AAEV,YAAM,IAAsB,KAAK,qBAC/B,GACA,KAAK,OAAO,EAAe,KAAA,CAAM;AAEnC,MAAM,KACJ,EAAM,IAAI,CAAA;AAAA;AAId,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAiB,KAAK,eAAe,GAAY,OAAA;AACvD,MAAA,EAAe,SAAS,IAAI,KAAK,GAAG,CAAA,GACpC,EAAM,IAAI,CAAA;AAEV,YAAM,IAAuB,KAAK,qBAChC,GACA,KAAK,OAAO,EAAe,KAAA,CAAM;AAEnC,MAAM,KACJ,EAAM,IAAI,CAAA;AAAA;;EAUhB,0BAAgE;AAC9D,WAAO,EACL,QAAQ;AAAA,MACN;AAAA,QAAE,KAAK;AAAA,QAAgB,MAAM;AAAA;MAC7B;AAAA,QAAE,KAAK;AAAA,QAAkB,MAAM;AAAA,QAAU,KAAK;AAAA,QAAG,MAAM;AAAA;MACvD;AAAA,QAAE,KAAK;AAAA,QAAQ,MAAM;AAAA,QAAU,KAAK;AAAA,QAAG,KAAK;AAAA,QAAI,MAAM;AAAA;MACvD;AAAA;EAQL,oBAA6B,GAA+C;AAC1E,UAAM,IAAW,oBAAI,IAAA,GACf,IAAe,EAAO,IAAI,cAAA;AAChC,WAAA,EAAS,IAAI,gBAAgB,MAAiB,MAAA,GAC9C,EAAS,IAAI,kBAAkB,WAAW,EAAO,IAAI,gBAAA,KAAqB,GAAA,CAAI,GAC9E,EAAS,IAAI,QAAQ,WAAW,EAAO,IAAI,MAAA,KAAW,GAAA,CAAI,GACnD;AAAA;EAOT,oBAA6B,GAAiD;AAC5E,UAAM,IAAS,oBAAI,IAAA,GACb,IAAe,EAAS,IAAI,cAAA;AAClC,WAAA,EAAO,IAAI,gBAAgB,IAAe,SAAS,QAAA,GACnD,EAAS,IAAI,kBAAkB,WAAW,EAAO,IAAI,gBAAA,KAAqB,GAAA,CAAI,GAC9E,EAAO,IAAI,QAAQ,EAAS,IAAI,MAAA,EAAQ,SAAA,CAAU,GAC3C;AAAA;EAGT,wBAAiC,GAA0B,GAA6B;AACtF,UAAM,IAAiB,KAAK,mBAAmB,CAAA;AAC/C,QAAI,CAAC,EAAgB;AAErB,IAAI,EAAO,IAAI,cAAA,MAAoB,WACjC,EAAe,SAAS,eAAe,WAEvC,EAAe,SAAS,eAAe;AAGzC,UAAM,IAAQ,WAAW,EAAO,IAAI,MAAA,KAAW,GAAA;AAC/C,IAAA,EAAS,MAAM,IAAI,GAAO,GAAO,CAAA,GACjC,KAAK,gBAAgB,GAAU,IAAA;AAAA;EAYjC,gBAAyB,GAA0B,GAAoC;AACrF,UAAM,IAAiB,KAAK,mBAAmB,CAAA;AAC/C,QAAI,CAAC,EAAgB;AAErB,UAAM,IAAgB,KAAK,mBAAmB,CAAA;AAG9C,QAAI,CAAC,KAAS,CAAC,KAAK,mBAAmB;AACrC,WAAK,cAAc,GAAU,CAAA,GACzB,KAAe,KAAK,uBAAuB,CAAA;AAC/C;AAAA;AAUF,QANA,KAAK,oBAAoB,CAAA,GAGrB,KAAe,KAAK,sBAAsB,GAAe,CAAA,GAGzD,KAAK,kBAAkB,qBAAqB,WAAW;AACzD,MAAI,EAAM,gBAER,EAAe,SAAS,KAAK,KAAK,kBAAkB,EAAM,KAAA,CAAM,IAEhE,EAAe,SAAS,KAAK,KAAK,kBAAkB,EAAM,KAAA,CAAM;AAElE;AAAA;AAIF,QAAI,EAAM,eAAe;AACvB,WAAK,kBAAkB,GAAU,GAAgB,CAAA;AACjD;AAAA;AAIF,SAAK,cAAc,GAAU,CAAA,GAC7B,EAAe,SAAS,KAAK,KAAK,kBAAkB,EAAM,KAAA,CAAM;AAAA;EAQlE,kBAA0B,GAA4B;AACpD,WAAI,MAAU,WAAiB,KAAK,kBAC7B,KAAK;AAAA;EAId,kBAA0B,GAA4B;AACpD,WAAI,MAAU,YAAkB,KAAK,gBACjC,MAAU,YAAkB,KAAK,kBAC9B,KAAK,kBAAkB,CAAA;AAAA;EAYhC,kBACE,GACA,GACA,GACM;AAEN,QAAI,EAAS,SAAS,uBAAuB,EAAM,UAAW;AAE9D,UAAM,IAAgB,KAAK,mBAAmB,CAAA,GAExC,IAAM,KAAK,kBAAmB,gBAE9B,KADO,EAAM,iBAAiB,EAAM,aACX;AAG/B,QAAI,IAA8B,EAAS,SAAS;AACpD,IAAK,MACH,IAAQ,IAAI,EAAM,eAAe,CAAA,GACjC,EAAS,SAAS,QAAQ;AAI5B,UAAM,IAAW,EAAe,SAAS;AACzC,QAAI,IAAa;AAAA,MAAC;AAAA,MAAG;AAAA,MAAG;AAAA;AACxB,QAAI,GAAe;AACjB,WAAK,sBAAsB,CAAA;AAC3B,YAAM,IAAM,EAAc;AAC1B,MAAA,IAAa;AAAA,QAAC,EAAI,MAAM;AAAA,QAAG,EAAI,MAAM;AAAA,QAAG,EAAI,MAAM;AAAA;;AAIpD,IAAI,EAAS,SAAS,iBACnB,EAAS,SAAS,cAAwC,KAAA,GAEzD,EAAS,SAAS,eACpB,EAAM,YAAY,EAAS,SAAS,WAAA;AAItC,UAAM,IAAU,KAAK,kBAAkB,EAAM,SAAA,EAAY,GACnD,IAAe,IAAI,EAAM,cAAc,kBAAkB,GAAiB,CAC9E,IAAI,EAAM,oBACR,8BACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAU,CAAA,CAAQ,CACpB,CACF,GACK,IAAiB,EAAM,WAAW,CAAA;AAUxC,QATA,EAAe,OAAO,EAAM,UAC5B,EAAe,oBAAoB,IACnC,EAAe,KAAA,GAEf,EAAS,SAAS,qBAAqB,EAAM,WAC7C,EAAS,SAAS,gBAAgB,GAClC,EAAS,SAAS,cAAc,GAG5B,GAAe;AACjB,WAAK,oBAAoB,CAAA;AACzB,YAAM,IAAY,IAAI,EAAM,cAAc,eAAe,GAAiB,CACxE,IAAI,EAAM,mBACR,4BACA,CAAC,GAAG,CAAA,GACJ;AAAA,QAAC,GAAG;AAAA,QAAY;AAAA,QAAG;AAAA,QAAG;AAAA,OAAE,CACzB,CACF,GACK,IAAc,EAAM,WAAW,CAAA;AACrC,MAAA,EAAY,OAAO,EAAM,UACzB,EAAY,oBAAoB,IAChC,EAAY,KAAA,GAEZ,EAAS,SAAS,cAAc,GAChC,EAAS,SAAS,YAAY;AAAA;;EAOlC,oBAA4B,GAAgC;AAC1D,QAAI,CAAC,EAAS,SAAS,YAAa;AACpC,UAAM,IAAQ,EAAS,SAAS;AAC/B,IAAA,EAAS,SAAS,YAAsC,KAAA,GACrD,KAAS,EAAS,SAAS,aAC7B,EAAM,YAAY,EAAS,SAAS,SAAA,GAEtC,OAAO,EAAS,SAAS,aACzB,OAAO,EAAS,SAAS;AAAA;EAM3B,cAAsB,GAA0B,GAAsC;AACpF,UAAM,IAAQ,EAAS,SAAS;AAChC,IAAI,MACF,EAAM,cAAA,GACN,EAAM,YAAY,CAAA,GAClB,OAAO,EAAS,SAAS,QAE3B,OAAO,EAAS,SAAS,eACzB,OAAO,EAAS,SAAS,aACzB,OAAO,EAAS,SAAS,oBACzB,OAAO,EAAS,SAAS,aACzB,OAAO,EAAS,SAAS,WAGrB,EAAe,SAAS,iBAAiB,WAC3C,EAAe,SAAS,KAAK,KAAK,eAAA,IAElC,EAAe,SAAS,KAAK,KAAK,aAAA;AAAA;EAQtC,sBAA8B,GAAiC;AAE7D,IADY,EAAc,SAClB,SAAS,YAAY,EAAW,oBACxC,EAAc,WAAW,KAAK,OAAO,EAAe,KAAA,EAAO,MAAA,GAC1D,EAAc,SAAuC,SAAS,UAC7D,EAAW;AAAA;EAGf,uBAA+B,GAAiC;AAC9D,UAAM,IAAM,EAAc;AAC1B,IAAI,EAAI,SAAS,YAAY,EAAW,oBACxC,EAAI,QAAA,GACJ,EAAc,WAAW,KAAK,OAAO,EAAe,KAAA;AAAA;EAGtD,sBAA8B,GAA2B,GAA6B;AACpF,QAAI,CAAC,EAAM,aAAa,CAAC,EAAM,UAAU,IAAI,QAAA,EAAW;AAExD,SAAK,sBAAsB,CAAA;AAC3B,UAAM,IAAM,EAAc,UAEpB,IAAc,EAAM,UAAU,IAAI,QAAA;AAExC,IAAI,EAAY,cAAc,EAAY,aACxC,EAAI,MAAM,KAAK,KAAK,qBAAA,IACX,EAAY,aACrB,EAAI,MAAM,KAAK,KAAK,aAAA,IACX,EAAY,aACrB,EAAI,MAAM,KAAK,KAAK,aAAA,IAEpB,EAAI,MAAM,KAAK,KAAK,UAAA;AAAA;EAQxB,mBAA2B,GAAiD;AAC1E,QAAI,IAAwC;AAE5C,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,SAAS,EAAM,SAAS,SAAS,qBAC1D,IAAiB;AAAA,QAId;AAAA;EAGT,mBAA2B,GAA6C;AACtE,QAAI,IAA0B;AAE9B,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,gBACzD,IAAO;AAAA,QAIJ;AAAA;GCvZE,KAAb,cAAoD,EAA2B;AAAA,EAE7E,kBAAmC,IAAI,EAAM,MAAM,CAAC,KAAK,KAAK,GAAG,CAAC,KAAK,KAAK,GAAG,CAAA;AAAA,EAE/E,kBAAmC,IAAI,EAAM,MAAM,CAAC,KAAK,KAAK,GAAG,KAAK,KAAK,GAAG,CAAA;AAAA,EAE9E,qBAAsC,IAAI,EAAM,YAAY,KAAK,KAAK,GAAA;AAAA,EAGtE,wBAAyC,IAAI,EAAM,MAAM,QAAA;AAAA,EAEzD,gBAAiC,IAAI,EAAM,MAAM,OAAA;AAAA,EAEjD,gBAAiC,IAAI,EAAM,MAAM,QAAA;AAAA,EAEjD,aAA8B,IAAI,EAAM,MAAM,QAAA;AAAA,EAE9C,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,aAAa,GAAsB,GAAwC;AACzE,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAG9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAI3B,UAAM,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,KAAK,GAAG,GAAA;AAC1E,IAAA,EAAM,IAAI,CAAA,GACV,EAAO,SAAS,IAAI,MAAM,GAAG,CAAA;AAG7B,UAAM,IAAiB,IAAI,EAAM,MAAA;AACjC,IAAA,EAAe,OAAO,kBACtB,EAAe,WAAW;AAAA,MACxB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,MACN,cAAc;AAAA,OAEhB,EAAe,SAAS,IAAI,KAAK,GAAG,CAAA,GACpC,EAAe,SAAS,KAAK,KAAK,eAAA,GAClC,EAAM,IAAI,CAAA;AAEV,UAAM,IAAY,IAAI,EAAM,KAAK,KAAK,oBAAoB,KAAK,OAAO,EAAe,KAAA,CAAM;AAC3F,WAAA,EAAU,OAAO,aACjB,EAAU,WAAW,EAAE,MAAM,YAAA,GAC7B,EAAU,SAAS,IAAI,MAAM,GAAG,CAAA,GAChC,EAAe,IAAI,CAAA,GAGf,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAG5C,KAAK,wBAAwB,GAAO,EAAU,MAAA,GACvC;AAAA;EAGT,iBAAyB,GAAsB,GAAwB,GAAoB;AACzF,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAiB,KAAK,eAAe,GAAY,QAAQ,IAAI,EAAM,MAAM,GAAG,KAAK,CAAA,CAAE;AACzF,MAAA,EAAe,SAAS,IAAI,IAAI,MAAM,CAAA,GACtC,EAAM,IAAI,CAAA;AAEV,YAAM,IAAuB,KAAK,qBAChC,GACA,KAAK,OAAO,EAAe,KAAA,CAAM;AAEnC,MAAM,KACJ,EAAM,IAAI,CAAA;AAAA;AAId,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAiB,KAAK,eAAe,GAAY,QAAQ,IAAI,EAAM,MAAM,GAAG,MAAM,CAAA,CAAE;AAC1F,MAAA,EAAe,SAAS,IAAI,IAAI,KAAK,CAAA,GACrC,EAAM,IAAI,CAAA;AAEV,YAAM,IAAuB,KAAK,qBAChC,GACA,KAAK,OAAO,EAAe,KAAA,CAAM;AAEnC,MAAM,KACJ,EAAM,IAAI,CAAA;AAAA;AAId,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAiB,KAAK,eAAe,GAAY,OAAA;AACvD,MAAA,EAAe,SAAS,IAAI,KAAK,GAAG,CAAA,GACpC,EAAM,IAAI,CAAA;AAEV,YAAM,IAAuB,KAAK,qBAChC,GACA,KAAK,OAAO,EAAe,KAAA,CAAM;AAEnC,MAAM,KACJ,EAAM,IAAI,CAAA;AAAA;;EAUhB,0BAAgE;AAC9D,WAAO,EACL,QAAQ;AAAA,MACN;AAAA,QAAE,KAAK;AAAA,QAAgB,MAAM;AAAA;MAC7B;AAAA,QAAE,KAAK;AAAA,QAAkB,MAAM;AAAA,QAAU,KAAK;AAAA,QAAG,MAAM;AAAA;MACvD;AAAA,QAAE,KAAK;AAAA,QAAQ,MAAM;AAAA,QAAU,KAAK;AAAA,QAAG,KAAK;AAAA,QAAI,MAAM;AAAA;MACvD;AAAA;EAQL,oBAA6B,GAA+C;AAC1E,UAAM,IAAW,oBAAI,IAAA,GACf,IAAe,EAAO,IAAI,cAAA;AAChC,WAAA,EAAS,IAAI,gBAAgB,MAAiB,QAAA,GAC9C,EAAS,IAAI,kBAAkB,WAAW,EAAO,IAAI,gBAAA,KAAqB,GAAA,CAAI,GAC9E,EAAS,IAAI,QAAQ,WAAW,EAAO,IAAI,MAAA,KAAW,GAAA,CAAI,GACnD;AAAA;EAOT,oBAA6B,GAAiD;AAC5E,UAAM,IAAS,oBAAI,IAAA,GACb,IAAe,EAAS,IAAI,cAAA;AAClC,WAAA,EAAO,IAAI,gBAAgB,IAAe,WAAW,QAAA,GACrD,EAAO,IAAI,kBAAkB,EAAS,IAAI,gBAAA,EAAkB,SAAA,CAAU,GACtE,EAAO,IAAI,QAAQ,EAAS,IAAI,MAAA,EAAQ,SAAA,CAAU,GAC3C;AAAA;EAGT,wBAAiC,GAA0B,GAA6B;AACtF,UAAM,IAAiB,KAAK,mBAAmB,CAAA;AAC/C,QAAI,CAAC,EAAgB;AAErB,IAAI,EAAO,IAAI,cAAA,MAAoB,WACjC,EAAe,SAAS,eAAe,WAEvC,EAAe,SAAS,eAAe;AAGzC,UAAM,IAAQ,WAAW,EAAO,IAAI,MAAA,KAAW,GAAA;AAC/C,IAAA,EAAS,MAAM,IAAI,GAAO,GAAO,CAAA,GACjC,KAAK,gBAAgB,GAAU,IAAA;AAAA;EAYjC,gBAAyB,GAA0B,GAAoC;AACrF,UAAM,IAAiB,KAAK,mBAAmB,CAAA;AAC/C,QAAI,CAAC,EAAgB;AAErB,UAAM,IAAgB,KAAK,mBAAmB,CAAA;AAG9C,QAAI,CAAC,KAAS,CAAC,KAAK,mBAAmB;AACrC,WAAK,cAAc,GAAU,CAAA,GACzB,KAAe,KAAK,uBAAuB,CAAA;AAC/C;AAAA;AAUF,QANA,KAAK,oBAAoB,CAAA,GAGrB,KAAe,KAAK,sBAAsB,GAAe,CAAA,GAGzD,KAAK,kBAAkB,qBAAqB,WAAW;AACzD,MAAI,EAAM,gBAER,EAAe,SAAS,KAAK,KAAK,kBAAkB,EAAM,KAAA,CAAM,IAEhE,EAAe,SAAS,KAAK,KAAK,kBAAkB,EAAM,KAAA,CAAM;AAElE;AAAA;AAIF,QAAI,EAAM,eAAe;AACvB,WAAK,kBAAkB,GAAU,GAAgB,CAAA;AACjD;AAAA;AAIF,SAAK,cAAc,GAAU,CAAA,GAC7B,EAAe,SAAS,KAAK,KAAK,kBAAkB,EAAM,KAAA,CAAM;AAAA;EAQlE,kBAA0B,GAA4B;AACpD,WAAI,MAAU,WAAiB,KAAK,kBAC7B,KAAK;AAAA;EAId,kBAA0B,GAA4B;AACpD,WAAI,MAAU,SAAe,KAAK,kBAC9B,MAAU,SAAe,KAAK,kBAC3B,KAAK,kBAAkB,CAAA;AAAA;EAYhC,kBACE,GACA,GACA,GACM;AAEN,QAAI,EAAS,SAAS,uBAAuB,EAAM,UAAW;AAE9D,UAAM,IAAgB,KAAK,mBAAmB,CAAA,GAExC,IAAM,KAAK,kBAAmB,gBAE9B,KADO,EAAM,iBAAiB,EAAM,aACX;AAG/B,QAAI,IAA8B,EAAS,SAAS;AACpD,IAAK,MACH,IAAQ,IAAI,EAAM,eAAe,CAAA,GACjC,EAAS,SAAS,QAAQ;AAI5B,UAAM,IAAW,EAAe,SAAS;AACzC,QAAI,IAAa;AAAA,MAAC;AAAA,MAAG;AAAA,MAAG;AAAA;AACxB,QAAI,GAAe;AACjB,WAAK,sBAAsB,CAAA;AAC3B,YAAM,IAAM,EAAc;AAC1B,MAAA,IAAa;AAAA,QAAC,EAAI,MAAM;AAAA,QAAG,EAAI,MAAM;AAAA,QAAG,EAAI,MAAM;AAAA;;AAIpD,IAAI,EAAS,SAAS,iBACnB,EAAS,SAAS,cAAwC,KAAA,GAEzD,EAAS,SAAS,eACpB,EAAM,YAAY,EAAS,SAAS,WAAA;AAItC,UAAM,IAAU,KAAK,kBAAkB,EAAM,SAAA,EAAY,GACnD,IAAe,IAAI,EAAM,cAAc,uBAAuB,GAAiB,CACnF,IAAI,EAAM,oBACR,8BACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAU,CAAA,CAAQ,CACpB,CACF,GACK,IAAiB,EAAM,WAAW,CAAA;AAUxC,QATA,EAAe,OAAO,EAAM,UAC5B,EAAe,oBAAoB,IACnC,EAAe,KAAA,GAEf,EAAS,SAAS,qBAAqB,EAAM,WAC7C,EAAS,SAAS,gBAAgB,GAClC,EAAS,SAAS,cAAc,GAG5B,GAAe;AACjB,WAAK,oBAAoB,CAAA;AACzB,YAAM,IAAY,IAAI,EAAM,cAAc,oBAAoB,GAAiB,CAC7E,IAAI,EAAM,mBACR,4BACA,CAAC,GAAG,CAAA,GACJ;AAAA,QAAC,GAAG;AAAA,QAAY;AAAA,QAAG;AAAA,QAAG;AAAA,OAAE,CACzB,CACF,GACK,IAAc,EAAM,WAAW,CAAA;AACrC,MAAA,EAAY,OAAO,EAAM,UACzB,EAAY,oBAAoB,IAChC,EAAY,KAAA,GAEZ,EAAS,SAAS,cAAc,GAChC,EAAS,SAAS,YAAY;AAAA;;EAOlC,oBAA4B,GAAgC;AAC1D,QAAI,CAAC,EAAS,SAAS,YAAa;AACpC,UAAM,IAAQ,EAAS,SAAS;AAC/B,IAAA,EAAS,SAAS,YAAsC,KAAA,GACrD,KAAS,EAAS,SAAS,aAC7B,EAAM,YAAY,EAAS,SAAS,SAAA,GAEtC,OAAO,EAAS,SAAS,aACzB,OAAO,EAAS,SAAS;AAAA;EAM3B,cAAsB,GAA0B,GAAsC;AACpF,UAAM,IAAQ,EAAS,SAAS;AAChC,IAAI,MACF,EAAM,cAAA,GACN,EAAM,YAAY,CAAA,GAClB,OAAO,EAAS,SAAS,QAE3B,OAAO,EAAS,SAAS,eACzB,OAAO,EAAS,SAAS,aACzB,OAAO,EAAS,SAAS,oBACzB,OAAO,EAAS,SAAS,aACzB,OAAO,EAAS,SAAS,WAGrB,EAAe,SAAS,iBAAiB,WAC3C,EAAe,SAAS,KAAK,KAAK,eAAA,IAElC,EAAe,SAAS,KAAK,KAAK,eAAA;AAAA;EAQtC,sBAA8B,GAAiC;AAE7D,IADY,EAAc,SAClB,SAAS,YAAY,EAAW,oBACxC,EAAc,WAAW,KAAK,OAAO,EAAe,KAAA,EAAO,MAAA,GAC1D,EAAc,SAAuC,SAAS,UAC7D,EAAW;AAAA;EAGf,uBAA+B,GAAiC;AAC9D,UAAM,IAAM,EAAc;AAC1B,IAAI,EAAI,SAAS,YAAY,EAAW,oBACxC,EAAI,QAAA,GACJ,EAAc,WAAW,KAAK,OAAO,EAAe,KAAA;AAAA;EAGtD,sBAA8B,GAA2B,GAA6B;AACpF,QAAI,CAAC,EAAM,aAAa,CAAC,EAAM,UAAU,IAAI,QAAA,EAAW;AAExD,SAAK,sBAAsB,CAAA;AAC3B,UAAM,IAAM,EAAc,UAEpB,IAAc,EAAM,UAAU,IAAI,QAAA;AAExC,IAAI,EAAY,cAAc,EAAY,aACxC,EAAI,MAAM,KAAK,KAAK,qBAAA,IACX,EAAY,aACrB,EAAI,MAAM,KAAK,KAAK,aAAA,IACX,EAAY,aACrB,EAAI,MAAM,KAAK,KAAK,aAAA,IAEpB,EAAI,MAAM,KAAK,KAAK,UAAA;AAAA;EAQxB,mBAA2B,GAAiD;AAC1E,QAAI,IAAwC;AAE5C,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,SAAS,EAAM,SAAS,SAAS,qBAC1D,IAAiB;AAAA,QAId;AAAA;EAGT,mBAA2B,GAA6C;AACtE,QAAI,IAA0B;AAE9B,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,gBACzD,IAAO;AAAA,QAIJ;AAAA;GClbE,KAAb,cAAwC,EAA2B;AAAA,EAEjE,oBAAqC,GAAa,KAAK,KAAK,KAAK,EAAA;AAAA,EAEjE,gBAAiC,GAAwB,KAAK,MAAM,MAAM,KAAK,KAAK,EAAA;AAAA,EAEpF,gBAAiC,IAAI,EAAM,iBACzC,MACA,MACA,KACA,IACA,GACA,IACA,KAAK,KAAK,GACV,KAAK,EAAA;AAAA,EAEP,eAAgC,IAAI,EAAM,oBAAoB;AAAA,IAC5D,OAAO;AAAA,IACP,aAAa;AAAA,IACb,SAAS;AAAA,IACT,UAAU;AAAA,IACV,mBAAmB;AAAA,IACnB,UAAU,EAAE,SAAS,EAAW,QAAA;AAAA,GACjC;AAAA,EACD,gBAAiC,IAAI,EAAM,oBAAoB;AAAA,IAC7D,OAAO;AAAA,IACP,aAAa;AAAA,IACb,SAAS;AAAA,IACT,UAAU;AAAA,IACV,mBAAmB;AAAA,IACnB,UAAU,EAAE,SAAS,EAAW,QAAA;AAAA,GACjC;AAAA,EAED,qBAAsC,IAAI,EAAM,MAAM,GAAG,KAAK,IAAI,CAAA;AAAA,EAClE,oBAAqC,IAAI,EAAM,MAAM,GAAG,GAAG,CAAA;AAAA,EAE3D,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,aAAa,GAAsB,GAAwC;AACzE,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAG9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAG3B,UAAM,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,GAAG,GAAG,CAAA;AACxE,IAAA,EAAM,IAAI,CAAA;AAEV,UAAM,IAAW,IAAI,EAAM,KAAK,KAAK,mBAAmB,KAAK,OAAO,EAAe,KAAA,CAAM;AACzF,IAAA,EAAS,WAAW;AAAA,MAClB,MAAM;AAAA,MACN,aAAa,EAAM,SAAS;AAAA,MAC5B,MAAM;AAAA,OAER,EAAS,QAAQ,CAAC,KAAK,KAAK,CAAA,GAC5B,EAAS,SAAS,IAAI,GAAG,GAAG,CAAA,GAC5B,EAAM,IAAI,CAAA;AAGV,UAAM,IAAW,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,YAAA;AACzD,IAAA,EAAS,SAAS,IAAI,GAAG,KAAK,CAAA,GAC9B,EAAM,IAAI,CAAA;AACV,UAAM,IAAU,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,aAAA;AACxD,IAAA,EAAQ,QAAQ,KAAK,EAAA,GACrB,EAAQ,SAAS,IAAI,GAAG,KAAK,CAAA,GAC7B,EAAM,IAAI,CAAA;AAGV,UAAM,IAAY,KAAK,gBAAgB,CAAA;AACvC,WAAA,EAAM,IAAI,CAAA,GACV,EAAU,SAAS,KAAK,KAAK,kBAAA,GAGzB,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAG5C,KAAK,wBAAwB,GAAO,EAAU,MAAA,GACvC;AAAA;EAGT,iBAAyB,GAAsB,GAAwB,GAAoB;AACzF,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,KAAA;AAC9C,MAAA,EAAS,SAAS,IAAI,GAAG,GAAG,KAAA,GAC5B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,QAAA;AAC9C,MAAA,EAAS,SAAS,IAAI,GAAG,GAAG,IAAA,GAC5B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,OAAA;AACpD,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,CAAA,GAClC,EAAM,IAAI,CAAA;AAAA;;EAId,gBAAwB,GAAmC;AACzD,UAAM,IAAY,IAAI,EAAM,MAAA;AAC5B,IAAA,EAAU,OAAO,aACjB,EAAU,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,MACN,cAAc;AAAA,OAEhB,EAAU,aAAA,GACV,EAAU,kBAAkB,EAAA;AAE5B,UAAM,IAAO,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,OAAO,EAAe,YAAA,CAAa;AACxF,WAAA,EAAK,WAAW;AAAA,MACd,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAU,IAAI,CAAA,GACd,EAAK,QAAQ,KAAK,KAAK,CAAA,GACvB,EAAK,WAAW,IAAA,GAChB,EAAK,WAAW,IAAA,GAEhB,EAAK,aAAA,GACL,EAAK,kBAAkB,EAAA,GAEhB;AAAA;EAST,wBAAiC,GAA4D;AAC3F,WAAO,EACL,QAAQ,CACN;AAAA,MAAE,KAAK;AAAA,MAAa,MAAM;AAAA,OAC1B;AAAA,MAAE,KAAK;AAAA,MAAc,MAAM;AAAA,MAAU,KAAK;AAAA,KAAG,EAC9C;AAAA;EAUL,oBAA6B,GAA+C;AAC1E,UAAM,IAAW,oBAAI,IAAA,GACf,IAAY,EAAO,IAAI,WAAA;AAC7B,WAAA,EAAS,IAAI,aAAa,KAAa,MAAA,GACvC,EAAS,IAAI,cAAc,WAAW,EAAO,IAAI,YAAA,KAAiB,GAAA,CAAI,GAC/D;AAAA;EAST,oBAA6B,GAAiD;AAC5E,UAAM,IAAS,oBAAI,IAAA,GACb,IAAY,EAAS,IAAI,WAAA;AAC/B,WAAA,EAAO,IAAI,aAAa,IAAY,SAAS,OAAA,GAC7C,EAAO,IAAI,cAAc,EAAS,IAAI,YAAA,EAAc,SAAA,CAAU,GACvD;AAAA;EAGT,wBAAiC,GAA0B,GAA6B;AACtF,UAAM,IAAY,KAAK,cAAc,CAAA;AACrC,IAAK,MAEL,EAAU,SAAS,YAAY,EAAO,IAAI,WAAA,MAAiB,QAEvD,EAAU,SAAS,YACrB,EAAU,SAAS,KAAK,KAAK,kBAAA,IAE7B,EAAU,SAAS,KAAK,KAAK,iBAAA;AAAA;EAkBjC,gBAAyB,GAA0B,GAAoC;AACrF,UAAM,IAAY,KAAK,cAAc,CAAA;AACrC,QAAI,CAAC,EAAW;AAKhB,QAAI,CAAC,KAAS,CAAC,KAAK,mBAAmB;AACrC,WAAK,cAAc,GAAU,CAAA;AAC7B;AAAA;AAGF,QAAI,KAAK,kBAAkB,qBAAqB,WAAW;AACzD,MAAA,EAAU,SAAS,KAAK,KAAK,iBAAiB,EAAM,KAAA,CAAM;AAC1D;AAAA;AAIF,QAAI,CAAC,EAAM,eAAe;AACxB,WAAK,cAAc,GAAU,CAAA,GAC7B,EAAU,SAAS,KAAK,KAAK,iBAAiB,EAAM,KAAA,CAAM;AAC1D;AAAA;AAGF,UAAM,IAAgB,KAAK,iBAAiB,EAAM,KAAA,EAAS,GACrD,IAAiB,KAAK,iBAAiB,EAAM,SAAA,EAAa;AAEhE,SAAK,aACH,GACA,GACA,GACA,EAAM,WACN,GACA,EAAM,cAAA;AAAA;EAOV,aACE,GACA,GACA,GACA,GACA,GACA,GACM;AAEN,QAAI,EAAS,SAAS,sBAAsB,EAC1C;AAGF,UAAM,IAAyB,KAAK,kBAAmB,gBACjD,IAAO,IAAa;AAE1B,QAAI,IAA8B,EAAS,SAAS;AACpD,IAAK,MACH,IAAQ,IAAI,EAAM,eAAe,CAAA,GACjC,EAAS,SAAS,QAAQ;AAI5B,UAAM,IAAkB,EAAU,SAAS,KAAK,IAAI,KAAK;AAEzD,IAAI,EAAS,SAAS,iBACnB,EAAS,SAAS,cAAwC,KAAA,GAEzD,EAAS,SAAS,eACpB,EAAM,YAAY,EAAS,SAAS,WAAA;AAItC,UAAM,IAAkB,IAAO;AAC/B,IAAA,IAAiB,IAAkB,KAAK;AAGxC,UAAM,IAAQ,IAAI,EAAM,oBACtB,yBACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAiB,CAAA,CAAe,GAG7B,IAAO,IAAI,EAAM,cAAc,qBAAqB,GAAiB,CAAC,CAAA,CAAM,GAC5E,IAAS,EAAM,WAAW,CAAA;AAChC,IAAA,EAAO,OAAO,EAAM,UACpB,EAAO,oBAAoB,IAC3B,EAAO,KAAA,GAGP,EAAS,SAAS,qBAAqB,GACvC,EAAS,SAAS,gBAAgB,GAClC,EAAS,SAAS,cAAc;AAAA;EAMlC,cAAsB,GAA0B,GAAiC;AAC/E,UAAM,IAAQ,EAAS,SAAS;AAChC,IAAI,MACF,EAAM,cAAA,GACN,EAAM,YAAY,CAAA,GAClB,OAAO,EAAS,SAAS,QAE3B,OAAO,EAAS,SAAS,eACzB,OAAO,EAAS,SAAS,aAGrB,EAAU,SAAS,YACrB,EAAU,SAAS,KAAK,KAAK,kBAAA,IAE7B,EAAU,SAAS,KAAK,KAAK,iBAAA;AAAA;EAIjC,iBAAyB,GAAe;AACtC,WAAI,MAAU,SACL,KAAK,qBAEP,KAAK;AAAA;EAYd,cAAsB,GAAiD;AACrE,QAAI,IAAuC;AAE3C,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,YAAY,EAAM,SAAS,SAAS,gBAC7D,IAAgB;AAAA,QAGb;AAAA;GCvVE,KAAb,cAA2C,EAA2B;AAAA,EAEpE,gBAAiC,GAAwB,KAAK,KAAK,GAAG,MAAM,KAAK,EAAA;AAAA,EAEjF,qBAAsC,GAA4B,KAAK,KAAK,GAAG,MAAM,KAAK,EAAA;AAAA,EAG1F,cAA+B,GAAwB,GAAG,KAAK,MAAM,MAAM,KAAK,EAAA;AAAA,EAEhF,mBAAoC,GAA4B,GAAG,KAAK,MAAM,MAAM,KAAK,EAAA;AAAA,EAEzF,kBAAmC,IAAI,EAAM,MAAM,QAAA;AAAA,EACnD,iBAAkC,IAAI,EAAM,MAAM,OAAA;AAAA,EAClD,+BAAgD;AAAA,EAChD,8BAA+C;AAAA,EAE/C,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,aAAa,GAAsB,GAAwC;AACzE,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAE9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA,MACzB,SAAS;AAAA;AAIX,UAAM,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,KAAK,GAAG,GAAA;AAC1E,WAAA,EAAM,IAAI,CAAA,GAEV,KAAK,gBAAgB,GAAO,EAAA,GAC5B,KAAK,YAAY,GAAO,EAAA,GAGpB,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAG5C,KAAK,wBAAwB,GAAO,EAAU,MAAA,GACvC;AAAA;EAGT,iBAAyB,GAAsB,GAAwB,GAAoB;AACzF,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,OAAO,IAAI,EAAM,MAAM,GAAG,GAAG,IAAA,CAAK;AAChF,MAAA,EAAS,SAAS,IAAI,OAAO,GAAG,KAAA,GAChC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,UAAU,IAAI,EAAM,MAAM,GAAG,GAAG,IAAA,CAAK;AACnF,MAAA,EAAS,SAAS,IAAI,OAAO,GAAG,IAAA,GAChC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAY,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAClD,QAAI,GAAW;AACb,YAAM,IAAa,KAAK,eAAe,GAAW,MAAA;AAClD,MAAA,EAAW,SAAS,IAAI,MAAM,GAAG,CAAA,GACjC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,OAAA;AACpD,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,CAAA,GAClC,EAAM,IAAI,CAAA;AAGV,YAAM,IAAuB,KAAK,qBAChC,GACA,KAAK,OAAO,EAAe,KAAA,CAAM;AAEnC,MAAM,MACJ,EAAqB,SAAS,OAAO,kBACrC,EAAM,IAAI,CAAA;AAAA;;EAKhB,gBAAwB,GAAuB,GAAsC;AACnF,UAAM,IAAc,KAAK,iBAAiB,CAAA;AAC1C,QAAI,GAAa;AAEf,UAAI,MAAoB,EAAY,SAAS,gBAC3C,QAAO;AAGT,MAAA,EAAM,OAAO,CAAA;AAAA;AAGf,UAAM,IAAW,IACb,IAAI,EAAM,KAAK,KAAK,aAAa,KAAK,OAAO,EAAe,KAAA,CAAM,IAClE,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,OAAO,EAAe,KAAA,CAAM;AACxE,IAAA,EAAS,WAAW;AAAA,MAClB,MAAM;AAAA,MACN,aAAa,EAAM,SAAS;AAAA,MAC5B,MAAM;AAAA,MACW,iBAAA;AAAA,OAEnB,EAAS,QAAQ,CAAC,KAAK,KAAK,CAAA;AAC5B,UAAM,IAAO,IAAkB,QAAQ;AACvC,WAAA,EAAS,SAAS,IAAI,GAAM,OAAO,CAAA,GACnC,EAAM,IAAI,CAAA,GACH;AAAA;EAGT,YAAoB,GAAuB,GAAsC;AAC/E,UAAM,IAAU,KAAK,aAAa,CAAA;AAClC,QAAI,GAAS;AAEX,UAAI,MAAoB,EAAQ,SAAS,gBACvC,QAAO;AAGT,MAAA,EAAM,OAAO,CAAA;AAAA;AAGf,UAAM,IAAO,IACT,IAAI,EAAM,KAAK,KAAK,kBAAkB,KAAK,OAAO,EAAe,SAAA,CAAU,IAC3E,IAAI,EAAM,KAAK,KAAK,oBAAoB,KAAK,OAAO,EAAe,SAAA,CAAU;AACjF,IAAA,EAAK,OAAO,QACZ,EAAK,WAAW;AAAA,MACd,MAAM;AAAA,MACN,aAAa,EAAM,SAAS;AAAA,MAC5B,MAAM;AAAA,MACW,iBAAA;AAAA,MACjB,cAAc,IAAkB,QAAQ;AAAA,OAE1C,EAAK,QAAQ,CAAC,KAAK,KAAK,CAAA;AACxB,UAAM,IAAO,IAAkB,QAAQ;AACvC,WAAA,EAAK,SAAS,IAAI,GAAM,OAAO,CAAA,GAC/B,EAAM,IAAI,CAAA,GACH;AAAA;EAST,wBAAiC,GAA2D;AAE1F,WAAO,EACL,QAAQ;AAAA,MACN;AAAA,QACE,KAAK;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,UAAE,MAAM;AAAA,UAAS,KAAK;AAAA,UAAQ,SAAS;AAAA;;MAElD;AAAA,QAAE,KAAK;AAAA,QAAmB,MAAM;AAAA;MAChC;AAAA,QAAE,KAAK;AAAA,QAAkB,MAAM;AAAA,QAAU,KAAK;AAAA,QAAG,WATjC,GAAQ,IAAI,oBAAA,KAAyB,aASsB;AAAA;MAC3E;AAAA,QAAE,KAAK;AAAA,QAAuB,MAAM;AAAA;MACrC;AAAA;EAWL,oBAA6B,GAA+C;AAC1E,UAAM,IAAW,oBAAI,IAAA;AACrB,IAAA,EAAS,IAAI,sBAAsB,EAAO,IAAI,oBAAA,KAAyB,OAAA;AACvE,UAAM,IAAkB,EAAO,IAAI,iBAAA;AACnC,WAAA,EAAS,IAAI,mBAAmB,MAAoB,UAAA,GACpD,EAAS,IAAI,kBAAkB,WAAW,EAAO,IAAI,gBAAA,KAAqB,GAAA,CAAI,GAC9E,EAAS,IAAI,uBAAuB,WAAW,EAAO,IAAI,qBAAA,KAA0B,GAAA,CAAI,GACjF;AAAA;EAUT,oBAA6B,GAAiD;AAC5E,UAAM,IAAS,oBAAI,IAAA;AACnB,IAAA,EAAO,IAAI,sBAAsB,EAAS,IAAI,oBAAA,KAAyB,OAAA;AACvE,UAAM,IAAkB,EAAS,IAAI,iBAAA;AACrC,WAAA,EAAO,IAAI,mBAAmB,IAAkB,aAAa,UAAA,GAC7D,EAAO,IAAI,kBAAkB,EAAS,IAAI,gBAAA,EAAkB,SAAA,CAAU,GACtE,EAAO,IAAI,uBAAuB,EAAS,IAAI,qBAAA,EAAuB,SAAA,KAAc,IAAA,GAC7E;AAAA;EAGT,wBAAiC,GAA0B,GAA6B;AAEtF,QAAI,CADiB,KAAK,iBAAiB,CAAA,EACxB;AAEnB,UAAM,IAAqB,KAAK,uBAAuB,CAAA,GACjD,IAAY,KAAK,cAAc,GAAU,KAAA,GACzC,IAAY,KAAK,cAAc,GAAU,KAAA;AAK/C,IAFE,EAAA,KAAU,EAAO,IAAI,iBAAA,MAAqB,EAAO,IAAI,iBAAA,MAAuB,cAG5E,KAAK,gBAAgB,GAAU,EAAA,GAC/B,KAAK,YAAY,GAAU,EAAA,GACvB,EAAS,SAAS,YAAY,eAChC,EAAS,SAAS,UAAU,YAE5B,GAAoB,SAAS,MAAM,IAAI,IAAI,EAAA,GAErC,MACJ,EAAU,qBAAqB,IAAI,EAAM,MAAM,GAAG,GAAG,IAAA,CAAK,GAC1D,EAAU,OAAQ,SAAS,IAAI,OAAO,GAAG,KAAA,IAErC,MACJ,EAAU,qBAAqB,IAAI,EAAM,MAAM,GAAG,GAAG,IAAA,CAAK,GAC1D,EAAU,OAAQ,SAAS,IAAI,OAAO,GAAG,IAAA,OAIzC,EAAS,SAAS,YAAY,aAChC,EAAS,SAAS,UAAU,UAC5B,KAAK,gBAAgB,GAAU,EAAA,GAC/B,KAAK,YAAY,GAAU,EAAA,GAE3B,GAAoB,SAAS,MAAM,KAAK,KAAK,GAAA,GAEvC,MACJ,EAAU,qBAAqB,IAAI,EAAM,MAAM,GAAG,GAAG,IAAA,CAAK,GAC1D,EAAU,OAAQ,SAAS,IAAI,OAAO,GAAG,KAAA,IAErC,MACJ,EAAU,qBAAqB,IAAI,EAAM,MAAM,GAAG,GAAG,IAAA,CAAK,GAC1D,EAAU,OAAQ,SAAS,IAAI,OAAO,GAAG,IAAA;AAAA;EAYjD,gBAAyB,GAA0B,GAAoC;AACrF,UAAM,IAAW,KAAK,aAAa,CAAA;AACnC,QAAK,GAGL;AAAA,UAAI,CAAC,KAAS,CAAC,KAAK,qBAAqB,EAAM,UAAU,iBAAiB;AACxE,aAAK,cAAc,CAAA,GACnB,KAAK,2BAA2B,CAAA;AAChC;AAAA;AAGF,UAAI,EAAM,UAAU,QAAQ;AAC1B,aAAK,cAAc,CAAA,GACnB,KAAK,cAAc,GAAU,KAAK,iBAAiB,KAAK,4BAAA;AACxD;AAAA;AAGF,UAAI,EAAM,UAAU,OAAO;AACzB,aAAK,cAAc,CAAA,GACnB,KAAK,cAAc,GAAU,KAAK,gBAAgB,KAAK,2BAAA;AACvD;AAAA;AAIF,UAAI,KAAK,kBAAkB,qBAAqB,WAAW;AACzD,QAAI,EAAM,UAAU,WAClB,KAAK,cAAc,GAAU,KAAK,gBAAgB,KAAK,2BAAA,IAGvD,KAAK,cAAc,GAAU,KAAK,iBAAiB,KAAK,4BAAA;AAE1D;AAAA;AAIF,MAAI,EAAM,iBACR,KAAK,kBAAkB,GAAU,GAAU,CAAA;AAAA;AAAA;EAQ/C,cAAsB,GAAsB,GAAoB,GAAiC;AAC/F,SAAK,0BAA0B,CAAA;AAC/B,UAAM,IAAM,EAAS;AACrB,IAAA,EAAI,MAAM,KAAK,CAAA,GACf,EAAI,SAAS,KAAK,CAAA,GAClB,EAAI,oBAAoB;AAAA;EAG1B,0BAAkC,GAA4B;AAE5D,IADY,EAAS,SACb,SAAS,YAAY,EAAW,oBACxC,EAAS,WAAW,KAAK,OAAO,EAAe,SAAA,EAAW,MAAA,GACzD,EAAS,SAAuC,SAAS,UAAU,EAAW;AAAA;EAGjF,2BAAmC,GAA4B;AAC7D,UAAM,IAAM,EAAS;AACrB,IAAI,EAAI,SAAS,YAAY,EAAW,oBACxC,EAAI,QAAA,GACJ,EAAS,WAAW,KAAK,OAAO,EAAe,SAAA;AAAA;EAGjD,cAAsB,GAAgC;AACpD,UAAM,IAAQ,EAAS,SAAS;AAChC,IAAI,MACF,EAAM,cAAA,GACN,EAAM,YAAY,CAAA,GAClB,OAAO,EAAS,SAAS,QAE3B,OAAO,EAAS,SAAS,eACzB,OAAO,EAAS,SAAS,aACzB,OAAO,EAAS,SAAS;AAAA;EAO3B,kBACE,GACA,GACA,GACM;AAEN,QAAI,EAAS,SAAS,uBAAuB,EAAM,UAAW;AAE9D,UAAM,IAAM,KAAK,kBAAmB,gBAE9B,KADO,EAAM,iBAAiB,EAAM,aACX;AAE/B,SAAK,0BAA0B,CAAA;AAC/B,UAAM,IAAM,EAAS,UAEf,IAAU,EAAM,UAAU,WAAW,KAAK,kBAAkB,KAAK,gBACjE,IACJ,EAAM,UAAU,WACZ,KAAK,+BACL,KAAK,6BAGL,IAAa;AAAA,MAAC,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA,OAClD,IAAS;AAAA,MAAC,EAAQ;AAAA,MAAG,EAAQ;AAAA,MAAG,EAAQ;AAAA,OACxC,IAAmB,EAAI;AAE7B,QAAI,IAA8B,EAAS,SAAS;AACpD,IAAK,MACH,IAAQ,IAAI,EAAM,eAAe,CAAA,GACjC,EAAS,SAAS,QAAQ,IAIxB,EAAS,SAAS,iBACnB,EAAS,SAAS,cAAwC,KAAA,GAEzD,EAAS,SAAS,eACpB,EAAM,YAAY,EAAS,SAAS,WAAA;AAGtC,UAAM,IAAO,IAAI,EAAM,cAAc,aAAa,GAAiB;AAAA,MACjE,IAAI,EAAM,mBACR,uBACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAG,GAAY,GAAG,CAAA,CAAO;AAAA,MAE5B,IAAI,EAAM,mBACR,0BACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAG,GAAY,GAAG,CAAA,CAAO;AAAA,MAE5B,IAAI,EAAM,oBACR,mCACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAkB,CAAA,CAAY;AAAA,KAElC,GACK,IAAS,EAAM,WAAW,CAAA;AAChC,IAAA,EAAO,OAAO,EAAM,UACpB,EAAO,oBAAoB,IAC3B,EAAO,KAAA,GAEP,EAAS,SAAS,qBAAqB,EAAM,WAC7C,EAAS,SAAS,gBAAgB,GAClC,EAAS,SAAS,cAAc;AAAA;EAYlC,iBAAyB,GAA6C;AACpE,QAAI,IAAkC;AAEtC,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,eACzD,IAAe;AAAA,QAGZ;AAAA;EAYT,aAAqB,GAA6C;AAChE,QAAI,IAA8B;AAElC,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,WACzD,IAAW;AAAA,QAGR;AAAA;EAYT,uBAAiC,GAA6C;AAC5E,QAAI,IAAwC;AAE5C,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,qBACzD,IAAqB;AAAA,QAGlB;AAAA;GC7cE,KAAb,cAA2C,EAA2B;AAAA,EAEpE,gBAAmC,GAAgB,KAAK,KAAK,KAAK,KAAK,EAAA;AAAA,EAEvE,YAA+B,GAAoB,KAAK,KAAK,KAAK,KAAK,EAAA;AAAA,EAEvE,kBAAqC,IAAI,EAAM,iBAC7C,KACA,KACA,KACA,IACA,GACA,IACA,GACA,KAAK,KAAK,CAAA;AAAA,EAGZ,kBAAqC,IAAI,EAAM,MAAM,QAAA;AAAA,EACrD,iBAAoC,IAAI,EAAM,MAAM,OAAA;AAAA,EACpD,+BAAkD;AAAA,EAClD,8BAAiD;AAAA,EAEjD,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,kBAA2B;AACzB,WAAO,KAAK;AAAA;EAGd,aAAa,GAAsB,GAAwC;AACzE,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAG9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAI3B,UAAM,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,GAAG,GAAG,GAAA;AACxE,IAAA,EAAM,IAAI,CAAA;AAGV,UAAM,IAAW,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,OAAO,EAAe,KAAA,CAAM;AACrF,IAAA,EAAS,WAAW;AAAA,MAClB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAS,QAAQ,CAAC,KAAK,KAAK,CAAA,GAC5B,EAAS,QAAQ,KAAK,EAAA,GACtB,EAAS,SAAS,IAAI,GAAG,MAAM,CAAA,GAC/B,EAAM,IAAI,CAAA;AAEV,UAAM,IAAO,IAAI,EAAM,KAAK,KAAK,WAAW,KAAK,OAAO,EAAe,SAAA,CAAU;AACjF,WAAA,EAAK,OAAO,QACZ,EAAK,WAAW;AAAA,MACd,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,MACN,cAAc;AAAA,OAEhB,EAAK,QAAQ,CAAC,KAAK,KAAK,CAAA,GACxB,EAAK,QAAQ,KAAK,EAAA,GAClB,EAAK,SAAS,IAAI,GAAG,MAAM,CAAA,GAC3B,EAAM,IAAI,CAAA,GAGN,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAG5C,KAAK,wBAAwB,GAAO,EAAU,MAAA,GACvC;AAAA;EAGT,iBAA2B,GAAsB,GAAwB,GAAoB;AAC3F,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,QAAA;AAC9C,MAAA,EAAS,SAAS,IAAI,MAAM,GAAG,IAAA,GAC/B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,KAAA;AAC9C,MAAA,EAAS,SAAS,IAAI,MAAM,GAAG,KAAA,GAC/B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,OAAA;AACpD,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,GAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,OAAA;AACpD,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,IAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,MAAA;AACpD,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,CAAA,GAClC,EAAM,IAAI,CAAA;AAAA;;EAUd,wBAAiC,GAA2D;AAE1F,WAAO,EACL,QAAQ;AAAA,MACN;AAAA,QACE,KAAK;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,UAAE,MAAM;AAAA,UAAS,KAAK;AAAA,UAAQ,SAAS;AAAA;;MAElD;AAAA,QAAE,KAAK;AAAA,QAAmB,MAAM;AAAA;MAChC;AAAA,QAAE,KAAK;AAAA,QAAkB,MAAM;AAAA,QAAU,KAAK;AAAA,QAAG,WATjC,GAAQ,IAAI,oBAAA,KAAyB,aASsB;AAAA;MAC3E;AAAA,QAAE,KAAK;AAAA,QAAuB,MAAM;AAAA;MACrC;AAAA;EAWL,oBAA6B,GAA+C;AAC1E,UAAM,IAAW,oBAAI,IAAA;AACrB,IAAA,EAAS,IAAI,sBAAsB,EAAO,IAAI,oBAAA,KAAyB,OAAA;AACvE,UAAM,IAAkB,EAAO,IAAI,iBAAA;AACnC,WAAA,EAAS,IAAI,mBAAmB,MAAoB,UAAA,GACpD,EAAS,IAAI,kBAAkB,WAAW,EAAO,IAAI,gBAAA,KAAqB,GAAA,CAAI,GAC9E,EAAS,IAAI,uBAAuB,WAAW,EAAO,IAAI,qBAAA,KAA0B,GAAA,CAAI,GACjF;AAAA;EAUT,oBAA6B,GAAiD;AAC5E,UAAM,IAAS,oBAAI,IAAA;AACnB,IAAA,EAAO,IAAI,sBAAsB,EAAS,IAAI,oBAAA,KAAyB,OAAA;AACvE,UAAM,IAAkB,EAAS,IAAI,iBAAA;AACrC,WAAA,EAAO,IAAI,mBAAmB,IAAkB,aAAa,UAAA,GAC7D,EAAO,IAAI,kBAAkB,EAAS,IAAI,gBAAA,EAAkB,SAAA,CAAU,GACtE,EAAO,IAAI,uBAAuB,EAAS,IAAI,qBAAA,EAAuB,SAAA,KAAc,IAAA,GAC7E;AAAA;EAGT,wBAAiC,GAA0B,GAA6B;AACtF,UAAM,IAAW,KAAK,aAAa,CAAA;AACnC,QAAI,CAAC,EAAU;AAEf,QAAI,IAAqB,KAAK,uBAAuB,CAAA;AAErD,IAAI,EAAO,IAAI,iBAAA,MAAuB,cACpC,EAAS,SAAS,eAAe,QAC5B,MACH,IAAqB,IAAI,EAAM,KAC7B,KAAK,iBACL,KAAK,OAAO,EAAe,KAAA,CAAM,GAEnC,EAAmB,WAAW;AAAA,MAC5B,MAAM;AAAA,MACN,aAAa,EAAS,SAAS;AAAA,MAC/B,MAAM;AAAA,OAGR,EAAmB,SAAS,IAAI,MAAM,MAAM,KAAA,GAC5C,EAAS,IAAI,CAAA,OAGf,EAAS,SAAS,eAAe,OAC7B,KACF,EAAS,OAAO,CAAA;AAAA;EAWtB,gBAAyB,GAA0B,GAAoC;AACrF,UAAM,IAAW,KAAK,aAAa,CAAA;AACnC,QAAK,GAEL;AAAA,UAAI,CAAC,KAAS,CAAC,KAAK,qBAAqB,EAAM,UAAU,iBAAiB;AACxE,aAAK,cAAc,CAAA,GACnB,KAAK,2BAA2B,CAAA;AAChC;AAAA;AAGF,UAAI,EAAM,UAAU,QAAQ;AAC1B,aAAK,cAAc,CAAA,GACnB,KAAK,cAAc,GAAU,KAAK,iBAAiB,KAAK,4BAAA;AACxD;AAAA;AAGF,UAAI,EAAM,UAAU,OAAO;AACzB,aAAK,cAAc,CAAA,GACnB,KAAK,cAAc,GAAU,KAAK,gBAAgB,KAAK,2BAAA;AACvD;AAAA;AAIF,UAAI,KAAK,kBAAkB,qBAAqB,WAAW;AACzD,QAAI,EAAM,UAAU,WAClB,KAAK,cAAc,GAAU,KAAK,gBAAgB,KAAK,2BAAA,IAGvD,KAAK,cAAc,GAAU,KAAK,iBAAiB,KAAK,4BAAA;AAE1D;AAAA;AAIF,MAAI,EAAM,iBACR,KAAK,kBAAkB,GAAU,GAAU,CAAA;AAAA;AAAA;EAQ/C,cAAsB,GAAsB,GAAoB,GAAiC;AAC/F,SAAK,0BAA0B,CAAA;AAC/B,UAAM,IAAM,EAAS;AACrB,IAAA,EAAI,MAAM,KAAK,CAAA,GACf,EAAI,SAAS,KAAK,CAAA,GAClB,EAAI,oBAAoB;AAAA;EAG1B,0BAAkC,GAA4B;AAE5D,IADY,EAAS,SACb,SAAS,YAAY,EAAW,oBACxC,EAAS,WAAW,KAAK,OAAO,EAAe,SAAA,EAAW,MAAA,GACzD,EAAS,SAAuC,SAAS,UAAU,EAAW;AAAA;EAGjF,2BAAmC,GAA4B;AAC7D,UAAM,IAAM,EAAS;AACrB,IAAI,EAAI,SAAS,YAAY,EAAW,oBACxC,EAAI,QAAA,GACJ,EAAS,WAAW,KAAK,OAAO,EAAe,SAAA;AAAA;EAGjD,cAAsB,GAAgC;AACpD,UAAM,IAAQ,EAAS,SAAS;AAChC,IAAI,MACF,EAAM,cAAA,GACN,EAAM,YAAY,CAAA,GAClB,OAAO,EAAS,SAAS,QAE3B,OAAO,EAAS,SAAS,eACzB,OAAO,EAAS,SAAS,aACzB,OAAO,EAAS,SAAS;AAAA;EAO3B,kBACE,GACA,GACA,GACM;AACN,QAAI,EAAS,SAAS,uBAAuB,EAAM,UAAW;AAE9D,UAAM,IAAM,KAAK,kBAAmB,gBAE9B,KADO,EAAM,iBAAiB,EAAM,aACX;AAE/B,SAAK,0BAA0B,CAAA;AAC/B,UAAM,IAAM,EAAS,UAEf,IAAU,EAAM,UAAU,WAAW,KAAK,kBAAkB,KAAK,gBACjE,IACJ,EAAM,UAAU,WACZ,KAAK,+BACL,KAAK,6BAEL,IAAa;AAAA,MAAC,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA,OAClD,IAAS;AAAA,MAAC,EAAQ;AAAA,MAAG,EAAQ;AAAA,MAAG,EAAQ;AAAA,OACxC,IAAmB,EAAI;AAE7B,QAAI,IAA8B,EAAS,SAAS;AACpD,IAAK,MACH,IAAQ,IAAI,EAAM,eAAe,CAAA,GACjC,EAAS,SAAS,QAAQ,IAGxB,EAAS,SAAS,iBACnB,EAAS,SAAS,cAAwC,KAAA,GAEzD,EAAS,SAAS,eACpB,EAAM,YAAY,EAAS,SAAS,WAAA;AAGtC,UAAM,IAAO,IAAI,EAAM,cAAc,aAAa,GAAiB;AAAA,MACjE,IAAI,EAAM,mBACR,uBACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAG,GAAY,GAAG,CAAA,CAAO;AAAA,MAE5B,IAAI,EAAM,mBACR,0BACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAG,GAAY,GAAG,CAAA,CAAO;AAAA,MAE5B,IAAI,EAAM,oBACR,mCACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAkB,CAAA,CAAY;AAAA,KAElC,GACK,IAAS,EAAM,WAAW,CAAA;AAChC,IAAA,EAAO,OAAO,EAAM,UACpB,EAAO,oBAAoB,IAC3B,EAAO,KAAA,GAEP,EAAS,SAAS,qBAAqB,EAAM,WAC7C,EAAS,SAAS,gBAAgB,GAClC,EAAS,SAAS,cAAc;AAAA;EAYlC,iBAA2B,GAA6C;AACtE,QAAI,IAAkC;AAEtC,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,eACzD,IAAe;AAAA,QAGZ;AAAA;EAYT,aAAuB,GAA6C;AAClE,QAAI,IAA8B;AAElC,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,WACzD,IAAW;AAAA,QAGR;AAAA;EAYT,uBAAiC,GAA6C;AAC5E,QAAI,IAAwC;AAE5C,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,qBACzD,IAAqB;AAAA,QAGlB;AAAA;GC5ZE,KAAb,cAA4C,GAAsB;AAAA,EAEhE,gBAA4C,GAAgB,GAAG,KAAK,MAAM,KAAK,EAAA;AAAA,EAE/E,YAAwC,GAAoB,GAAG,KAAK,MAAM,KAAK,EAAA;AAAA,EAE/E,kBAA8C,IAAI,EAAM,iBACtD,MACA,MACA,KACA,IACA,GACA,IACA,GACA,KAAK,KAAK,CAAA;AAAA,EAGZ,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,aAAsB,GAAsB,GAAwC;AAClF,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAG9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAI3B,UAAM,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,KAAK,GAAG,GAAA;AAC1E,IAAA,EAAM,IAAI,CAAA;AAGV,UAAM,IAAW,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,OAAO,EAAe,KAAA,CAAM;AACrF,IAAA,EAAS,WAAW;AAAA,MAClB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAS,QAAQ,CAAC,KAAK,KAAK,CAAA,GAC5B,EAAS,QAAQ,KAAK,EAAA,GACtB,EAAS,SAAS,IAAI,OAAO,MAAM,CAAA,GACnC,EAAM,IAAI,CAAA;AAEV,UAAM,IAAO,IAAI,EAAM,KAAK,KAAK,WAAW,KAAK,OAAO,EAAe,SAAA,CAAU;AACjF,WAAA,EAAK,OAAO,QACZ,EAAK,WAAW;AAAA,MACd,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,MACN,cAAc;AAAA,OAEhB,EAAK,QAAQ,CAAC,KAAK,KAAK,CAAA,GACxB,EAAK,QAAQ,KAAK,EAAA,GAClB,EAAK,SAAS,IAAI,OAAO,MAAM,CAAA,GAC/B,EAAM,IAAI,CAAA,GAGN,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAG5C,KAAK,wBAAwB,GAAO,EAAU,MAAA,GACvC;AAAA;EAGT,iBACE,GACA,GACA,GACA;AACA,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,UAAU,IAAI,EAAM,MAAM,GAAG,GAAG,IAAA,CAAK;AACnF,MAAA,EAAS,SAAS,IAAI,MAAM,GAAG,IAAA,GAC/B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,OAAO,IAAI,EAAM,MAAM,GAAG,GAAG,IAAA,CAAK;AAChF,MAAA,EAAS,SAAS,IAAI,MAAM,GAAG,KAAA,GAC/B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,OAAA;AACpD,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,GAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,OAAA;AACpD,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,GAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,OAAA;AACpD,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,IAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,OAAA;AACpD,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,IAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,MAAA;AACpD,MAAA,EAAY,SAAS,IAAI,OAAO,GAAG,CAAA,GACnC,EAAM,IAAI,CAAA;AAAA;;EAId,wBAAiC,GAA0B,GAA6B;AACtF,UAAM,IAAW,KAAK,aAAa,CAAA;AACnC,QAAI,CAAC,EAAU;AAEf,QAAI,IAAqB,KAAK,uBAAuB,CAAA;AAErD,IAAI,EAAO,IAAI,iBAAA,MAAuB,cACpC,EAAS,SAAS,eAAe,QAC5B,MACH,IAAqB,IAAI,EAAM,KAC7B,KAAK,iBACL,KAAK,OAAO,EAAe,KAAA,CAAM,GAEnC,EAAmB,WAAW;AAAA,MAC5B,MAAM;AAAA,MACN,aAAa,EAAS,SAAS;AAAA,MAC/B,MAAM;AAAA,OAGR,EAAmB,SAAS,IAAI,IAAI,MAAM,IAAA,GAC1C,EAAS,IAAI,CAAA,OAGf,EAAS,SAAS,eAAe,OAC7B,KACF,EAAS,OAAO,CAAA;AAAA;GCxJX,KAAb,cAA4C,GAAsB;AAAA,EAEhE,gBAA4C,GAAgB,KAAK,KAAK,MAAM,KAAK,EAAA;AAAA,EAEjF,YAAwC,GAAoB,KAAK,KAAK,MAAM,KAAK,EAAA;AAAA,EAEjF,kBAA8C,IAAI,EAAM,iBACtD,MACA,MACA,KACA,IACA,GACA,IACA,GACA,KAAK,KAAK,CAAA;AAAA,EAGZ,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,aAAsB,GAAsB,GAAwC;AAClF,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAG9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAI3B,UAAM,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,GAAG,GAAG,GAAA;AACxE,IAAA,EAAM,IAAI,CAAA;AAGV,UAAM,IAAW,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,OAAO,EAAe,KAAA,CAAM;AACrF,IAAA,EAAS,WAAW;AAAA,MAClB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAS,QAAQ,CAAC,KAAK,KAAK,CAAA,GAC5B,EAAS,QAAQ,KAAK,EAAA,GACtB,EAAS,SAAS,IAAI,MAAM,MAAM,CAAA,GAClC,EAAM,IAAI,CAAA;AAEV,UAAM,IAAO,IAAI,EAAM,KAAK,KAAK,WAAW,KAAK,OAAO,EAAe,SAAA,CAAU;AACjF,WAAA,EAAK,OAAO,QACZ,EAAK,WAAW;AAAA,MACd,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,MACN,cAAc;AAAA,OAEhB,EAAK,QAAQ,CAAC,KAAK,KAAK,CAAA,GACxB,EAAK,QAAQ,KAAK,EAAA,GAClB,EAAK,SAAS,IAAI,MAAM,MAAM,CAAA,GAC9B,EAAM,IAAI,CAAA,GAGN,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAG5C,KAAK,wBAAwB,GAAO,EAAU,MAAA,GACvC;AAAA;EAGT,iBACE,GACA,GACA,GACA;AACA,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,UAAU,IAAI,EAAM,MAAM,GAAG,GAAG,GAAA,CAAI;AAClF,MAAA,EAAS,SAAS,IAAI,KAAK,GAAG,GAAA,GAC9B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,EAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,OAAO,IAAI,EAAM,MAAM,GAAG,GAAG,GAAA,CAAI;AAC/E,MAAA,EAAS,SAAS,IAAI,KAAK,GAAG,IAAA,GAC9B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,OAAA;AACpD,MAAA,EAAY,SAAS,IAAI,KAAK,GAAG,IAAA,GACjC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,OAAA;AACpD,MAAA,EAAY,SAAS,IAAI,KAAK,GAAG,GAAA,GACjC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,OAAA;AACpD,MAAA,EAAY,SAAS,IAAI,KAAK,GAAG,GAAA,GACjC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,OAAA;AACpD,MAAA,EAAY,SAAS,IAAI,KAAK,GAAG,GAAA,GACjC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,OAAA;AACpD,MAAA,EAAY,SAAS,IAAI,KAAK,GAAG,IAAA,GACjC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,OAAA;AACpD,MAAA,EAAY,SAAS,IAAI,KAAK,GAAG,IAAA,GACjC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,OAAA;AACpD,MAAA,EAAY,SAAS,IAAI,KAAK,GAAG,IAAA,GACjC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,OAAA;AACpD,MAAA,EAAY,SAAS,IAAI,KAAK,GAAG,KAAA,GACjC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,MAAA;AACpD,MAAA,EAAY,SAAS,IAAI,OAAO,GAAG,KAAA,GACnC,EAAM,IAAI,CAAA;AAAA;;EAId,wBAAiC,GAA0B,GAA6B;AACtF,UAAM,IAAW,KAAK,aAAa,CAAA;AACnC,QAAI,CAAC,EAAU;AAEf,QAAI,IAAqB,KAAK,uBAAuB,CAAA;AAErD,IAAI,EAAO,IAAI,iBAAA,MAAuB,cACpC,EAAS,SAAS,eAAe,QAC5B,MACH,IAAqB,IAAI,EAAM,KAC7B,KAAK,iBACL,KAAK,OAAO,EAAe,KAAA,CAAM,GAEnC,EAAmB,WAAW;AAAA,MAC5B,MAAM;AAAA,MACN,aAAa,EAAS,SAAS;AAAA,MAC/B,MAAM;AAAA,OAGR,EAAmB,SAAS,IAAI,MAAM,MAAM,IAAA,GAC5C,EAAS,IAAI,CAAA,OAGf,EAAS,SAAS,eAAe,OAC7B,KACF,EAAS,OAAO,CAAA;AAAA;GCpLX,KAAb,cAA0C,EAA2B;AAAA,EAEnE,gBAAmC,EAAe,KAAK,KAAK,KAAK,KAAK,EAAA;AAAA,EAEtE,YAA+B,EAAmB,KAAK,KAAK,KAAK,KAAK,EAAA;AAAA,EAEtE,kBAAqC,IAAI,EAAM,iBAC7C,KACA,KACA,KACA,IACA,GACA,IACA,GACA,KAAK,KAAK,CAAA;AAAA,EAGZ,kBAAqC,IAAI,EAAM,MAAM,QAAA;AAAA,EACrD,iBAAoC,IAAI,EAAM,MAAM,OAAA;AAAA,EACpD,+BAAkD;AAAA,EAClD,8BAAiD;AAAA,EAEjD,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,kBAA2B;AACzB,WAAO,KAAK;AAAA;EAGd,aAAa,GAAsB,GAAwC;AACzE,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAE9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAI3B,UAAM,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,GAAG,GAAG,GAAA;AACxE,IAAA,EAAM,IAAI,CAAA;AAGV,UAAM,IAAW,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,OAAO,EAAe,KAAA,CAAM;AACrF,IAAA,EAAS,WAAW;AAAA,MAClB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAS,QAAQ,CAAC,KAAK,KAAK,CAAA,GAC5B,EAAS,QAAQ,KAAK,EAAA,GACtB,EAAS,SAAS,IAAI,GAAG,MAAM,CAAA,GAC/B,EAAM,IAAI,CAAA;AAEV,UAAM,IAAO,IAAI,EAAM,KAAK,KAAK,WAAW,KAAK,OAAO,EAAe,SAAA,CAAU;AACjF,WAAA,EAAK,OAAO,QACZ,EAAK,WAAW;AAAA,MACd,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,MACN,cAAc;AAAA,OAEhB,EAAK,QAAQ,CAAC,KAAK,KAAK,CAAA,GACxB,EAAK,QAAQ,KAAK,EAAA,GAClB,EAAK,SAAS,IAAI,GAAG,MAAM,CAAA,GAC3B,EAAM,IAAI,CAAA,GAGN,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAG5C,KAAK,wBAAwB,GAAO,EAAU,MAAA,GACvC;AAAA;EAGT,iBAA2B,GAAsB,GAAwB,GAAoB;AAC3F,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,QAAA;AAC9C,MAAA,EAAS,SAAS,IAAI,MAAM,GAAG,IAAA,GAC/B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,KAAA;AAC9C,MAAA,EAAS,SAAS,IAAI,MAAM,GAAG,KAAA,GAC/B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,MAAM,GAAG,CAAA,CAAE;AACxF,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,GAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,OAAO,GAAG,CAAA,CAAE;AACzF,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,IAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,MAAA;AACpD,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,CAAA,GAClC,EAAM,IAAI,CAAA;AAAA;;EAUd,wBAAiC,GAA2D;AAE1F,WAAO,EACL,QAAQ;AAAA,MACN;AAAA,QACE,KAAK;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,UAAE,MAAM;AAAA,UAAS,KAAK;AAAA,UAAQ,SAAS;AAAA;;MAElD;AAAA,QAAE,KAAK;AAAA,QAAmB,MAAM;AAAA;MAChC;AAAA,QAAE,KAAK;AAAA,QAAkB,MAAM;AAAA,QAAU,KAAK;AAAA,QAAG,WATjC,GAAQ,IAAI,oBAAA,KAAyB,aASsB;AAAA;MAC3E;AAAA,QAAE,KAAK;AAAA,QAAuB,MAAM;AAAA;MACrC;AAAA;EAWL,oBAA6B,GAA+C;AAC1E,UAAM,IAAW,oBAAI,IAAA;AACrB,IAAA,EAAS,IAAI,sBAAsB,EAAO,IAAI,oBAAA,KAAyB,OAAA;AACvE,UAAM,IAAkB,EAAO,IAAI,iBAAA;AACnC,WAAA,EAAS,IAAI,mBAAmB,MAAoB,UAAA,GACpD,EAAS,IAAI,kBAAkB,WAAW,EAAO,IAAI,gBAAA,KAAqB,GAAA,CAAI,GAC9E,EAAS,IAAI,uBAAuB,WAAW,EAAO,IAAI,qBAAA,KAA0B,GAAA,CAAI,GACjF;AAAA;EAUT,oBAA6B,GAAiD;AAC5E,UAAM,IAAS,oBAAI,IAAA;AACnB,IAAA,EAAO,IAAI,sBAAsB,EAAS,IAAI,oBAAA,KAAyB,OAAA;AACvE,UAAM,IAAkB,EAAS,IAAI,iBAAA;AACrC,WAAA,EAAO,IAAI,mBAAmB,IAAkB,aAAa,UAAA,GAC7D,EAAO,IAAI,kBAAkB,EAAS,IAAI,gBAAA,EAAkB,SAAA,CAAU,GACtE,EAAO,IAAI,uBAAuB,EAAS,IAAI,qBAAA,EAAuB,SAAA,KAAc,IAAA,GAC7E;AAAA;EAGT,wBAAiC,GAA0B,GAA6B;AACtF,UAAM,IAAW,KAAK,aAAa,CAAA;AACnC,QAAI,CAAC,EAAU;AAEf,QAAI,IAAqB,KAAK,uBAAuB,CAAA;AAErD,IAAI,EAAO,IAAI,iBAAA,MAAuB,cACpC,EAAS,SAAS,eAAe,QAC5B,MACH,IAAqB,IAAI,EAAM,KAC7B,KAAK,iBACL,KAAK,OAAO,EAAe,KAAA,CAAM,GAEnC,EAAmB,WAAW;AAAA,MAC5B,MAAM;AAAA,MACN,aAAa,EAAS,SAAS;AAAA,MAC/B,MAAM;AAAA,OAGR,EAAmB,SAAS,IAAI,MAAM,MAAM,KAAA,GAC5C,EAAS,IAAI,CAAA,OAGf,EAAS,SAAS,eAAe,OAC7B,KACF,EAAS,OAAO,CAAA;AAAA;EAWtB,gBAAyB,GAA0B,GAAoC;AACrF,UAAM,IAAW,KAAK,aAAa,CAAA;AACnC,QAAK,GAEL;AAAA,UAAI,CAAC,KAAS,CAAC,KAAK,qBAAqB,EAAM,UAAU,iBAAiB;AACxE,aAAK,cAAc,CAAA,GACnB,KAAK,2BAA2B,CAAA;AAChC;AAAA;AAGF,UAAI,EAAM,UAAU,QAAQ;AAC1B,aAAK,cAAc,CAAA,GACnB,KAAK,cAAc,GAAU,KAAK,iBAAiB,KAAK,4BAAA;AACxD;AAAA;AAGF,UAAI,EAAM,UAAU,OAAO;AACzB,aAAK,cAAc,CAAA,GACnB,KAAK,cAAc,GAAU,KAAK,gBAAgB,KAAK,2BAAA;AACvD;AAAA;AAIF,UAAI,KAAK,kBAAkB,qBAAqB,WAAW;AACzD,QAAI,EAAM,UAAU,WAClB,KAAK,cAAc,GAAU,KAAK,gBAAgB,KAAK,2BAAA,IAGvD,KAAK,cAAc,GAAU,KAAK,iBAAiB,KAAK,4BAAA;AAE1D;AAAA;AAIF,MAAI,EAAM,iBACR,KAAK,kBAAkB,GAAU,GAAU,CAAA;AAAA;AAAA;EAQ/C,cAAsB,GAAsB,GAAoB,GAAiC;AAC/F,SAAK,0BAA0B,CAAA;AAC/B,UAAM,IAAM,EAAS;AACrB,IAAA,EAAI,MAAM,KAAK,CAAA,GACf,EAAI,SAAS,KAAK,CAAA,GAClB,EAAI,oBAAoB;AAAA;EAG1B,0BAAkC,GAA4B;AAE5D,IADY,EAAS,SACb,SAAS,YAAY,EAAW,oBACxC,EAAS,WAAW,KAAK,OAAO,EAAe,SAAA,EAAW,MAAA,GACzD,EAAS,SAAuC,SAAS,UAAU,EAAW;AAAA;EAGjF,2BAAmC,GAA4B;AAC7D,UAAM,IAAM,EAAS;AACrB,IAAI,EAAI,SAAS,YAAY,EAAW,oBACxC,EAAI,QAAA,GACJ,EAAS,WAAW,KAAK,OAAO,EAAe,SAAA;AAAA;EAGjD,cAAsB,GAAgC;AACpD,UAAM,IAAQ,EAAS,SAAS;AAChC,IAAI,MACF,EAAM,cAAA,GACN,EAAM,YAAY,CAAA,GAClB,OAAO,EAAS,SAAS,QAE3B,OAAO,EAAS,SAAS,eACzB,OAAO,EAAS,SAAS,aACzB,OAAO,EAAS,SAAS;AAAA;EAO3B,kBACE,GACA,GACA,GACM;AACN,QAAI,EAAS,SAAS,uBAAuB,EAAM,UAAW;AAE9D,UAAM,IAAM,KAAK,kBAAmB,gBAE9B,KADO,EAAM,iBAAiB,EAAM,aACX;AAE/B,SAAK,0BAA0B,CAAA;AAC/B,UAAM,IAAM,EAAS,UAEf,IAAU,EAAM,UAAU,WAAW,KAAK,kBAAkB,KAAK,gBACjE,IACJ,EAAM,UAAU,WACZ,KAAK,+BACL,KAAK,6BAEL,IAAa;AAAA,MAAC,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA,OAClD,IAAS;AAAA,MAAC,EAAQ;AAAA,MAAG,EAAQ;AAAA,MAAG,EAAQ;AAAA,OACxC,IAAmB,EAAI;AAE7B,QAAI,IAA8B,EAAS,SAAS;AACpD,IAAK,MACH,IAAQ,IAAI,EAAM,eAAe,CAAA,GACjC,EAAS,SAAS,QAAQ,IAGxB,EAAS,SAAS,iBACnB,EAAS,SAAS,cAAwC,KAAA,GAEzD,EAAS,SAAS,eACpB,EAAM,YAAY,EAAS,SAAS,WAAA;AAGtC,UAAM,IAAO,IAAI,EAAM,cAAc,aAAa,GAAiB;AAAA,MACjE,IAAI,EAAM,mBACR,uBACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAG,GAAY,GAAG,CAAA,CAAO;AAAA,MAE5B,IAAI,EAAM,mBACR,0BACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAG,GAAY,GAAG,CAAA,CAAO;AAAA,MAE5B,IAAI,EAAM,oBACR,mCACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAkB,CAAA,CAAY;AAAA,KAElC,GACK,IAAS,EAAM,WAAW,CAAA;AAChC,IAAA,EAAO,OAAO,EAAM,UACpB,EAAO,oBAAoB,IAC3B,EAAO,KAAA,GAEP,EAAS,SAAS,qBAAqB,EAAM,WAC7C,EAAS,SAAS,gBAAgB,GAClC,EAAS,SAAS,cAAc;AAAA;EAYlC,iBAA2B,GAA6C;AACtE,QAAI,IAAkC;AAEtC,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,eACzD,IAAe;AAAA,QAGZ;AAAA;EAYT,aAAuB,GAA6C;AAClE,QAAI,IAA8B;AAElC,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,WACzD,IAAW;AAAA,QAGR;AAAA;EAYT,uBAAiC,GAA6C;AAC5E,QAAI,IAAwC;AAE5C,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,qBACzD,IAAqB;AAAA,QAGlB;AAAA;GC3ZE,KAAb,cAA2C,GAAqB;AAAA,EAE9D,gBAA4C,EAAe,GAAG,KAAK,MAAM,KAAK,EAAA;AAAA,EAE9E,YAAwC,EAAmB,GAAG,KAAK,MAAM,KAAK,EAAA;AAAA,EAE9E,kBAA8C,IAAI,EAAM,iBACtD,MACA,MACA,KACA,IACA,GACA,IACA,GACA,KAAK,KAAK,CAAA;AAAA,EAGZ,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,aAAsB,GAAsB,GAAwC;AAClF,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAE9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAI3B,UAAM,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,KAAK,GAAG,GAAA;AAC1E,IAAA,EAAM,IAAI,CAAA;AAGV,UAAM,IAAW,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,OAAO,EAAe,KAAA,CAAM;AACrF,IAAA,EAAS,WAAW;AAAA,MAClB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAS,QAAQ,CAAC,KAAK,KAAK,CAAA,GAC5B,EAAS,QAAQ,KAAK,EAAA,GACtB,EAAS,SAAS,IAAI,OAAO,MAAM,CAAA,GACnC,EAAM,IAAI,CAAA;AAEV,UAAM,IAAO,IAAI,EAAM,KAAK,KAAK,WAAW,KAAK,OAAO,EAAe,SAAA,CAAU;AACjF,WAAA,EAAK,OAAO,QACZ,EAAK,WAAW;AAAA,MACd,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,MACN,cAAc;AAAA,OAEhB,EAAK,QAAQ,CAAC,KAAK,KAAK,CAAA,GACxB,EAAK,QAAQ,KAAK,EAAA,GAClB,EAAK,SAAS,IAAI,OAAO,MAAM,CAAA,GAC/B,EAAM,IAAI,CAAA,GAGN,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAG5C,KAAK,wBAAwB,GAAO,EAAU,MAAA,GACvC;AAAA;EAGT,iBACE,GACA,GACA,GACA;AACA,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,UAAU,IAAI,EAAM,MAAM,GAAG,GAAG,IAAA,CAAK;AACnF,MAAA,EAAS,SAAS,IAAI,MAAM,GAAG,IAAA,GAC/B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,OAAO,IAAI,EAAM,MAAM,GAAG,GAAG,IAAA,CAAK;AAChF,MAAA,EAAS,SAAS,IAAI,MAAM,GAAG,KAAA,GAC/B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,KAAK,GAAG,CAAA,CAAE;AACvF,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,GAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,KAAK,GAAG,CAAA,CAAE;AACvF,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,GAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,MAAM,GAAG,CAAA,CAAE;AACxF,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,IAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,MAAM,GAAG,CAAA,CAAE;AACxF,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,IAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,MAAA;AACpD,MAAA,EAAY,SAAS,IAAI,OAAO,GAAG,CAAA,GACnC,EAAM,IAAI,CAAA;AAAA;;EAId,wBAAiC,GAA0B,GAA6B;AACtF,UAAM,IAAW,KAAK,aAAa,CAAA;AACnC,QAAI,CAAC,EAAU;AAEf,QAAI,IAAqB,KAAK,uBAAuB,CAAA;AAErD,IAAI,EAAO,IAAI,iBAAA,MAAuB,cACpC,EAAS,SAAS,eAAe,QAC5B,MACH,IAAqB,IAAI,EAAM,KAC7B,KAAK,iBACL,KAAK,OAAO,EAAe,KAAA,CAAM,GAEnC,EAAmB,WAAW;AAAA,MAC5B,MAAM;AAAA,MACN,aAAa,EAAS,SAAS;AAAA,MAC/B,MAAM;AAAA,OAGR,EAAmB,SAAS,IAAI,IAAI,MAAM,IAAA,GAC1C,EAAS,IAAI,CAAA,OAGf,EAAS,SAAS,eAAe,OAC7B,KACF,EAAS,OAAO,CAAA;AAAA;GCvJX,KAAb,cAA2C,GAAqB;AAAA,EAE9D,gBAA4C,EAAe,KAAK,KAAK,MAAM,KAAK,EAAA;AAAA,EAEhF,YAAwC,EAAmB,KAAK,KAAK,MAAM,KAAK,EAAA;AAAA,EAEhF,kBAA8C,IAAI,EAAM,iBACtD,MACA,MACA,KACA,IACA,GACA,IACA,GACA,KAAK,KAAK,CAAA;AAAA,EAGZ,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,aAAsB,GAAsB,GAAwC;AAClF,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAE9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAI3B,UAAM,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,GAAG,GAAG,GAAA;AACxE,IAAA,EAAM,IAAI,CAAA;AAGV,UAAM,IAAW,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,OAAO,EAAe,KAAA,CAAM;AACrF,IAAA,EAAS,WAAW;AAAA,MAClB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAS,QAAQ,CAAC,KAAK,KAAK,CAAA,GAC5B,EAAS,QAAQ,KAAK,EAAA,GACtB,EAAS,SAAS,IAAI,MAAM,MAAM,CAAA,GAClC,EAAM,IAAI,CAAA;AAEV,UAAM,IAAO,IAAI,EAAM,KAAK,KAAK,WAAW,KAAK,OAAO,EAAe,SAAA,CAAU;AACjF,WAAA,EAAK,OAAO,QACZ,EAAK,WAAW;AAAA,MACd,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,MACN,cAAc;AAAA,OAEhB,EAAK,QAAQ,CAAC,KAAK,KAAK,CAAA,GACxB,EAAK,QAAQ,KAAK,EAAA,GAClB,EAAK,SAAS,IAAI,MAAM,MAAM,CAAA,GAC9B,EAAM,IAAI,CAAA,GAGN,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAG5C,KAAK,wBAAwB,GAAO,EAAU,MAAA,GACvC;AAAA;EAGT,iBACE,GACA,GACA,GACA;AACA,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,UAAU,IAAI,EAAM,MAAM,GAAG,GAAG,GAAA,CAAI;AAClF,MAAA,EAAS,SAAS,IAAI,KAAK,GAAG,GAAA,GAC9B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,EAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,OAAO,IAAI,EAAM,MAAM,GAAG,GAAG,GAAA,CAAI;AAC/E,MAAA,EAAS,SAAS,IAAI,KAAK,GAAG,IAAA,GAC9B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,KAAK,GAAG,CAAA,CAAE;AACvF,MAAA,EAAY,SAAS,IAAI,KAAK,GAAG,IAAA,GACjC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,MAAM,GAAG,CAAA,CAAE;AACxF,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,GAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,KAAK,GAAG,CAAA,CAAE;AACvF,MAAA,EAAY,SAAS,IAAI,OAAO,GAAG,GAAA,GACnC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,MAAM,GAAG,CAAA,CAAE;AACxF,MAAA,EAAY,SAAS,IAAI,OAAO,GAAG,GAAA,GACnC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,OAAO,GAAG,CAAA,CAAE;AACzF,MAAA,EAAY,SAAS,IAAI,OAAO,GAAG,IAAA,GACnC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,MAAM,GAAG,CAAA,CAAE;AACxF,MAAA,EAAY,SAAS,IAAI,OAAO,GAAG,IAAA,GACnC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,OAAO,GAAG,CAAA,CAAE;AACzF,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,IAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,MAAM,GAAG,CAAA,CAAE;AACxF,MAAA,EAAY,SAAS,IAAI,KAAK,GAAG,KAAA,GACjC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,MAAA;AACpD,MAAA,EAAY,SAAS,IAAI,OAAO,GAAG,KAAA,GACnC,EAAM,IAAI,CAAA;AAAA;;EAId,wBAAiC,GAA0B,GAA6B;AACtF,UAAM,IAAW,KAAK,aAAa,CAAA;AACnC,QAAI,CAAC,EAAU;AAEf,QAAI,IAAqB,KAAK,uBAAuB,CAAA;AAErD,IAAI,EAAO,IAAI,iBAAA,MAAuB,cACpC,EAAS,SAAS,eAAe,QAC5B,MACH,IAAqB,IAAI,EAAM,KAC7B,KAAK,iBACL,KAAK,OAAO,EAAe,KAAA,CAAM,GAEnC,EAAmB,WAAW;AAAA,MAC5B,MAAM;AAAA,MACN,aAAa,EAAS,SAAS;AAAA,MAC/B,MAAM;AAAA,OAGR,EAAmB,SAAS,IAAI,MAAM,MAAM,IAAA,GAC5C,EAAS,IAAI,CAAA,OAGf,EAAS,SAAS,eAAe,OAC7B,KACF,EAAS,OAAO,CAAA;AAAA;GCnLX,KAAb,cAA0C,EAA2B;AAAA,EAEnE,YAA+B,GAAoB,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,EAAA;AAAA,EAEjF,gBAAmC,EAAe,KAAK,KAAK,KAAK,KAAK,EAAA;AAAA,EAEtE,YAA+B,EAAmB,KAAK,KAAK,KAAK,KAAK,EAAA;AAAA,EAEtE,kBAAqC,IAAI,EAAM,iBAC7C,KACA,KACA,KACA,IACA,GACA,IACA,GACA,KAAK,KAAK,CAAA;AAAA,EAGZ,kBAAqC,IAAI,EAAM,MAAM,QAAA;AAAA,EACrD,iBAAoC,IAAI,EAAM,MAAM,OAAA;AAAA,EACpD,+BAAkD;AAAA,EAClD,8BAAiD;AAAA,EAEjD,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,kBAA2B;AACzB,WAAO,KAAK;AAAA;EAGd,aAAa,GAAsB,GAAwC;AACzE,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAE9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAI3B,UAAM,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,KAAK,GAAG,GAAA;AAC1E,IAAA,EAAM,IAAI,CAAA;AAGV,UAAM,IAAW,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,OAAO,EAAe,KAAA,CAAM;AACrF,IAAA,EAAS,WAAW;AAAA,MAClB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAS,QAAQ,CAAC,KAAK,KAAK,CAAA,GAC5B,EAAS,QAAQ,KAAK,EAAA,GACtB,EAAS,SAAS,IAAI,GAAG,MAAM,CAAA,GAC/B,EAAM,IAAI,CAAA;AAEV,UAAM,IAAO,IAAI,EAAM,KAAK,KAAK,WAAW,KAAK,OAAO,EAAe,SAAA,CAAU;AACjF,IAAA,EAAK,OAAO,QACZ,EAAK,WAAW;AAAA,MACd,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,MACN,cAAc;AAAA,OAEhB,EAAK,QAAQ,CAAC,KAAK,KAAK,CAAA,GACxB,EAAK,QAAQ,KAAK,EAAA,GAClB,EAAK,SAAS,IAAI,GAAG,MAAM,CAAA,GAC3B,EAAM,IAAI,CAAA;AAEV,UAAM,IAAO,IAAI,EAAM,KAAK,KAAK,WAAW,KAAK,OAAO,EAAe,KAAA,CAAM;AAC7E,WAAA,EAAK,WAAW;AAAA,MACd,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAK,QAAQ,CAAC,KAAK,KAAK,CAAA,GACxB,EAAK,QAAQ,KAAK,EAAA,GAClB,EAAK,SAAS,IAAI,OAAO,MAAM,CAAA,GAC/B,EAAM,IAAI,CAAA,GAGN,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAG5C,KAAK,wBAAwB,GAAO,EAAU,MAAA,GACvC;AAAA;EAGT,iBAA2B,GAAsB,GAAwB,GAAoB;AAC3F,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,QAAA;AAC9C,MAAA,EAAS,SAAS,IAAI,MAAM,GAAG,IAAA,GAC/B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,KAAA;AAC9C,MAAA,EAAS,SAAS,IAAI,MAAM,GAAG,KAAA,GAC/B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,MAAM,GAAG,CAAA,CAAE;AACxF,MAAA,EAAY,SAAS,IAAI,KAAK,GAAG,GAAA,GACjC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,OAAO,GAAG,CAAA,CAAE;AACzF,MAAA,EAAY,SAAS,IAAI,KAAK,GAAG,IAAA,GACjC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,MAAA;AACpD,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,KAAA,GAClC,EAAM,IAAI,CAAA;AAAA;;EAUd,wBAAiC,GAA2D;AAE1F,WAAO,EACL,QAAQ;AAAA,MACN;AAAA,QACE,KAAK;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,UAAE,MAAM;AAAA,UAAS,KAAK;AAAA,UAAQ,SAAS;AAAA;;MAElD;AAAA,QAAE,KAAK;AAAA,QAAmB,MAAM;AAAA;MAChC;AAAA,QAAE,KAAK;AAAA,QAAkB,MAAM;AAAA,QAAU,KAAK;AAAA,QAAG,WATjC,GAAQ,IAAI,oBAAA,KAAyB,aASsB;AAAA;MAC3E;AAAA,QAAE,KAAK;AAAA,QAAuB,MAAM;AAAA;MACrC;AAAA;EAWL,oBAA6B,GAA+C;AAC1E,UAAM,IAAW,oBAAI,IAAA;AACrB,IAAA,EAAS,IAAI,sBAAsB,EAAO,IAAI,oBAAA,KAAyB,OAAA;AACvE,UAAM,IAAkB,EAAO,IAAI,iBAAA;AACnC,WAAA,EAAS,IAAI,mBAAmB,MAAoB,UAAA,GACpD,EAAS,IAAI,kBAAkB,WAAW,EAAO,IAAI,gBAAA,KAAqB,GAAA,CAAI,GAC9E,EAAS,IAAI,uBAAuB,WAAW,EAAO,IAAI,qBAAA,KAA0B,GAAA,CAAI,GACjF;AAAA;EAUT,oBAA6B,GAAiD;AAC5E,UAAM,IAAS,oBAAI,IAAA;AACnB,IAAA,EAAO,IAAI,sBAAsB,EAAS,IAAI,oBAAA,KAAyB,OAAA;AACvE,UAAM,IAAkB,EAAS,IAAI,iBAAA;AACrC,WAAA,EAAO,IAAI,mBAAmB,IAAkB,aAAa,UAAA,GAC7D,EAAO,IAAI,kBAAkB,EAAS,IAAI,gBAAA,EAAkB,SAAA,CAAU,GACtE,EAAO,IAAI,uBAAuB,EAAS,IAAI,qBAAA,EAAuB,SAAA,KAAc,IAAA,GAC7E;AAAA;EAGT,wBAAiC,GAA0B,GAA6B;AACtF,UAAM,IAAW,KAAK,aAAa,CAAA;AACnC,QAAI,CAAC,EAAU;AAEf,QAAI,IAAqB,KAAK,uBAAuB,CAAA;AAErD,IAAI,EAAO,IAAI,iBAAA,MAAuB,cACpC,EAAS,SAAS,eAAe,QAC5B,MACH,IAAqB,IAAI,EAAM,KAC7B,KAAK,iBACL,KAAK,OAAO,EAAe,KAAA,CAAM,GAEnC,EAAmB,WAAW;AAAA,MAC5B,MAAM;AAAA,MACN,aAAa,EAAS,SAAS;AAAA,MAC/B,MAAM;AAAA,OAGR,EAAmB,SAAS,IAAI,MAAM,MAAM,IAAA,GAC5C,EAAS,IAAI,CAAA,OAGf,EAAS,SAAS,eAAe,OAC7B,KACF,EAAS,OAAO,CAAA;AAAA;EAWtB,gBAAyB,GAA0B,GAAoC;AACrF,UAAM,IAAW,KAAK,aAAa,CAAA;AACnC,QAAK,GAEL;AAAA,UAAI,CAAC,KAAS,CAAC,KAAK,qBAAqB,EAAM,UAAU,iBAAiB;AACxE,aAAK,cAAc,CAAA,GACnB,KAAK,2BAA2B,CAAA;AAChC;AAAA;AAGF,UAAI,EAAM,UAAU,QAAQ;AAC1B,aAAK,cAAc,CAAA,GACnB,KAAK,cAAc,GAAU,KAAK,iBAAiB,KAAK,4BAAA;AACxD;AAAA;AAGF,UAAI,EAAM,UAAU,OAAO;AACzB,aAAK,cAAc,CAAA,GACnB,KAAK,cAAc,GAAU,KAAK,gBAAgB,KAAK,2BAAA;AACvD;AAAA;AAIF,UAAI,KAAK,kBAAkB,qBAAqB,WAAW;AACzD,QAAI,EAAM,UAAU,WAClB,KAAK,cAAc,GAAU,KAAK,gBAAgB,KAAK,2BAAA,IAGvD,KAAK,cAAc,GAAU,KAAK,iBAAiB,KAAK,4BAAA;AAE1D;AAAA;AAIF,MAAI,EAAM,iBACR,KAAK,kBAAkB,GAAU,GAAU,CAAA;AAAA;AAAA;EAQ/C,cAAsB,GAAsB,GAAoB,GAAiC;AAC/F,SAAK,0BAA0B,CAAA;AAC/B,UAAM,IAAM,EAAS;AACrB,IAAA,EAAI,MAAM,KAAK,CAAA,GACf,EAAI,SAAS,KAAK,CAAA,GAClB,EAAI,oBAAoB;AAAA;EAG1B,0BAAkC,GAA4B;AAE5D,IADY,EAAS,SACb,SAAS,YAAY,EAAW,oBACxC,EAAS,WAAW,KAAK,OAAO,EAAe,SAAA,EAAW,MAAA,GACzD,EAAS,SAAuC,SAAS,UAAU,EAAW;AAAA;EAGjF,2BAAmC,GAA4B;AAC7D,UAAM,IAAM,EAAS;AACrB,IAAI,EAAI,SAAS,YAAY,EAAW,oBACxC,EAAI,QAAA,GACJ,EAAS,WAAW,KAAK,OAAO,EAAe,SAAA;AAAA;EAGjD,cAAsB,GAAgC;AACpD,UAAM,IAAQ,EAAS,SAAS;AAChC,IAAI,MACF,EAAM,cAAA,GACN,EAAM,YAAY,CAAA,GAClB,OAAO,EAAS,SAAS,QAE3B,OAAO,EAAS,SAAS,eACzB,OAAO,EAAS,SAAS,aACzB,OAAO,EAAS,SAAS;AAAA;EAO3B,kBACE,GACA,GACA,GACM;AACN,QAAI,EAAS,SAAS,uBAAuB,EAAM,UAAW;AAE9D,UAAM,IAAM,KAAK,kBAAmB,gBAE9B,KADO,EAAM,iBAAiB,EAAM,aACX;AAE/B,SAAK,0BAA0B,CAAA;AAC/B,UAAM,IAAM,EAAS,UAEf,IAAU,EAAM,UAAU,WAAW,KAAK,kBAAkB,KAAK,gBACjE,IACJ,EAAM,UAAU,WACZ,KAAK,+BACL,KAAK,6BAEL,IAAa;AAAA,MAAC,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA,OAClD,IAAS;AAAA,MAAC,EAAQ;AAAA,MAAG,EAAQ;AAAA,MAAG,EAAQ;AAAA,OACxC,IAAmB,EAAI;AAE7B,QAAI,IAA8B,EAAS,SAAS;AACpD,IAAK,MACH,IAAQ,IAAI,EAAM,eAAe,CAAA,GACjC,EAAS,SAAS,QAAQ,IAGxB,EAAS,SAAS,iBACnB,EAAS,SAAS,cAAwC,KAAA,GAEzD,EAAS,SAAS,eACpB,EAAM,YAAY,EAAS,SAAS,WAAA;AAGtC,UAAM,IAAO,IAAI,EAAM,cAAc,aAAa,GAAiB;AAAA,MACjE,IAAI,EAAM,mBACR,uBACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAG,GAAY,GAAG,CAAA,CAAO;AAAA,MAE5B,IAAI,EAAM,mBACR,0BACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAG,GAAY,GAAG,CAAA,CAAO;AAAA,MAE5B,IAAI,EAAM,oBACR,mCACA,CAAC,GAAG,CAAA,GACJ,CAAC,GAAkB,CAAA,CAAY;AAAA,KAElC,GACK,IAAS,EAAM,WAAW,CAAA;AAChC,IAAA,EAAO,OAAO,EAAM,UACpB,EAAO,oBAAoB,IAC3B,EAAO,KAAA,GAEP,EAAS,SAAS,qBAAqB,EAAM,WAC7C,EAAS,SAAS,gBAAgB,GAClC,EAAS,SAAS,cAAc;AAAA;EAYlC,iBAA2B,GAA6C;AACtE,QAAI,IAAkC;AAEtC,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,eACzD,IAAe;AAAA,QAGZ;AAAA;EAYT,aAAuB,GAA6C;AAClE,QAAI,IAA8B;AAElC,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,WACzD,IAAW;AAAA,QAGR;AAAA;EAYT,uBAAiC,GAA6C;AAC5E,QAAI,IAAwC;AAE5C,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,qBACzD,IAAqB;AAAA,QAGlB;AAAA;GCxaE,KAAb,cAA2C,GAAqB;AAAA,EAE9D,YAAwC,GAAoB,GAAG,KAAK,MAAM,MAAM,KAAK,KAAK,EAAA;AAAA,EAE1F,gBAA4C,EAAe,GAAG,KAAK,MAAM,KAAK,EAAA;AAAA,EAE9E,YAAwC,EAAmB,GAAG,KAAK,MAAM,KAAK,EAAA;AAAA,EAE9E,kBAA8C,IAAI,EAAM,iBACtD,MACA,MACA,KACA,IACA,GACA,IACA,GACA,KAAK,KAAK,CAAA;AAAA,EAGZ,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,aAAsB,GAAsB,GAAwC;AAClF,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAE9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAI3B,UAAM,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,GAAG,GAAG,GAAA;AACxE,IAAA,EAAM,IAAI,CAAA;AAGV,UAAM,IAAW,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,OAAO,EAAe,KAAA,CAAM;AACrF,IAAA,EAAS,WAAW;AAAA,MAClB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAS,QAAQ,CAAC,KAAK,KAAK,CAAA,GAC5B,EAAS,QAAQ,KAAK,EAAA,GACtB,EAAS,SAAS,IAAI,OAAO,MAAM,CAAA,GACnC,EAAM,IAAI,CAAA;AAEV,UAAM,IAAO,IAAI,EAAM,KAAK,KAAK,WAAW,KAAK,OAAO,EAAe,SAAA,CAAU;AACjF,IAAA,EAAK,OAAO,QACZ,EAAK,WAAW;AAAA,MACd,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,MACN,cAAc;AAAA,OAEhB,EAAK,QAAQ,CAAC,KAAK,KAAK,CAAA,GACxB,EAAK,QAAQ,KAAK,EAAA,GAClB,EAAK,SAAS,IAAI,OAAO,MAAM,CAAA,GAC/B,EAAM,IAAI,CAAA;AAEV,UAAM,IAAO,IAAI,EAAM,KAAK,KAAK,WAAW,KAAK,OAAO,EAAe,KAAA,CAAM;AAC7E,WAAA,EAAK,WAAW;AAAA,MACd,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAK,QAAQ,CAAC,KAAK,KAAK,CAAA,GACxB,EAAK,QAAQ,KAAK,EAAA,GAClB,EAAK,SAAS,IAAI,OAAO,MAAM,CAAA,GAC/B,EAAM,IAAI,CAAA,GAGN,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAG5C,KAAK,wBAAwB,GAAO,EAAU,MAAA,GACvC;AAAA;EAGT,iBACE,GACA,GACA,GACA;AACA,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,UAAU,IAAI,EAAM,MAAM,GAAG,GAAG,IAAA,CAAK;AACnF,MAAA,EAAS,SAAS,IAAI,MAAM,GAAG,IAAA,GAC/B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,OAAO,IAAI,EAAM,MAAM,GAAG,GAAG,IAAA,CAAK;AAChF,MAAA,EAAS,SAAS,IAAI,MAAM,GAAG,KAAA,GAC/B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,KAAK,GAAG,CAAA,CAAE;AACvF,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,GAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,KAAK,GAAG,CAAA,CAAE;AACvF,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,GAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,MAAM,GAAG,CAAA,CAAE;AACxF,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,IAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,MAAM,GAAG,CAAA,CAAE;AACxF,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,IAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,MAAA;AACpD,MAAA,EAAY,SAAS,IAAI,OAAO,GAAG,CAAA,GACnC,EAAM,IAAI,CAAA;AAAA;;EAId,wBAAiC,GAA0B,GAA6B;AACtF,UAAM,IAAW,KAAK,aAAa,CAAA;AACnC,QAAI,CAAC,EAAU;AAEf,QAAI,IAAqB,KAAK,uBAAuB,CAAA;AAErD,IAAI,EAAO,IAAI,iBAAA,MAAuB,cACpC,EAAS,SAAS,eAAe,QAC5B,MACH,IAAqB,IAAI,EAAM,KAC7B,KAAK,iBACL,KAAK,OAAO,EAAe,KAAA,CAAM,GAEnC,EAAmB,WAAW;AAAA,MAC5B,MAAM;AAAA,MACN,aAAa,EAAS,SAAS;AAAA,MAC/B,MAAM;AAAA,OAGR,EAAmB,SAAS,IAAI,IAAI,MAAM,IAAA,GAC1C,EAAS,IAAI,CAAA,OAGf,EAAS,SAAS,eAAe,OAC7B,KACF,EAAS,OAAO,CAAA;AAAA;GCpKX,KAAb,cAA2C,GAAqB;AAAA,EAE9D,YAAwC,GAAoB,KAAK,KAAK,KAAK,MAAM,KAAK,KAAK,EAAA;AAAA,EAE3F,gBAA4C,EAAe,KAAK,KAAK,MAAM,KAAK,EAAA;AAAA,EAEhF,YAAwC,EAAmB,KAAK,KAAK,MAAM,KAAK,EAAA;AAAA,EAEhF,kBAA8C,IAAI,EAAM,iBACtD,MACA,MACA,KACA,IACA,GACA,IACA,GACA,KAAK,KAAK,CAAA;AAAA,EAGZ,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,aAAsB,GAAsB,GAAwC;AAClF,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAE9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAI3B,UAAM,IAAS,KAAK,sBAAsB,EAAU,IAAI,EAAM,IAAI,GAAG,GAAG,GAAA;AACxE,IAAA,EAAM,IAAI,CAAA;AAGV,UAAM,IAAW,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,OAAO,EAAe,KAAA,CAAM;AACrF,IAAA,EAAS,WAAW;AAAA,MAClB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAS,QAAQ,CAAC,KAAK,KAAK,CAAA,GAC5B,EAAS,QAAQ,KAAK,EAAA,GACtB,EAAS,SAAS,IAAI,OAAO,MAAM,CAAA,GACnC,EAAM,IAAI,CAAA;AAEV,UAAM,IAAO,IAAI,EAAM,KAAK,KAAK,WAAW,KAAK,OAAO,EAAe,SAAA,CAAU;AACjF,IAAA,EAAK,OAAO,QACZ,EAAK,WAAW;AAAA,MACd,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,MACN,cAAc;AAAA,OAEhB,EAAK,QAAQ,CAAC,KAAK,KAAK,CAAA,GACxB,EAAK,QAAQ,KAAK,EAAA,GAClB,EAAK,SAAS,IAAI,OAAO,MAAM,CAAA,GAC/B,EAAM,IAAI,CAAA;AAEV,UAAM,IAAO,IAAI,EAAM,KAAK,KAAK,WAAW,KAAK,OAAO,EAAe,KAAA,CAAM;AAC7E,WAAA,EAAK,WAAW;AAAA,MACd,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAK,QAAQ,CAAC,KAAK,KAAK,CAAA,GACxB,EAAK,QAAQ,KAAK,EAAA,GAClB,EAAK,SAAS,IAAI,MAAM,MAAM,CAAA,GAC9B,EAAM,IAAI,CAAA,GAGN,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAG5C,KAAK,wBAAwB,GAAO,EAAU,MAAA,GACvC;AAAA;EAGT,iBACE,GACA,GACA,GACA;AACA,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,UAAU,IAAI,EAAM,MAAM,GAAG,GAAG,GAAA,CAAI;AAClF,MAAA,EAAS,SAAS,IAAI,KAAK,GAAG,GAAA,GAC9B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,EAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,OAAO,IAAI,EAAM,MAAM,GAAG,GAAG,GAAA,CAAI;AAC/E,MAAA,EAAS,SAAS,IAAI,KAAK,GAAG,IAAA,GAC9B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,KAAK,GAAG,CAAA,CAAE;AACvF,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,IAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,MAAM,GAAG,CAAA,CAAE;AACxF,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,GAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,KAAK,GAAG,CAAA,CAAE;AACvF,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,GAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,MAAM,GAAG,CAAA,CAAE;AACxF,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,GAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,OAAO,GAAG,CAAA,CAAE;AACzF,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,IAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,MAAM,GAAG,CAAA,CAAE;AACxF,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,IAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,OAAO,GAAG,CAAA,CAAE;AACzF,MAAA,EAAY,SAAS,IAAI,KAAK,GAAG,IAAA,GACjC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,SAAS,IAAI,EAAM,MAAM,MAAM,GAAG,CAAA,CAAE;AACxF,MAAA,EAAY,SAAS,IAAI,KAAK,GAAG,KAAA,GACjC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,MAAA;AACpD,MAAA,EAAY,SAAS,IAAI,OAAO,GAAG,CAAA,GACnC,EAAM,IAAI,CAAA;AAAA;;EAId,wBAAiC,GAA0B,GAA6B;AACtF,UAAM,IAAW,KAAK,aAAa,CAAA;AACnC,QAAI,CAAC,EAAU;AAEf,QAAI,IAAqB,KAAK,uBAAuB,CAAA;AAErD,IAAI,EAAO,IAAI,iBAAA,MAAuB,cACpC,EAAS,SAAS,eAAe,QAC5B,MACH,IAAqB,IAAI,EAAM,KAC7B,KAAK,iBACL,KAAK,OAAO,EAAe,KAAA,CAAM,GAEnC,EAAmB,WAAW;AAAA,MAC5B,MAAM;AAAA,MACN,aAAa,EAAS,SAAS;AAAA,MAC/B,MAAM;AAAA,OAGR,EAAmB,SAAS,IAAI,OAAO,MAAM,KAAA,GAC7C,EAAS,IAAI,CAAA,OAGf,EAAS,SAAS,eAAe,OAC7B,KACF,EAAS,OAAO,CAAA;AAAA;GCvLX,KAAb,cAA4C,EAA2B;AAAA,EACrE,gBAAiC,GAAuB,KAAI,KAAI,KAAI,GAAA;AAAA,EAEpE,iBAAkC,IAAI,EAAM,YAAY,KAAK,KAAI,GAAA;AAAA,EACjE,sBAAuC,IAAI,EAAM,YAAY,KAAK,KAAI,GAAA;AAAA,EAEtE,kBAAmC,GACjC,GACA,KACA,KACA,KACA,GACA,IACA,GAAA;AAAA,EAIF,iBAAkC,IAAI,EAAM,YAAY,KAAK,KAAK,GAAA;AAAA,EAClE,oBAAqC,IAAI,EAAM,YAAY,GAAG,KAAK,GAAA;AAAA,EAEnE,oBAAuC,IAAI,EAAM,MAAM,QAAA;AAAA,EACvD,mBAAsC,IAAI,EAAM,MAAM,OAAA;AAAA,EACtD,iCAAoD;AAAA,EACpD,gCAAmD;AAAA,EAEnD,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,aAAa,GAAsB,GAAwC;AACzE,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAE9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAG3B,UAAM,IAAS,KAAK,sBAClB,EAAU,IACV,EAAM,IACN,KAAI,GAAE,GAAA;AACR,IAAA,EAAM,IAAI,CAAA;AAGV,UAAM,IAAW,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,OAAO,EAAe,KAAA,CAAM;AACrF,IAAA,EAAS,WAAW;AAAA,MAClB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAS,QAAQ,CAAC,KAAK,KAAK,CAAA,GAC5B,EAAS,SAAS,IAAI,GAAG,GAAG,CAAA,GAC5B,EAAM,IAAI,CAAA;AAGV,UAAM,IAAY,IAAI,EAAM,KAAK,KAAK,gBAAgB,KAAK,OAAO,EAAe,KAAA,CAAM;AACvF,IAAA,EAAU,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAU,SAAS,IAAI,GAAG,KAAK,CAAA,GAC/B,EAAM,IAAI,CAAA;AACV,UAAM,IAAoB,IAAI,EAAM,KAAK,KAAK,qBAAqB,KAAK,OAAO,EAAe,SAAA,CAAU;AACxG,IAAA,EAAkB,WAAW;AAAA,MAAC,GAAG,EAAU;AAAA,MAAU,MAAM;AAAA,OAC3D,EAAkB,SAAS,IAAI,MAAM,KAAK,CAAA,GAC1C,EAAM,IAAI,CAAA;AACV,UAAM,IAAqB,IAAI,EAAM,KAAK,KAAK,qBAAqB,KAAK,OAAO,EAAe,SAAA,CAAU;AACzG,IAAA,EAAmB,WAAW;AAAA,MAAC,GAAG,EAAU;AAAA,MAAU,MAAM;AAAA,OAC5D,EAAmB,SAAS,IAAI,KAAK,KAAK,CAAA,GAC1C,EAAM,IAAI,CAAA;AAGV,UAAM,IAAY,IAAI,EAAM,KAC1B,KAAK,iBACL,KAAK,OAAO,EAAe,SAAA,CAAU;AAEvC,IAAA,EAAU,OAAO,aACjB,EAAU,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,MACN,cAAc;AAAA,OAEhB,EAAU,QAAQ,CAAC,KAAK,KAAK,CAAA,GAE7B,EAAU,SAAS,IAAI,GAAG,GAAG,KAAA,GAC7B,EAAM,IAAI,CAAA;AAGV,UAAM,IAAW,IAAI,EAAM,KAAK,KAAK,gBAAgB,KAAK,OAAO,EAAe,KAAA,CAAM;AACtF,IAAA,EAAS,WAAW;AAAA,MAClB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAS,SAAS,IAAI,GAAG,KAAK,KAAA,GAC9B,EAAM,IAAI,CAAA;AAGV,UAAM,IAAc,IAAI,EAAM,KAC5B,KAAK,mBACL,KAAK,OAAO,EAAe,SAAA,CAAU;AAEvC,WAAA,EAAY,OAAO,eACnB,EAAY,WAAW;AAAA,MACrB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,MACN,cAAc;AAAA,OAEhB,EAAY,SAAS,IAAI,GAAG,KAAK,IAAA,GACjC,EAAM,IAAI,CAAA,GAEN,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAGrC;AAAA;EAGT,iBAA2B,GAAsB,GAAwB,GAAoB;AAC3F,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,KAAA;AAC9C,MAAA,EAAS,SAAS,IAAI,GAAG,GAAG,IAAA,GAC5B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,MAAA;AACpD,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,IAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,MAAA;AACpD,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,GAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,OAAA;AAC9C,MAAA,EAAS,SAAS,IAAI,KAAK,GAAG,IAAA,GAC9B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAY,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAClD,QAAI,GAAW;AACb,YAAM,IAAa,KAAK,eAAe,GAAW,OAAA;AAClD,MAAA,EAAW,SAAS,IAAI,KAAK,GAAG,GAAA,GAChC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,QAAA;AAC9C,MAAA,EAAS,SAAS,IAAI,GAAG,GAAG,GAAA,GAC5B,EAAM,IAAI,CAAA;AAAA;;EAId,wBAAiC,GAA2D;AAE1F,WAAO,EACL,QAAQ;AAAA,MACN;AAAA,QACE,KAAK;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,UAAE,MAAM;AAAA,UAAS,KAAK;AAAA,UAAQ,SAAS;AAAA;;MAElD;AAAA,QAAE,KAAK;AAAA,QAAkB,MAAM;AAAA,QAAU,KAAK;AAAA,QAAG,WARjC,GAAQ,IAAI,oBAAA,KAAyB,aAQsB;AAAA;MAC3E;AAAA,QAAE,KAAK;AAAA,QAAuB,MAAM;AAAA;MACrC;AAAA;EAIL,oBAA6B,GAA+C;AAC1E,UAAM,IAAW,oBAAI,IAAA;AACrB,WAAA,EAAS,IAAI,sBAAsB,EAAO,IAAI,oBAAA,KAAyB,OAAA,GACvE,EAAS,IAAI,kBAAkB,WAAW,EAAO,IAAI,gBAAA,KAAqB,GAAA,CAAI,GAC9E,EAAS,IAAI,uBAAuB,WAAW,EAAO,IAAI,qBAAA,KAA0B,GAAA,CAAI,GACjF;AAAA;EAGT,oBAA6B,GAAiD;AAC5E,UAAM,IAAS,oBAAI,IAAA;AACnB,WAAA,EAAO,IAAI,sBAAsB,EAAS,IAAI,oBAAA,KAAyB,OAAA,GACvE,EAAO,IAAI,kBAAkB,EAAS,IAAI,gBAAA,EAAkB,SAAA,CAAU,GACtE,EAAO,IAAI,uBAAuB,EAAS,IAAI,qBAAA,EAAuB,SAAA,KAAc,IAAA,GAC7E;AAAA;EAIT,gBAAyB,GAA0B,GAAoC;AACrF,UAAM,IAAY,KAAK,aAAa,GAAU,WAAA,GACxC,IAAc,KAAK,aAAa,GAAU,aAAA;AAChD,QAAI,CAAC,KAAa,CAAC,EAAa;AAEhC,QAAI,CAAC,KAAS,CAAC,KAAK,qBAAqB,EAAM,UAAU,iBAAiB;AACxE,WAAK,cAAc,CAAA,GACnB,KAAK,6BAA6B,CAAA,GAClC,KAAK,6BAA6B,CAAA;AAClC;AAAA;AAGF,UAAM,IAAe,EAAM,MAAM,WAAW,IAAA,GACtC,IAAqB,IACtB,EAAM,WAAW,IAAI,WAAA,KAAgB,MACtC,EAAM,OACJ,IAAqB,IAAgB,EAAM,aAAa,IAAc,EAAM,OAE5E,IAAY,SAAS,GAAY,EAAA,GACjC,IAAY,SAAS,GAAY,EAAA,GACjC,KAAe,IAAY,OAAO,GAClC,KAAe,IAAY,OAAO,GAClC,KAAiB,IAAY,OAAO,GACpC,KAAiB,IAAY,OAAO;AAG1C,QAAI,CAAC,KAAgB,CAAC,EAAM,eAAe;AACzC,WAAK,cAAc,CAAA,GACnB,KAAK,gBAAgB,GAAW,CAAA,GAChC,KAAK,gBAAgB,GAAa,CAAA;AAClC;AAAA;AAIF,QAAI,KAAK,kBAAkB,qBAAqB,WAAW;AACzD,WAAK,gBAAgB,GAAW,CAAA,GAChC,KAAK,gBAAgB,GAAa,CAAA;AAClC;AAAA;AAIF,SAAK,gBACH,GACA,GACA,GACA,GACA,GACA,GACA,GACA,CAAA;AAAA;EAQJ,gBAAwB,GAAkB,GAAqB;AAC7D,SAAK,4BAA4B,CAAA;AACjC,UAAM,IAAM,EAAK,UACX,IAAQ,IAAO,KAAK,oBAAoB,KAAK,kBAC7C,IAAY,IACd,KAAK,iCACL,KAAK;AACT,IAAA,EAAI,MAAM,KAAK,CAAA,GACf,EAAI,SAAS,KAAK,CAAA,GAClB,EAAI,oBAAoB;AAAA;EAG1B,4BAAoC,GAAwB;AAE1D,IADY,EAAK,SACT,SAAS,YAAY,EAAW,oBACxC,EAAK,WAAW,KAAK,OAAO,EAAe,SAAA,EAAW,MAAA,GACrD,EAAK,SAAuC,SAAS,UAAU,EAAW;AAAA;EAG7E,6BAAqC,GAAwB;AAC3D,UAAM,IAAM,EAAK;AACjB,IAAI,EAAI,SAAS,YAAY,EAAW,oBACxC,EAAI,QAAA,GACJ,EAAK,WAAW,KAAK,OAAO,EAAe,SAAA;AAAA;EAG7C,cAAsB,GAAgC;AACpD,UAAM,IAAQ,EAAS,SAAS;AAChC,IAAI,MACF,EAAM,cAAA,GACN,EAAM,YAAY,CAAA,GAClB,OAAO,EAAS,SAAS,QAE3B,OAAO,EAAS,SAAS,eACzB,OAAO,EAAS,SAAS,aACzB,OAAO,EAAS,SAAS;AAAA;EAQ3B,gBACE,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACM;AACN,QAAI,EAAS,SAAS,uBAAuB,EAAM,UAAW;AAE9D,UAAM,IAAM,KAAK,kBAAmB,gBAE9B,KADO,EAAM,iBAAiB,EAAM,aACX;AAE/B,SAAK,4BAA4B,CAAA,GACjC,KAAK,4BAA4B,CAAA,GAG7B,MAAgB,KAClB,KAAK,gBAAgB,GAAW,CAAA,GAE9B,MAAkB,KACpB,KAAK,gBAAgB,GAAa,CAAA;AAGpC,UAAM,IAAgC,CAAA;AAStC,QARI,MAAgB,KAClB,KAAK,kBAAkB,GAAQ,aAAa,GAAW,GAAa,CAAA,GAElE,MAAkB,KACpB,KAAK,kBAAkB,GAAQ,eAAe,GAAa,GAAe,CAAA,GAIxE,EAAO,WAAW,GAAG;AACvB,MAAA,EAAS,SAAS,qBAAqB,EAAM;AAC7C;AAAA;AAGF,QAAI,IAA8B,EAAS,SAAS;AACpD,IAAK,MACH,IAAQ,IAAI,EAAM,eAAe,CAAA,GACjC,EAAS,SAAS,QAAQ,IAExB,EAAS,SAAS,iBACnB,EAAS,SAAS,cAAwC,KAAA,GAEzD,EAAS,SAAS,eACpB,EAAM,YAAY,EAAS,SAAS,WAAA;AAGtC,UAAM,IAAO,IAAI,EAAM,cAAc,oBAAoB,GAAiB,CAAA,GACpE,IAAS,EAAM,WAAW,CAAA;AAChC,IAAA,EAAO,OAAO,EAAM,UACpB,EAAO,oBAAoB,IAC3B,EAAO,KAAA,GAEP,EAAS,SAAS,qBAAqB,EAAM,WAC7C,EAAS,SAAS,gBAAgB,GAClC,EAAS,SAAS,cAAc;AAAA;EAGlC,kBACE,GACA,GACA,GACA,GACA,GACM;AACN,UAAM,IAAM,EAAK,UACX,IAAU;AAAA,MAAC,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA,OAC/C,IAAU,IAAS,KAAK,oBAAoB,KAAK,kBACjD,IAAQ;AAAA,MAAC,EAAQ;AAAA,MAAG,EAAQ;AAAA,MAAG,EAAQ;AAAA,OACvC,IAAgB,EAAI,mBACpB,IAAc,IAChB,KAAK,iCACL,KAAK;AAET,IAAA,EAAO,KACL,IAAI,EAAM,mBACR,GAAG,CAAA,mBACH,CAAC,GAAG,CAAA,GACJ,CAAC,GAAG,GAAS,GAAG,CAAA,CAAM,GAExB,IAAI,EAAM,mBACR,GAAG,CAAA,sBACH,CAAC,GAAG,CAAA,GACJ,CAAC,GAAG,GAAS,GAAG,CAAA,CAAM,GAExB,IAAI,EAAM,oBACR,GAAG,CAAA,+BACH,CAAC,GAAG,CAAA,GACJ,CAAC,GAAe,CAAA,CAAY,CAC7B;AAAA;EAKL,aAAuB,GAA0B,GAAiC;AAChF,QAAI,IAA2B;AAC/B,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,MACzD,IAAQ;AAAA,QAGL;AAAA;GCxZE,KAAb,cAAwC,EAA2B;AAAA,EACjE,gBAAiC,GAAuB,KAAI,KAAI,KAAI,GAAA;AAAA,EAEpE,iBAAkC,IAAI,EAAM,YAAY,MAAM,KAAI,IAAA;AAAA,EAClE,sBAAuC,IAAI,EAAM,YAAY,OAAO,KAAI,IAAA;AAAA,EAExE,mBAAoC,GAClC,KACA,OACA,MACA,KACA,KACA,IACA,GAAA;AAAA,EAIF,YAA6B,IAAI,EAAM,YAAY,MAAM,KAAK,IAAA;AAAA,EAE9D,oBAAuC,IAAI,EAAM,MAAM,QAAA;AAAA,EACvD,mBAAsC,IAAI,EAAM,MAAM,OAAA;AAAA,EACtD,iCAAoD;AAAA,EACpD,gCAAmD;AAAA,EAEnD,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,aAAa,GAAsB,GAAwC;AACzE,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAE9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAG3B,UAAM,IAAS,KAAK,sBAClB,EAAU,IACV,EAAM,IACN,KAAI,GAAE,GAAA;AACR,IAAA,EAAM,IAAI,CAAA;AAGV,UAAM,IAAW,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,OAAO,EAAe,KAAA,CAAM;AACrF,IAAA,EAAS,WAAW;AAAA,MAClB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAS,QAAQ,CAAC,KAAK,KAAK,CAAA,GAC5B,EAAS,SAAS,IAAI,GAAG,GAAG,CAAA,GAC5B,EAAM,IAAI,CAAA;AAGV,UAAM,IAAY,IAAI,EAAM,KAAK,KAAK,gBAAgB,KAAK,OAAO,EAAe,KAAA,CAAM;AACvF,IAAA,EAAU,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAU,SAAS,IAAI,GAAG,KAAK,CAAA,GAC/B,EAAM,IAAI,CAAA;AACV,UAAM,IAAoB,IAAI,EAAM,KAAK,KAAK,qBAAqB,KAAK,OAAO,EAAe,SAAA,CAAU;AACxG,IAAA,EAAkB,WAAW;AAAA,MAAC,GAAG,EAAU;AAAA,MAAU,MAAM;AAAA,OAC3D,EAAkB,SAAS,IAAI,SAAS,KAAK,CAAA,GAC7C,EAAM,IAAI,CAAA;AACV,UAAM,IAAqB,IAAI,EAAM,KAAK,KAAK,qBAAqB,KAAK,OAAO,EAAe,SAAA,CAAU;AACzG,IAAA,EAAmB,WAAW;AAAA,MAAC,GAAG,EAAU;AAAA,MAAU,MAAM;AAAA,OAC5D,EAAmB,SAAS,IAAI,QAAQ,KAAK,CAAA,GAC7C,EAAM,IAAI,CAAA;AAGV,UAAM,IAAY,IAAI,EAAM,KAC1B,KAAK,kBACL,KAAK,OAAO,EAAe,SAAA,CAAU;AAEvC,IAAA,EAAU,OAAO,aACjB,EAAU,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,MACN,cAAc;AAAA,OAEhB,EAAU,QAAQ,CAAC,KAAK,KAAK,CAAA,GAE7B,EAAU,SAAS,IAAI,GAAG,GAAG,OAAA,GAC7B,EAAM,IAAI,CAAA;AAGV,UAAM,IAAO,IAAI,EAAM,KAAK,KAAK,WAAW,KAAK,OAAO,EAAe,KAAA,CAAM;AAC7E,IAAA,EAAK,WAAW;AAAA,MACd,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAK,SAAS,IAAI,GAAG,KAAK,CAAA,GAC1B,EAAM,IAAI,CAAA;AAGV,UAAM,IAAc,IAAI,EAAM,KAC5B,KAAK,kBACL,KAAK,OAAO,EAAe,SAAA,CAAU;AAEvC,WAAA,EAAY,OAAO,eACnB,EAAY,WAAW;AAAA,MACrB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,MACN,cAAc;AAAA,OAEhB,EAAY,QAAQ,KAAK,KAAK,CAAA,GAC9B,EAAY,QAAQ,CAAC,KAAK,EAAA,GAC1B,EAAY,SAAS,IAAI,GAAG,GAAG,MAAA,GAC/B,EAAM,IAAI,CAAA,GAEN,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAGrC;AAAA;EAGT,iBAA2B,GAAsB,GAAwB,GAAoB;AAC3F,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,KAAA;AAC9C,MAAA,EAAS,SAAS,IAAI,GAAG,GAAG,IAAA,GAC5B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAc,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACpD,QAAI,GAAa;AACf,YAAM,IAAe,KAAK,eAAe,GAAa,KAAA;AACtD,MAAA,EAAa,SAAS,IAAI,IAAI,GAAG,IAAA,GACjC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,MAAA;AACpD,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,KAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,MAAA;AACpD,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,IAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,OAAA;AAC9C,MAAA,EAAS,SAAS,IAAI,KAAK,GAAG,KAAA,GAC9B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAe,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACrD,QAAI,GAAc;AAChB,YAAM,IAAgB,KAAK,eAAe,GAAc,OAAA;AACxD,MAAA,EAAc,SAAS,IAAI,KAAK,GAAG,IAAA,GACnC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,QAAA;AAC9C,MAAA,EAAS,SAAS,IAAI,GAAG,GAAG,GAAA,GAC5B,EAAM,IAAI,CAAA;AAAA;;EAId,wBAAiC,GAA2D;AAE1F,WAAO,EACL,QAAQ;AAAA,MACN;AAAA,QACE,KAAK;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,UAAE,MAAM;AAAA,UAAS,KAAK;AAAA,UAAQ,SAAS;AAAA;;MAElD;AAAA,QAAE,KAAK;AAAA,QAAkB,MAAM;AAAA,QAAU,KAAK;AAAA,QAAG,WARjC,GAAQ,IAAI,oBAAA,KAAyB,aAQsB;AAAA;MAC3E;AAAA,QAAE,KAAK;AAAA,QAAuB,MAAM;AAAA;MACrC;AAAA;EAIL,oBAA6B,GAA+C;AAC1E,UAAM,IAAW,oBAAI,IAAA;AACrB,WAAA,EAAS,IAAI,sBAAsB,EAAO,IAAI,oBAAA,KAAyB,OAAA,GACvE,EAAS,IAAI,kBAAkB,WAAW,EAAO,IAAI,gBAAA,KAAqB,GAAA,CAAI,GAC9E,EAAS,IAAI,uBAAuB,WAAW,EAAO,IAAI,qBAAA,KAA0B,GAAA,CAAI,GACjF;AAAA;EAGT,oBAA6B,GAAiD;AAC5E,UAAM,IAAS,oBAAI,IAAA;AACnB,WAAA,EAAO,IAAI,sBAAsB,EAAS,IAAI,oBAAA,KAAyB,OAAA,GACvE,EAAO,IAAI,kBAAkB,EAAS,IAAI,gBAAA,EAAkB,SAAA,CAAU,GACtE,EAAO,IAAI,uBAAuB,EAAS,IAAI,qBAAA,EAAuB,SAAA,KAAc,IAAA,GAC7E;AAAA;EAIT,gBAAyB,GAA0B,GAAoC;AACrF,UAAM,IAAY,KAAK,aAAa,GAAU,WAAA,GACxC,IAAc,KAAK,aAAa,GAAU,aAAA;AAChD,QAAI,CAAC,KAAa,CAAC,EAAa;AAEhC,QAAI,CAAC,KAAS,CAAC,KAAK,qBAAqB,EAAM,UAAU,iBAAiB;AACxE,WAAK,cAAc,CAAA,GACnB,KAAK,6BAA6B,CAAA,GAClC,KAAK,6BAA6B,CAAA;AAClC;AAAA;AAGF,UAAM,IAAe,EAAM,MAAM,WAAW,IAAA,GACtC,IAAqB,IACtB,EAAM,WAAW,IAAI,WAAA,KAAgB,MACtC,EAAM,OACJ,IAAqB,IAAgB,EAAM,aAAa,IAAc,EAAM,OAE5E,IAAY,SAAS,GAAY,EAAA,GACjC,IAAY,SAAS,GAAY,EAAA,GACjC,KAAe,IAAY,OAAO,GAClC,KAAe,IAAY,OAAO,GAClC,KAAiB,IAAY,OAAO,GACpC,KAAiB,IAAY,OAAO;AAG1C,QAAI,CAAC,KAAgB,CAAC,EAAM,eAAe;AACzC,WAAK,cAAc,CAAA,GACnB,KAAK,gBAAgB,GAAW,CAAA,GAChC,KAAK,gBAAgB,GAAa,CAAA;AAClC;AAAA;AAIF,QAAI,KAAK,kBAAkB,qBAAqB,WAAW;AACzD,WAAK,gBAAgB,GAAW,CAAA,GAChC,KAAK,gBAAgB,GAAa,CAAA;AAClC;AAAA;AAIF,SAAK,gBACH,GACA,GACA,GACA,GACA,GACA,GACA,GACA,CAAA;AAAA;EAQJ,gBAAwB,GAAkB,GAAqB;AAC7D,SAAK,4BAA4B,CAAA;AACjC,UAAM,IAAM,EAAK,UACX,IAAQ,IAAO,KAAK,oBAAoB,KAAK,kBAC7C,IAAY,IACd,KAAK,iCACL,KAAK;AACT,IAAA,EAAI,MAAM,KAAK,CAAA,GACf,EAAI,SAAS,KAAK,CAAA,GAClB,EAAI,oBAAoB;AAAA;EAG1B,4BAAoC,GAAwB;AAE1D,IADY,EAAK,SACT,SAAS,YAAY,EAAW,oBACxC,EAAK,WAAW,KAAK,OAAO,EAAe,SAAA,EAAW,MAAA,GACrD,EAAK,SAAuC,SAAS,UAAU,EAAW;AAAA;EAG7E,6BAAqC,GAAwB;AAC3D,UAAM,IAAM,EAAK;AACjB,IAAI,EAAI,SAAS,YAAY,EAAW,oBACxC,EAAI,QAAA,GACJ,EAAK,WAAW,KAAK,OAAO,EAAe,SAAA;AAAA;EAG7C,cAAsB,GAAgC;AACpD,UAAM,IAAQ,EAAS,SAAS;AAChC,IAAI,MACF,EAAM,cAAA,GACN,EAAM,YAAY,CAAA,GAClB,OAAO,EAAS,SAAS,QAE3B,OAAO,EAAS,SAAS,eACzB,OAAO,EAAS,SAAS,aACzB,OAAO,EAAS,SAAS;AAAA;EAQ3B,gBACE,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACM;AACN,QAAI,EAAS,SAAS,uBAAuB,EAAM,UAAW;AAE9D,UAAM,IAAM,KAAK,kBAAmB,gBAE9B,KADO,EAAM,iBAAiB,EAAM,aACX;AAE/B,SAAK,4BAA4B,CAAA,GACjC,KAAK,4BAA4B,CAAA,GAG7B,MAAgB,KAClB,KAAK,gBAAgB,GAAW,CAAA,GAE9B,MAAkB,KACpB,KAAK,gBAAgB,GAAa,CAAA;AAGpC,UAAM,IAAgC,CAAA;AAStC,QARI,MAAgB,KAClB,KAAK,kBAAkB,GAAQ,aAAa,GAAW,GAAa,CAAA,GAElE,MAAkB,KACpB,KAAK,kBAAkB,GAAQ,eAAe,GAAa,GAAe,CAAA,GAIxE,EAAO,WAAW,GAAG;AACvB,MAAA,EAAS,SAAS,qBAAqB,EAAM;AAC7C;AAAA;AAGF,QAAI,IAA8B,EAAS,SAAS;AACpD,IAAK,MACH,IAAQ,IAAI,EAAM,eAAe,CAAA,GACjC,EAAS,SAAS,QAAQ,IAExB,EAAS,SAAS,iBACnB,EAAS,SAAS,cAAwC,KAAA,GAEzD,EAAS,SAAS,eACpB,EAAM,YAAY,EAAS,SAAS,WAAA;AAGtC,UAAM,IAAO,IAAI,EAAM,cAAc,gBAAgB,GAAiB,CAAA,GAChE,IAAS,EAAM,WAAW,CAAA;AAChC,IAAA,EAAO,OAAO,EAAM,UACpB,EAAO,oBAAoB,IAC3B,EAAO,KAAA,GAEP,EAAS,SAAS,qBAAqB,EAAM,WAC7C,EAAS,SAAS,gBAAgB,GAClC,EAAS,SAAS,cAAc;AAAA;EAGlC,kBACE,GACA,GACA,GACA,GACA,GACM;AACN,UAAM,IAAM,EAAK,UACX,IAAU;AAAA,MAAC,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA,OAC/C,IAAU,IAAS,KAAK,oBAAoB,KAAK,kBACjD,IAAQ;AAAA,MAAC,EAAQ;AAAA,MAAG,EAAQ;AAAA,MAAG,EAAQ;AAAA,OACvC,IAAgB,EAAI,mBACpB,IAAc,IAChB,KAAK,iCACL,KAAK;AAET,IAAA,EAAO,KACL,IAAI,EAAM,mBACR,GAAG,CAAA,mBACH,CAAC,GAAG,CAAA,GACJ,CAAC,GAAG,GAAS,GAAG,CAAA,CAAM,GAExB,IAAI,EAAM,mBACR,GAAG,CAAA,sBACH,CAAC,GAAG,CAAA,GACJ,CAAC,GAAG,GAAS,GAAG,CAAA,CAAM,GAExB,IAAI,EAAM,oBACR,GAAG,CAAA,+BACH,CAAC,GAAG,CAAA,GACJ,CAAC,GAAe,CAAA,CAAY,CAC7B;AAAA;EAKL,aAAuB,GAA0B,GAAiC;AAChF,QAAI,IAA2B;AAC/B,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,MAAI,aAAiB,EAAM,QAAQ,EAAM,SAAS,SAAS,MACzD,IAAQ;AAAA,QAGL;AAAA;GCtaE,KAAb,cAAgD,EAA2B;AAAA,EACzE,gBAAiC,GAAa,KAAI,KAAI,MAAK,IAAG,KAAI,GAAA;AAAA,EAClE,0BAA2C,IAAI,EAAM,YAAY,KAAK,KAAI,IAAA;AAAA,EAC1E,4BAA6C,IAAI,EAAM,YAAY,GAAG,KAAI,GAAA;AAAA,EAC1E,mBAAoC,IAAI,EAAM,YAAY,KAAK,KAAK,KAAA;AAAA,EAEpE,oBAAuC,IAAI,EAAM,MAAM,QAAA;AAAA,EACvD,mBAAsC,IAAI,EAAM,MAAM,OAAA;AAAA,EACtD,iCAAoD;AAAA,EACpD,gCAAmD;AAAA,EAEnD,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,aAAa,GAAsB,GAAwC;AACzE,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAE9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAG3B,UAAM,IAAS,KAAK,sBAClB,EAAU,IACV,EAAM,IACN,GAAE,KAAI,EAAA;AACR,IAAA,EAAM,IAAI,CAAA;AAGV,UAAM,IAAW,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,OAAO,EAAe,KAAA,CAAM;AACrF,IAAA,EAAS,WAAW;AAAA,MAClB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAS,QAAQ,CAAC,KAAK,KAAK,CAAA,GAC5B,EAAS,SAAS,IAAI,GAAG,GAAG,CAAA,GAC5B,EAAM,IAAI,CAAA;AAGV,UAAM,IAAY,IAAI,EAAM,KAAK,KAAK,yBAAyB,KAAK,OAAO,EAAe,KAAA,CAAM;AAChG,IAAA,EAAU,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAU,SAAS,IAAI,GAAG,KAAK,CAAA,GAC/B,EAAM,IAAI,CAAA;AAEV,QAAI,IAAS;AACb,WAAM,IAAS,KAAE;AACf,UAAG,IAAS,GAAE;AACZ,cAAM,IAAa,IAAI,EAAM,KAAK,KAAK,2BAA2B,KAAK,OAAO,EAAe,KAAA,CAAM;AACnG,QAAA,EAAW,WAAW;AAAA,UACpB,MAAM;AAAA,UACN,aAAa,EAAU;AAAA,UACvB,MAAM,wBAAwB,CAAA;AAAA,WAEhC,EAAW,SAAS,IAAI,GAAG,KAAK,SAAS,QAAM,CAAA,GAC/C,EAAM,IAAI,CAAA;AAAA;AAEZ,YAAM,IAAY,IAAI,EAAM,KAAK,KAAK,kBAAkB,KAAK,OAAO,EAAe,SAAA,CAAU;AAC7F,MAAA,EAAU,OAAO,cAAc,CAAA,IAC/B,EAAU,WAAW;AAAA,QACnB,MAAM;AAAA,QACN,aAAa,EAAU;AAAA,QACvB,MAAM,cAAc,CAAA;AAAA,SAEtB,EAAU,SAAS,IAAI,MAAM,KAAK,UAAU,QAAM,CAAA,GAClD,EAAM,IAAI,CAAA;AACV,YAAM,IAAU,IAAI,EAAM,KAAK,KAAK,kBAAkB,KAAK,OAAO,EAAe,SAAA,CAAU;AAC3F,MAAA,EAAQ,OAAO,YAAY,CAAA,IAC3B,EAAQ,WAAW;AAAA,QACjB,MAAM;AAAA,QACN,aAAa,EAAU;AAAA,QACvB,MAAM,YAAY,CAAA;AAAA,SAEpB,EAAQ,SAAS,IAAI,MAAM,KAAK,UAAU,QAAM,CAAA,GAChD,EAAM,IAAI,CAAA,GAEV,KAAS;AAAA;AAGX,WAAI,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAGrC;AAAA;EAGT,iBAA2B,GAAsB,GAAwB,GAAoB;AAC3F,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,KAAA;AAC9C,MAAA,EAAS,SAAS,IAAI,GAAG,GAAG,IAAA,GAC5B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAc,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACpD,QAAI,GAAa;AACf,YAAM,IAAe,KAAK,eAAe,GAAa,KAAA;AACtD,MAAA,EAAa,SAAS,IAAI,KAAK,GAAG,IAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,QAAI,IAAS;AAEb,WAAM,IAAS,MAAG;AAChB,YAAM,IAAO,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAC7C,UAAG,CAAC,GAAK;AACP,QAAA,KAAU;AACV;AAAA;AAEF,YAAM,IAAW,KAAK,eAAe,GAAM,QAAQ,IAAI,EAAM,MAAM,MAAM,GAAG,CAAA,CAAE,GAExE,IAAiB,IAAS;AAChC,MAAA,EAAS,SAAS,IAAI,OAAO,IAAe,MAAM,GAAG,KAAK,OAAO,CAAA,GACjE,EAAM,IAAI,CAAA,GACV,KAAU;AAAA;AAIZ,WAAM,IAAS,MAAG;AAChB,YAAM,IAAO,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAC7C,UAAG,CAAC,GAAK;AACP,QAAA,KAAU;AACV;AAAA;AAEF,YAAM,IAAW,KAAK,eAAe,GAAM,QAAQ,IAAI,EAAM,MAAM,OAAO,GAAG,CAAA,CAAE,GAEzE,IAAiB,IAAS;AAChC,MAAA,EAAS,SAAS,IAAI,QAAQ,IAAe,MAAM,GAAG,OAAO,OAAO,CAAA,GACpE,EAAM,IAAI,CAAA,GACV,KAAU;AAAA;AAIZ,WAAM,IAAS,MAAG;AAChB,YAAM,IAAO,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAC7C,UAAG,CAAC,GAAK;AACP,QAAA,KAAU;AACV;AAAA;AAEF,YAAM,IAAW,KAAK,eAAe,GAAM,OAAA,GAErC,IAAiB,IAAS;AAChC,MAAA,EAAS,SAAS,IAAI,KAAK,GAAG,KAAK,IAAI,CAAA,GACvC,EAAM,IAAI,CAAA,GACV,KAAU;AAAA;AAGZ,UAAM,IAAe,EAAQ,SAAS,EAAU,KAAK,EAAA,CAAA;AACrD,QAAI,GAAc;AAChB,YAAM,IAAgB,KAAK,eAAe,GAAc,QAAA;AACxD,MAAA,EAAc,SAAS,IAAI,KAAK,GAAG,GAAA,GACnC,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,EAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,QAAA;AAC9C,MAAA,EAAS,SAAS,IAAI,GAAG,GAAG,GAAA,GAC5B,EAAM,IAAI,CAAA;AAAA;;EAId,wBAAiC,GAA2D;AAE1F,WAAO,EACL,QAAQ;AAAA,MACN;AAAA,QACE,KAAK;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,UAAE,MAAM;AAAA,UAAS,KAAK;AAAA,UAAQ,SAAS;AAAA;;MAElD;AAAA,QAAE,KAAK;AAAA,QAAkB,MAAM;AAAA,QAAU,KAAK;AAAA,QAAG,WARjC,GAAQ,IAAI,oBAAA,KAAyB,aAQsB;AAAA;MAC3E;AAAA,QAAE,KAAK;AAAA,QAAuB,MAAM;AAAA;MACrC;AAAA;EAIL,oBAA6B,GAA+C;AAC1E,UAAM,IAAW,oBAAI,IAAA;AACrB,WAAA,EAAS,IAAI,sBAAsB,EAAO,IAAI,oBAAA,KAAyB,OAAA,GACvE,EAAS,IAAI,kBAAkB,WAAW,EAAO,IAAI,gBAAA,KAAqB,GAAA,CAAI,GAC9E,EAAS,IAAI,uBAAuB,WAAW,EAAO,IAAI,qBAAA,KAA0B,GAAA,CAAI,GACjF;AAAA;EAGT,oBAA6B,GAAiD;AAC5E,UAAM,IAAS,oBAAI,IAAA;AACnB,WAAA,EAAO,IAAI,sBAAsB,EAAS,IAAI,oBAAA,KAAyB,OAAA,GACvE,EAAO,IAAI,kBAAkB,EAAS,IAAI,gBAAA,EAAkB,SAAA,CAAU,GACtE,EAAO,IAAI,uBAAuB,EAAS,IAAI,qBAAA,EAAuB,SAAA,KAAc,IAAA,GAC7E;AAAA;EAIT,gBAAyB,GAA0B,GAAoC;AACrF,UAAM,IAAQ,KAAK,gBAAgB,CAAA;AACnC,QAAI,EAAM,WAAW,EAAG;AAExB,QAAI,CAAC,KAAS,CAAC,KAAK,qBAAqB,EAAM,UAAU,iBAAiB;AACxE,WAAK,cAAc,CAAA;AACnB,iBAAW,EAAE,MAAA,EAAA,KAAU,EAAO,MAAK,uBAAuB,CAAA;AAC1D;AAAA;AAGF,UAAM,IAAe,EAAM,MAAM,WAAW,IAAA,GACtC,IAAqB,IACtB,EAAM,WAAW,IAAI,WAAA,KAAgB,SACtC,EAAM,OACJ,IAAqB,IAAgB,EAAM,aAAa,IAAc,EAAM,OAE5E,IAAY,SAAS,GAAY,EAAA,GACjC,IAAY,SAAS,GAAY,EAAA;AAGvC,QAAI,CAAC,KAAgB,CAAC,EAAM,eAAe;AACzC,WAAK,cAAc,CAAA;AACnB,iBAAW,EAAE,MAAA,GAAM,QAAA,EAAA,KAAY,EAC7B,MAAK,cAAc,IAAQ,KAAa,IAAU,OAAO,CAAA;AAE3D;AAAA;AAIF,QAAI,KAAK,kBAAkB,qBAAqB,WAAW;AACzD,iBAAW,EAAE,MAAA,GAAM,QAAA,EAAA,KAAY,EAC7B,MAAK,cAAc,IAAQ,KAAa,IAAU,OAAO,CAAA;AAE3D;AAAA;AAIF,SAAK,cAAc,GAAU,GAAO,GAAO,GAAW,CAAA;AAAA;EAOxD,cAAsB,GAAkB,GAAqB;AAC3D,SAAK,sBAAsB,CAAA;AAC3B,UAAM,IAAM,EAAK,UACX,IAAQ,IAAO,KAAK,oBAAoB,KAAK,kBAC7C,IAAY,IACd,KAAK,iCACL,KAAK;AACT,IAAA,EAAI,MAAM,KAAK,CAAA,GACf,EAAI,SAAS,KAAK,CAAA,GAClB,EAAI,oBAAoB;AAAA;EAG1B,sBAA8B,GAAwB;AAEpD,IADY,EAAK,SACT,SAAS,YAAY,EAAW,oBACxC,EAAK,WAAW,KAAK,OAAO,EAAe,SAAA,EAAW,MAAA,GACrD,EAAK,SAAuC,SAAS,UAAU,EAAW;AAAA;EAG7E,uBAA+B,GAAwB;AACrD,UAAM,IAAM,EAAK;AACjB,IAAI,EAAI,SAAS,YAAY,EAAW,oBACxC,EAAI,QAAA,GACJ,EAAK,WAAW,KAAK,OAAO,EAAe,SAAA;AAAA;EAG7C,cAAsB,GAAgC;AACpD,UAAM,IAAQ,EAAS,SAAS;AAChC,IAAI,MACF,EAAM,cAAA,GACN,EAAM,YAAY,CAAA,GAClB,OAAO,EAAS,SAAS,QAE3B,OAAO,EAAS,SAAS,eACzB,OAAO,EAAS,SAAS,aACzB,OAAO,EAAS,SAAS;AAAA;EAQ3B,gBAAwB,GAAgF;AACtG,UAAM,IAA+D,CAAA;AACrE,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,UAAI,EAAE,aAAiB,EAAM,MAAO;AACpC,YAAM,IAAO,EAAM,SAAS;AAC5B,UAAI,CAAC,EAAM;AAEX,YAAM,IAAW,EAAK,MAAM,iBAAA;AAC5B,UAAI,GAAU;AACZ,cAAM,IAAI,SAAS,EAAS,CAAA,CAAA;AAC5B,QAAA,EAAO,KAAK;AAAA,UAAE,MAAM;AAAA,UAAO,QAAQ,IAAI;AAAA,UAAG,MAAM,EAAM;AAAA,SAAM;AAC5D;AAAA;AAEF,YAAM,IAAa,EAAK,MAAM,mBAAA;AAC9B,UAAI,GAAY;AACd,cAAM,IAAI,SAAS,EAAW,CAAA,CAAA;AAC9B,QAAA,EAAO,KAAK;AAAA,UAAE,MAAM;AAAA,UAAO,QAAQ,IAAI,IAAI;AAAA,UAAG,MAAM,EAAM;AAAA,SAAM;AAAA;QAG7D;AAAA;EAWT,cACE,GACA,GACA,GACA,GACA,GACM;AACN,QAAI,EAAS,SAAS,uBAAuB,EAAM,UAAW;AAE9D,UAAM,IAAM,KAAK,kBAAmB,gBAE9B,KADO,EAAM,iBAAiB,EAAM,aACX,GAEzB,IAAgC,CAAA;AAEtC,eAAW,EAAE,MAAA,GAAM,QAAA,GAAQ,MAAA,EAAA,KAAU,GAAO;AAC1C,YAAM,KAAa,KAAa,IAAU,OAAO,GAC3C,KAAa,KAAa,IAAU,OAAO;AAIjD,UAFA,KAAK,sBAAsB,CAAA,GAEvB,MAAa,GAAU;AAEzB,aAAK,cAAc,GAAM,CAAA;AACzB;AAAA;AAGF,WAAK,gBAAgB,GAAQ,GAAM,GAAM,GAAU,CAAA;AAAA;AAIrD,QAAI,EAAO,WAAW,GAAG;AACvB,MAAA,EAAS,SAAS,qBAAqB,EAAM;AAC7C;AAAA;AAGF,QAAI,IAA8B,EAAS,SAAS;AACpD,IAAK,MACH,IAAQ,IAAI,EAAM,eAAe,CAAA,GACjC,EAAS,SAAS,QAAQ,IAExB,EAAS,SAAS,iBACnB,EAAS,SAAS,cAAwC,KAAA,GAEzD,EAAS,SAAS,eACpB,EAAM,YAAY,EAAS,SAAS,WAAA;AAGtC,UAAM,IAAO,IAAI,EAAM,cAAc,cAAc,GAAiB,CAAA,GAC9D,IAAS,EAAM,WAAW,CAAA;AAChC,IAAA,EAAO,OAAO,EAAM,UACpB,EAAO,oBAAoB,IAC3B,EAAO,KAAA,GAEP,EAAS,SAAS,qBAAqB,EAAM,WAC7C,EAAS,SAAS,gBAAgB,GAClC,EAAS,SAAS,cAAc;AAAA;EAGlC,gBACE,GACA,GACA,GACA,GACA,GACM;AACN,UAAM,IAAM,EAAK,UACX,IAAU;AAAA,MAAC,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA,OAC/C,IAAU,IAAS,KAAK,oBAAoB,KAAK,kBACjD,IAAQ;AAAA,MAAC,EAAQ;AAAA,MAAG,EAAQ;AAAA,MAAG,EAAQ;AAAA,OACvC,IAAgB,EAAI,mBACpB,IAAc,IAChB,KAAK,iCACL,KAAK;AAET,IAAA,EAAO,KACL,IAAI,EAAM,mBACR,GAAG,CAAA,mBACH,CAAC,GAAG,CAAA,GACJ,CAAC,GAAG,GAAS,GAAG,CAAA,CAAM,GAExB,IAAI,EAAM,mBACR,GAAG,CAAA,sBACH,CAAC,GAAG,CAAA,GACJ,CAAC,GAAG,GAAS,GAAG,CAAA,CAAM,GAExB,IAAI,EAAM,oBACR,GAAG,CAAA,+BACH,CAAC,GAAG,CAAA,GACJ,CAAC,GAAe,CAAA,CAAY,CAC7B;AAAA;GCxZM,KAAb,cAAyD,EAA2B;AAAA,EAClF,gBAAiC,GAAuB,KAAI,KAAI,KAAI,GAAA;AAAA,EACpE,0BAA2C,IAAI,EAAM,YAAY,KAAK,KAAI,GAAA;AAAA,EAC1E,4BAA6C,IAAI,EAAM,YAAY,MAAM,KAAI,GAAA;AAAA,EAC7E,mBAAoC,IAAI,EAAM,YAAY,MAAM,KAAK,MAAA;AAAA,EAErE,uBAAwC,IAAI,EAAM,YAAY,MAAM,KAAK,CAAA;AAAA,EACzE,sBAAuC,IAAI,EAAM,YAAY,MAAM,KAAK,IAAA;AAAA,EAExE,wBAAyC,GACrC,MACA,OACA,MACA,OACA,OACA,IACA,GAAA;AAAA,EAGJ,oBAAuC,IAAI,EAAM,MAAM,QAAA;AAAA,EACvD,mBAAsC,IAAI,EAAM,MAAM,OAAA;AAAA,EACtD,uBAA0C,IAAI,EAAM,MAAM,QAAA;AAAA,EAC1D,wBAA2C,IAAI,EAAM,MAAM,CAAA;AAAA,EAC3D,iCAAoD;AAAA,EACpD,gCAAmD;AAAA,EACnD,oCAAuD;AAAA,EAEvD,cAAc;AACZ,UAAA,GACA,KAAK,iBAAiB,EAAc;AAAA;EAGtC,aAAa,GAAsB,GAAwC;AACzE,QAAI,EAAU,SAAS,KAAK,eAC1B,OAAM,IAAI,MAAM,+BAA+B,KAAK,cAAA,WAAyB,EAAU,IAAA,GAAK;AAE9F,UAAM,IAAQ,IAAI,EAAM,MAAA;AACxB,IAAA,EAAM,WAAW;AAAA,MACf,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,eAAe,EAAU;AAAA;AAG3B,UAAM,IAAS,KAAK,sBAClB,EAAU,IACV,EAAM,IACN,GAAE,KAAI,GAAA;AACR,IAAA,EAAM,IAAI,CAAA;AAGV,UAAM,IAAW,IAAI,EAAM,KAAK,KAAK,eAAe,KAAK,OAAO,EAAe,KAAA,CAAM;AACrF,IAAA,EAAS,WAAW;AAAA,MAClB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAS,QAAQ,CAAC,KAAK,KAAK,CAAA,GAC5B,EAAS,SAAS,IAAI,GAAG,GAAG,CAAA,GAC5B,EAAM,IAAI,CAAA;AAGV,UAAM,IAAY,IAAI,EAAM,KAAK,KAAK,yBAAyB,KAAK,OAAO,EAAe,KAAA,CAAM;AAChG,IAAA,EAAU,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAU,SAAS,IAAI,GAAG,KAAK,CAAA,GAC/B,EAAM,IAAI,CAAA;AAEV,QAAI,IAAS;AACb,WAAM,IAAS,KAAE;AACf,UAAG,IAAS,GAAE;AACZ,cAAM,IAAa,IAAI,EAAM,KAAK,KAAK,2BAA2B,KAAK,OAAO,EAAe,KAAA,CAAM;AACnG,QAAA,EAAW,WAAW;AAAA,UACpB,MAAM;AAAA,UACN,aAAa,EAAU;AAAA,UACvB,MAAM,wBAAwB,CAAA;AAAA,WAEhC,EAAW,SAAS,IAAI,OAAO,KAAK,UAAU,SAAO,CAAA,GACrD,EAAM,IAAI,CAAA;AAAA;AAEZ,YAAM,IAAa,IAAI,EAAM,KAAK,KAAK,kBAAkB,KAAK,OAAO,EAAe,SAAA,CAAU;AAC9F,MAAA,EAAW,OAAO,eAAe,CAAA,IACjC,EAAW,WAAW;AAAA,QACpB,MAAM;AAAA,QACN,aAAa,EAAU;AAAA,QACvB,MAAM,eAAe,CAAA;AAAA,SAEvB,EAAW,SAAS,IAAI,OAAO,KAAK,WAAW,SAAO,CAAA,GACtD,EAAM,IAAI,CAAA,GAEV,KAAS;AAAA;AAKX,UAAM,IAAc,IAAI,EAAM,KAAK,KAAK,uBAAuB,KAAK,OAAO,EAAe,KAAA,CAAM;AAChG,IAAA,EAAY,WAAW;AAAA,MACrB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAY,QAAQ,CAAC,KAAK,KAAK,CAAA,GAC/B,EAAY,QAAQ,CAAC,KAAK,EAAA,GAC1B,EAAY,SAAS,IAAI,QAAQ,KAAK,OAAA,GACtC,EAAM,IAAI,CAAA;AAEV,UAAM,IAAc,IAAI,EAAM,KAAK,KAAK,uBAAuB,KAAK,OAAO,EAAe,KAAA,CAAM;AAChG,IAAA,EAAY,WAAW;AAAA,MACrB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAY,QAAQ,KAAK,KAAK,CAAA,GAC9B,EAAY,QAAQ,CAAC,KAAK,EAAA,GAC1B,EAAY,SAAS,IAAI,QAAQ,GAAG,MAAA,GACpC,EAAM,IAAI,CAAA;AAEV,UAAM,IAAY,IAAI,EAAM,KAAK,KAAK,sBAAsB,KAAK,OAAO,EAAe,SAAA,CAAU;AACjG,IAAA,EAAU,OAAO,wBACjB,EAAU,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAU,SAAS,IAAI,QAAQ,KAAI,CAAA,GACnC,EAAM,IAAI,CAAA;AAEV,UAAM,IAAe,IAAI,EAAM,KAAK,KAAK,qBAAqB,KAAK,OAAO,EAAe,KAAA,CAAM;AAC/F,IAAA,EAAa,OAAO,4BACpB,EAAa,WAAW;AAAA,MACtB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAa,SAAS,IAAI,QAAQ,KAAI,CAAA,GACtC,EAAM,IAAI,CAAA;AAEV,UAAM,IAAgB,IAAI,EAAM,KAAK,KAAK,qBAAqB,KAAK,OAAO,EAAe,KAAA,CAAM;AAChG,WAAA,EAAc,OAAO,6BACrB,EAAc,WAAW;AAAA,MACvB,MAAM;AAAA,MACN,aAAa,EAAU;AAAA,MACvB,MAAM;AAAA,OAER,EAAc,SAAS,IAAI,QAAQ,KAAI,CAAA,GACvC,EAAM,IAAI,CAAA,GAKN,EAAU,KAAK,SAAS,KAC1B,KAAK,iBAAiB,GAAW,GAAS,CAAA,GAGrC;AAAA;EAGT,iBAA2B,GAAsB,GAAwB,GAAoB;AAC3F,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,KAAA;AAC9C,MAAA,EAAS,SAAS,IAAI,GAAG,GAAG,IAAA,GAC5B,EAAM,IAAI,CAAA;AAAA;AAGZ,UAAM,IAAa,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AACnD,QAAI,GAAY;AACd,YAAM,IAAc,KAAK,eAAe,GAAY,KAAA;AACpD,MAAA,EAAY,SAAS,IAAI,MAAM,GAAG,IAAA,GAClC,EAAM,IAAI,CAAA;AAAA;AAGZ,QAAI,IAAS;AAEb,WAAM,IAAS,MAAG;AAChB,YAAM,IAAO,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAC7C,UAAG,CAAC,GAAK;AACP,QAAA,KAAU;AACV;AAAA;AAEF,YAAM,IAAW,KAAK,eAAe,GAAM,MAAA,GAErC,IAAiB,IAAS;AAChC,MAAA,EAAS,SAAS,IAAI,MAAM,GAAG,QAAQ,MAAM,CAAA,GAC7C,EAAM,IAAI,CAAA,GACV,KAAU;AAAA;AAIZ,WAAM,IAAS,MAAG;AAChB,YAAM,IAAO,EAAQ,SAAS,EAAU,KAAK,CAAA,CAAA;AAC7C,UAAG,CAAC,GAAK;AACP,QAAA,KAAU;AACV;AAAA;AAEF,YAAM,IAAW,KAAK,eAAe,GAAM,OAAA,GAErC,IAAiB,IAAS;AAChC,MAAA,EAAS,SAAS,IAAI,KAAK,GAAG,QAAQ,MAAM,CAAA,GAC5C,EAAM,IAAI,CAAA,GACV,KAAU;AAAA;AAGZ,UAAM,IAAU,EAAQ,SAAS,EAAU,KAAK,EAAA,CAAA;AAChD,QAAI,GAAS;AACX,YAAM,IAAW,KAAK,eAAe,GAAS,QAAA;AAC9C,MAAA,EAAS,SAAS,IAAI,GAAG,GAAG,GAAA,GAC5B,EAAM,IAAI,CAAA;AAAA;;EAId,wBAAiC,GAA2D;AAE1F,WAAO,EACL,QAAQ;AAAA,MACN;AAAA,QACE,KAAK;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,UAAE,MAAM;AAAA,UAAS,KAAK;AAAA,UAAQ,SAAS;AAAA;;MAElD;AAAA,QAAE,KAAK;AAAA,QAAkB,MAAM;AAAA,QAAU,KAAK;AAAA,QAAG,WARjC,GAAQ,IAAI,oBAAA,KAAyB,aAQsB;AAAA;MAC3E;AAAA,QAAE,KAAK;AAAA,QAAuB,MAAM;AAAA;MACrC;AAAA;EAIL,oBAA6B,GAA+C;AAC1E,UAAM,IAAW,oBAAI,IAAA;AACrB,WAAA,EAAS,IAAI,sBAAsB,EAAO,IAAI,oBAAA,KAAyB,OAAA,GACvE,EAAS,IAAI,kBAAkB,WAAW,EAAO,IAAI,gBAAA,KAAqB,GAAA,CAAI,GAC9E,EAAS,IAAI,uBAAuB,WAAW,EAAO,IAAI,qBAAA,KAA0B,GAAA,CAAI,GACjF;AAAA;EAGT,oBAA6B,GAAiD;AAC5E,UAAM,IAAS,oBAAI,IAAA;AACnB,WAAA,EAAO,IAAI,sBAAsB,EAAS,IAAI,oBAAA,KAAyB,OAAA,GACvE,EAAO,IAAI,kBAAkB,EAAS,IAAI,gBAAA,EAAkB,SAAA,CAAU,GACtE,EAAO,IAAI,uBAAuB,EAAS,IAAI,qBAAA,EAAuB,SAAA,KAAc,IAAA,GAC7E;AAAA;EAIT,gBAAyB,GAA0B,GAAoC;AACrF,UAAM,IAAQ,KAAK,gBAAgB,CAAA;AACnC,QAAI,EAAM,WAAW,EAAG;AAExB,QAAI,CAAC,KAAS,CAAC,KAAK,qBAAqB,EAAM,UAAU,iBAAiB;AACxE,WAAK,cAAc,CAAA;AACnB,iBAAW,KAAQ,EAAO,MAAK,uBAAuB,CAAA;AACtD;AAAA;AAGF,UAAM,IAAe,EAAM,MAAM,WAAW,IAAA,GACtC,IAAqB,IACtB,EAAM,WAAW,IAAI,WAAA,KAAgB,QACtC,EAAM,OACJ,IAAqB,IAAgB,EAAM,aAAa,IAAc,EAAM,OAE5E,IAAY,SAAS,GAAY,EAAA,GACjC,IAAY,SAAS,GAAY,EAAA;AAGvC,QAAI,CAAC,KAAgB,CAAC,EAAM,eAAe;AACzC,WAAK,cAAc,CAAA;AACnB,iBAAW,KAAQ,EACjB,MAAK,cAAc,IAAQ,KAAa,EAAK,SAAU,OAAO,CAAA;AAEhE;AAAA;AAIF,QAAI,KAAK,kBAAkB,qBAAqB,WAAW;AACzD,iBAAW,KAAQ,EACjB,MAAK,cAAc,IAAQ,KAAa,EAAK,SAAU,OAAO,CAAA;AAEhE;AAAA;AAIF,SAAK,cAAc,GAAU,GAAO,GAAO,GAAW,CAAA;AAAA;EAaxD,YAAoB,GAAgB,GAA2B;AAC7D,WAAI,MAAS,SACJ,IACH;AAAA,MAAE,OAAO,KAAK;AAAA,MAAsB,UAAU,KAAK;AAAA,MAAuB,WAAW,KAAK;AAAA,QAC1F;AAAA,MAAE,OAAO,KAAK;AAAA,MAAmB,UAAU,KAAK;AAAA,MAAmB,WAAW,KAAK;AAAA,SAExE,MAAS,UAAU,CAAC,IAAO,KAExC;AAAA,MAAE,OAAO,KAAK;AAAA,MAAmB,UAAU,KAAK;AAAA,MAAmB,WAAW,KAAK;AAAA,QACnF;AAAA,MAAE,OAAO,KAAK;AAAA,MAAkB,UAAU,KAAK;AAAA,MAAkB,WAAW,KAAK;AAAA;;EAOvF,cAAsB,GAAgB,GAAqB;AACzD,SAAK,sBAAsB,EAAK,IAAA;AAChC,UAAM,IAAM,EAAK,KAAK,UAChB,IAAS,KAAK,YAAY,EAAK,MAAM,CAAA;AAC3C,IAAA,EAAI,MAAM,KAAK,EAAO,KAAA,GACtB,EAAI,SAAS,KAAK,EAAO,QAAA,GACzB,EAAI,oBAAoB,EAAO;AAAA;EAGjC,sBAA8B,GAAwB;AAEpD,IADY,EAAK,SACT,SAAS,YAAY,EAAW,oBACxC,EAAK,WAAW,KAAK,OAAO,EAAe,SAAA,EAAW,MAAA,GACrD,EAAK,SAAuC,SAAS,UAAU,EAAW;AAAA;EAG7E,uBAA+B,GAAsB;AACnD,UAAM,IAAM,EAAK,KAAK;AACtB,IAAI,EAAI,SAAS,YAAY,EAAW,oBACxC,EAAI,QAAA,GACJ,EAAK,KAAK,WAAW,KAAK,OAAO,EAAK,gBAAA;AAAA;EAGxC,cAAsB,GAAgC;AACpD,UAAM,IAAQ,EAAS,SAAS;AAChC,IAAI,MACF,EAAM,cAAA,GACN,EAAM,YAAY,CAAA,GAClB,OAAO,EAAS,SAAS,QAE3B,OAAO,EAAS,SAAS,eACzB,OAAO,EAAS,SAAS,aACzB,OAAO,EAAS,SAAS;AAAA;EAY3B,gBAAwB,GAAsC;AAC5D,UAAM,IAAqB,CAAA;AAC3B,WAAA,EAAS,SAAA,CAAU,MAAU;AAC3B,UAAI,EAAE,aAAiB,EAAM,MAAO;AACpC,YAAM,IAAO,EAAM,SAAS;AAC5B,UAAI,CAAC,EAAM;AAEX,YAAM,IAAc,EAAK,MAAM,oBAAA;AAC/B,UAAI,GAAa;AACf,cAAM,IAAI,SAAS,EAAY,CAAA,CAAA;AAC/B,QAAA,EAAO,KAAK;AAAA,UAAE,MAAM;AAAA,UAAO,QAAQ;AAAA,UAAG,MAAM,EAAM;AAAA,UAAM,MAAM;AAAA,UAAU,kBAAkB,EAAe;AAAA,SAAW;AACpH;AAAA;AAEF,UAAI,MAAS,wBAAwB;AACnC,QAAA,EAAO,KAAK;AAAA,UAAE,MAAM;AAAA,UAAO,QAAQ;AAAA,UAAG,MAAM,EAAM;AAAA,UAAM,MAAM;AAAA,UAAS,kBAAkB,EAAe;AAAA,SAAW;AACnH;AAAA;AAEF,OAAI,MAAS,oBAAoB,MAAS,sBACxC,EAAO,KAAK;AAAA,QAAE,MAAM;AAAA,QAAO,QAAQ;AAAA,QAAG,MAAM,EAAM;AAAA,QAAM,MAAM;AAAA,QAAQ,kBAAkB,EAAe;AAAA,OAAO;AAAA,QAG3G;AAAA;EAWT,cACE,GACA,GACA,GACA,GACA,GACM;AACN,QAAI,EAAS,SAAS,uBAAuB,EAAM,UAAW;AAE9D,UAAM,IAAM,KAAK,kBAAmB,gBAE9B,KADO,EAAM,iBAAiB,EAAM,aACX,GAEzB,IAAgC,CAAA;AAEtC,eAAW,KAAQ,GAAO;AACxB,YAAM,KAAa,KAAa,EAAK,SAAU,OAAO,GAChD,KAAa,KAAa,EAAK,SAAU,OAAO;AAItD,UAFA,KAAK,sBAAsB,EAAK,IAAA,GAE5B,MAAa,GAAU;AACzB,aAAK,cAAc,GAAM,CAAA;AACzB;AAAA;AAEF,WAAK,gBAAgB,GAAQ,GAAM,GAAU,CAAA;AAAA;AAG/C,QAAI,EAAO,WAAW,GAAG;AACvB,MAAA,EAAS,SAAS,qBAAqB,EAAM;AAC7C;AAAA;AAGF,QAAI,IAA8B,EAAS,SAAS;AACpD,IAAK,MACH,IAAQ,IAAI,EAAM,eAAe,CAAA,GACjC,EAAS,SAAS,QAAQ,IAExB,EAAS,SAAS,iBACnB,EAAS,SAAS,cAAwC,KAAA,GAEzD,EAAS,SAAS,eACpB,EAAM,YAAY,EAAS,SAAS,WAAA;AAGtC,UAAM,IAAO,IAAI,EAAM,cAAc,uBAAuB,GAAiB,CAAA,GACvE,IAAS,EAAM,WAAW,CAAA;AAChC,IAAA,EAAO,OAAO,EAAM,UACpB,EAAO,oBAAoB,IAC3B,EAAO,KAAA,GAEP,EAAS,SAAS,qBAAqB,EAAM,WAC7C,EAAS,SAAS,gBAAgB,GAClC,EAAS,SAAS,cAAc;AAAA;EAGlC,gBACE,GACA,GACA,GACA,GACM;AACN,UAAM,IAAM,EAAK,KAAK,UAChB,IAAS,KAAK,YAAY,EAAK,MAAM,CAAA,GACrC,IAAY;AAAA,MAAC,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA,MAAG,EAAI,MAAM;AAAA,OACjD,IAAe;AAAA,MAAC,EAAI,SAAS;AAAA,MAAG,EAAI,SAAS;AAAA,MAAG,EAAI,SAAS;AAAA,OAC7D,IAAU;AAAA,MAAC,EAAO,MAAM;AAAA,MAAG,EAAO,MAAM;AAAA,MAAG,EAAO,MAAM;AAAA,OACxD,IAAa;AAAA,MAAC,EAAO,SAAS;AAAA,MAAG,EAAO,SAAS;AAAA,MAAG,EAAO,SAAS;AAAA;AAE1E,IAAA,EAAO,KACL,IAAI,EAAM,mBACR,GAAG,EAAK,IAAA,mBACR,CAAC,GAAG,CAAA,GACJ,CAAC,GAAG,GAAW,GAAG,CAAA,CAAQ,GAE5B,IAAI,EAAM,mBACR,GAAG,EAAK,IAAA,sBACR,CAAC,GAAG,CAAA,GACJ,CAAC,GAAG,GAAc,GAAG,CAAA,CAAW,GAElC,IAAI,EAAM,oBACR,GAAG,EAAK,IAAA,+BACR,CAAC,GAAG,CAAA,GACJ,CAAC,EAAI,mBAAmB,EAAO,SAAA,CAAU,CAC1C;AAAA;;AC5cP,SAAgB,GACd,GACyB;AACzB,SAAO,EAAS,SAAS,SAAS,oBAAA,CAAqB,MACrD,EACG,IAAI,EAAc,SAAS,IAAI,GAAA,CAAsB,EACrD,IAAI,EAAc,OAAO,IAAI,GAAA,CAAoB,EACjD,IAAI,EAAc,OAAO,IAAI,GAAA,CAAoB,EACjD,IAAI,EAAc,QAAQ,IAAI,GAAA,CAAqB,EACnD,IAAI,EAAc,mBAAmB,IAAI,GAAA,CAAgC,EACzE,IAAI,EAAc,WAAW,IAAI,GAAA,CAAwB,EACzD,IAAI,EAAc,cAAc,IAAI,GAAA,CAA2B,EAC/D,IAAI,EAAc,OAAO,IAAI,GAAA,CAAoB,EACjD,IAAI,EAAc,UAAU,IAAI,GAAA,CAAuB,CAAC;;AAY/D,SAAgB,GACd,GACyB;AACzB,SAAO,EAAS,SAAS,SAAS,eAAA,CAAgB,MAChD,EACG,IAAI,EAAc,UAAU,IAAI,GAAA,CAAuB,EACvD,IAAI,EAAc,UAAU,IAAI,GAAA,CAAuB,EACvD,IAAI,EAAc,WAAW,IAAI,GAAA,CAAwB,EACzD,IAAI,EAAc,WAAW,IAAI,GAAA,CAAwB,EACzD,IAAI,EAAc,SAAS,IAAI,GAAA,CAAsB,EACrD,IAAI,EAAc,UAAU,IAAI,GAAA,CAAuB,EACvD,IAAI,EAAc,UAAU,IAAI,GAAA,CAAuB,EACvD,IAAI,EAAc,SAAS,IAAI,GAAA,CAAsB,EACrD,IAAI,EAAc,UAAU,IAAI,GAAA,CAAuB,EACvD,IAAI,EAAc,UAAU,IAAI,GAAA,CAAuB,CAAC;;AAW/D,SAAgB,GACd,GACyB;AACzB,SAAO,EAAS,SAAS,cAAc,cAAA,CAAe,MACpD,EACK,IAAI,EAAc,WAAW,IAAI,GAAA,CAAwB,EACzD,IAAI,EAAc,OAAO,IAAI,GAAA,CAAoB,EACjD,IAAI,EAAc,eAAe,IAAI,GAAA,CAA4B,EACjE,IAAI,EAAc,wBAAwB,IAAI,GAAA,CAAqC,CAAC"}