simple-circuit-engine 0.0.10 → 0.0.12

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 (141) hide show
  1. package/AGENTS.md +13 -7
  2. package/CLAUDE.md +16 -103
  3. package/README.md +8 -5
  4. package/dist/core/index.d.ts +2 -23
  5. package/dist/core/index.js +75 -2000
  6. package/dist/core/setup.d.ts +17 -0
  7. package/dist/core/simulation/CircuitRunner.d.ts +176 -0
  8. package/dist/core/simulation/DirtyTracker.d.ts +87 -0
  9. package/dist/core/simulation/EventQueue.d.ts +68 -0
  10. package/dist/core/simulation/StateManager.d.ts +100 -0
  11. package/dist/core/simulation/behaviors/BehaviorRegistry.d.ts +70 -0
  12. package/dist/core/simulation/behaviors/ComponentBehavior.d.ts +53 -0
  13. package/dist/core/simulation/behaviors/basic/BatteryBehavior.d.ts +14 -0
  14. package/dist/core/simulation/behaviors/basic/ClockBehavior.d.ts +24 -0
  15. package/dist/core/simulation/behaviors/basic/DoubleThrowSwitchBehavior.d.ts +33 -0
  16. package/dist/core/simulation/behaviors/basic/LightbulbBehavior.d.ts +23 -0
  17. package/dist/core/simulation/behaviors/basic/RectangleLEDBehavior.d.ts +24 -0
  18. package/dist/core/simulation/behaviors/basic/RelayBehavior.d.ts +33 -0
  19. package/dist/core/simulation/behaviors/basic/SmallLEDBehavior.d.ts +24 -0
  20. package/dist/core/simulation/behaviors/basic/SwitchBehavior.d.ts +33 -0
  21. package/dist/core/simulation/behaviors/basic/index.d.ts +20 -0
  22. package/dist/core/simulation/behaviors/gates/InverterBehavior.d.ts +29 -0
  23. package/dist/core/simulation/behaviors/gates/Nand4GateBehavior.d.ts +18 -0
  24. package/dist/core/simulation/behaviors/gates/Nand8GateBehavior.d.ts +18 -0
  25. package/dist/core/simulation/behaviors/gates/NandGateBehavior.d.ts +28 -0
  26. package/dist/core/simulation/behaviors/gates/Nor4GateBehavior.d.ts +18 -0
  27. package/dist/core/simulation/behaviors/gates/Nor8GateBehavior.d.ts +18 -0
  28. package/dist/core/simulation/behaviors/gates/NorGateBehavior.d.ts +22 -0
  29. package/dist/core/simulation/behaviors/gates/Xor4GateBehavior.d.ts +18 -0
  30. package/dist/core/simulation/behaviors/gates/Xor8GateBehavior.d.ts +18 -0
  31. package/dist/core/simulation/behaviors/gates/XorGateBehavior.d.ts +22 -0
  32. package/dist/core/simulation/behaviors/gates/index.d.ts +34 -0
  33. package/dist/core/simulation/behaviors/index.d.ts +24 -0
  34. package/dist/core/simulation/behaviors/types.d.ts +101 -0
  35. package/dist/core/simulation/index.d.ts +18 -0
  36. package/dist/core/simulation/states/ComponentState.d.ts +57 -0
  37. package/dist/core/simulation/states/SimulationState.d.ts +46 -0
  38. package/dist/core/simulation/states/basic/BatteryState.d.ts +16 -0
  39. package/dist/core/simulation/states/basic/ClockState.d.ts +16 -0
  40. package/dist/core/simulation/states/basic/DoubleThrowSwitchState.d.ts +21 -0
  41. package/dist/core/simulation/states/basic/LightbulbState.d.ts +21 -0
  42. package/dist/core/simulation/states/basic/RectangleLEDState.d.ts +9 -0
  43. package/dist/core/simulation/states/basic/RelayState.d.ts +25 -0
  44. package/dist/core/simulation/states/basic/SmallLEDState.d.ts +21 -0
  45. package/dist/core/simulation/states/basic/SwitchState.d.ts +25 -0
  46. package/dist/core/simulation/states/gates/InverterState.d.ts +17 -0
  47. package/dist/core/simulation/states/gates/Nand4GateState.d.ts +10 -0
  48. package/dist/core/simulation/states/gates/Nand8GateState.d.ts +10 -0
  49. package/dist/core/simulation/states/gates/NandGateState.d.ts +16 -0
  50. package/dist/core/simulation/states/gates/Nor4GateState.d.ts +10 -0
  51. package/dist/core/simulation/states/gates/Nor8GateState.d.ts +10 -0
  52. package/dist/core/simulation/states/gates/NorGateState.d.ts +10 -0
  53. package/dist/core/simulation/states/gates/Xor4GateState.d.ts +10 -0
  54. package/dist/core/simulation/states/gates/Xor8GateState.d.ts +10 -0
  55. package/dist/core/simulation/states/gates/XorGateState.d.ts +10 -0
  56. package/dist/core/simulation/states/gates/index.d.ts +25 -0
  57. package/dist/core/simulation/states/index.d.ts +26 -0
  58. package/dist/core/simulation/states/types.d.ts +32 -0
  59. package/dist/core/simulation/types.d.ts +155 -0
  60. package/dist/core/topology/Circuit.d.ts +420 -0
  61. package/dist/core/topology/CircuitMetadata.d.ts +24 -0
  62. package/dist/core/topology/CircuitOptions.d.ts +14 -0
  63. package/dist/core/topology/Component.d.ts +152 -0
  64. package/dist/core/topology/ENode.d.ts +200 -0
  65. package/dist/core/topology/Wire.d.ts +130 -0
  66. package/dist/core/topology/delays.d.ts +52 -0
  67. package/dist/core/topology/index.d.ts +14 -0
  68. package/dist/core/topology/types.d.ts +175 -0
  69. package/dist/core/utils/CameraOptions.d.ts +83 -0
  70. package/dist/core/utils/MemoizeDecorator.d.ts +9 -0
  71. package/dist/core/utils/Position.d.ts +166 -0
  72. package/dist/core/utils/Position3D.d.ts +77 -0
  73. package/dist/core/utils/Rotation.d.ts +82 -0
  74. package/dist/core/utils/index.d.ts +24 -0
  75. package/dist/core/utils/types.d.ts +35 -0
  76. package/dist/core-Bjta9Y7_.js +2707 -0
  77. package/dist/core-Bjta9Y7_.js.map +1 -0
  78. package/dist/index.d.ts +13 -6286
  79. package/dist/index.js +120 -100
  80. package/dist/scene/CircuitEngine.d.ts +270 -0
  81. package/dist/scene/index.d.ts +1 -2
  82. package/dist/scene/index.js +44 -38
  83. package/dist/scene/setup.d.ts +18 -0
  84. package/dist/scene/shared/AbstractCircuitController.d.ts +211 -0
  85. package/dist/scene/shared/BranchingPointVisualFactory.d.ts +70 -0
  86. package/dist/scene/shared/EventEmitter.d.ts +92 -0
  87. package/dist/scene/shared/HoverManager.d.ts +151 -0
  88. package/dist/scene/shared/SelectionManager.d.ts +159 -0
  89. package/dist/scene/shared/WireVisualManager.d.ts +242 -0
  90. package/dist/scene/shared/components/ComponentVisualFactory.d.ts +438 -0
  91. package/dist/scene/shared/components/DefaultVisualFactory.d.ts +51 -0
  92. package/dist/scene/shared/components/FactoryRegistry.d.ts +84 -0
  93. package/dist/scene/shared/components/GroupedFactoryRegistry.d.ts +153 -0
  94. package/dist/scene/shared/components/basic/BatteryVisualFactory.d.ts +13 -0
  95. package/dist/scene/shared/components/basic/ClockVisualFactory.d.ts +79 -0
  96. package/dist/scene/shared/components/basic/DoubleThrowSwitchVisualFactory.d.ts +87 -0
  97. package/dist/scene/shared/components/basic/LabelVisualFactory.d.ts +148 -0
  98. package/dist/scene/shared/components/basic/LightbulbVisualFactory.d.ts +72 -0
  99. package/dist/scene/shared/components/basic/RectangleLEDVisualFactory.d.ts +86 -0
  100. package/dist/scene/shared/components/basic/RelayVisualFactory.d.ts +92 -0
  101. package/dist/scene/shared/components/basic/SmallLEDVisualFactory.d.ts +86 -0
  102. package/dist/scene/shared/components/basic/SwitchVisualFactory.d.ts +85 -0
  103. package/dist/scene/shared/components/gates/InverterVisualFactory.d.ts +104 -0
  104. package/dist/scene/shared/components/gates/Nand4GateVisualFactory.d.ts +27 -0
  105. package/dist/scene/shared/components/gates/Nand8GateVisualFactory.d.ts +27 -0
  106. package/dist/scene/shared/components/gates/NandGateVisualFactory.d.ts +101 -0
  107. package/dist/scene/shared/components/gates/Nor4GateVisualFactory.d.ts +27 -0
  108. package/dist/scene/shared/components/gates/Nor8GateVisualFactory.d.ts +27 -0
  109. package/dist/scene/shared/components/gates/NorGateVisualFactory.d.ts +101 -0
  110. package/dist/scene/shared/components/gates/Xor4GateVisualFactory.d.ts +29 -0
  111. package/dist/scene/shared/components/gates/Xor8GateVisualFactory.d.ts +29 -0
  112. package/dist/scene/shared/components/gates/XorGateVisualFactory.d.ts +103 -0
  113. package/dist/scene/shared/components/index.d.ts +29 -0
  114. package/dist/scene/shared/components/types.d.ts +43 -0
  115. package/dist/scene/shared/types.d.ts +476 -0
  116. package/dist/scene/shared/utils/CameraUtils.d.ts +23 -0
  117. package/dist/scene/shared/utils/ColorUtils.d.ts +26 -0
  118. package/dist/scene/shared/utils/ControlsUtils.d.ts +8 -0
  119. package/dist/scene/shared/utils/GeometryUtils.d.ts +261 -0
  120. package/dist/scene/shared/utils/LayerConstants.d.ts +40 -0
  121. package/dist/scene/shared/utils/LightingUtils.d.ts +31 -0
  122. package/dist/scene/shared/utils/MaterialUtils.d.ts +73 -0
  123. package/dist/scene/shared/utils/Options.d.ts +16 -0
  124. package/dist/scene/simulation/CircuitRunnerController.d.ts +227 -0
  125. package/dist/scene/static/CircuitController.d.ts +227 -0
  126. package/dist/scene/static/CircuitWriter.d.ts +146 -0
  127. package/dist/scene/static/PinTooltipWidget.d.ts +26 -0
  128. package/dist/scene/static/tools/BuildTool.d.ts +286 -0
  129. package/dist/scene/static/tools/ComponentPickerWidget.d.ts +82 -0
  130. package/dist/scene/static/tools/ConfigPanelWidget.d.ts +93 -0
  131. package/dist/scene/static/tools/MultiSelectTool.d.ts +265 -0
  132. package/dist/scene-CVsDdySt.js +7357 -0
  133. package/dist/scene-CVsDdySt.js.map +1 -0
  134. package/package.json +24 -25
  135. package/dist/CircuitRunner-DEb7JdNf.js +0 -1809
  136. package/dist/CircuitRunner-DEb7JdNf.js.map +0 -1
  137. package/dist/core/index.js.map +0 -1
  138. package/dist/index.js.map +0 -1
  139. package/dist/scene/index.js.map +0 -1
  140. package/dist/setup-AB1vFiis.js +0 -9918
  141. package/dist/setup-AB1vFiis.js.map +0 -1
@@ -0,0 +1,211 @@
1
+ import { MapControls } from 'three/addons/controls/MapControls.js';
2
+ import { UUID, Circuit } from '../../core/index.ts';
3
+ import { EventEmitter } from './EventEmitter';
4
+ import { IFactoryRegistry } from './components/ComponentVisualFactory';
5
+ import { ControllerEventMap, ControllerOptions, HoveredElement, HoverableType, SharedResources, VisualContext } from './types';
6
+ import { HoverManager } from './HoverManager';
7
+ import { Line2 } from 'three/examples/jsm/lines/Line2.js';
8
+ import { BranchingPointVisualFactory } from './BranchingPointVisualFactory';
9
+ import { WireVisualManager } from './WireVisualManager';
10
+ /**
11
+ * Abstract Circuit Controller
12
+ * @module scene/shared/AbstractCircuitController
13
+ *
14
+ * Base class for circuit visualization controllers.
15
+ * Provides common Three.js scene management, camera controls, and hover detection.
16
+ */
17
+ import * as THREE from 'three';
18
+ /**
19
+ * Abstract base class for circuit controllers.
20
+ *
21
+ * Provides common functionality for both static editing (CircuitController)
22
+ * and live simulation (CircuitRunnerController) modes:
23
+ * - Three.js scene, camera, and MapControls management
24
+ * - Container lifecycle (initialize/dispose)
25
+ * - Hover detection via HoverManager
26
+ * - Visual object tracking maps
27
+ *
28
+ * Subclasses must implement:
29
+ * - Circuit/CircuitRunner specific logic
30
+ * - Visual creation and update methods
31
+ * - Mode-specific features (editing tools or simulation interpolation)
32
+ */
33
+ export declare abstract class AbstractCircuitController extends EventEmitter<ControllerEventMap> {
34
+ protected _container: HTMLElement | null;
35
+ protected _scene: THREE.Scene | null;
36
+ protected _grid: THREE.GridHelper | null;
37
+ protected _camera: THREE.PerspectiveCamera | null;
38
+ protected _mapControls: MapControls | null;
39
+ protected _circuit: Circuit | null;
40
+ readonly wireVisualManager: WireVisualManager;
41
+ protected _initialized: boolean;
42
+ protected _options: ControllerOptions | null;
43
+ protected _active: boolean;
44
+ protected _disposed: boolean;
45
+ protected _gridHalfSize: number;
46
+ readonly factoryRegistry: IFactoryRegistry;
47
+ readonly branchingPointVisualFactory: BranchingPointVisualFactory;
48
+ protected _componentObject3Ds: Map<UUID, THREE.Object3D>;
49
+ protected _enodeObject3Ds: Map<UUID, THREE.Object3D>;
50
+ protected _wireObject3Ds: Map<UUID, Line2>;
51
+ protected _hoverManager: HoverManager | null;
52
+ protected _mouseMoveHandler: ((event: MouseEvent) => void) | null;
53
+ protected _mouseLeaveHandler: ((event: MouseEvent) => void) | null;
54
+ protected _mapControlsChangeHandler: (() => void) | null;
55
+ protected _sharedResources: SharedResources | null;
56
+ protected _useSharedResources: boolean;
57
+ /**
58
+ * Create a new circuit controller
59
+ *
60
+ * @param factoryRegistry - Component visual factory registry
61
+ * @param sharedResources - Optional shared resources for facade pattern (CircuitEngine)
62
+ * @throws {TypeError} If factoryRegistry is null/undefined
63
+ */
64
+ constructor(factoryRegistry: IFactoryRegistry, sharedResources?: SharedResources);
65
+ get componentObject3Ds(): Map<UUID, THREE.Object3D>;
66
+ get enodeObject3Ds(): Map<UUID, THREE.Object3D>;
67
+ get wireObject3Ds(): Map<UUID, Line2>;
68
+ get visualContext(): VisualContext;
69
+ protected get grid(): THREE.GridHelper | null;
70
+ protected set grid(grid: THREE.GridHelper);
71
+ /**
72
+ * Initialize the controller with a DOM container.
73
+ * Creates scene, camera, lights, MapControls, and HoverManager.
74
+ *
75
+ * When sharedResources were provided in constructor, uses those instead
76
+ * of creating new resources. This enables the CircuitEngine facade pattern.
77
+ *
78
+ * @param container - HTMLElement to attach scene to
79
+ * @param options - Optional configuration
80
+ * @throws {TypeError} If container is not a valid HTMLElement
81
+ * @throws {Error} If already initialized
82
+ */
83
+ initialize(container: HTMLElement, options?: ControllerOptions): void;
84
+ /**
85
+ * Hook for subclasses to perform additional initialization.
86
+ * Called after base initialization but before emitting 'ready'.
87
+ *
88
+ * @param options - Controller options passed to initialize()
89
+ */
90
+ protected abstract onInitialize(options?: ControllerOptions): void;
91
+ /**
92
+ * Emit the ready event with controller-specific data.
93
+ */
94
+ protected abstract emitReady(): void;
95
+ /**
96
+ * Emit an error event.
97
+ */
98
+ protected emitError(error: Error): void;
99
+ /**
100
+ * Check that controller is initialized and not disposed.
101
+ * @throws {Error} If not initialized or already disposed
102
+ */
103
+ protected _checkInitialized(): void;
104
+ /**
105
+ * Clean up all WebGL resources.
106
+ * Disposes geometries, materials, controls, and clears event listeners.
107
+ *
108
+ * When using shared resources, does not dispose scene, camera, controls, or hover manager
109
+ * as those are owned by the CircuitEngine facade.
110
+ */
111
+ dispose(): void;
112
+ /**
113
+ * Hook for subclasses to perform cleanup before base dispose.
114
+ */
115
+ protected abstract onDispose(): void;
116
+ /**
117
+ * Remove all visual objects from scene.
118
+ * Subclasses must implement to handle their specific wire types.
119
+ */
120
+ protected abstract _removeAllVisuals(): void;
121
+ setActive(active: boolean): void;
122
+ protected abstract onSetActive(active: boolean): void;
123
+ /**
124
+ * Set the current circuit to visualize or null to clear the scene
125
+ * @param circuit
126
+ */
127
+ abstract setCircuit(circuit: Circuit | null): void;
128
+ /**
129
+ * Get the current circuit being visualized
130
+ */
131
+ getCircuit(): Circuit | null;
132
+ protected abstract onSetCircuit(): void;
133
+ /**
134
+ * Loads a new circuit to visualize or null for clearing the scene
135
+ * @param circuit
136
+ */
137
+ protected _setCircuit(circuit: Circuit | null): void;
138
+ /**
139
+ * get the object3D (Group for components and enodes, Line2 for wires) by hoverable type and id
140
+ * @param type
141
+ * @param id
142
+ */
143
+ getObject3D(type: HoverableType, id: UUID): THREE.Object3D | undefined;
144
+ /**
145
+ * Get the MapControls instance for direct manipulation.
146
+ */
147
+ getControls(): MapControls | null;
148
+ /**
149
+ * Get the current cursor position on the ground plane (y=0) in world coordinates
150
+ * The position is clamped within the circuit grid boundaries but not snapped to grid
151
+ * @param bound - Whether to constrain position within grid boundaries, default false
152
+ */
153
+ cursorGroundPlanePosition(bound?: boolean): THREE.Vector3;
154
+ /**
155
+ * Initialize HoverManager for hover detection
156
+ *
157
+ * @private
158
+ */
159
+ private _initializeHoverManager;
160
+ protected _setupMouseCallbacks(): void;
161
+ /**
162
+ * Get the currently hovered element.
163
+ */
164
+ getHoveredElement(): HoveredElement | null;
165
+ /**
166
+ * Enable or disable hover detection.
167
+ */
168
+ setHoverEnabled(enabled: boolean): void;
169
+ /**
170
+ * Check if hover detection is enabled.
171
+ */
172
+ isHoverEnabled(): boolean;
173
+ /**
174
+ * Get the Three.js scene for rendering.
175
+ * @throws {Error} If not initialized
176
+ */
177
+ getScene(): THREE.Scene;
178
+ /**
179
+ * Get the Three.js camera for rendering.
180
+ * @throws {Error} If not initialized
181
+ */
182
+ getCamera(): THREE.PerspectiveCamera;
183
+ /**
184
+ * Get the HTML container element.
185
+ * @throws {Error} If not initialized
186
+ */
187
+ getContainer(): HTMLElement;
188
+ /**
189
+ * Check if controller is initialized.
190
+ */
191
+ get isInitialized(): boolean;
192
+ /**
193
+ * Check if controller is disposed.
194
+ */
195
+ get isDisposed(): boolean;
196
+ /**
197
+ * event handler when the container size changes
198
+ * Can override the container boundingClientRect size by providing width and height
199
+ * - Update camera projection matrix
200
+ * - Update viewport size for Line2 material resolution
201
+ * Should be called when the container size changes (e.g., window resize)
202
+ *
203
+ * @param width - New width (optional, uses container size if not provided)
204
+ * @param height - New height (optional, uses container size if not provided)
205
+ */
206
+ onContainerResize(width?: number, height?: number): void;
207
+ /**
208
+ * Hook for subclasses to handle container resize.
209
+ */
210
+ protected onResize(_width: number, _height: number): void;
211
+ }
@@ -0,0 +1,70 @@
1
+ import { ENodeSourceType, ENode } from '../../core/index.ts';
2
+ /**
3
+ * Branching Point Visual Factory
4
+ * @module scene/shared/components/BranchingPointVisualFactory
5
+ *
6
+ * Creates cone-shaped visuals for branching point enodes with:
7
+ * - SourceType-based color coding (white/red/blue)
8
+ * - Hover/selection feedback via brightness shift
9
+ * - Hitbox for raycasting on ENODE layer
10
+ */
11
+ import * as THREE from 'three';
12
+ /**
13
+ * Factory for creating branching point visuals.
14
+ *
15
+ * Branching points are rendered as cones with colors indicating their sourceType:
16
+ * - White (0xffffff): No source (null)
17
+ * - Red (0xff0000): Voltage source
18
+ * - Blue (0x0000ff): Current source
19
+ *
20
+ * Hover and selection states use brightness shift of the base color.
21
+ */
22
+ export declare class BranchingPointVisualFactory {
23
+ private static readonly COLORS;
24
+ private static readonly DEFAULT_HOVER_COLOR;
25
+ private static readonly HOVER_EMISSIVE;
26
+ private static readonly SELECTED_EMISSIVE;
27
+ private static readonly CONE_RADIUS;
28
+ private static readonly CONE_HEIGHT;
29
+ private static readonly CONE_SEGMENTS;
30
+ private static readonly HITBOX_SQUARE;
31
+ /**
32
+ * Create visual representation for a branching point.
33
+ * @param enode - The branching point ENode
34
+ * @returns THREE.Group containing cone mesh and hitbox
35
+ */
36
+ createVisual(enode: ENode): THREE.Group;
37
+ /**
38
+ * Update branching point object3D's visual to reflect sourceType change.
39
+ * @param object3D - The branching point basis object3D (group)
40
+ * @param sourceType - New source type
41
+ */
42
+ updateSourceType(object3D: THREE.Object3D, sourceType: ENodeSourceType | null): void;
43
+ protected colorForElectricalState(state: 'current' | 'voltage' | 'vc' | 'idle'): number;
44
+ /**
45
+ * Apply hover object3D feedback.
46
+ * @param object3D - The branching point basis object3D (group)
47
+ */
48
+ applyHover(object3D: THREE.Object3D): void;
49
+ /**
50
+ * Remove hover object3D feedback.
51
+ * @param object3D - The branching point basis object3D (group)
52
+ */
53
+ removeHover(object3D: THREE.Object3D): void;
54
+ /**
55
+ * Apply selection object3D feedback.
56
+ * @param object3D - The branching point basis object3D (group)
57
+ */
58
+ applySelection(object3D: THREE.Object3D): void;
59
+ /**
60
+ * Remove selection object3D feedback.
61
+ * @param object3D - The branching point basis object3D (group)
62
+ */
63
+ removeSelection(object3D: THREE.Object3D): void;
64
+ /**
65
+ * Get the base color for a sourceType.
66
+ * @param sourceType - Source type (null, 'Voltage', or 'Current')
67
+ * @returns Color hex value
68
+ */
69
+ private getColorForSourceType;
70
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Type-safe Event Emitter
3
+ * @module scene/shared/EventEmitter
4
+ *
5
+ * Provides type-safe event handling without external dependencies.
6
+ * Generic EventMap type ensures compile-time type safety for event payloads.
7
+ */
8
+ /**
9
+ * Generic event emitter with type-safe event emission and subscription
10
+ *
11
+ * @template EventMap - Map of event names to their payload types
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * interface MyEvents {
16
+ * click: { x: number; y: number };
17
+ * error: { message: string };
18
+ * }
19
+ *
20
+ * const emitter = new EventEmitter<MyEvents>();
21
+ * emitter.on('click', ({ x, y }) => console.log(x, y)); // Type-safe!
22
+ * emitter.emit('click', { x: 10, y: 20 });
23
+ * ```
24
+ */
25
+ export declare class EventEmitter<EventMap extends Record<string, any>> {
26
+ private listeners;
27
+ /**
28
+ * Register an event listener
29
+ *
30
+ * @param event - Event name to listen for
31
+ * @param callback - Function to call when event occurs
32
+ *
33
+ * @remarks
34
+ * Same callback can be registered multiple times (will be called multiple times).
35
+ * Callbacks are wrapped in try-catch to prevent errors from breaking emission.
36
+ */
37
+ on<K extends keyof EventMap>(event: K, callback: (payload: EventMap[K]) => void): void;
38
+ /**
39
+ * Unregister an event listener
40
+ *
41
+ * @param event - Event name
42
+ * @param callback - Function to remove (must be same reference used in on())
43
+ *
44
+ * @remarks
45
+ * If callback was registered multiple times, only removes one registration.
46
+ */
47
+ off<K extends keyof EventMap>(event: K, callback: (payload: EventMap[K]) => void): void;
48
+ /**
49
+ * Emit an event to all registered listeners
50
+ * This method is public so that tools may emit events directly on behalf of the EventEmitter owner (controller).
51
+ *
52
+ * @param event - Event name to emit
53
+ * @param payload - Event-specific payload
54
+ *
55
+ * @remarks
56
+ * Listeners are called in registration order.
57
+ * Errors in callbacks are caught and logged but do not stop other callbacks.
58
+ */
59
+ emit<K extends keyof EventMap>(event: K, payload: EventMap[K]): void;
60
+ /**
61
+ * Remove all listeners for a specific event or all events
62
+ *
63
+ * @param event - Optional event name. If omitted, removes all listeners for all events.
64
+ */
65
+ removeAllListeners<K extends keyof EventMap>(event?: K): void;
66
+ /**
67
+ * Get count of listeners for a specific event
68
+ *
69
+ * @param event - Event name
70
+ * @returns Number of registered listeners
71
+ */
72
+ listenerCount<K extends keyof EventMap>(event: K): number;
73
+ /**
74
+ * Register a listener that receives all events
75
+ *
76
+ * @param callback - Function called with event name and payload for every event
77
+ * @returns Cleanup function to remove the listener
78
+ *
79
+ * @remarks
80
+ * Useful for event forwarding/delegation patterns where you want to
81
+ * re-emit all events from one emitter to another.
82
+ *
83
+ * @example
84
+ * ```typescript
85
+ * const cleanup = source.onAny((event, payload) => {
86
+ * target.emit(event, payload);
87
+ * });
88
+ * // Later: cleanup();
89
+ * ```
90
+ */
91
+ onAny(callback: <K extends keyof EventMap>(event: K, payload: EventMap[K]) => void): () => void;
92
+ }
@@ -0,0 +1,151 @@
1
+ import { HoveredElement } from './types';
2
+ /**
3
+ * HoverManager Implementation
4
+ * @module scene/shared/HoverManager
5
+ *
6
+ * Handles priority-based hover detection using Three.js Raycaster and Layers.
7
+ * Implements priority: enode > component > wire
8
+ */
9
+ import * as THREE from 'three';
10
+ /**
11
+ * Callback type for hover state changes
12
+ */
13
+ export type HoverCallback = (element: HoveredElement | null, previousElement: HoveredElement | null) => void;
14
+ /**
15
+ * HoverManager Class
16
+ *
17
+ * Manages hover detection using Three.js Raycaster against hitbox layers.
18
+ * Implements priority-based detection: enode > component > wire.
19
+ */
20
+ export declare class HoverManager {
21
+ private scene;
22
+ private camera;
23
+ private raycaster;
24
+ private readonly groundPlane;
25
+ private readonly groundPlanePosition;
26
+ private currentlyHovered;
27
+ private callbacks;
28
+ private enabled;
29
+ private initialized;
30
+ private lastMouseX;
31
+ private lastMouseY;
32
+ private lastUpdateTime;
33
+ private throttleMs;
34
+ /**
35
+ * Create a new HoverManager
36
+ *
37
+ * @param scene - Three.js scene containing hitbox meshes
38
+ * @param camera - Three.js camera for raycasting
39
+ */
40
+ constructor(scene: THREE.Scene, camera: THREE.Camera);
41
+ /**
42
+ * Get the last computed ground plane position under the mouse
43
+ *
44
+ * @returns THREE.Vector3 position on ground plane
45
+ */
46
+ getGroundPlanePosition(): THREE.Vector3;
47
+ /**
48
+ * Update hover state based on normalized mouse coordinates
49
+ * Also update the VERY IMPORTANT ground plane position used for CircuitManager cursorGroundPlanePosition
50
+ *
51
+ * Performs priority-based raycasting against hitbox layers.
52
+ * If hover state changes, triggers onHoverChange callback.
53
+ *
54
+ * @param normalizedX - Mouse X in normalized HTML container coordinates [-1, 1]
55
+ * @param normalizedY - Mouse Y in normalized HTML container coordinates [-1, 1]
56
+ */
57
+ updateFromMouse(normalizedX: number, normalizedY: number): void;
58
+ /**
59
+ * Force update hover state at current mouse position
60
+ *
61
+ * Useful after camera changes or scene updates to refresh hover state
62
+ * without requiring a new mouse event.
63
+ */
64
+ refresh(): void;
65
+ /**
66
+ * Clear current hover state
67
+ *
68
+ * Triggers unhover callback if an element was hovered.
69
+ * Call this when mouse leaves the container.
70
+ */
71
+ clear(): void;
72
+ /**
73
+ * Get the currently hovered element
74
+ *
75
+ * @returns HoveredElement if something is hovered, null otherwise
76
+ */
77
+ getHoveredElement(): HoveredElement | null;
78
+ /**
79
+ * Register callback for hover state changes
80
+ *
81
+ * Callback is invoked when:
82
+ * - Hover starts (element becomes non-null)
83
+ * - Hover changes to different element
84
+ * - Hover ends (element becomes null)
85
+ *
86
+ * @param callback - Function to call on hover state change
87
+ */
88
+ onHoverChange(callback: HoverCallback): void;
89
+ /**
90
+ * Remove previously registered hover change callback
91
+ *
92
+ * @param callback - Same function reference passed to onHoverChange
93
+ */
94
+ offHoverChange(callback: HoverCallback): void;
95
+ /**
96
+ * Enable or disable hover detection
97
+ *
98
+ * When disabled, updateFromMouse() becomes a no-op.
99
+ * Useful for temporarily disabling hover during drag operations.
100
+ *
101
+ * @param enabled - Whether to enable hover detection
102
+ */
103
+ setEnabled(enabled: boolean): void;
104
+ /**
105
+ * Check if hover detection is enabled
106
+ *
107
+ * @returns true if enabled
108
+ */
109
+ isEnabled(): boolean;
110
+ /**
111
+ * Set initialization state (to prevent double init)
112
+ * @param initialized
113
+ */
114
+ setInitialized(initialized: boolean): void;
115
+ /**
116
+ * Check if HoverManager is initialized (to prevent double init)
117
+ */
118
+ isInitialized(): boolean;
119
+ /**
120
+ * Clean up resources
121
+ *
122
+ * Removes all callbacks and clears state.
123
+ * Call when disposing the scene controllerType.
124
+ */
125
+ dispose(): void;
126
+ /**
127
+ * Perform raycasting on a specific layer and extract hit information
128
+ *
129
+ * @param layer - Hitbox layer number to raycast against
130
+ * @param hoverableType - Type for HoveredElement
131
+ * @param objectType - CircuitSceneObjectType for event payload
132
+ * @returns HoveredElement if hit found, null otherwise
133
+ */
134
+ private _raycastLayer;
135
+ /**
136
+ * Update hover state and trigger callbacks if changed
137
+ *
138
+ * Compares new hit with currentlyHovered and only triggers callbacks on change.
139
+ *
140
+ * @param newHit - New hover element or null
141
+ */
142
+ private _updateHoverState;
143
+ /**
144
+ * Compare two hover elements for equality
145
+ *
146
+ * @param a - First element
147
+ * @param b - Second element
148
+ * @returns true if both represent the same hover state
149
+ */
150
+ private _isSameHover;
151
+ }
@@ -0,0 +1,159 @@
1
+ import { UUID } from '../../core/index.ts';
2
+ import { HoverableType, SelectionData } from './types';
3
+ /**
4
+ * Callback invoked when selection changes
5
+ *
6
+ * @param newSelection - The new selection, or null if deselected
7
+ * @param previousSelection - The previous selection, or null if none was selected
8
+ */
9
+ export type SelectionCallback = (newSelection: SelectionData | null, previousSelection: SelectionData | null) => void;
10
+ /**
11
+ * Manages selected components, enodes or wires for the circuit scene.
12
+ * Multi-selection is planned (notably with the use of a flexible SelectionData) but still to implement.
13
+ *
14
+ * Key current responsibilities:
15
+ * - Track -currently single object- selection state
16
+ * - Notify listeners of selection changes (observer pattern)
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const selectionManager = new SelectionManager();
21
+ *
22
+ *
23
+ * // Select a single object
24
+ * selectionManager.selectOne('component', 'component-uuid-1234');
25
+ * selectionManager.selectOne('enode', 'enode-uuid-1234');
26
+ * selectionManager.selectOne('wire', 'wire-uuid-1234');
27
+ *
28
+ * // Deselect
29
+ * selectionManager.deselect();
30
+ * ```
31
+ */
32
+ export declare class SelectionManager {
33
+ /** Current selection */
34
+ private selection;
35
+ /** Timestamp when selection occurred (for double-click detection) */
36
+ private selectedAt;
37
+ /** Registered selection change callbacks */
38
+ private callbacks;
39
+ /**
40
+ * Create a new SelectionManager
41
+ */
42
+ constructor();
43
+ /**
44
+ * Get the current selection
45
+ *
46
+ * @returns The SelectionData, or null if nothing is selected
47
+ */
48
+ getSelection(): SelectionData | null;
49
+ /**
50
+ * Get the timestamp when selection occurred
51
+ *
52
+ * @returns Timestamp in milliseconds, or null if nothing is selected
53
+ */
54
+ getSelectedAt(): number | null;
55
+ /**
56
+ * Check if a specific object is selected
57
+ *
58
+ * @param type - The type of hoverable object
59
+ * @param objectId - The object ID to check
60
+ * @returns true if the object is currently selected
61
+ */
62
+ isSelected(type: HoverableType, objectId: UUID): boolean;
63
+ /**
64
+ * Check if anything is selected
65
+ *
66
+ * @returns true if one object is currently selected or several objects are currently selected
67
+ */
68
+ hasSelection(): boolean;
69
+ private _selectionsEqual;
70
+ /**
71
+ * Select one object
72
+ *
73
+ * If another object was previously selected or a multi selection existed, they will be deselected first.
74
+ *
75
+ * @param type - The type of hoverable object to select
76
+ * @param objectId - The object ID to select
77
+ * @param userData - Optional userData of the 3D object being selected
78
+ */
79
+ selectOne(type: HoverableType, objectId: UUID, userData?: object | undefined): void;
80
+ /**
81
+ * Deselect the current selection
82
+ */
83
+ deselect(): void;
84
+ /**
85
+ * Select multiple elements at once, replacing any existing selection
86
+ *
87
+ * Creates a MultiSelectionData with the provided element maps.
88
+ * Empty maps are allowed; passing all empty maps clears the selection.
89
+ *
90
+ * @param components - Map of component IDs to optional metadata
91
+ * @param enodes - Map of enode IDs to optional metadata
92
+ * @param wires - Map of wire IDs to optional metadata
93
+ */
94
+ selectMultiple(components?: Map<UUID, string | null>, enodes?: Map<UUID, string | null>, wires?: Map<UUID, string | null>): void;
95
+ /**
96
+ * Add a single element to the current selection
97
+ *
98
+ * If current selection is null, creates a mono selection.
99
+ * If current selection is mono, converts to multi and adds element.
100
+ * If current selection is multi, adds element to appropriate map.
101
+ *
102
+ * No-op if element is already selected.
103
+ *
104
+ * @param type - Type of element to add
105
+ * @param objectId - UUID of element to add
106
+ * @param userData - Optional metadata for the element
107
+ */
108
+ addToSelection(type: HoverableType, objectId: UUID, userData?: object): void;
109
+ /**
110
+ * Remove a single element from the current selection
111
+ *
112
+ * If element is in a mono selection, clears the selection.
113
+ * If element is in a multi selection, removes from appropriate map.
114
+ * If multi selection becomes single element, converts to mono.
115
+ *
116
+ * No-op if element is not selected.
117
+ *
118
+ * @param type - Type of element to remove
119
+ * @param objectId - UUID of element to remove
120
+ */
121
+ removeFromSelection(type: HoverableType, objectId: UUID): void;
122
+ /**
123
+ * Get the total count of selected elements across all types
124
+ *
125
+ * @returns Number of selected elements (0 if no selection)
126
+ */
127
+ getSelectionCount(): number;
128
+ /**
129
+ * Get all selected element IDs grouped by type
130
+ *
131
+ * Returns empty arrays if no selection.
132
+ * For mono selection, returns single-element array in appropriate category.
133
+ *
134
+ * @returns Object with arrays of selected IDs by type
135
+ */
136
+ getSelectedIds(): {
137
+ components: UUID[];
138
+ enodes: UUID[];
139
+ wires: UUID[];
140
+ };
141
+ /**
142
+ * Register a callback for selection changes
143
+ *
144
+ * @param callback - Function to call when selection changes
145
+ * @returns Unsubscribe function
146
+ */
147
+ onSelectionChange(callback: SelectionCallback): () => void;
148
+ /**
149
+ * Notify all registered callbacks of selection change
150
+ *
151
+ * @param newSelection - New selection
152
+ * @param previousSelection - Previous selection
153
+ */
154
+ private notifyCallbacks;
155
+ /**
156
+ * Clean up resources
157
+ */
158
+ dispose(): void;
159
+ }