simple-circuit-engine 0.0.13 → 0.0.14

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 (77) hide show
  1. package/AGENTS.md +7 -5
  2. package/README.md +8 -8
  3. package/dist/core/index.js +110 -85
  4. package/dist/core/setup.d.ts +10 -0
  5. package/dist/core/simulation/behaviors/index.d.ts +10 -0
  6. package/dist/core/simulation/behaviors/interface/EightInputBehavior.d.ts +9 -0
  7. package/dist/core/simulation/behaviors/interface/EightLightBehavior.d.ts +9 -0
  8. package/dist/core/simulation/behaviors/interface/FourInputBehavior.d.ts +9 -0
  9. package/dist/core/simulation/behaviors/interface/FourLightBehavior.d.ts +9 -0
  10. package/dist/core/simulation/behaviors/interface/InputBehaviorMixin.d.ts +37 -0
  11. package/dist/core/simulation/behaviors/interface/LightBehaviorMixin.d.ts +60 -0
  12. package/dist/core/simulation/behaviors/interface/OneInputBehavior.d.ts +9 -0
  13. package/dist/core/simulation/behaviors/interface/OneLightBehavior.d.ts +9 -0
  14. package/dist/core/simulation/behaviors/interface/TwoInputBehavior.d.ts +9 -0
  15. package/dist/core/simulation/behaviors/interface/TwoLightBehavior.d.ts +9 -0
  16. package/dist/core/simulation/behaviors/interface/index.d.ts +0 -0
  17. package/dist/core/simulation/states/index.d.ts +10 -0
  18. package/dist/core/simulation/states/interface/EightInputState.d.ts +6 -0
  19. package/dist/core/simulation/states/interface/EightLightState.d.ts +6 -0
  20. package/dist/core/simulation/states/interface/FourInputState.d.ts +6 -0
  21. package/dist/core/simulation/states/interface/FourLightState.d.ts +6 -0
  22. package/dist/core/simulation/states/interface/InputState.d.ts +45 -0
  23. package/dist/core/simulation/states/interface/LightState.d.ts +54 -0
  24. package/dist/core/simulation/states/interface/OneInputState.d.ts +6 -0
  25. package/dist/core/simulation/states/interface/OneLightState.d.ts +6 -0
  26. package/dist/core/simulation/states/interface/TwoInputState.d.ts +6 -0
  27. package/dist/core/simulation/states/interface/TwoLightState.d.ts +6 -0
  28. package/dist/core/simulation/types.d.ts +9 -1
  29. package/dist/core/topology/Component.d.ts +4 -0
  30. package/dist/core/topology/ENode.d.ts +7 -2
  31. package/dist/core/topology/index.d.ts +2 -0
  32. package/dist/core/topology/networkTraversal.d.ts +50 -0
  33. package/dist/core/topology/types.d.ts +13 -2
  34. package/dist/{core-b6Q8w2sn.js → core-HH6iRWtB.js} +1850 -739
  35. package/dist/core-HH6iRWtB.js.map +1 -0
  36. package/dist/i18n/index.d.ts +276 -0
  37. package/dist/i18n/locales/en.json.d.ts +98 -0
  38. package/dist/i18n/locales/fr.json.d.ts +98 -0
  39. package/dist/index.js +172 -135
  40. package/dist/scene/CircuitEngine.d.ts +36 -4
  41. package/dist/scene/index.d.ts +6 -2
  42. package/dist/scene/index.js +62 -50
  43. package/dist/scene/setup.d.ts +10 -0
  44. package/dist/scene/shared/AbstractCircuitController.d.ts +5 -3
  45. package/dist/scene/shared/HoverManager.d.ts +15 -0
  46. package/dist/scene/shared/components/index.d.ts +10 -0
  47. package/dist/scene/shared/components/interface/EightInputVisualFactory.d.ts +15 -0
  48. package/dist/scene/shared/components/interface/EightLightVisualFactory.d.ts +15 -0
  49. package/dist/scene/shared/components/interface/FourInputVisualFactory.d.ts +15 -0
  50. package/dist/scene/shared/components/interface/FourLightVisualFactory.d.ts +15 -0
  51. package/dist/scene/shared/components/interface/InputVisualFactoryBase.d.ts +50 -0
  52. package/dist/scene/shared/components/interface/LightVisualFactoryBase.d.ts +46 -0
  53. package/dist/scene/shared/components/interface/OneInputVisualFactory.d.ts +15 -0
  54. package/dist/scene/shared/components/interface/OneLightVisualFactory.d.ts +15 -0
  55. package/dist/scene/shared/components/interface/TwoInputVisualFactory.d.ts +15 -0
  56. package/dist/scene/shared/components/interface/TwoLightVisualFactory.d.ts +15 -0
  57. package/dist/scene/shared/types.d.ts +34 -2
  58. package/dist/scene/shared/utils/ControlsUtils.d.ts +1 -1
  59. package/dist/scene/shared/utils/GeometryUtils.d.ts +35 -0
  60. package/dist/scene/static/CircuitController.d.ts +12 -1
  61. package/dist/scene/static/tools/BuildTool.d.ts +77 -0
  62. package/dist/scene/static/tools/MultiWiringPlacement.d.ts +40 -0
  63. package/dist/scene/widgets/HelpWidget.d.ts +13 -0
  64. package/dist/scene/widgets/ModeWidget.d.ts +16 -0
  65. package/dist/scene/widgets/MultiWiringWidget.d.ts +21 -0
  66. package/dist/scene/{static → widgets}/PinTooltipWidget.d.ts +2 -1
  67. package/dist/scene/widgets/SimulationPlayerWidget.d.ts +62 -0
  68. package/dist/scene/widgets/ToolsWidget.d.ts +18 -0
  69. package/dist/scene/widgets/WidgetsManager.d.ts +65 -0
  70. package/dist/scene/widgets/assets/icons.d.ts +20 -0
  71. package/dist/scene/widgets/index.d.ts +9 -0
  72. package/dist/scene/widgets/styles.d.ts +33 -0
  73. package/dist/{scene-D4QcWeiq.js → scene-C1xhdw_B.js} +3311 -1478
  74. package/dist/scene-C1xhdw_B.js.map +1 -0
  75. package/package.json +2 -2
  76. package/dist/core-b6Q8w2sn.js.map +0 -1
  77. package/dist/scene-D4QcWeiq.js.map +0 -1
@@ -0,0 +1,15 @@
1
+ import { Component } from '../../../../core/index.ts';
2
+ import { VisualContext } from '../../types';
3
+ import { LightVisualFactoryBase } from './LightVisualFactoryBase';
4
+ import * as THREE from 'three';
5
+ /** Visual factory for the EightLight component (eight-light input mirror). */
6
+ export declare class EightLightVisualFactory extends LightVisualFactoryBase {
7
+ protected readonly bitCount = 8;
8
+ protected readonly holePositions: Array<{
9
+ x: number;
10
+ y: number;
11
+ }>;
12
+ protected readonly envelopeHeight = 8.4;
13
+ constructor();
14
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
15
+ }
@@ -0,0 +1,15 @@
1
+ import { Component } from '../../../../core/index.ts';
2
+ import { VisualContext } from '../../types';
3
+ import { InputVisualFactoryBase } from './InputVisualFactoryBase';
4
+ import * as THREE from 'three';
5
+ /** Visual factory for the FourInput component (four-switch user input). */
6
+ export declare class FourInputVisualFactory extends InputVisualFactoryBase {
7
+ protected readonly bitCount = 4;
8
+ protected readonly holePositions: Array<{
9
+ x: number;
10
+ y: number;
11
+ }>;
12
+ protected readonly envelopeHeight = 4.4;
13
+ constructor();
14
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
15
+ }
@@ -0,0 +1,15 @@
1
+ import { Component } from '../../../../core/index.ts';
2
+ import { VisualContext } from '../../types';
3
+ import { LightVisualFactoryBase } from './LightVisualFactoryBase';
4
+ import * as THREE from 'three';
5
+ /** Visual factory for the FourLight component (four-light input mirror). */
6
+ export declare class FourLightVisualFactory extends LightVisualFactoryBase {
7
+ protected readonly bitCount = 4;
8
+ protected readonly holePositions: Array<{
9
+ x: number;
10
+ y: number;
11
+ }>;
12
+ protected readonly envelopeHeight = 4.4;
13
+ constructor();
14
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
15
+ }
@@ -0,0 +1,50 @@
1
+ import { ComponentVisualFactoryBase } from '../ComponentVisualFactory';
2
+ import { Component, ComponentState } from '../../../../core/index.ts';
3
+ import { ConfigFormDefinition, VisualContext } from '../../types';
4
+ import * as THREE from 'three';
5
+ /**
6
+ * Shared visual factory for the OneInput, TwoInput, FourInput and EightInput
7
+ * components.
8
+ *
9
+ * Builds an `IceBoxGeometry` envelope with `bitCount` rectangular holes laid
10
+ * out along the z-axis. Each hole houses a switch button mesh that animates:
11
+ *
12
+ * - **Position (y)**: snaps/slides between `SWITCH_Y_OFF` (logic 0) and
13
+ * `SWITCH_Y_ON` (logic 1) when the switch is moving.
14
+ * - **Color**: dark-gray when no simulation context, blue (low) ↔ red (high)
15
+ * during simulation.
16
+ *
17
+ * Animations follow the per-switch parameters in {@link InputState}: while a
18
+ * switch is mid-transition, only its mesh is animated; settled switches stay
19
+ * snapped to the colour/position dictated by the current driving state.
20
+ */
21
+ export declare abstract class InputVisualFactoryBase extends ComponentVisualFactoryBase {
22
+ /** Number of switches/outputs (1, 2, 4 or 8). */
23
+ protected abstract readonly bitCount: number;
24
+ /** Number of switches/outputs (1, 2, 4 or 8). */
25
+ protected abstract readonly holePositions: Array<{
26
+ x: number;
27
+ y: number;
28
+ }>;
29
+ /** Total z-axis length of the envelope. */
30
+ protected abstract readonly envelopeHeight: number;
31
+ private _envelopeGeom;
32
+ private _switchGeom;
33
+ protected createVisualBase(component: Component, context: VisualContext): THREE.Object3D;
34
+ protected createPinsVisual(component: Component, context: VisualContext, group: THREE.Group): void;
35
+ protected getDefaultInitialState(): string;
36
+ protected getInitialStateOptions(): {
37
+ [key: string]: string;
38
+ };
39
+ getConfigFormDefinition(): ConfigFormDefinition | null;
40
+ mapCoreConfigToForm(config: Map<string, string>): Map<string, any>;
41
+ mapFormToCoreConfig(formData: Map<string, any>): Map<string, string>;
42
+ updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void;
43
+ updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void;
44
+ private _snapSwitch;
45
+ private _setSwitchColor;
46
+ private _ensureClonedMaterial;
47
+ private _restoreSharedMaterial;
48
+ private _cleanupMixer;
49
+ private _findSwitches;
50
+ }
@@ -0,0 +1,46 @@
1
+ import { ComponentVisualFactoryBase } from '../ComponentVisualFactory';
2
+ import { Component, ComponentState } from '../../../../core/index.ts';
3
+ import { ConfigFormDefinition, VisualContext } from '../../types';
4
+ import * as THREE from 'three';
5
+ /**
6
+ * Shared visual factory for the OneLight, TwoLight, FourLight and EightLight
7
+ * components.
8
+ *
9
+ * Builds a `RoundIceBoxGeometry` envelope with `bitCount` circular holes laid
10
+ * out along the z-axis. Each hole houses a glass cylinder + half-sphere
11
+ * representing a light:
12
+ *
13
+ * - **Stable state**: each light glows (yellow emissive) or is dim (low
14
+ * opacity, no emissive) according to the corresponding hex bit.
15
+ * - **Moving state**: settled lights snap to the driving (prevState) bit;
16
+ * pending lights smoothly animate emissive intensity / opacity / color over
17
+ * their per-light span.
18
+ * - **Indeterminate state**: every light is restored to the neutral glass.
19
+ */
20
+ export declare abstract class LightVisualFactoryBase extends ComponentVisualFactoryBase {
21
+ /** Number of lights / outputs (1, 2, 4 or 8). */
22
+ protected abstract readonly bitCount: number;
23
+ /** Centre (x,y) of each hole in envelope-local 2D space. */
24
+ protected abstract readonly holePositions: Array<{
25
+ x: number;
26
+ y: number;
27
+ }>;
28
+ /** Total z-axis length of the envelope. */
29
+ protected abstract readonly envelopeHeight: number;
30
+ private _envelopeGeom;
31
+ private _baseGeom;
32
+ private _bulbGeom;
33
+ protected createVisualBase(component: Component, context: VisualContext): THREE.Object3D;
34
+ protected createPinsVisual(component: Component, context: VisualContext, group: THREE.Group): void;
35
+ getConfigFormDefinition(): ConfigFormDefinition | null;
36
+ mapCoreConfigToForm(config: Map<string, string>): Map<string, any>;
37
+ mapFormToCoreConfig(formData: Map<string, any>): Map<string, string>;
38
+ updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void;
39
+ updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void;
40
+ private _snapLight;
41
+ private _setLightMaterial;
42
+ private _ensureClonedMaterial;
43
+ private _restoreSharedMaterial;
44
+ private _cleanupMixer;
45
+ private _findLights;
46
+ }
@@ -0,0 +1,15 @@
1
+ import { Component } from '../../../../core/index.ts';
2
+ import { VisualContext } from '../../types';
3
+ import { InputVisualFactoryBase } from './InputVisualFactoryBase';
4
+ import * as THREE from 'three';
5
+ /** Visual factory for the OneInput component (single-switch user input). */
6
+ export declare class OneInputVisualFactory extends InputVisualFactoryBase {
7
+ protected readonly bitCount = 1;
8
+ protected readonly holePositions: Array<{
9
+ x: number;
10
+ y: number;
11
+ }>;
12
+ protected readonly envelopeHeight = 1.4;
13
+ constructor();
14
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
15
+ }
@@ -0,0 +1,15 @@
1
+ import { Component } from '../../../../core/index.ts';
2
+ import { VisualContext } from '../../types';
3
+ import { LightVisualFactoryBase } from './LightVisualFactoryBase';
4
+ import * as THREE from 'three';
5
+ /** Visual factory for the OneLight component (single-light input mirror). */
6
+ export declare class OneLightVisualFactory extends LightVisualFactoryBase {
7
+ protected readonly bitCount = 1;
8
+ protected readonly holePositions: Array<{
9
+ x: number;
10
+ y: number;
11
+ }>;
12
+ protected readonly envelopeHeight = 1.4;
13
+ constructor();
14
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
15
+ }
@@ -0,0 +1,15 @@
1
+ import { Component } from '../../../../core/index.ts';
2
+ import { VisualContext } from '../../types';
3
+ import { InputVisualFactoryBase } from './InputVisualFactoryBase';
4
+ import * as THREE from 'three';
5
+ /** Visual factory for the TwoInput component (two-switch user input). */
6
+ export declare class TwoInputVisualFactory extends InputVisualFactoryBase {
7
+ protected readonly bitCount = 2;
8
+ protected readonly holePositions: Array<{
9
+ x: number;
10
+ y: number;
11
+ }>;
12
+ protected readonly envelopeHeight = 2.4;
13
+ constructor();
14
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
15
+ }
@@ -0,0 +1,15 @@
1
+ import { Component } from '../../../../core/index.ts';
2
+ import { VisualContext } from '../../types';
3
+ import { LightVisualFactoryBase } from './LightVisualFactoryBase';
4
+ import * as THREE from 'three';
5
+ /** Visual factory for the TwoLight component (two-light input mirror). */
6
+ export declare class TwoLightVisualFactory extends LightVisualFactoryBase {
7
+ protected readonly bitCount = 2;
8
+ protected readonly holePositions: Array<{
9
+ x: number;
10
+ y: number;
11
+ }>;
12
+ protected readonly envelopeHeight = 2.4;
13
+ constructor();
14
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
15
+ }
@@ -7,6 +7,7 @@ import { IFactoryRegistry } from './components/ComponentVisualFactory';
7
7
  import { BranchingPointVisualFactory } from './BranchingPointVisualFactory';
8
8
  import { WireVisualManager } from './WireVisualManager';
9
9
  import { HoverManager } from './HoverManager';
10
+ import { ILogicPinMetadata } from '../../core/topology/types';
10
11
  /**
11
12
  * Shared types for 3D Circuit Renderers
12
13
  * @module scene/shared/types
@@ -48,7 +49,7 @@ export type ModelEditAction = 'edit' | 'add' | 'delete';
48
49
  /**
49
50
  * Supported scene controllerType event types (includes tool system events)
50
51
  */
51
- export type ControllerEvent = 'ready' | 'error' | 'circuitLoaded' | 'circuitCleared' | 'gridPositionMove' | 'hover' | 'unhover' | 'select' | 'deselect' | 'toolActivated' | 'toolDeactivated' | 'toolOperationStarted' | 'toolOperationCompleted' | 'toolOperationCancelled' | 'toolValidationError' | 'cursorChangeRequested' | 'circuitElementAction' | 'circuitMetadataEdition' | 'selectionChange' | 'simulationPlayed' | 'simulationPaused' | 'simulationStepped' | 'simulationTick' | 'simulationUserCommand' | 'simulationStopped' | 'simulationSpeedChanged' | 'componentHelpRequested';
52
+ export type ControllerEvent = 'ready' | 'error' | 'circuitLoaded' | 'circuitCleared' | 'gridPositionMove' | 'hover' | 'unhover' | 'select' | 'deselect' | 'toolActivated' | 'toolDeactivated' | 'toolOperationStarted' | 'toolOperationCompleted' | 'toolOperationCancelled' | 'toolValidationError' | 'cursorChangeRequested' | 'circuitElementAction' | 'circuitMetadataEdition' | 'selectionChange' | 'simulationPlayed' | 'simulationPaused' | 'simulationStepped' | 'simulationTick' | 'simulationUserCommand' | 'simulationStopped' | 'simulationSpeedChanged' | 'multiWiringChanged' | 'componentHelpRequested';
52
53
  /**
53
54
  * Event payload map for type-safe event emission
54
55
  */
@@ -161,6 +162,10 @@ export interface ControllerEventMap {
161
162
  previousSpeed: number;
162
163
  newSpeed: number;
163
164
  };
165
+ /** Emitted when the multi-wiring flag toggles (engine + edit-controller scope) */
166
+ multiWiringChanged: {
167
+ multiWiring: boolean;
168
+ };
164
169
  /** Emitted when user clicks a pin tooltip to request component help */
165
170
  componentHelpRequested: {
166
171
  componentType: ComponentType;
@@ -200,6 +205,7 @@ export interface EnodeHitboxUserData {
200
205
  componentId: string | null;
201
206
  label: string | null;
202
207
  componentType: ComponentType | null;
208
+ logicMetadata: ILogicPinMetadata | null;
203
209
  }
204
210
  /**
205
211
  * UserData structure for component hitbox meshes
@@ -326,6 +332,8 @@ export interface SharedResources {
326
332
  scene: THREE.Scene;
327
333
  /** Perspective camera for rendering */
328
334
  camera: THREE.PerspectiveCamera;
335
+ /** WebGL renderer owned by the consumer and passed in at `initialize()`. Exposed here so factories/managers can access `renderer.domElement`, `getPixelRatio()`, and `capabilities` without another bump. */
336
+ renderer: THREE.WebGLRenderer;
329
337
  /** MapControls for pan/zoom/rotate interaction */
330
338
  mapControls: MapControls;
331
339
  /** Grid helper */
@@ -354,6 +362,14 @@ export interface ModeChangedEvent {
354
362
  /** Previous mode before transition */
355
363
  previousMode?: EngineMode;
356
364
  }
365
+ /**
366
+ * Event emitted when the user requests help via the in-scene HelpWidget.
367
+ * Library-neutral: consumers decide how to render help (modal, sidebar, etc.).
368
+ */
369
+ export interface HelpRequestedEvent {
370
+ /** Mode the engine was in when help was requested */
371
+ mode: EngineMode;
372
+ }
357
373
  /**
358
374
  * Combined event map for CircuitEngine.
359
375
  * Includes all controller events plus controller-specific events.
@@ -361,6 +377,8 @@ export interface ModeChangedEvent {
361
377
  export interface CircuitEngineEventMap extends ControllerEventMap {
362
378
  /** Emitted after mode transition completes */
363
379
  modeChanged: ModeChangedEvent;
380
+ /** Emitted when the user clicks the help button in the integrated HelpWidget */
381
+ helpRequested: HelpRequestedEvent;
364
382
  }
365
383
  /**
366
384
  * Configuration options for Controllers and Engine
@@ -372,7 +390,7 @@ export interface CircuitEngineEventMap extends ControllerEventMap {
372
390
  *
373
391
  * @example
374
392
  * ```typescript
375
- * controllerType.initialize(container, {
393
+ * controllerType.initialize(container, renderer, {
376
394
  * mapControls: {
377
395
  * enableRotate: false, // Disable rotation for 2D-only view
378
396
  * maxDistance: 50, // Limit zoom out
@@ -422,6 +440,15 @@ export interface ControllerOptions {
422
440
  simulationSpeed?: number;
423
441
  /** If CircuitRunnerController plays automatically at activation */
424
442
  simulationAutoPlay?: boolean;
443
+ /** Initial multi-wiring flag (default: false) */
444
+ multiWiring?: boolean;
445
+ }
446
+ /**
447
+ * Configuration options for the integrated widgets overlay
448
+ */
449
+ export interface WidgetsOptions {
450
+ /** Disable the bundled overlay widgets (default: true = enabled) */
451
+ enabled?: boolean;
425
452
  }
426
453
  /**
427
454
  * Configuration options for CircuitEngine initialization
@@ -441,6 +468,11 @@ export interface EngineOptions {
441
468
  * @default { enableHistory: false }
442
469
  */
443
470
  runnerOptions?: IRunnerOptions;
471
+ /**
472
+ * Integrated widgets overlay configuration
473
+ * @default { enabled: true }
474
+ */
475
+ widgets?: WidgetsOptions;
444
476
  }
445
477
  /**
446
478
  * Control type for config form fields
@@ -5,4 +5,4 @@ import { MapControlsOptions } from '../types';
5
5
  * @module scene/shared/utils/ControlsUtils
6
6
  */
7
7
  import * as THREE from 'three';
8
- export declare function createMapControls(camera: THREE.PerspectiveCamera, container: HTMLElement, options: MapControlsOptions): MapControls;
8
+ export declare function createMapControls(camera: THREE.PerspectiveCamera, canvas: HTMLCanvasElement, options: MapControlsOptions): MapControls;
@@ -335,3 +335,38 @@ export declare function DripHoleGeometry(width: number, height: number, thicknes
335
335
  * @constructor
336
336
  */
337
337
  export declare function KuKoGeometry(kuWidth: number, koWidth: number, height: number, kuThickness: number, koThickness: number, depth: number, steps?: number): ExtrudeGeometry;
338
+ /**
339
+ * Create an ExtrudeGeometry shaped like a rectangle (frame/border) with rectangular holes in it
340
+ * Shape is centered at origin.
341
+ * by `thickness` on all four sides.
342
+ *
343
+ * @param width - Total width of the outer rectangle
344
+ * @param height - Total height of the outer rectangle
345
+ * @param holeWidth - width of a rectangular hole
346
+ * @param holeHeight - height of a rectangular hole
347
+ * @param holePositions - array of xy positions of the center of holes
348
+ * @param depth - Extrusion depth
349
+ * @param steps - Number of extrusion steps
350
+ * @constructor
351
+ */
352
+ export declare function IceBoxGeometry(width: number, height: number, holeWidth: number, holeHeight: number, holePositions: Array<{
353
+ x: number;
354
+ y: number;
355
+ }>, depth: number, steps?: number): ExtrudeGeometry;
356
+ /**
357
+ * Create an ExtrudeGeometry shaped like a rectangle (frame/border) with circular holes in it
358
+ * Shape is centered at origin.
359
+ * by `thickness` on all four sides.
360
+ *
361
+ * @param width - Total width of the outer rectangle
362
+ * @param height - Total height of the outer rectangle
363
+ * @param holeRadius - radius of a hole
364
+ * @param holePositions - array of xy positions of the center of holes
365
+ * @param depth - Extrusion depth
366
+ * @param steps - Number of extrusion steps
367
+ * @constructor
368
+ */
369
+ export declare function RoundIceBoxGeometry(width: number, height: number, holeRadius: number, holePositions: Array<{
370
+ x: number;
371
+ y: number;
372
+ }>, depth: number, steps?: number): ExtrudeGeometry;
@@ -30,6 +30,7 @@ export declare class CircuitController extends AbstractCircuitController {
30
30
  private _tooltipMouseMoveHandler;
31
31
  private _tools;
32
32
  private _activeTool;
33
+ private _multiWiring;
33
34
  /**
34
35
  * Constructor and initialization
35
36
  */
@@ -46,7 +47,7 @@ export declare class CircuitController extends AbstractCircuitController {
46
47
  * Specific Initialization logic, performed after AbstractCircuitController initialization
47
48
  * @private
48
49
  */
49
- protected onInitialize(_options?: ControllerOptions): void;
50
+ protected onInitialize(options?: ControllerOptions): void;
50
51
  protected emitReady(): void;
51
52
  /**
52
53
  * Enable or disable edit mode (FR-006, FR-027)
@@ -107,6 +108,16 @@ export declare class CircuitController extends AbstractCircuitController {
107
108
  * @returns Current tool type or null if no tool is active
108
109
  */
109
110
  getActiveTool(): ToolType | null;
111
+ /**
112
+ * Whether the multi-wiring flag is currently enabled.
113
+ * Wiring tools may use it to create several wires per pull (semantics handled
114
+ * elsewhere; this controller only owns the flag and emits its changes).
115
+ */
116
+ get multiWiring(): boolean;
117
+ /**
118
+ * Toggle the multi-wiring flag and emit `multiWiringChanged` on transition.
119
+ */
120
+ setMultiWiring(value: boolean): void;
110
121
  /**
111
122
  * Set the active editing tool (FR-026, FR-028, FR-034)
112
123
  * Only one tool can be active at a time
@@ -123,6 +123,83 @@ export declare class BuildTool implements IEditingTool {
123
123
  * @param hoveredElement - Currently hovered element at wire creation end
124
124
  */
125
125
  private completeWireCreation;
126
+ /**
127
+ * If multi-wiring is enabled and both endpoints are logic pins on distinct
128
+ * interfaces, fan out follow-up wires at matching offsets. Best-effort: any
129
+ * per-index failure is logged and skipped.
130
+ */
131
+ private createMultiWiringFollowUpWires;
132
+ /**
133
+ * If multi-wiring is enabled and the wire ended on empty space (i.e. created
134
+ * a fresh standalone branching point) from a logic pin, fan out follow-up
135
+ * branching points and follow-up wires for the remaining indices of the
136
+ * source interface. Best-effort: any per-step failure is logged and skipped.
137
+ */
138
+ private createMultiWiringRule2Followups;
139
+ /**
140
+ * If multi-wiring is enabled, the source is a branching point logically
141
+ * rooted on a single logic pin at distance Dl > 0, and the target is a
142
+ * fresh standalone branching point, fan out follow-up BPs through the
143
+ * existing BP-network: for each interface index `i + j` find a sibling
144
+ * BP at the same logic distance Dl and place a new target BP near it
145
+ * (rule-2-style growth/shrink). Stops at the first index whose sibling
146
+ * candidate count within the proximity threshold is not exactly 1.
147
+ */
148
+ private createMultiWiringRule3AFollowups;
149
+ /**
150
+ * Compute `4 × |pin_(i+1) − pin_i|` in XZ for a logic interface, used as the
151
+ * proximity threshold by rules 3A/3B/3C/4. Returns null if any look-up fails
152
+ * or the spacing is degenerate.
153
+ */
154
+ private computeAaPinSpacingThreshold;
155
+ /** Internal: index of a pin within its logic interface. Throws on missing
156
+ * metadata (caller must have already checked). */
157
+ private indexOfPin;
158
+ /**
159
+ * Per-`j` sibling picker shared by rules 3A, 3B, 3C: forward-explore at
160
+ * logic distance `Dl` from `followUpPinId`, filter candidates by XZ
161
+ * proximity to `prevRefXZ` (≤ `threshold`), and require a singleton.
162
+ *
163
+ * @returns the chosen sibling, or a sentinel describing why we stopped.
164
+ */
165
+ private selectSiblingWithThreshold;
166
+ /**
167
+ * Backward-explore the BP-network from `bpId` and return the closest pin of biggest interface (with non-null logicMetadata) by size
168
+ * Optionally if wantedLogicType is specified it filters out interfaces whose subtype differs from `wantedLogicType`
169
+ * Tie on `Dl` → first BFS encounter (Map insertion order).
170
+ */
171
+ private findBestAnchor;
172
+ /**
173
+ * World position of any ENode (Pin or BranchingPoint). Pins resolve via
174
+ * the component group + WireVisualManager; BPs via gridToWorldPosition.
175
+ * Returns null if the lookup chain breaks.
176
+ */
177
+ private getEnodeWorldPosition;
178
+ /**
179
+ * If multi-wiring is enabled, the source is an existing BP and the target
180
+ * is an existing logic pin, search the source-BP network for an
181
+ * opposite-type anchor pin BB-jBB; then for each follow-up index pair, wire
182
+ * a sibling BP on BB's interface side to the corresponding AA follow-up
183
+ * pin. No new BPs are created.
184
+ */
185
+ private createMultiWiringRule3BFollowups;
186
+ /**
187
+ * Mirror of 3B: source is the logic pin (= AA-i), target is the existing
188
+ * BP whose network is searched for an opposite-type anchor BB-jBB. The
189
+ * just-created src→tgt wire makes `src` reachable at Dl=1 from `tgt`, but
190
+ * the opposite-type filter excludes it (same subtype as itself). No new
191
+ * BPs are created.
192
+ */
193
+ private createMultiWiringRule3CFollowups;
194
+ /**
195
+ * Triggered after a dbl-click split creates a new BP. Backward-explore the
196
+ * new BP for the closest logic pin AA-i; identify the AA-side and beyond
197
+ * wires; for each follow-up pin AA-(i+j), find a sibling BP at the same
198
+ * Dl, compute candidate position via the `v3Delta` mirror, and split the
199
+ * sibling's wire to its BFS predecessor at that position. Iteration stops
200
+ * on threshold break or empty candidate set.
201
+ */
202
+ private createMultiWiringRule4Followups;
126
203
  /**
127
204
  * Start wire dragging operation
128
205
  * @param wireId - Wire being dragged
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Pure geometry helpers for the multi-wiring rule 2 fan-out (direct injection
3
+ * from a logic interface to a branching point).
4
+ *
5
+ * Kept free of Three.js so it can be unit-tested without jsdom or a renderer.
6
+ * Operates on the XZ plane only (Y is dropped because branching points persist
7
+ * as 2-D grid cells via `worldToGridPosition`).
8
+ *
9
+ * @module scene/static/tools/MultiWiringPlacement
10
+ */
11
+ export interface XZ {
12
+ x: number;
13
+ z: number;
14
+ }
15
+ /** Compute follow-up branching point positions for rule 2.
16
+ *
17
+ * Algorithm (linear step in wire direction):
18
+ * W = bp0 - pinPositions[0]
19
+ * u = W / |W|
20
+ * bp_j = pinPositions[j] + W + sign * j * |s_j| * u (s_j = pin spacing j-1 -> j)
21
+ *
22
+ * For `sign === -1` (logicOutput) the cumulative offset is clamped so the BP
23
+ * never reaches or passes its pin (a residual 1-cell wire is preserved).
24
+ *
25
+ * @param pinPositions Pin world XZ positions in interface-index order
26
+ * (length = count + 1; index 0 is the source pin).
27
+ * @param bp0 User-clicked BP world position.
28
+ * @param sign +1 for logicInput (extend), -1 for logicOutput (shrink).
29
+ * @returns Array of `count` follow-up BP positions (XZ), or `[]` if the wire
30
+ * vector is degenerate.
31
+ */
32
+ export declare function computeRule2BpPositions(pinPositions: XZ[], bp0: XZ, sign: 1 | -1): XZ[];
33
+ /** If the float BP would round to the same integer grid cell as the parallel
34
+ * placement (i.e. step rounds away), nudge it by 1 unit along `u` so the
35
+ * fan-out remains visually perceptible at 1-cell pin spacings.
36
+ *
37
+ * Snapping uses the same convention as `worldToGridPosition`:
38
+ * `(round(x), round(-z))`.
39
+ */
40
+ export declare function nudgeIfSameGridCell(bp: XZ, parallelBp: XZ, u: XZ): XZ;
@@ -0,0 +1,13 @@
1
+ import { EngineMode } from '../shared/types';
2
+ export declare class HelpWidget {
3
+ private readonly _button;
4
+ private readonly _onClick;
5
+ constructor(initialMode: EngineMode, onClick: () => void);
6
+ mount(parent: HTMLElement): void;
7
+ setMode(mode: EngineMode): void;
8
+ setVisible(visible: boolean): void;
9
+ setLanguage(_lng: string): void;
10
+ dispose(): void;
11
+ /** Test/debug accessor. */
12
+ get element(): HTMLButtonElement;
13
+ }
@@ -0,0 +1,16 @@
1
+ import { EngineMode } from '../shared/types';
2
+ export declare class ModeWidget {
3
+ private readonly _button;
4
+ private readonly _onToggle;
5
+ private _mode;
6
+ constructor(initialMode: EngineMode, onToggle: () => void);
7
+ mount(parent: HTMLElement): void;
8
+ setMode(mode: EngineMode): void;
9
+ setLanguage(_lng: string): void;
10
+ /** Always visible — kept for symmetry with other widgets. */
11
+ setVisible(visible: boolean): void;
12
+ dispose(): void;
13
+ /** Test/debug accessor. */
14
+ get element(): HTMLButtonElement;
15
+ private _render;
16
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Multi-Wiring Widget
3
+ * @module scene/widgets/MultiWiringWidget
4
+ *
5
+ * Single toggle button (verr-maj style) that flips the engine's multi-wiring
6
+ * flag. Visible only in edit mode.
7
+ */
8
+ export declare class MultiWiringWidget {
9
+ private readonly _button;
10
+ private readonly _onToggle;
11
+ private _active;
12
+ constructor(initialActive: boolean, onToggle: () => void);
13
+ mount(parent: HTMLElement): void;
14
+ setVisible(visible: boolean): void;
15
+ setActive(active: boolean): void;
16
+ setLanguage(_lng: string): void;
17
+ dispose(): void;
18
+ /** Test/debug accessor. */
19
+ get element(): HTMLButtonElement;
20
+ private _render;
21
+ }
@@ -1,4 +1,5 @@
1
1
  import { ComponentType } from '../../core/index.ts';
2
+ import { ILogicPinMetadata } from '../../core/topology/types';
2
3
  /**
3
4
  * Manages a transient HTML tooltip displayed when hovering a component pin.
4
5
  *
@@ -15,7 +16,7 @@ export declare class PinTooltipWidget {
15
16
  * Show tooltip at the given client coordinates for the specified pin.
16
17
  * If already showing for the same component type, only repositions.
17
18
  */
18
- show(pinLabel: string, componentType: ComponentType, clientX: number, clientY: number): void;
19
+ show(pinLabel: string, componentType: ComponentType, logicMetadata: ILogicPinMetadata | null, clientX: number, clientY: number): void;
19
20
  /** Hide and remove the tooltip element */
20
21
  hide(): void;
21
22
  /** Update cursor position without recreating the tooltip */
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Simulation Player Widget
3
+ * @module scene/widgets/SimulationPlayerWidget
4
+ *
5
+ * Horizontal control strip: stop | (spacer) | play/pause | speed slider |
6
+ * TPS indicator | step. Visible only in simulation mode.
7
+ *
8
+ * Play/pause button icon reflects current state: `pauseIcon` while playing,
9
+ * `playIcon` while paused. The step button relies on the engine pausing
10
+ * automatically when stepping while playing.
11
+ */
12
+ export interface SimulationPlayerCallbacks {
13
+ onTogglePlay: () => void;
14
+ onSpeedChange: (tps: number) => void;
15
+ onStep: () => void;
16
+ onStopReset: () => void;
17
+ }
18
+ export declare class SimulationPlayerWidget {
19
+ private readonly _root;
20
+ private readonly _stopBtn;
21
+ private readonly _playPauseBtn;
22
+ private readonly _stepBtn;
23
+ private readonly _slider;
24
+ private readonly _speedLabel;
25
+ private readonly _callbacks;
26
+ private _isPlaying;
27
+ private _currentTick;
28
+ constructor(minSpeed: number, maxSpeed: number, initialSpeed: number, initialPlaying: boolean, callbacks: SimulationPlayerCallbacks);
29
+ mount(parent: HTMLElement): void;
30
+ setVisible(visible: boolean): void;
31
+ setPosition(top: number, left: number): void;
32
+ setSpeed(tps: number): void;
33
+ setTick(tick: number): void;
34
+ setPlaying(playing: boolean): void;
35
+ setLanguage(_lng: string): void;
36
+ dispose(): void;
37
+ /** Test/debug accessor. */
38
+ get element(): HTMLDivElement;
39
+ /** Test/debug accessor. */
40
+ get sliderElement(): HTMLInputElement;
41
+ /** Test/debug accessor. */
42
+ get stopButton(): HTMLButtonElement;
43
+ /** Test/debug accessor. */
44
+ get playPauseButton(): HTMLButtonElement;
45
+ /** Test/debug accessor. */
46
+ get stepButton(): HTMLButtonElement;
47
+ private _makeStopButton;
48
+ private _makePlayPauseButton;
49
+ private _makeStepButton;
50
+ private _makeSlider;
51
+ private _makeSpeedLabel;
52
+ private _renderLabel;
53
+ private _renderPlayPauseIcon;
54
+ private _playPauseTitle;
55
+ private _renderThumbColor;
56
+ /**
57
+ * Inject a one-shot stylesheet for the slider thumb so it visually matches
58
+ * `slider.png` (orange round thumb on a grey track) on browsers that ignore
59
+ * `accent-color`. Idempotent across multiple widget instances.
60
+ */
61
+ private _injectThumbStyles;
62
+ }