simple-circuit-engine 0.0.12 → 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 (99) hide show
  1. package/AGENTS.md +9 -6
  2. package/CLAUDE.md +2 -0
  3. package/README.md +8 -8
  4. package/dist/core/index.js +109 -74
  5. package/dist/core/setup.d.ts +18 -0
  6. package/dist/core/simulation/behaviors/arithmetic/AdderBehavior.d.ts +28 -0
  7. package/dist/core/simulation/behaviors/arithmetic/ArithmeticBehaviorMixin.d.ts +63 -0
  8. package/dist/core/simulation/behaviors/arithmetic/EightBitAdderBehavior.d.ts +51 -0
  9. package/dist/core/simulation/behaviors/arithmetic/EightBitOnesComplementBehavior.d.ts +29 -0
  10. package/dist/core/simulation/behaviors/arithmetic/HalfAdderBehavior.d.ts +22 -0
  11. package/dist/core/simulation/behaviors/arithmetic/index.d.ts +7 -0
  12. package/dist/core/simulation/behaviors/index.d.ts +14 -0
  13. package/dist/core/simulation/behaviors/interface/EightInputBehavior.d.ts +9 -0
  14. package/dist/core/simulation/behaviors/interface/EightLightBehavior.d.ts +9 -0
  15. package/dist/core/simulation/behaviors/interface/FourInputBehavior.d.ts +9 -0
  16. package/dist/core/simulation/behaviors/interface/FourLightBehavior.d.ts +9 -0
  17. package/dist/core/simulation/behaviors/interface/InputBehaviorMixin.d.ts +37 -0
  18. package/dist/core/simulation/behaviors/interface/LightBehaviorMixin.d.ts +60 -0
  19. package/dist/core/simulation/behaviors/interface/OneInputBehavior.d.ts +9 -0
  20. package/dist/core/simulation/behaviors/interface/OneLightBehavior.d.ts +9 -0
  21. package/dist/core/simulation/behaviors/interface/TwoInputBehavior.d.ts +9 -0
  22. package/dist/core/simulation/behaviors/interface/TwoLightBehavior.d.ts +9 -0
  23. package/dist/core/simulation/behaviors/interface/index.d.ts +0 -0
  24. package/dist/core/simulation/states/arithmetic/AdderState.d.ts +16 -0
  25. package/dist/core/simulation/states/arithmetic/ArithmeticState.d.ts +53 -0
  26. package/dist/core/simulation/states/arithmetic/EightBitAdderState.d.ts +25 -0
  27. package/dist/core/simulation/states/arithmetic/EightBitOnesComplementState.d.ts +18 -0
  28. package/dist/core/simulation/states/arithmetic/HalfAdderState.d.ts +16 -0
  29. package/dist/core/simulation/states/arithmetic/index.d.ts +7 -0
  30. package/dist/core/simulation/states/index.d.ts +15 -0
  31. package/dist/core/simulation/states/interface/EightInputState.d.ts +6 -0
  32. package/dist/core/simulation/states/interface/EightLightState.d.ts +6 -0
  33. package/dist/core/simulation/states/interface/FourInputState.d.ts +6 -0
  34. package/dist/core/simulation/states/interface/FourLightState.d.ts +6 -0
  35. package/dist/core/simulation/states/interface/InputState.d.ts +45 -0
  36. package/dist/core/simulation/states/interface/LightState.d.ts +54 -0
  37. package/dist/core/simulation/states/interface/OneInputState.d.ts +6 -0
  38. package/dist/core/simulation/states/interface/OneLightState.d.ts +6 -0
  39. package/dist/core/simulation/states/interface/TwoInputState.d.ts +6 -0
  40. package/dist/core/simulation/states/interface/TwoLightState.d.ts +6 -0
  41. package/dist/core/simulation/types.d.ts +10 -2
  42. package/dist/core/topology/Component.d.ts +6 -1
  43. package/dist/core/topology/ENode.d.ts +7 -2
  44. package/dist/core/topology/index.d.ts +2 -0
  45. package/dist/core/topology/networkTraversal.d.ts +50 -0
  46. package/dist/core/topology/types.d.ts +31 -6
  47. package/dist/core-HH6iRWtB.js +4671 -0
  48. package/dist/core-HH6iRWtB.js.map +1 -0
  49. package/dist/i18n/index.d.ts +1158 -0
  50. package/dist/i18n/locales/en.json.d.ts +366 -0
  51. package/dist/i18n/locales/fr.json.d.ts +366 -0
  52. package/dist/index.d.ts +1 -0
  53. package/dist/index.js +172 -118
  54. package/dist/scene/CircuitEngine.d.ts +49 -4
  55. package/dist/scene/index.d.ts +6 -2
  56. package/dist/scene/index.js +62 -45
  57. package/dist/scene/setup.d.ts +18 -0
  58. package/dist/scene/shared/AbstractCircuitController.d.ts +11 -3
  59. package/dist/scene/shared/HoverManager.d.ts +15 -0
  60. package/dist/scene/shared/components/arithmetic/AdderVisualFactory.d.ts +54 -0
  61. package/dist/scene/shared/components/arithmetic/EightBitAdderVisualFactory.d.ts +45 -0
  62. package/dist/scene/shared/components/arithmetic/EightBitOnesComplementVisualFactory.d.ts +63 -0
  63. package/dist/scene/shared/components/arithmetic/HalfAdderVisualFactory.d.ts +55 -0
  64. package/dist/scene/shared/components/arithmetic/index.d.ts +4 -0
  65. package/dist/scene/shared/components/index.d.ts +14 -0
  66. package/dist/scene/shared/components/interface/EightInputVisualFactory.d.ts +15 -0
  67. package/dist/scene/shared/components/interface/EightLightVisualFactory.d.ts +15 -0
  68. package/dist/scene/shared/components/interface/FourInputVisualFactory.d.ts +15 -0
  69. package/dist/scene/shared/components/interface/FourLightVisualFactory.d.ts +15 -0
  70. package/dist/scene/shared/components/interface/InputVisualFactoryBase.d.ts +50 -0
  71. package/dist/scene/shared/components/interface/LightVisualFactoryBase.d.ts +46 -0
  72. package/dist/scene/shared/components/interface/OneInputVisualFactory.d.ts +15 -0
  73. package/dist/scene/shared/components/interface/OneLightVisualFactory.d.ts +15 -0
  74. package/dist/scene/shared/components/interface/TwoInputVisualFactory.d.ts +15 -0
  75. package/dist/scene/shared/components/interface/TwoLightVisualFactory.d.ts +15 -0
  76. package/dist/scene/shared/types.d.ts +34 -4
  77. package/dist/scene/shared/utils/ControlsUtils.d.ts +1 -1
  78. package/dist/scene/shared/utils/GeometryUtils.d.ts +111 -0
  79. package/dist/scene/static/CircuitController.d.ts +13 -1
  80. package/dist/scene/static/tools/BuildTool.d.ts +81 -0
  81. package/dist/scene/static/tools/ComponentPickerWidget.d.ts +7 -0
  82. package/dist/scene/static/tools/ConfigPanelWidget.d.ts +14 -0
  83. package/dist/scene/static/tools/MultiWiringPlacement.d.ts +40 -0
  84. package/dist/scene/widgets/HelpWidget.d.ts +13 -0
  85. package/dist/scene/widgets/ModeWidget.d.ts +16 -0
  86. package/dist/scene/widgets/MultiWiringWidget.d.ts +21 -0
  87. package/dist/scene/{static → widgets}/PinTooltipWidget.d.ts +7 -1
  88. package/dist/scene/widgets/SimulationPlayerWidget.d.ts +62 -0
  89. package/dist/scene/widgets/ToolsWidget.d.ts +18 -0
  90. package/dist/scene/widgets/WidgetsManager.d.ts +65 -0
  91. package/dist/scene/widgets/assets/icons.d.ts +20 -0
  92. package/dist/scene/widgets/index.d.ts +9 -0
  93. package/dist/scene/widgets/styles.d.ts +33 -0
  94. package/dist/{scene-CVsDdySt.js → scene-C1xhdw_B.js} +3993 -772
  95. package/dist/scene-C1xhdw_B.js.map +1 -0
  96. package/package.json +14 -9
  97. package/dist/core-Bjta9Y7_.js +0 -2707
  98. package/dist/core-Bjta9Y7_.js.map +0 -1
  99. package/dist/scene-CVsDdySt.js.map +0 -1
@@ -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
@@ -452,8 +484,6 @@ export type ConfigControlType = 'dropdown' | 'color' | 'number' | 'text' | 'bool
452
484
  export interface ConfigFieldDefinition {
453
485
  /** Config map key (e.g., "activeColor", "initialState") */
454
486
  key: string;
455
- /** Human-readable label for the form field */
456
- label: string;
457
487
  /** Control type to render */
458
488
  type: ConfigControlType;
459
489
  /** Options for dropdown type (array or label-value object) */
@@ -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;
@@ -247,6 +247,36 @@ export declare function XorGateTailGeometry(orWidth: number, orHeight: number, t
247
247
  */
248
248
  export declare function LGeometry(width: number, height: number, thickness: number, angle: number, invert: boolean, junctionRadius: number, depth: number, steps?: number): ExtrudeGeometry;
249
249
  export declare function CyclicTrapezoidGeometry(width: number, tailHeight: number, headHeight: number, thickness: number, depth: number, steps?: number): ExtrudeGeometry;
250
+ /**
251
+ * Create an ExtrudeGeometry shaped like an empty rectangle (frame/border).
252
+ * Shape is centered at origin. The inner hole is a smaller rectangle inset
253
+ * by `thickness` on all four sides.
254
+ *
255
+ * @param width - Total width of the outer rectangle
256
+ * @param height - Total height of the outer rectangle
257
+ * @param thickness - Wall thickness of the frame (inset on each side)
258
+ * @param depth - Extrusion depth
259
+ * @param steps - Number of extrusion steps
260
+ * @constructor
261
+ */
262
+ export declare function EmptyRectangleGeometry(width: number, height: number, thickness: number, depth: number, steps?: number): ExtrudeGeometry;
263
+ /**
264
+ * Create an ExtrudeGeometry shaped like a rectangle with a rectangular "nail"
265
+ * (intrusion or protrusion)
266
+ * The main rectangle is centered at origin; the nail hangs inside or outside it.
267
+ *
268
+ * @param width - Total width of the rectangle
269
+ * @param height - Height of the main rectangle body
270
+ * @param nailWidth - Width of the nail intrusion
271
+ * @param nailHeight - Height of the nail
272
+ * @param nailX - Horizontal center of the nail, measured from the rectangle's left edge
273
+ * @param protusion - if false nail will enter inside the rectangle (intrusion), else outside (protusion)
274
+ * @param depth - Extrusion depth
275
+ * @param steps - Number of extrusion steps
276
+ * @throws Error if nail dimensions or position are geometrically invalid
277
+ * @constructor
278
+ */
279
+ export declare function RectangleWithNailGeometry(width: number, height: number, nailWidth: number, nailHeight: number, nailX: number, protusion: boolean, depth: number, steps?: number): ExtrudeGeometry;
250
280
  /**
251
281
  * Create an ExtrudeGeometry for the inner hole of a cyclic trapezoid.
252
282
  * Returns null when the geometry should be solid (no hole).
@@ -259,3 +289,84 @@ export declare function CyclicTrapezoidGeometry(width: number, tailHeight: numbe
259
289
  * @param steps - Number of extrusion steps
260
290
  */
261
291
  export declare function CyclicTrapezoidHoleGeometry(width: number, tailHeight: number, headHeight: number, thickness: number, depth: number, steps?: number): ExtrudeGeometry | null;
292
+ /**
293
+ * Create an ExtrudeGeometry for a drip (teardrop) shape.
294
+ * The shape has a sharp pointed tip at the top and a rounded bottom.
295
+ * Shape is centered at origin.
296
+ *
297
+ * Construction: the rounded bottom is a circle of radius width/2 centered at
298
+ * y = -height/2 + width/2. Two straight tangent lines connect the circle to the
299
+ * tip at (0, +height/2). This requires height > width so the tip sits above the
300
+ * circle and the tangent lines exist.
301
+ *
302
+ * @param width - Total width
303
+ * @param height - Total height (must be > width)
304
+ * @param thickness - Wall thickness of the shell
305
+ * @param depth - Extrusion depth
306
+ * @param steps - Number of extrusion steps
307
+ * @throws Error if height <= width (tip too low for tangent construction)
308
+ * @constructor
309
+ */
310
+ export declare function DripGeometry(width: number, height: number, thickness: number, depth: number, steps?: number): ExtrudeGeometry;
311
+ /**
312
+ * Create an ExtrudeGeometry for the inner hole of a drip shape.
313
+ * Returns null when thickness is large enough that the drip has no hole.
314
+ *
315
+ * @param width - Total width
316
+ * @param height - Total height (must be > width)
317
+ * @param thickness - Wall thickness
318
+ * @param depth - Extrusion depth
319
+ * @param steps - Number of extrusion steps
320
+ * @throws Error if height <= width
321
+ */
322
+ export declare function DripHoleGeometry(width: number, height: number, thickness: number, depth: number, steps?: number): ExtrudeGeometry | null;
323
+ /**
324
+ * Create an ExtrudeGeometry shaped like japanese hiragana Ku and katakana Ko (くコ)
325
+ * Shape is centered horizontally at the junction between the two and vertically at the middle height
326
+ * by `kuThickness` on all four sides.
327
+ *
328
+ * @param kuWidth - width of the left ku part (outer)
329
+ * @param koWidth - width of the right ko part (outer)
330
+ * @param height - Total height of the outer geometry
331
+ * @param kuThickness - Wall kuThickness of the frame (inset on ku side)
332
+ * @param koThickness - Wall koThickness of the frame (inset on ko side)
333
+ * @param depth - Extrusion depth
334
+ * @param steps - Number of extrusion steps
335
+ * @constructor
336
+ */
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
  */
@@ -41,11 +42,12 @@ export declare class CircuitController extends AbstractCircuitController {
41
42
  * @throws {TypeError} factoryRegistry is null/undefined
42
43
  */
43
44
  constructor(factoryRegistry: IFactoryRegistry, sharedResources?: SharedResources);
45
+ setLanguage(lng: string): void;
44
46
  /**
45
47
  * Specific Initialization logic, performed after AbstractCircuitController initialization
46
48
  * @private
47
49
  */
48
- protected onInitialize(_options?: ControllerOptions): void;
50
+ protected onInitialize(options?: ControllerOptions): void;
49
51
  protected emitReady(): void;
50
52
  /**
51
53
  * Enable or disable edit mode (FR-006, FR-027)
@@ -106,6 +108,16 @@ export declare class CircuitController extends AbstractCircuitController {
106
108
  * @returns Current tool type or null if no tool is active
107
109
  */
108
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;
109
121
  /**
110
122
  * Set the active editing tool (FR-026, FR-028, FR-034)
111
123
  * Only one tool can be active at a time
@@ -49,6 +49,10 @@ export declare class BuildTool implements IEditingTool {
49
49
  * Cancels any active operations and removes all event handlers
50
50
  */
51
51
  onDeactivate(): void;
52
+ /**
53
+ * Forward a language change to the component picker.
54
+ */
55
+ setLanguage(lng: string): void;
52
56
  /**
53
57
  * Cancel current ongoing operation : can be called from outside if needed
54
58
  */
@@ -119,6 +123,83 @@ export declare class BuildTool implements IEditingTool {
119
123
  * @param hoveredElement - Currently hovered element at wire creation end
120
124
  */
121
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;
122
203
  /**
123
204
  * Start wire dragging operation
124
205
  * @param wireId - Wire being dragged
@@ -31,6 +31,7 @@ export interface ComponentPickerState {
31
31
  export declare class ComponentPickerWidget {
32
32
  private readonly registry;
33
33
  private container;
34
+ private titleEl;
34
35
  private groupDropdown;
35
36
  private itemList;
36
37
  private state;
@@ -69,6 +70,12 @@ export declare class ComponentPickerWidget {
69
70
  * Full cleanup including state reset.
70
71
  */
71
72
  dispose(): void;
73
+ /**
74
+ * Refresh all user-visible text in place after a language change.
75
+ * No-op if the widget is not currently open.
76
+ * Preserves drag position, scroll, resize dimensions, and current selection.
77
+ */
78
+ setLanguage(_lng: string): void;
72
79
  private createDOM;
73
80
  private renderItemList;
74
81
  private createItemElement;
@@ -16,6 +16,8 @@ export declare class ConfigPanelWidget {
16
16
  private readonly editComponentConfig;
17
17
  private _isOpen;
18
18
  private _currentComponentId;
19
+ private _currentComponent;
20
+ private _currentFactory;
19
21
  private gui;
20
22
  private container;
21
23
  private formDataObject;
@@ -77,6 +79,18 @@ export declare class ConfigPanelWidget {
77
79
  * @param factory - Visual factory for the component
78
80
  */
79
81
  private onValueChange;
82
+ /**
83
+ * Resolve a form field label via i18n, falling back to a humanised form
84
+ * of the field key when no translation is available (e.g. "transitionSpan" →
85
+ * "Transition Span"). The fallback covers two cases: (a) the consumer never
86
+ * called registerSceTranslations, (b) the key is not yet in the locale file.
87
+ */
88
+ private _resolveFieldLabel;
89
+ /**
90
+ * Refresh the panel to display strings in the current language.
91
+ * No-op if the panel is not open.
92
+ */
93
+ setLanguage(_lng: string): void;
80
94
  /**
81
95
  * Rebuild the GUI in place, re-reading the updated component config.
82
96
  * Used after interdependent field changes (defaultLogicFamily, activationLogic).
@@ -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
+ }