simple-circuit-engine 0.0.1

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.
@@ -0,0 +1,1683 @@
1
+ import { e as T, d as g, P, C as b, c as _, i as C, f as A, b as L, s as E, a as p, T as y } from "../CircuitRunner-CAeE31M5.js";
2
+ import { j as Z, D as j, E as ee, m as te, h as ne, R as ie, l as se, S as oe, k as re, g as ae } from "../CircuitRunner-CAeE31M5.js";
3
+ class w {
4
+ /**
5
+ * Unique identifier for this ENode.
6
+ * @readonly
7
+ */
8
+ id;
9
+ /**
10
+ * Type of electrical node (Pin or BranchingPoint).
11
+ * @readonly
12
+ */
13
+ type;
14
+ /**
15
+ * Parent component UUID (only for pin nodes).
16
+ * Undefined for branching point nodes.
17
+ * @readonly
18
+ */
19
+ component;
20
+ /**
21
+ * Pin label within component (only for pin nodes).
22
+ * Undefined for branching point nodes.
23
+ * @readonly
24
+ */
25
+ pinLabel;
26
+ /**
27
+ * Grid position (only for branching point nodes).
28
+ * Undefined for pin nodes (position derived from component).
29
+ * @readonly
30
+ */
31
+ position;
32
+ /**
33
+ * Set of wire UUIDs connected to this node.
34
+ * Mutable to allow wire connections/disconnections.
35
+ */
36
+ wires;
37
+ /**
38
+ * Is the ENode a source of voltage or current?
39
+ */
40
+ source;
41
+ /**
42
+ * Create a new electrical node.
43
+ *
44
+ * **Note**: Typically ENodes are created automatically by Circuit.
45
+ * This constructor is used internally.
46
+ *
47
+ * @param type - Node type (Pin or BranchingPoint)
48
+ * @param component - Parent component UUID (pin nodes only)
49
+ * @param pinLabel - Pin label (pin nodes only)
50
+ * @param position - Grid position (branching points only)
51
+ * @param source - Source type (Voltage/Current) or undefined
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * // Pin node (internal to Circuit)
56
+ * const pinNode = new ENode(
57
+ * ENodeType.Pin,
58
+ * componentId,
59
+ * '0', // first pin
60
+ * undefined,
61
+ * undefined
62
+ * );
63
+ *
64
+ * // Branching point node
65
+ * const branchNode = new ENode(
66
+ * ENodeType.BranchingPoint,
67
+ * undefined,
68
+ * undefined,
69
+ * new Position(15, 25),
70
+ * undefined
71
+ * );
72
+ * ```
73
+ */
74
+ constructor(e, t, i, n, s = void 0) {
75
+ this.id = T(), this.type = e, this.component = t, this.pinLabel = i, this.position = n, this.wires = /* @__PURE__ */ new Set(), this.source = s;
76
+ }
77
+ /**
78
+ * Get the position of this electrical node.
79
+ *
80
+ * **Pin nodes**: Derives position from parent component.
81
+ * **Branching nodes**: Returns stored position directly.
82
+ *
83
+ * @param circuit - Circuit instance (needed to look up component for pin nodes)
84
+ * @returns Position on the grid
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * const circuit = new Circuit();
89
+ * const component = circuit.addComponent(
90
+ * new Position(10, 20),
91
+ * new Rotation(0),
92
+ * 1
93
+ * );
94
+ *
95
+ * const pinNode = circuit.getENode(component.pins[0]);
96
+ * const position = pinNode.getPosition(circuit);
97
+ * console.log(position.x); // 10 (derived from component)
98
+ * ```
99
+ */
100
+ getPosition(e) {
101
+ if (this.type === g.Pin) {
102
+ if (!this.component)
103
+ throw new Error("Pin node missing component reference");
104
+ const t = e.getComponent(this.component);
105
+ if (!t)
106
+ throw new Error(`Component ${this.component} not found for pin node ${this.id}`);
107
+ return t.position;
108
+ }
109
+ if (!this.position)
110
+ throw new Error("Branching point node missing position");
111
+ return this.position;
112
+ }
113
+ /**
114
+ * Update the enode (branching point only)'s position.
115
+ *
116
+ * @param newPosition - The new position for the enode
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * const component = circuit.getComponent(componentId);
121
+ * component.setPosition(new Position(15, 25));
122
+ * ```
123
+ */
124
+ setPosition(e) {
125
+ Object.defineProperty(this, "position", {
126
+ value: e,
127
+ writable: !1,
128
+ enumerable: !0,
129
+ configurable: !0
130
+ });
131
+ }
132
+ /**
133
+ * Update the enode's source type.
134
+ * @param sourceType
135
+ */
136
+ setSourceType(e) {
137
+ Object.defineProperty(this, "source", {
138
+ value: e,
139
+ writable: !0,
140
+ enumerable: !0,
141
+ configurable: !0
142
+ });
143
+ }
144
+ /**
145
+ * Serialize ENode to JSON.
146
+ *
147
+ * @returns Plain object representation
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * const json = enode.toJSON();
152
+ * console.log(json);
153
+ * // Pin node:
154
+ * // {
155
+ * // id: "uuid",
156
+ * // type: "Pin",
157
+ * // component: "component-uuid",
158
+ * // pinLabel: "0"
159
+ * // }
160
+ *
161
+ * // Branching node:
162
+ * // {
163
+ * // id: "uuid",
164
+ * // type: "BranchingPoint",
165
+ * // position: { x: 15, y: 25 }
166
+ * // }
167
+ * ```
168
+ */
169
+ toJSON() {
170
+ const e = {
171
+ id: this.id,
172
+ type: this.type,
173
+ source: this.source || null
174
+ };
175
+ return this.type === g.Pin ? (e.component = this.component || null, e.pinLabel = this.pinLabel || null) : e.position = this.position?.toJSON() || null, e;
176
+ }
177
+ /**
178
+ * Deserialize ENode from JSON.
179
+ *
180
+ * @param json - ENode data
181
+ * @returns ENode instance
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * const json = {
186
+ * id: "uuid",
187
+ * type: "Pin",
188
+ * component: "component-uuid",
189
+ * pinLabel: "0"
190
+ * };
191
+ *
192
+ * const enode = ENode.fromJSON(json);
193
+ * ```
194
+ */
195
+ static fromJSON(e) {
196
+ const t = e.position ? P.fromJSON(e.position) : void 0, i = new w(e.type, e.component, e.pinLabel, t, e.source);
197
+ return Object.defineProperty(i, "id", {
198
+ value: e.id,
199
+ writable: !1,
200
+ enumerable: !0,
201
+ configurable: !1
202
+ }), i;
203
+ }
204
+ }
205
+ class f {
206
+ /**
207
+ * Unique identifier for this wire.
208
+ * @readonly
209
+ */
210
+ id;
211
+ /**
212
+ * First connected ENode UUID.
213
+ * @readonly
214
+ */
215
+ node1;
216
+ /**
217
+ * Second connected ENode UUID.
218
+ * @readonly
219
+ */
220
+ node2;
221
+ /**
222
+ * Optional intermediate positions for wire routing.
223
+ * Empty array indicates straight-line connection.
224
+ * @readonly
225
+ */
226
+ intermediatePositions;
227
+ /**
228
+ * Create a new wire.
229
+ *
230
+ * **Note**: Typically wires are created via `Circuit.addWire()` which
231
+ * handles validation and bidirectional reference updates. This constructor
232
+ * is used internally by Circuit.
233
+ *
234
+ * @param node1 - First ENode UUID
235
+ * @param node2 - Second ENode UUID
236
+ * @param intermediatePositions - Optional waypoints for rendering
237
+ *
238
+ * @example
239
+ * ```typescript
240
+ * // Usually created via Circuit:
241
+ * const wire = circuit.addWire(nodeId1, nodeId2);
242
+ *
243
+ * // With intermediate positions:
244
+ * const wire = circuit.addWire(
245
+ * nodeId1,
246
+ * nodeId2,
247
+ * [new Position(5, 10), new Position(15, 10)]
248
+ * );
249
+ * ```
250
+ */
251
+ constructor(e, t, i = []) {
252
+ this.id = T(), this.node1 = e, this.node2 = t, this.intermediatePositions = i;
253
+ }
254
+ /**
255
+ * Check if this is a straight-line wire.
256
+ *
257
+ * @returns true if no intermediate positions, false otherwise
258
+ *
259
+ * @example
260
+ * ```typescript
261
+ * const straightWire = new Wire(node1, node2);
262
+ * console.log(straightWire.isStraightLine()); // true
263
+ *
264
+ * const curvedWire = new Wire(node1, node2, [new Position(5, 5)]);
265
+ * console.log(curvedWire.isStraightLine()); // false
266
+ * ```
267
+ */
268
+ isStraightLine() {
269
+ return this.intermediatePositions.length === 0;
270
+ }
271
+ /**
272
+ * Serialize wire to JSON.
273
+ *
274
+ * @returns Plain object representation
275
+ *
276
+ * @example
277
+ * ```typescript
278
+ * const json = wire.toJSON();
279
+ * console.log(json);
280
+ * // {
281
+ * // id: "uuid",
282
+ * // node1: "node-uuid-1",
283
+ * // node2: "node-uuid-2",
284
+ * // intermediatePositions: [{ x: 5, y: 10 }]
285
+ * // }
286
+ * ```
287
+ */
288
+ toJSON() {
289
+ return {
290
+ id: this.id,
291
+ node1: this.node1,
292
+ node2: this.node2,
293
+ intermediatePositions: this.intermediatePositions.map((e) => e.toJSON())
294
+ };
295
+ }
296
+ /**
297
+ * Deserialize wire from JSON.
298
+ *
299
+ * @param json - Wire data
300
+ * @returns Wire instance
301
+ *
302
+ * @example
303
+ * ```typescript
304
+ * const json = {
305
+ * id: "uuid",
306
+ * node1: "node-uuid-1",
307
+ * node2: "node-uuid-2",
308
+ * intermediatePositions: [{ x: 5, y: 10 }]
309
+ * };
310
+ *
311
+ * const wire = Wire.fromJSON(json);
312
+ * ```
313
+ */
314
+ static fromJSON(e) {
315
+ const t = e.intermediatePositions.map((n) => P.fromJSON(n)), i = new f(e.node1, e.node2, t);
316
+ return Object.defineProperty(i, "id", {
317
+ value: e.id,
318
+ writable: !1,
319
+ enumerable: !0,
320
+ configurable: !1
321
+ }), i;
322
+ }
323
+ }
324
+ class v {
325
+ /**
326
+ * Create a new CircuitMetadata holding general information about the Circuit.
327
+ *
328
+ * @param name - Name of the circuit
329
+ * @param size - Size of the circuit grid
330
+ * @param divisions - Divisions in the circuit grid
331
+ * @param cameraOptions - Camera Options at startup
332
+ * @throws {TypeError} If size or divisions are not integers
333
+ */
334
+ constructor(e, t, i, n) {
335
+ if (this.name = e, this.size = t, this.divisions = i, this.cameraOptions = n, !Number.isInteger(t) || !Number.isInteger(i))
336
+ throw new TypeError(
337
+ `Size and divisions must be integers (got size=${t}, divisions=${i})`
338
+ );
339
+ }
340
+ toJSON() {
341
+ return {
342
+ name: this.name,
343
+ size: this.size,
344
+ divisions: this.divisions,
345
+ cameraOptions: this.cameraOptions.toJSON()
346
+ };
347
+ }
348
+ static fromJSON(e) {
349
+ return new v(
350
+ e.name,
351
+ e.size,
352
+ e.divisions,
353
+ b.fromJSON(e.cameraOptions)
354
+ );
355
+ }
356
+ toString() {
357
+ return `CircuitMetadata(${this.name}, ${this.size}, ${this.divisions}, ${this.cameraOptions.toString()})`;
358
+ }
359
+ }
360
+ class O {
361
+ /**
362
+ * Circuit metadata holding general information.
363
+ * @private
364
+ */
365
+ metadata;
366
+ /**
367
+ * Map of all components in the circuit (UUID → Component).
368
+ * @private
369
+ */
370
+ components;
371
+ /**
372
+ * Map of all electrical nodes in the circuit (UUID → ENode).
373
+ * Includes both pin nodes and branching point nodes.
374
+ * @private
375
+ */
376
+ enodes;
377
+ /**
378
+ * Map of all wires in the circuit (UUID → Wire).
379
+ * @private
380
+ */
381
+ wires;
382
+ /**
383
+ * Create a new empty circuit.
384
+ */
385
+ constructor(e = "Untitled Circuit") {
386
+ this.metadata = new v(e, 10, 10, new b()), this.components = /* @__PURE__ */ new Map(), this.enodes = /* @__PURE__ */ new Map(), this.wires = /* @__PURE__ */ new Map();
387
+ }
388
+ get name() {
389
+ return this.metadata.name;
390
+ }
391
+ set name(e) {
392
+ if (typeof e != "string" || e.trim() === "")
393
+ throw new TypeError("Circuit name must be a non-empty string");
394
+ this.metadata.name = e;
395
+ }
396
+ /**
397
+ * Add a new component to the circuit.
398
+ *
399
+ * Automatically creates pin ENodes for the component and links them
400
+ * bidirectionally. Pin ENode UUIDs are stored in the component's pins array.
401
+ * Pin labels are derived from the ComponentType metadata.
402
+ *
403
+ * @param type - Component type (Battery, Switch, LED, etc.)
404
+ * @param position - Grid position (x, y integers)
405
+ * @param rotation - Orientation angle (integer degrees)
406
+ * @param config - Optional configuration map for component-specific settings
407
+ * @returns The created Component
408
+ * @throws {TypeError} If position/rotation coordinates are not integers
409
+ *
410
+ * @example
411
+ * ```typescript
412
+ * const lightbulb = circuit.addComponent(
413
+ * new Position(10, 20),
414
+ * new Rotation(90),
415
+ * ComponentType.Lightbulb
416
+ * );
417
+ *
418
+ * console.log(lightbulb.type); // ComponentType.Lightbulb
419
+ * console.log(lightbulb.pins.length); // 2
420
+ * console.log(lightbulb.position.x); // 10
421
+ * ```
422
+ */
423
+ addComponent(e, t, i, n) {
424
+ const s = _(e), o = new C(e, t, i, []);
425
+ n && (o.config = new Map(n));
426
+ const a = [];
427
+ for (const [r, c] of s.pins) {
428
+ const l = new w(
429
+ g.Pin,
430
+ o.id,
431
+ r,
432
+ void 0,
433
+ // Pin position derived from component,
434
+ c
435
+ );
436
+ this.enodes.set(l.id, l), a.push(l.id);
437
+ }
438
+ return Object.defineProperty(o, "pins", {
439
+ value: a,
440
+ writable: !1,
441
+ enumerable: !0,
442
+ configurable: !1
443
+ }), this.components.set(o.id, o), o;
444
+ }
445
+ /**
446
+ * Remove a component from the circuit.
447
+ *
448
+ * **Cascade deletion** removes:
449
+ * - All pin ENodes belonging to the component
450
+ * - All Wires connected to those pins
451
+ *
452
+ * @param id - Component UUID
453
+ * @throws {Error} If component does not exist
454
+ * @returns Object containing arrays of deleted Wires and ENodes IDs
455
+ *
456
+ * @example
457
+ * ```typescript
458
+ * circuit.removeComponent(componentId);
459
+ * // Component, its pins, and connected wires are all removed
460
+ * ```
461
+ */
462
+ removeComponent(e) {
463
+ const t = this.components.get(e);
464
+ if (!t)
465
+ throw new Error(`Component ${e} does not exist`);
466
+ const i = [], n = [];
467
+ for (const s of t.pins) {
468
+ const o = this.enodes.get(s);
469
+ if (o) {
470
+ const a = Array.from(o.wires);
471
+ for (const r of a)
472
+ this.removeWire(r), i.push(r);
473
+ }
474
+ this.enodes.delete(s), n.push(s);
475
+ }
476
+ return this.components.delete(e), { deletedWires: i, deletedENodes: n };
477
+ }
478
+ hasComponent(e) {
479
+ return this.components.has(e);
480
+ }
481
+ /**
482
+ * Get a component by ID.
483
+ *
484
+ * @param id - Component UUID
485
+ * @returns The Component or undefined if not found
486
+ *
487
+ * @example
488
+ * ```typescript
489
+ * const component = circuit.getComponent(componentId);
490
+ * if (component) {
491
+ * console.log(component.position);
492
+ * }
493
+ * ```
494
+ */
495
+ getComponent(e) {
496
+ return this.components.get(e);
497
+ }
498
+ /**
499
+ * Get all components in the circuit.
500
+ *
501
+ * Returns a new array on each call (defensive copy).
502
+ *
503
+ * @returns Array of all Components
504
+ *
505
+ * @example
506
+ * ```typescript
507
+ * const components = circuit.getAllComponents();
508
+ * console.log(`Circuit has ${components.length} components`);
509
+ *
510
+ * for (const comp of components) {
511
+ * console.log(comp.id, comp.position);
512
+ * }
513
+ * ```
514
+ */
515
+ getAllComponents() {
516
+ return Array.from(this.components.values());
517
+ }
518
+ getAllComponentsByType(e) {
519
+ const t = [];
520
+ for (const i of this.components.values())
521
+ i.type === e && t.push(i);
522
+ return t;
523
+ }
524
+ getFirstComponentOfType(e) {
525
+ for (const t of this.components.values())
526
+ if (t.type === e)
527
+ return t;
528
+ }
529
+ /**
530
+ * Get an electrical node by ID.
531
+ *
532
+ * Note: ENodes are automatically managed and not directly created
533
+ * or removed by users.
534
+ *
535
+ * @param id - ENode UUID
536
+ * @returns The ENode or undefined if not found
537
+ *
538
+ * @example
539
+ * ```typescript
540
+ * const component = circuit.addComponent(
541
+ * new Position(10, 20),
542
+ * new Rotation(0),
543
+ * 2
544
+ * );
545
+ *
546
+ * const pinId = component.pins[0];
547
+ * const enode = circuit.getENode(pinId);
548
+ * console.log(enode.type); // ENodeType.Pin
549
+ * ```
550
+ */
551
+ getENode(e) {
552
+ return this.enodes.get(e);
553
+ }
554
+ /**
555
+ * Get all electrical nodes in the circuit.
556
+ *
557
+ * Includes both pin nodes (from components) and branching point nodes
558
+ * (from wire splits).
559
+ *
560
+ * Returns a new array on each call (defensive copy).
561
+ *
562
+ * @returns Array of all ENodes
563
+ *
564
+ * @example
565
+ * ```typescript
566
+ * const enodes = circuit.getAllENodes();
567
+ * console.log(`Circuit has ${enodes.length} electrical nodes`);
568
+ *
569
+ * for (const enode of enodes) {
570
+ * console.log(enode.id, enode.type);
571
+ * }
572
+ * ```
573
+ */
574
+ getAllENodes() {
575
+ return Array.from(this.enodes.values());
576
+ }
577
+ /**
578
+ * Add a branching point electrical node at a specific position.
579
+ *
580
+ * Branching points are used to split wires and create junctions.
581
+ * @param position - Grid position for the branching point
582
+ * @param sourceType - Optional source type (voltage/current)
583
+ * @returns The created ENode
584
+ */
585
+ addBranchingPoint(e, t) {
586
+ const i = new w(
587
+ g.BranchingPoint,
588
+ void 0,
589
+ void 0,
590
+ e,
591
+ t
592
+ );
593
+ return this.enodes.set(i.id, i), i;
594
+ }
595
+ /**
596
+ * Remove a branching point electrical node from the circuit.
597
+ * Also removes all wires connected to this branching point if there are ony one or more than 2.
598
+ * In the case there are exactly two wires, they will be merged before removing the branching point.
599
+ *
600
+ * @param id - Branching point ENode UUID
601
+ * @throws {Error} If ENode does not exist or is not a branching point
602
+ */
603
+ removeBranchingPoint(e) {
604
+ const t = this.enodes.get(e);
605
+ if (!t)
606
+ throw new Error(`Enode ${e} does not exist`);
607
+ if (t.type !== g.BranchingPoint)
608
+ throw new Error(
609
+ `Enode ${e} is not a branching point, it must be removed with its component.`
610
+ );
611
+ const i = {}, n = this.getWiresByNode(e);
612
+ if (n.length === 1 || n.length > 2) {
613
+ const s = [];
614
+ for (const o of n)
615
+ this.removeWire(o.id), s.push(o.id);
616
+ Object.assign(i, { deletedWires: s });
617
+ } else if (n.length === 2) {
618
+ const s = n[0], o = n[1], a = s.node1 === e ? s.node2 : s.node1, r = o.node1 === e ? o.node2 : o.node1, c = [];
619
+ a === s.node1 ? c.push(...s.intermediatePositions) : a === s.node2 && c.push(...[...s.intermediatePositions].reverse()), c.push(t.getPosition(this)), r === o.node1 ? c.push(...[...o.intermediatePositions].reverse()) : r === o.node2 && c.push(...o.intermediatePositions), this.removeWire(s.id), this.removeWire(o.id);
620
+ const l = this.addWire(a, r, c);
621
+ if (l instanceof Error)
622
+ throw new Error(`Failed to merge wires at branching point ${e}: ${l.message}`);
623
+ Object.assign(i, { mergedWires: [s.id, o.id] }), Object.assign(i, { newWire: l });
624
+ }
625
+ return this.enodes.delete(e), i;
626
+ }
627
+ /**
628
+ * Add a wire connecting two electrical nodes.
629
+ *
630
+ * Validates that both nodes exist, not a self-connection, and no duplicate.
631
+ * Updates bidirectional references (Wire ↔ ENodes).
632
+ *
633
+ * @param node1 - First ENode UUID
634
+ * @param node2 - Second ENode UUID
635
+ * @param intermediatePositions - Optional path waypoints for rendering
636
+ * @returns The created Wire, or Error if validation fails
637
+ *
638
+ * @example
639
+ * ```typescript
640
+ * const comp1 = circuit.addComponent(new Position(0, 0), new Rotation(0), 1);
641
+ * const comp2 = circuit.addComponent(new Position(10, 10), new Rotation(0), 1);
642
+ *
643
+ * const wire = circuit.addWire(comp1.pins[0], comp2.pins[0]);
644
+ * if (wire instanceof Error) {
645
+ * console.error('Failed:', wire.message);
646
+ * }
647
+ * ```
648
+ */
649
+ addWire(e, t, i) {
650
+ if (e === t)
651
+ return new Error("Cannot create wire connecting node to itself");
652
+ const n = this.enodes.get(e), s = this.enodes.get(t);
653
+ if (!n || !s)
654
+ return new Error("Wire requires at least one existing ENode");
655
+ if (this.hasWireBetween(e, t))
656
+ return new Error("Duplicate wire between same nodes");
657
+ const o = new f(e, t, i || []);
658
+ return this.wires.set(o.id, o), n.wires.add(o.id), s.wires.add(o.id), o;
659
+ }
660
+ /**
661
+ * Remove a wire from the circuit.
662
+ *
663
+ * @param id - Wire UUID
664
+ * @throws {Error} If wire does not exist
665
+ *
666
+ * @example
667
+ * ```typescript
668
+ * circuit.removeWire(wireId);
669
+ * // Wire is removed
670
+ * ```
671
+ */
672
+ removeWire(e) {
673
+ const t = this.wires.get(e);
674
+ if (!t)
675
+ throw new Error(`Wire ${e} does not exist`);
676
+ const i = this.enodes.get(t.node1), n = this.enodes.get(t.node2);
677
+ i && i.wires.delete(e), n && n.wires.delete(e), this.wires.delete(e);
678
+ }
679
+ /**
680
+ * Split a wire in the circuit.
681
+ *
682
+ * It creates two new wires connected by either the target enode or a new branching point ENode at the specified position.
683
+ * Returns 2 UUIDS of the new wires.
684
+ *
685
+ * @param id - Wire UUID
686
+ * @param position
687
+ * @throws {Error} If wire does not exist
688
+ *
689
+ * @example
690
+ * ```typescript
691
+ * circuit.splitWire(wireId);
692
+ * // Wire and any orphaned branching points are removed
693
+ * ```
694
+ */
695
+ /**
696
+ * Split an existing wire at a position, creating a branching point.
697
+ * The original wire is removed and replaced with two new wires
698
+ * connecting through the new branching point.
699
+ * NB : in the special case where targetEnode belongs to a component where the wire is already connected
700
+ * only one new wire will be created as this method don't allow a wire directly connecting two pins of the same component.
701
+ *
702
+ * @param wireId - Wire to split
703
+ * @param position - Position for the new branching point : no effect if targetEnodeId provided
704
+ * @param targetEnodeId - if provided, the existing enode to split the wire at
705
+ * @returns Object containing the new branching point and an array of the two new wires
706
+ * @throws Error if wireId not found
707
+ */
708
+ splitWire(e, t, i = null) {
709
+ const n = this.wires.get(e);
710
+ if (!n)
711
+ throw new Error(`Wire ${e} does not exist`);
712
+ const s = this.enodes.get(n.node1), o = this.enodes.get(n.node2);
713
+ if (!s || !o)
714
+ throw new Error(`Wire ${e} is connected to non-existent ENodes`);
715
+ const a = [
716
+ s.getPosition(this),
717
+ ...n.intermediatePositions,
718
+ o.getPosition(this)
719
+ ], r = A(a, t), c = a.slice(1, r), l = a.slice(r, a.length - 1);
720
+ this.wires.delete(e), s.wires.delete(e), o.wires.delete(e);
721
+ let h;
722
+ if (i)
723
+ if (this.enodes.get(i))
724
+ h = this.enodes.get(i);
725
+ else
726
+ throw new Error(`Target ENode ${i} does not exist`);
727
+ else
728
+ h = this.addBranchingPoint(t);
729
+ const S = [];
730
+ if ((!h.component || s.component !== h.component) && !this.hasWireBetween(s.id, h.id)) {
731
+ const u = this.addWire(s.id, h.id, c);
732
+ u instanceof f ? (this.simplifyWireIntermediatePositions(u.id), S.push(u)) : console.warn(`Failure to create wire at split : ${u.message}`);
733
+ }
734
+ if ((!h.component || o.component !== h.component) && !this.hasWireBetween(o.id, h.id)) {
735
+ const u = this.addWire(h.id, o.id, l);
736
+ u instanceof f ? (this.simplifyWireIntermediatePositions(u.id), S.push(u)) : console.warn(`Failure to create wire at split : ${u.message}`);
737
+ }
738
+ return {
739
+ branchingPoint: h,
740
+ wires: S
741
+ };
742
+ }
743
+ getWireBetweenNodes(e, t) {
744
+ const i = this.enodes.get(e);
745
+ if (i)
746
+ for (const n of i.wires) {
747
+ const s = this.wires.get(n);
748
+ if (s && (s.node2 === t || s.node1 === t))
749
+ return s;
750
+ }
751
+ }
752
+ /**
753
+ * Get a wire by ID.
754
+ *
755
+ * @param id - Wire UUID
756
+ * @returns The Wire or undefined if not found
757
+ */
758
+ getWire(e) {
759
+ return this.wires.get(e);
760
+ }
761
+ /**
762
+ * Get all wires in the circuit.
763
+ *
764
+ * Returns a new array on each call (defensive copy).
765
+ *
766
+ * @returns Array of all Wires
767
+ */
768
+ getAllWires() {
769
+ return Array.from(this.wires.values());
770
+ }
771
+ /**
772
+ * Get all wires connected to a specific ENode.
773
+ *
774
+ * @param nodeId - ENode UUID
775
+ * @returns Array of connected Wires, or empty array if node not found
776
+ */
777
+ getWiresByNode(e) {
778
+ const t = this.enodes.get(e);
779
+ if (!t)
780
+ return [];
781
+ const i = [];
782
+ for (const n of t.wires) {
783
+ const s = this.wires.get(n);
784
+ s && i.push(s);
785
+ }
786
+ return i;
787
+ }
788
+ /**
789
+ * Get all wires connected to a component, e.g to any pin enode of the component.
790
+ *
791
+ * @param componentId - Component UUID
792
+ * @returns Array of connected Wires, or empty array if component not found
793
+ */
794
+ getWiresByComponent(e) {
795
+ const t = this.components.get(e);
796
+ if (!t)
797
+ return [];
798
+ const i = [];
799
+ for (const n of t.pins)
800
+ i.push(...this.getWiresByNode(n));
801
+ return i;
802
+ }
803
+ /**
804
+ * Get both ENodes connected by a wire.
805
+ *
806
+ * @param wireId - Wire UUID
807
+ * @returns Tuple [node1, node2] or undefined if wire not found
808
+ */
809
+ getNodesByWire(e) {
810
+ const t = this.wires.get(e);
811
+ if (!t)
812
+ return;
813
+ const i = this.enodes.get(t.node1), n = this.enodes.get(t.node2);
814
+ if (!(!i || !n))
815
+ return [i, n];
816
+ }
817
+ /**
818
+ * Check if a wire already exists between two nodes.
819
+ *
820
+ * Order-independent: returns true for (A, B) or (B, A).
821
+ *
822
+ * @param node1 - First ENode UUID
823
+ * @param node2 - Second ENode UUID
824
+ * @returns true if wire exists, false otherwise
825
+ */
826
+ hasWireBetween(e, t) {
827
+ const i = this.enodes.get(e);
828
+ if (!i)
829
+ return !1;
830
+ for (const n of i.wires) {
831
+ const s = this.wires.get(n);
832
+ if (s && (s.node2 === t || s.node1 === t))
833
+ return !0;
834
+ }
835
+ return !1;
836
+ }
837
+ /**
838
+ * Find all components with pins among the provided enode IDs set.
839
+ *
840
+ * @param pinIds - Set of pins UUIDs
841
+ * @returns Set of components UUIDs
842
+ */
843
+ getComponentsOfPins(e) {
844
+ const t = /* @__PURE__ */ new Set();
845
+ for (const i of e) {
846
+ const n = this.enodes.get(i);
847
+ n?.component && t.add(n.component);
848
+ }
849
+ return t;
850
+ }
851
+ /**
852
+ * Get a component's pin ENode by its label.
853
+ * @param component
854
+ * @param pinLabel
855
+ */
856
+ getComponentPinByLabel(e, t) {
857
+ let i = 0;
858
+ const n = L[e.type], s = Array.from(n.pins.keys());
859
+ for (const o of e.pins) {
860
+ const a = this.enodes.get(o), r = s[i];
861
+ if (r) {
862
+ if (a && r === t)
863
+ return a;
864
+ i++;
865
+ }
866
+ }
867
+ }
868
+ /**
869
+ * Update the intermediate positions of a wire.
870
+ * Update the wire in place.
871
+ *
872
+ * @param wireId - Wire to update
873
+ * @param intermediatePositions - New intermediate positions
874
+ * @param simplify - Whether to simplify positions by removing collinear points : useful when finalizing wire routing
875
+ * @returns The updated Wire
876
+ * @throws Error if wireId not found
877
+ */
878
+ updateWireIntermediatePositions(e, t, i = !1) {
879
+ const n = this.wires.get(e);
880
+ if (!n)
881
+ throw new Error(`Wire ${e} does not exist`);
882
+ if (i) {
883
+ const s = [
884
+ this.enodes.get(n.node1).getPosition(this),
885
+ ...t,
886
+ this.enodes.get(n.node2).getPosition(this)
887
+ ], o = E(s, 10);
888
+ n.intermediatePositions = o.slice(
889
+ 1,
890
+ o.length - 1
891
+ );
892
+ } else
893
+ n.intermediatePositions = t;
894
+ return n;
895
+ }
896
+ /**
897
+ * Simplify intermediate positions of a wire.
898
+ * Update the wire in place.
899
+ * @param wireId - Wire to simplify
900
+ * @returns The updated Wire
901
+ * @throws Error if wireId not found
902
+ */
903
+ simplifyWireIntermediatePositions(e) {
904
+ const t = this.wires.get(e);
905
+ if (!t)
906
+ throw new Error(`Wire ${e} does not exist`);
907
+ const i = [
908
+ this.enodes.get(t.node1).getPosition(this),
909
+ ...t.intermediatePositions,
910
+ this.enodes.get(t.node2).getPosition(this)
911
+ ], n = E(i, 5);
912
+ return t.intermediatePositions = n.slice(
913
+ 1,
914
+ n.length - 1
915
+ ), t.intermediatePositions = E(t.intermediatePositions), t;
916
+ }
917
+ /**
918
+ * Update the source type of an ENode (branching point or component pin).
919
+ * @param enodeId - ENode to update
920
+ * @param sourceType - New source type (null to clear)
921
+ * @throws Error if enodeId not found
922
+ */
923
+ updateENodeSourceType(e, t) {
924
+ const i = this.enodes.get(e);
925
+ if (!i)
926
+ throw new Error(`ENode ${e} does not exist`);
927
+ i.source = t || void 0;
928
+ }
929
+ /**
930
+ * iterate through all components, enodes and wires positions to get the size that allows to enclose all elements.
931
+ * @param margin - optional margin to add to the size
932
+ * @returns size that allows to enclose all elements plus margin
933
+ */
934
+ getEnclosingSize(e = 0) {
935
+ let t = 0;
936
+ for (const i of this.components.values())
937
+ t = Math.max(t, Math.abs(i.position.x), Math.abs(i.position.y));
938
+ for (const i of this.enodes.values()) {
939
+ if (i.type === g.Pin) continue;
940
+ const n = i.position;
941
+ n && (t = Math.max(t, Math.abs(n.x), Math.abs(n.y)));
942
+ }
943
+ for (const i of this.wires.values())
944
+ for (const n of i.intermediatePositions)
945
+ t = Math.max(t, Math.abs(n.x), Math.abs(n.y));
946
+ return Math.ceil(t * 2 + Math.max(e, 0));
947
+ }
948
+ /**
949
+ * Serialize circuit to JSON.
950
+ *
951
+ * @returns JSON-serializable object containing all components, enodes, and wires
952
+ *
953
+ * @example
954
+ * ```typescript
955
+ * const json = circuit.toJSON();
956
+ * localStorage.setItem('my-circuit', JSON.stringify(json));
957
+ * ```
958
+ */
959
+ toJSON() {
960
+ return {
961
+ metadata: this.metadata.toJSON(),
962
+ components: this.getAllComponents().map((e) => e.toJSON()),
963
+ enodes: this.getAllENodes().map((e) => e.toJSON()),
964
+ wires: this.getAllWires().map((e) => e.toJSON())
965
+ };
966
+ }
967
+ /**
968
+ * Deserialize circuit from JSON.
969
+ *
970
+ * @param json - Circuit data
971
+ * @returns Circuit instance
972
+ * @throws {Error} If JSON is invalid or violates invariants
973
+ *
974
+ * @example
975
+ * ```typescript
976
+ * const jsonStr = localStorage.getItem('my-circuit');
977
+ * const json = JSON.parse(jsonStr);
978
+ * const circuit = Circuit.fromJSON(json);
979
+ * ```
980
+ */
981
+ static fromJSON(e) {
982
+ const t = new O();
983
+ t.metadata = v.fromJSON(e.metadata);
984
+ for (const i of e.components) {
985
+ const n = C.fromJSON(
986
+ i
987
+ );
988
+ t.components.set(n.id, n);
989
+ }
990
+ for (const i of e.enodes) {
991
+ const n = w.fromJSON(
992
+ i
993
+ );
994
+ t.enodes.set(n.id, n);
995
+ }
996
+ if (e.wires)
997
+ for (const i of e.wires) {
998
+ const n = f.fromJSON(
999
+ i
1000
+ );
1001
+ t.wires.set(n.id, n);
1002
+ const s = t.enodes.get(n.node1), o = t.enodes.get(n.node2);
1003
+ s && s.wires.add(n.id), o && o.wires.add(n.id);
1004
+ }
1005
+ return t;
1006
+ }
1007
+ }
1008
+ class Q {
1009
+ behaviors;
1010
+ /**
1011
+ * Create a new empty behavior registry.
1012
+ */
1013
+ constructor() {
1014
+ this.behaviors = /* @__PURE__ */ new Map();
1015
+ }
1016
+ /**
1017
+ * Register a behavior for a component type.
1018
+ * Overwrites any existing behavior for the same type.
1019
+ *
1020
+ * @param behavior - The component behavior to register
1021
+ * @throws TypeError if behavior is null/undefined or componentType is empty
1022
+ * @returns The registry instance for chaining
1023
+ */
1024
+ register(e) {
1025
+ if (!e)
1026
+ throw new TypeError("Behavior cannot be null or undefined");
1027
+ if (!e.componentType || e.componentType.trim() === "")
1028
+ throw new TypeError("Behavior componentType cannot be empty");
1029
+ return this.behaviors.set(e.componentType, e), this;
1030
+ }
1031
+ /**
1032
+ * Register multiple behaviors at once.
1033
+ * Convenience method for bulk registration.
1034
+ *
1035
+ * @param behaviors - Array of behaviors to register
1036
+ */
1037
+ registerAll(e) {
1038
+ e.forEach((t) => this.register(t));
1039
+ }
1040
+ /**
1041
+ * Get the behavior for a component type.
1042
+ *
1043
+ * @param componentType - Type identifier (e.g., "battery", "led")
1044
+ * @returns The registered behavior, or undefined if not found
1045
+ */
1046
+ get(e) {
1047
+ return this.behaviors.get(e);
1048
+ }
1049
+ /**
1050
+ * Check if a behavior is registered for a component type.
1051
+ *
1052
+ * @param componentType - Type identifier to check
1053
+ * @returns True if behavior is registered
1054
+ */
1055
+ has(e) {
1056
+ return this.behaviors.has(e);
1057
+ }
1058
+ /**
1059
+ * Unregister a behavior for a component type.
1060
+ *
1061
+ * @param componentType - Type identifier to unregister
1062
+ * @returns True if behavior was found and removed
1063
+ */
1064
+ unregister(e) {
1065
+ return this.behaviors.delete(e);
1066
+ }
1067
+ /**
1068
+ * Clear all registered behaviors.
1069
+ */
1070
+ clear() {
1071
+ this.behaviors.clear();
1072
+ }
1073
+ /**
1074
+ * Get all registered component types.
1075
+ *
1076
+ * @returns Array of component type identifiers
1077
+ */
1078
+ getRegisteredTypes() {
1079
+ return Array.from(this.behaviors.keys());
1080
+ }
1081
+ /**
1082
+ * Get count of registered behaviors.
1083
+ *
1084
+ * @returns Number of registered behaviors
1085
+ */
1086
+ size() {
1087
+ return this.behaviors.size;
1088
+ }
1089
+ }
1090
+ class m {
1091
+ /**
1092
+ * Component UUID this state belongs to.
1093
+ * @readonly
1094
+ */
1095
+ componentId;
1096
+ /**
1097
+ * Current operational state (varies by component type).
1098
+ * Examples: "on", "off", "open", "closed", "activating", "active"
1099
+ */
1100
+ state;
1101
+ /**
1102
+ * Tick when this state started.
1103
+ */
1104
+ startTick;
1105
+ /**
1106
+ * Create a new component state.
1107
+ *
1108
+ * @param componentId - UUID of the component
1109
+ * @param initialState - Initial operational state
1110
+ */
1111
+ constructor(e, t) {
1112
+ this.componentId = e, this.state = t, this.startTick = 0;
1113
+ }
1114
+ hasSameComponent(e) {
1115
+ return this.componentId === e.componentId;
1116
+ }
1117
+ }
1118
+ class B extends m {
1119
+ /**
1120
+ * Create a new battery state.
1121
+ *
1122
+ * @param componentId - UUID of the battery component
1123
+ */
1124
+ constructor(e) {
1125
+ super(e, "on");
1126
+ }
1127
+ }
1128
+ class k {
1129
+ componentType = p.Battery;
1130
+ /**
1131
+ * Create initial state for a battery.
1132
+ *
1133
+ * @param component - The Battery component
1134
+ * @returns Battery Initial state (always active and delivering voltage)
1135
+ */
1136
+ createInitialState(e) {
1137
+ if (e.type !== p.Battery)
1138
+ throw new Error(`Invalid component type for BatteryBehavior: ${e.type}`);
1139
+ return new B(e.id);
1140
+ }
1141
+ allowConductivity(e, t, i, n, s) {
1142
+ return !1;
1143
+ }
1144
+ /**
1145
+ * Batteries are always on, and their pins are locked so this is more of a decorative function
1146
+ * @param component
1147
+ * @param componentState
1148
+ * @param nodeStates
1149
+ * @param _targetTick
1150
+ */
1151
+ onPinsChange(e, t, i, n) {
1152
+ const s = /* @__PURE__ */ new Map();
1153
+ for (const o in e.pins)
1154
+ s.set(e.getPinLabel(o), i.get(o));
1155
+ return {
1156
+ componentState: t,
1157
+ hasChanged: !1,
1158
+ scheduledEvents: []
1159
+ };
1160
+ }
1161
+ onUserCommand(e, t, i) {
1162
+ return {
1163
+ componentState: t,
1164
+ hasChanged: !1,
1165
+ scheduledEvents: []
1166
+ };
1167
+ }
1168
+ onEventFiring(e, t, i) {
1169
+ return {
1170
+ componentState: t,
1171
+ hasChanged: !1,
1172
+ scheduledEvents: []
1173
+ };
1174
+ }
1175
+ }
1176
+ class W extends m {
1177
+ /**
1178
+ * Create a new Lightbulb state.
1179
+ *
1180
+ * @param componentId - UUID of the Lightbulb component
1181
+ * @param initialState - Initial operational state (default: "off")
1182
+ */
1183
+ constructor(e, t = "off") {
1184
+ super(e, t);
1185
+ }
1186
+ /**
1187
+ * Check if Lightbulb is in lit state (on or going_on)
1188
+ */
1189
+ get isLit() {
1190
+ return this.state === "on" || this.state === "goingOn";
1191
+ }
1192
+ }
1193
+ class x {
1194
+ componentType = p.Lightbulb;
1195
+ /**
1196
+ * Create initial state for a lightbulb.
1197
+ *
1198
+ * @param component - The lightbulb component
1199
+ * @returns lightbulbInitial state (always active and delivering voltage)
1200
+ */
1201
+ createInitialState(e) {
1202
+ if (e.type !== p.Lightbulb)
1203
+ throw new Error(`Invalid component type for lightbulbBehavior: ${e.type}`);
1204
+ return new W(e.id);
1205
+ }
1206
+ allowConductivity(e, t, i, n, s) {
1207
+ return !0;
1208
+ }
1209
+ /**
1210
+ * @param component
1211
+ * @param state
1212
+ * @param nodeStates
1213
+ * @param targetTick
1214
+ */
1215
+ onPinsChange(e, t, i, n) {
1216
+ const s = /* @__PURE__ */ new Map();
1217
+ for (const c of e.pins)
1218
+ s.set(e.getPinLabel(c), i.get(c));
1219
+ let o = s.get("pin1").hasVoltage && s.get("pin1").hasCurrent || s.get("pin2").hasVoltage && s.get("pin2").hasCurrent || s.get("pin1").hasVoltage && s.get("pin2").hasCurrent || s.get("pin2").hasVoltage && s.get("pin1").hasCurrent, a = !1;
1220
+ const r = [];
1221
+ return o ? (t.state === "off" || t.state === "goingOff") && (a = !0, t.state = "goingOn", t.startTick = n, r.push({
1222
+ targetId: e.id,
1223
+ scheduledAtTick: n,
1224
+ readyAtTick: n + 1,
1225
+ // TODO handle component config later
1226
+ type: "GoingOnEnd",
1227
+ parameters: void 0
1228
+ }), t.state = "goingOn") : (t.state === "on" || t.state === "goingOn") && (a = !0, t.state = "goingOff", t.startTick = n, r.push({
1229
+ targetId: e.id,
1230
+ scheduledAtTick: n,
1231
+ readyAtTick: n + 1,
1232
+ // TODO handle component config later
1233
+ type: "GoingOffEnd",
1234
+ parameters: void 0
1235
+ }), t.state = "goingOff"), {
1236
+ componentState: t,
1237
+ hasChanged: a,
1238
+ scheduledEvents: r
1239
+ };
1240
+ }
1241
+ onUserCommand(e, t, i) {
1242
+ return {
1243
+ componentState: t,
1244
+ hasChanged: !1,
1245
+ scheduledEvents: []
1246
+ };
1247
+ }
1248
+ onEventFiring(e, t, i) {
1249
+ let n = !1;
1250
+ return i.type === "GoingOffEnd" ? t.state !== "off" && (n = !0, t.startTick = i.readyAtTick, t.state = "off") : i.type === "GoingOnEnd" && t.state !== "on" && (n = !0, t.startTick = i.readyAtTick, t.state = "on"), {
1251
+ componentState: t,
1252
+ hasChanged: n,
1253
+ scheduledEvents: []
1254
+ };
1255
+ }
1256
+ }
1257
+ class N extends m {
1258
+ /**
1259
+ * Create a new SmallLED state.
1260
+ *
1261
+ * @param componentId - UUID of the LED component
1262
+ * @param initialState - Initial operational state (default: "off")
1263
+ */
1264
+ constructor(e, t = "off") {
1265
+ super(e, t);
1266
+ }
1267
+ /**
1268
+ * Check if LED is in lit state (on or going_on)
1269
+ */
1270
+ get isLit() {
1271
+ return this.state === "on" || this.state === "goingOn";
1272
+ }
1273
+ }
1274
+ class I {
1275
+ componentType = p.SmallLED;
1276
+ /**
1277
+ * Create initial state for a smallLED.
1278
+ *
1279
+ * @param component - The smallLED component
1280
+ * @returns LED Initial state (always active and delivering voltage)
1281
+ */
1282
+ createInitialState(e) {
1283
+ if (e.type !== p.SmallLED)
1284
+ throw new Error(`Invalid component type for SmallLEDBehavior: ${e.type}`);
1285
+ return new N(e.id);
1286
+ }
1287
+ allowConductivity(e, t, i, n, s) {
1288
+ return !0;
1289
+ }
1290
+ /**
1291
+ * only symmetrical behavior of LEDS is handled for now
1292
+ * @param component
1293
+ * @param state
1294
+ * @param nodeStates
1295
+ * @param targetTick
1296
+ */
1297
+ onPinsChange(e, t, i, n) {
1298
+ const s = /* @__PURE__ */ new Map();
1299
+ for (const c of e.pins)
1300
+ s.set(e.getPinLabel(c), i.get(c));
1301
+ let o = s.get("anode").hasVoltage && s.get("anode").hasCurrent || s.get("cathode").hasVoltage && s.get("cathode").hasCurrent || s.get("anode").hasVoltage && s.get("cathode").hasCurrent || s.get("cathode").hasVoltage && s.get("anode").hasCurrent, a = !1;
1302
+ const r = [];
1303
+ return o ? (t.state === "off" || t.state === "goingOff") && (a = !0, t.state = "goingOn", t.startTick = n, r.push({
1304
+ targetId: e.id,
1305
+ scheduledAtTick: n,
1306
+ readyAtTick: n + 1,
1307
+ // TODO handle component config later
1308
+ type: "GoingOnEnd",
1309
+ parameters: void 0
1310
+ })) : (t.state === "on" || t.state === "goingOn") && (a = !0, t.state = "goingOff", t.startTick = n, r.push({
1311
+ targetId: e.id,
1312
+ scheduledAtTick: n,
1313
+ readyAtTick: n + 1,
1314
+ // TODO handle component config later
1315
+ type: "GoingOffEnd",
1316
+ parameters: void 0
1317
+ })), {
1318
+ componentState: t,
1319
+ hasChanged: a,
1320
+ scheduledEvents: r
1321
+ };
1322
+ }
1323
+ onUserCommand(e, t, i) {
1324
+ return {
1325
+ componentState: t,
1326
+ hasChanged: !1,
1327
+ scheduledEvents: []
1328
+ };
1329
+ }
1330
+ onEventFiring(e, t, i) {
1331
+ let n = !1;
1332
+ return i.type === "GoingOffEnd" ? t.state !== "off" && (n = !0, t.startTick = i.readyAtTick, t.state = "off") : i.type === "GoingOnEnd" && t.state !== "on" && (n = !0, t.startTick = i.readyAtTick, t.state = "on"), {
1333
+ componentState: t,
1334
+ hasChanged: n,
1335
+ scheduledEvents: []
1336
+ };
1337
+ }
1338
+ }
1339
+ class $ extends N {
1340
+ }
1341
+ class M extends I {
1342
+ componentType = p.RectangleLED;
1343
+ /**
1344
+ * Create initial state for a RectangleLED.
1345
+ *
1346
+ * @param component - The smallLED component
1347
+ * @returns LED Initial state (always active and delivering voltage)
1348
+ */
1349
+ createInitialState(e) {
1350
+ if (e.type !== p.RectangleLED)
1351
+ throw new Error(`Invalid component type for RectangleLEDBehavior: ${e.type}`);
1352
+ return new $(e.id);
1353
+ }
1354
+ }
1355
+ class J extends m {
1356
+ /**
1357
+ * Create a new Relay state.
1358
+ *
1359
+ * @param componentId - UUID of the Relay component
1360
+ * @param initialState - Initial operational state (default: "open")
1361
+ */
1362
+ constructor(e, t = "open") {
1363
+ super(e, t);
1364
+ }
1365
+ /**
1366
+ * Check if relay is in opening or closing state
1367
+ */
1368
+ get isInTransition() {
1369
+ return this.state === "closing" || this.state === "opening";
1370
+ }
1371
+ /**
1372
+ * Check if relay is in closed or closing state
1373
+ */
1374
+ get isClosed() {
1375
+ return this.state === "closed" || this.state === "closing";
1376
+ }
1377
+ }
1378
+ function D(d) {
1379
+ const e = parseInt(d.get("transitionSpan") || "", 10);
1380
+ return isNaN(e) || e < 1 ? y.TRANSITION_SPAN_TICKS : e;
1381
+ }
1382
+ class R {
1383
+ componentType = p.Relay;
1384
+ /**
1385
+ * Create initial state for a relay.
1386
+ *
1387
+ * @param component - The Relay component
1388
+ * @returns Relay Initial state (open by default)
1389
+ */
1390
+ createInitialState(e) {
1391
+ if (e.type !== p.Relay)
1392
+ throw new Error(`Invalid component type for RelayBehavior: ${e.type}`);
1393
+ const t = e.config.get("activationLogic") === "negative" ? "closed" : "open";
1394
+ return new J(e.id, t);
1395
+ }
1396
+ allowConductivity(e, t, i, n, s) {
1397
+ if (n === s) return !0;
1398
+ const o = e.getPinLabel(n), a = e.getPinLabel(s);
1399
+ if (!o || !a) return !1;
1400
+ const r = [o, a];
1401
+ return r.includes("cmd_in") && r.includes("cmd_out") ? !0 : r.includes("power_in") && r.includes("power_out") ? t.state === "closed" || t.state === "opening" : !1;
1402
+ }
1403
+ /**
1404
+ * Relay cmd pins need to have voltage and current so that relay contactor stays closed
1405
+ * @param component
1406
+ * @param state
1407
+ * @param nodeStates
1408
+ * @param targetTick
1409
+ */
1410
+ onPinsChange(e, t, i, n) {
1411
+ const s = /* @__PURE__ */ new Map();
1412
+ for (const h of e.pins)
1413
+ s.set(e.getPinLabel(h), i.get(h));
1414
+ const o = s.get("cmd_in").hasVoltage && s.get("cmd_in").hasCurrent || s.get("cmd_out").hasVoltage && s.get("cmd_out").hasCurrent || s.get("cmd_in").hasVoltage && s.get("cmd_out").hasCurrent || s.get("cmd_out").hasVoltage && s.get("cmd_in").hasCurrent, a = e.config.get("activationLogic") === "negative" ? !o : o;
1415
+ let r = !1;
1416
+ const c = [], l = D(e.config);
1417
+ return a ? (t.state === "open" || t.state === "opening") && (r = !0, t.state = "closing", t.startTick = n, c.push({
1418
+ targetId: e.id,
1419
+ scheduledAtTick: n,
1420
+ readyAtTick: n + l,
1421
+ type: "ClosingEnd",
1422
+ parameters: void 0
1423
+ })) : (t.state === "closed" || t.state === "closing") && (r = !0, t.state = "opening", t.startTick = n, c.push({
1424
+ targetId: e.id,
1425
+ scheduledAtTick: n,
1426
+ readyAtTick: n + l,
1427
+ type: "OpeningEnd",
1428
+ parameters: void 0
1429
+ })), {
1430
+ componentState: t,
1431
+ hasChanged: r,
1432
+ scheduledEvents: c
1433
+ };
1434
+ }
1435
+ onUserCommand(e, t, i) {
1436
+ return {
1437
+ componentState: t,
1438
+ hasChanged: !1,
1439
+ scheduledEvents: []
1440
+ };
1441
+ }
1442
+ onEventFiring(e, t, i) {
1443
+ let n = !1;
1444
+ return i.type === "ClosingEnd" ? t.state !== "closed" && (n = !0, t.startTick = i.readyAtTick, t.state = "closed") : i.type === "OpeningEnd" && t.state !== "open" && (n = !0, t.startTick = i.readyAtTick, t.state = "open"), {
1445
+ componentState: t,
1446
+ hasChanged: n,
1447
+ scheduledEvents: []
1448
+ };
1449
+ }
1450
+ }
1451
+ class F extends m {
1452
+ /**
1453
+ * Create a new Switch state.
1454
+ *
1455
+ * @param componentId - UUID of the Switch component
1456
+ * @param initialState - Initial operational state (default: "open")
1457
+ */
1458
+ constructor(e, t = "open") {
1459
+ super(e, t);
1460
+ }
1461
+ /**
1462
+ * Check if switch is in opening or closing state
1463
+ */
1464
+ get isInTransition() {
1465
+ return this.state === "closing" || this.state === "opening";
1466
+ }
1467
+ /**
1468
+ * Check if switch is in closed or closing state
1469
+ */
1470
+ get isClosed() {
1471
+ return this.state === "closed" || this.state === "closing";
1472
+ }
1473
+ }
1474
+ class V extends m {
1475
+ /**
1476
+ * Create a new Transistor state.
1477
+ *
1478
+ * @param componentId - UUID of the Transistor component
1479
+ * @param initialState - Initial operational state (default: "open")
1480
+ */
1481
+ constructor(e, t = "open") {
1482
+ super(e, t);
1483
+ }
1484
+ /**
1485
+ * Check if transistor is in opening or closing state
1486
+ */
1487
+ get isInTransition() {
1488
+ return this.state === "closing" || this.state === "opening";
1489
+ }
1490
+ /**
1491
+ * Check if transistor is in closed or closing state
1492
+ */
1493
+ get isClosed() {
1494
+ return this.state === "closed" || this.state === "closing";
1495
+ }
1496
+ }
1497
+ function U(d) {
1498
+ if (!d)
1499
+ return y.TRANSITION_SPAN_TICKS;
1500
+ const e = parseInt(d.get("tickCount") || "", 10);
1501
+ return isNaN(e) || e < 1 ? y.TRANSITION_SPAN_TICKS : e;
1502
+ }
1503
+ class z {
1504
+ componentType = p.Switch;
1505
+ /**
1506
+ * Create initial state for a switch.
1507
+ *
1508
+ * @param component - The Switch component
1509
+ * @returns Switch Initial state (open by default)
1510
+ */
1511
+ createInitialState(e) {
1512
+ if (e.type !== p.Switch)
1513
+ throw new Error(`Invalid component type for SwitchBehavior: ${e.type}`);
1514
+ const t = e.config.get("initialState") || "open";
1515
+ return new F(e.id, t);
1516
+ }
1517
+ allowConductivity(e, t, i, n, s) {
1518
+ return t.state === "closed" || t.state === "opening";
1519
+ }
1520
+ /**
1521
+ * Switches states depend on user interaction, not their pins so this is more of a decorative function
1522
+ * @param _component
1523
+ * @param componentState
1524
+ * @param _nodeStates
1525
+ * @param _targetTick
1526
+ */
1527
+ onPinsChange(e, t, i, n) {
1528
+ return {
1529
+ componentState: t,
1530
+ hasChanged: !1,
1531
+ scheduledEvents: []
1532
+ };
1533
+ }
1534
+ onUserCommand(e, t, i) {
1535
+ let n = !1;
1536
+ const s = [];
1537
+ if (i.type === "toggle_switch" && ["open", "closed"].includes(t.state)) {
1538
+ t.state = t.state === "open" ? "closing" : "opening", t.startTick = i.scheduledAtTick + 1, n = !0;
1539
+ const o = U(i.parameters);
1540
+ s.push({
1541
+ targetId: e.id,
1542
+ scheduledAtTick: t.startTick,
1543
+ readyAtTick: t.startTick + o,
1544
+ type: t.state === "closing" ? "ClosingEnd" : "OpeningEnd",
1545
+ parameters: void 0
1546
+ });
1547
+ }
1548
+ return {
1549
+ componentState: t,
1550
+ hasChanged: n,
1551
+ scheduledEvents: s
1552
+ };
1553
+ }
1554
+ onEventFiring(e, t, i) {
1555
+ let n = !1;
1556
+ return i.type === "ClosingEnd" ? t.state !== "closed" && (n = !0, t.startTick = i.readyAtTick, t.state = "closed") : i.type === "OpeningEnd" && t.state !== "open" && (n = !0, t.startTick = i.readyAtTick, t.state = "open"), {
1557
+ componentState: t,
1558
+ hasChanged: n,
1559
+ scheduledEvents: []
1560
+ };
1561
+ }
1562
+ }
1563
+ function G(d) {
1564
+ const e = parseInt(d.get("transitionSpan") || "", 10);
1565
+ return isNaN(e) || e < 1 ? y.TRANSITION_SPAN_TICKS : e;
1566
+ }
1567
+ class K {
1568
+ componentType = p.Transistor;
1569
+ /**
1570
+ * Create initial state for a transistor.
1571
+ *
1572
+ * @param component - The Transistor component
1573
+ * @returns Transistor Initial state (open by default)
1574
+ */
1575
+ createInitialState(e) {
1576
+ if (e.type !== p.Transistor)
1577
+ throw new Error(`Invalid component type for TransistorBehavior: ${e.type}`);
1578
+ const t = e.config.get("activationLogic") === "negative" ? "closed" : "open";
1579
+ return new V(e.id, t);
1580
+ }
1581
+ allowConductivity(e, t, i, n, s) {
1582
+ if (n === s) return !0;
1583
+ const o = e.getPinLabel(n), a = e.getPinLabel(s);
1584
+ if (!o || !a) return !1;
1585
+ const r = [o, a];
1586
+ return r.includes("collector") && r.includes("emitter") ? t.state === "closed" || t.state === "opening" : !1;
1587
+ }
1588
+ /**
1589
+ * Transistor Base need to have only voltage so that transistor contactor stays closed
1590
+ * @param component
1591
+ * @param state
1592
+ * @param nodeStates
1593
+ * @param targetTick
1594
+ */
1595
+ onPinsChange(e, t, i, n) {
1596
+ const s = /* @__PURE__ */ new Map();
1597
+ for (const h of e.pins)
1598
+ s.set(e.getPinLabel(h), i.get(h));
1599
+ const o = s.get("base").hasVoltage, a = e.config.get("activationLogic") === "negative" ? !o : o;
1600
+ let r = !1;
1601
+ const c = [], l = G(e.config);
1602
+ return a ? (t.state === "open" || t.state === "opening") && (r = !0, t.state = "closing", t.startTick = n, c.push({
1603
+ targetId: e.id,
1604
+ scheduledAtTick: n,
1605
+ readyAtTick: n + l,
1606
+ type: "ClosingEnd",
1607
+ parameters: void 0
1608
+ })) : (t.state === "closed" || t.state === "closing") && (r = !0, t.state = "opening", t.startTick = n, c.push({
1609
+ targetId: e.id,
1610
+ scheduledAtTick: n,
1611
+ readyAtTick: n + l,
1612
+ type: "OpeningEnd",
1613
+ parameters: void 0
1614
+ })), {
1615
+ componentState: t,
1616
+ hasChanged: r,
1617
+ scheduledEvents: c
1618
+ };
1619
+ }
1620
+ onUserCommand(e, t, i) {
1621
+ return {
1622
+ componentState: t,
1623
+ hasChanged: !1,
1624
+ scheduledEvents: []
1625
+ };
1626
+ }
1627
+ onEventFiring(e, t, i) {
1628
+ let n = !1;
1629
+ return i.type === "ClosingEnd" ? t.state !== "closed" && (n = !0, t.startTick = i.readyAtTick, t.state = "closed") : i.type === "OpeningEnd" && t.state !== "open" && (n = !0, t.startTick = i.readyAtTick, t.state = "open"), {
1630
+ componentState: t,
1631
+ hasChanged: n,
1632
+ scheduledEvents: []
1633
+ };
1634
+ }
1635
+ }
1636
+ function Y(d) {
1637
+ d.register(new k()).register(new x()).register(new M()).register(new R()).register(new I()).register(new z()).register(new K());
1638
+ }
1639
+ export {
1640
+ k as BatteryBehavior,
1641
+ B as BatteryState,
1642
+ Q as BehaviorRegistry,
1643
+ L as COMPONENT_TYPE_METADATA,
1644
+ b as CameraOptions,
1645
+ O as Circuit,
1646
+ v as CircuitMetadata,
1647
+ Z as CircuitRunner,
1648
+ C as Component,
1649
+ m as ComponentState,
1650
+ p as ComponentType,
1651
+ j as DirtyTracker,
1652
+ w as ENode,
1653
+ ee as ENodeSourceType,
1654
+ g as ENodeType,
1655
+ te as EventQueue,
1656
+ x as LightbulbBehavior,
1657
+ W as LightbulbState,
1658
+ P as Position,
1659
+ ne as Position3D,
1660
+ M as RectangleLEDBehavior,
1661
+ $ as RectangleLEDState,
1662
+ R as RelayBehavior,
1663
+ J as RelayState,
1664
+ ie as Rotation,
1665
+ se as SIMULATION_SPEED,
1666
+ oe as SimulationState,
1667
+ I as SmallLEDBehavior,
1668
+ N as SmallLEDState,
1669
+ re as StateManager,
1670
+ z as SwitchBehavior,
1671
+ F as SwitchState,
1672
+ y as TRANSITION_DEFAULTS,
1673
+ K as TransistorBehavior,
1674
+ V as TransistorState,
1675
+ f as Wire,
1676
+ A as findPositionBestIndex,
1677
+ T as generateUUID,
1678
+ ae as getAllComponentTypes,
1679
+ _ as getComponentTypeMetadata,
1680
+ Y as registerBasicComponentsBehaviors,
1681
+ E as simplifyPositions
1682
+ };
1683
+ //# sourceMappingURL=index.js.map