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,1477 @@
1
+ class w {
2
+ /**
3
+ * Create a new 3D position.
4
+ *
5
+ * @param x - X coordinate
6
+ * @param y - Y coordinate
7
+ * @param z - Z coordinate
8
+ */
9
+ constructor(t, e, r) {
10
+ this.x = t, this.y = e, this.z = r;
11
+ }
12
+ /**
13
+ * Check if this position equals another position.
14
+ *
15
+ * @param other - Position to compare with
16
+ * @returns true if x,y and z coordinates are equal
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const p1 = new Position(10, 20, 40);
21
+ * const p2 = new Position(10, 20, 40);
22
+ * const p3 = new Position(15, 20, 40);
23
+ *
24
+ * console.log(p1.equals(p2)); // true
25
+ * console.log(p1.equals(p3)); // false
26
+ * ```
27
+ */
28
+ equals(t) {
29
+ return this.x === t.x && this.y === t.y && this.z === t.z;
30
+ }
31
+ /**
32
+ * Serialize position to JSON.
33
+ *
34
+ * @returns Plain object with x, y and z properties
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const pos = new Position(10, 20, 40);
39
+ * const json = pos.toJSON();
40
+ * console.log(json); // { x: 10, y: 20, z: 40 }
41
+ * ```
42
+ */
43
+ toJSON() {
44
+ return { x: this.x, y: this.y, z: this.z };
45
+ }
46
+ /**
47
+ * Deserialize position from JSON.
48
+ *
49
+ * @param json - Plain object with x, y and z properties
50
+ * @returns Position instance
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * const json = { x: 10, y: 20, z: 40 };
55
+ * const pos = Position.fromJSON(json);
56
+ * console.log(pos.x); // 10
57
+ * ```
58
+ */
59
+ static fromJSON(t) {
60
+ return new w(t.x, t.y, t.z);
61
+ }
62
+ /**
63
+ * String representation for debugging.
64
+ *
65
+ * @returns String in format "Position(x, y, z)"
66
+ */
67
+ toString() {
68
+ return `Position(${this.x}, ${this.y}, ${this.z})`;
69
+ }
70
+ }
71
+ class k {
72
+ /**
73
+ * Create new camera options.
74
+ *
75
+ * @param position - Camera position (default: 0, 15, 15)
76
+ * @param lookAtPosition - Camera look-at target position (default: 0, 0, 0)
77
+ * @param fov - Field of view in degrees (default: 75)
78
+ * @param near - Near clipping plane distance (default: 0.1)
79
+ * @param far - Far clipping plane distance (default: 1000)
80
+ */
81
+ constructor(t = new w(0, 15, 15), e = new w(0, 0, 0), r = 75, i = 0.1, o = 1e3) {
82
+ this.position = t, this.lookAtPosition = e, this.fov = r, this.near = i, this.far = o;
83
+ }
84
+ /**
85
+ * Serialize camera options to JSON.
86
+ *
87
+ * @returns Plain object with camera configuration
88
+ *
89
+ * @example
90
+ * ```typescript
91
+ * const opts = new CameraOptions();
92
+ * const json = opts.toJSON();
93
+ * console.log(json);
94
+ * // {
95
+ * // position: { x: 0, y: 15, z: 0 },
96
+ * // lookAtPosition: { x: 0, y: 0, z: 0 },
97
+ * // fov: 75,
98
+ * // near: 0.1,
99
+ * // far: 1000
100
+ * // }
101
+ * ```
102
+ */
103
+ toJSON() {
104
+ return {
105
+ position: this.position.toJSON(),
106
+ lookAtPosition: this.lookAtPosition.toJSON(),
107
+ fov: this.fov,
108
+ near: this.near,
109
+ far: this.far
110
+ };
111
+ }
112
+ /**
113
+ * Deserialize camera options from JSON.
114
+ *
115
+ * @param json - Plain object with camera configuration
116
+ * @returns CameraOptions instance
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * const json = {
121
+ * position: { x: 0, y: 20, z: 10 },
122
+ * lookAtPosition: { x: 5, y: 0, z: 0 },
123
+ * fov: 60,
124
+ * near: 0.5,
125
+ * far: 2000
126
+ * };
127
+ * const opts = CameraOptions.fromJSON(json);
128
+ * console.log(opts.position.y); // 20
129
+ * ```
130
+ */
131
+ static fromJSON(t) {
132
+ return new k(
133
+ w.fromJSON(t.position),
134
+ w.fromJSON(t.lookAtPosition),
135
+ t.fov,
136
+ t.near,
137
+ t.far
138
+ );
139
+ }
140
+ /**
141
+ * String representation for debugging.
142
+ *
143
+ * @returns String with camera configuration details
144
+ */
145
+ toString() {
146
+ return `CameraOptions(position: ${this.position.toString()}, lookAt: ${this.lookAtPosition.toString()}, fov: ${this.fov}, near: ${this.near}, far: ${this.far})`;
147
+ }
148
+ }
149
+ var S = /* @__PURE__ */ ((n) => (n.Voltage = "Voltage", n.Current = "Current", n))(S || {}), T = /* @__PURE__ */ ((n) => (n.Battery = "battery", n.Switch = "switch", n.Lightbulb = "lightbulb", n.Relay = "relay", n.Transistor = "transistor", n.SmallLED = "smallLED", n.RectangleLED = "rectangleLED", n.Cube = "cube", n.Label = "label", n))(T || {});
150
+ const E = {
151
+ switch: {
152
+ id: "switch",
153
+ name: "Switch",
154
+ pins: /* @__PURE__ */ new Map([
155
+ ["input", void 0],
156
+ ["output", void 0]
157
+ ]),
158
+ config: /* @__PURE__ */ new Map([
159
+ ["initialState", "open"],
160
+ ["size", "1"]
161
+ ])
162
+ },
163
+ battery: {
164
+ id: "battery",
165
+ name: "Battery",
166
+ pins: /* @__PURE__ */ new Map([
167
+ ["cathode", S.Voltage],
168
+ ["anode", S.Current]
169
+ ]),
170
+ config: /* @__PURE__ */ new Map([])
171
+ },
172
+ lightbulb: {
173
+ id: "lightbulb",
174
+ name: "Lightbulb",
175
+ pins: /* @__PURE__ */ new Map([
176
+ ["pin1", void 0],
177
+ ["pin2", void 0]
178
+ ]),
179
+ config: /* @__PURE__ */ new Map([["size", "1"]])
180
+ },
181
+ relay: {
182
+ id: "relay",
183
+ name: "Relay",
184
+ pins: /* @__PURE__ */ new Map([
185
+ ["cmd_in", void 0],
186
+ ["cmd_out", void 0],
187
+ ["power_in", void 0],
188
+ ["power_out", void 0]
189
+ ]),
190
+ config: /* @__PURE__ */ new Map([
191
+ ["activationLogic", "positive"],
192
+ ["initializationOrder", ""]
193
+ ])
194
+ },
195
+ transistor: {
196
+ id: "transistor",
197
+ name: "Transistor",
198
+ pins: /* @__PURE__ */ new Map([
199
+ ["collector", void 0],
200
+ ["base", void 0],
201
+ ["emitter", void 0]
202
+ ]),
203
+ config: /* @__PURE__ */ new Map([
204
+ ["activationLogic", "positive"],
205
+ ["initializationOrder", ""]
206
+ ])
207
+ },
208
+ smallLED: {
209
+ id: "smallLED",
210
+ name: "SmallLED",
211
+ pins: /* @__PURE__ */ new Map([
212
+ ["cathode", void 0],
213
+ ["anode", void 0]
214
+ ]),
215
+ config: /* @__PURE__ */ new Map([
216
+ ["mode", "symmetric"],
217
+ ["idleColor", "white"],
218
+ ["activeColor", "#ffff00"],
219
+ ["size", "1"],
220
+ ["ywRatio", "1"]
221
+ ])
222
+ },
223
+ rectangleLED: {
224
+ id: "rectangleLED",
225
+ name: "RectangleLED",
226
+ pins: /* @__PURE__ */ new Map([
227
+ ["cathode", void 0],
228
+ ["anode", void 0]
229
+ ]),
230
+ config: /* @__PURE__ */ new Map([
231
+ ["mode", "symmetric"],
232
+ ["idleColor", "white"],
233
+ ["activeColor", "#ffff00"],
234
+ ["size", "1"],
235
+ ["hwRatio", "1"],
236
+ ["ywRatio", "1"]
237
+ ])
238
+ },
239
+ cube: {
240
+ id: "cube",
241
+ name: "Cube",
242
+ pins: /* @__PURE__ */ new Map([]),
243
+ config: /* @__PURE__ */ new Map([["color", "red"]])
244
+ },
245
+ label: {
246
+ id: "label",
247
+ name: "Label",
248
+ pins: /* @__PURE__ */ new Map([]),
249
+ config: /* @__PURE__ */ new Map([
250
+ ["text", "Label"],
251
+ ["size", "1"]
252
+ ])
253
+ }
254
+ };
255
+ function z() {
256
+ return Object.values(T);
257
+ }
258
+ function N(n) {
259
+ return E[n];
260
+ }
261
+ var M = /* @__PURE__ */ ((n) => (n.Pin = "Pin", n.BranchingPoint = "BranchingPoint", n))(M || {});
262
+ function I() {
263
+ if (typeof crypto < "u" && crypto.randomUUID)
264
+ return crypto.randomUUID();
265
+ const n = "0123456789abcdef", t = [8, 4, 4, 4, 12], e = [];
266
+ for (const c of t) {
267
+ let l = "";
268
+ for (let s = 0; s < c; s++) {
269
+ const h = Math.floor(Math.random() * 16);
270
+ l += n[h];
271
+ }
272
+ e.push(l);
273
+ }
274
+ const i = e.join("-").split("");
275
+ i[14] = "4";
276
+ const a = parseInt(i[19] ?? "0", 16) & 3 | 8;
277
+ return i[19] = n[a] ?? "0", i.join("");
278
+ }
279
+ class v {
280
+ /**
281
+ * Create a new position on the discrete grid.
282
+ *
283
+ * @param x - X coordinate (must be integer)
284
+ * @param y - Y coordinate (must be integer)
285
+ * @throws {TypeError} If x or y are not integers
286
+ */
287
+ constructor(t, e) {
288
+ if (this.x = t, this.y = e, !Number.isInteger(t) || !Number.isInteger(e))
289
+ throw new TypeError(`Position coordinates must be integers (got x=${t}, y=${e})`);
290
+ }
291
+ /**
292
+ * Check if this position equals another position.
293
+ *
294
+ * @param other - Position to compare with
295
+ * @returns true if both x and y coordinates are equal
296
+ *
297
+ * @example
298
+ * ```typescript
299
+ * const p1 = new Position(10, 20);
300
+ * const p2 = new Position(10, 20);
301
+ * const p3 = new Position(15, 20);
302
+ *
303
+ * console.log(p1.equals(p2)); // true
304
+ * console.log(p1.equals(p3)); // false
305
+ * ```
306
+ */
307
+ equals(t) {
308
+ return this.x === t.x && this.y === t.y;
309
+ }
310
+ /**
311
+ * Serialize position to JSON.
312
+ *
313
+ * @returns Plain object with x and y properties
314
+ *
315
+ * @example
316
+ * ```typescript
317
+ * const pos = new Position(10, 20);
318
+ * const json = pos.toJSON();
319
+ * console.log(json); // { x: 10, y: 20 }
320
+ * ```
321
+ */
322
+ toJSON() {
323
+ return { x: this.x, y: this.y };
324
+ }
325
+ /**
326
+ * Deserialize position from JSON.
327
+ *
328
+ * @param json - Plain object with x and y properties
329
+ * @returns Position instance
330
+ * @throws {TypeError} If coordinates are not integers
331
+ *
332
+ * @example
333
+ * ```typescript
334
+ * const json = { x: 10, y: 20 };
335
+ * const pos = Position.fromJSON(json);
336
+ * console.log(pos.x); // 10
337
+ * ```
338
+ */
339
+ static fromJSON(t) {
340
+ return new v(t.x, t.y);
341
+ }
342
+ /**
343
+ * String representation for debugging.
344
+ *
345
+ * @returns String in format "Position(x, y)"
346
+ */
347
+ toString() {
348
+ return `Position(${this.x}, ${this.y})`;
349
+ }
350
+ }
351
+ function W(n, t, e = 1 / 0) {
352
+ if (n.length === 0)
353
+ return 0;
354
+ if (n.length === 1)
355
+ return 1;
356
+ let r = 1;
357
+ for (let i = 0; i < n.length - 1; i++) {
358
+ const o = n[i], a = n[i + 1], c = O(t, o, a);
359
+ c < e && (e = c, r = i + 1);
360
+ }
361
+ return r;
362
+ }
363
+ function O(n, t, e) {
364
+ const r = e.x - t.x, i = e.y - t.y, o = r * r + i * i;
365
+ if (o === 0)
366
+ return Math.sqrt((n.x - t.x) ** 2 + (n.y - t.y) ** 2);
367
+ const a = Math.max(
368
+ 0,
369
+ Math.min(
370
+ 1,
371
+ ((n.x - t.x) * r + (n.y - t.y) * i) / o
372
+ )
373
+ ), c = t.x + a * r, l = t.y + a * i;
374
+ return Math.sqrt((n.x - c) ** 2 + (n.y - l) ** 2);
375
+ }
376
+ function L(n, t = 5) {
377
+ if (n.length <= 2)
378
+ return [...n];
379
+ const e = [n[0]];
380
+ for (let r = 1; r < n.length - 1; r++) {
381
+ const i = e[e.length - 1], o = n[r], a = n[r + 1];
382
+ D(i, o, a, t) || e.push(o);
383
+ }
384
+ return e.push(n[n.length - 1]), e;
385
+ }
386
+ function D(n, t, e, r = 5) {
387
+ const i = (t.x - n.x) * (e.y - n.y) - (t.y - n.y) * (e.x - n.x);
388
+ return Math.abs(i) <= r;
389
+ }
390
+ class C {
391
+ /**
392
+ * Create a new rotation with the specified angle.
393
+ *
394
+ * @param angle - Rotation angle in degrees (must be integer)
395
+ * @throws {TypeError} If angle is not an integer
396
+ */
397
+ constructor(t) {
398
+ if (this.angle = t, !Number.isInteger(t))
399
+ throw new TypeError(`Rotation angle must be an integer (got ${t})`);
400
+ }
401
+ /**
402
+ * Serialize rotation to JSON.
403
+ *
404
+ * @returns The angle value as a number
405
+ *
406
+ * @example
407
+ * ```typescript
408
+ * const rotation = new Rotation(90);
409
+ * const json = rotation.toJSON();
410
+ * console.log(json); // 90
411
+ * ```
412
+ */
413
+ toJSON() {
414
+ return this.angle;
415
+ }
416
+ /**
417
+ * Deserialize rotation from JSON.
418
+ *
419
+ * @param angle - Angle value
420
+ * @returns Rotation instance
421
+ * @throws {TypeError} If angle is not an integer
422
+ *
423
+ * @example
424
+ * ```typescript
425
+ * const rotation = Rotation.fromJSON(90);
426
+ * console.log(rotation.angle); // 90
427
+ * ```
428
+ */
429
+ static fromJSON(t) {
430
+ return new C(t);
431
+ }
432
+ /**
433
+ * Check if this rotation equals another rotation.
434
+ *
435
+ * @param other - Rotation to compare with
436
+ * @returns true if angles are equal
437
+ *
438
+ * @example
439
+ * ```typescript
440
+ * const r1 = new Rotation(90);
441
+ * const r2 = new Rotation(90);
442
+ * const r3 = new Rotation(180);
443
+ *
444
+ * console.log(r1.equals(r2)); // true
445
+ * console.log(r1.equals(r3)); // false
446
+ * ```
447
+ */
448
+ equals(t) {
449
+ return this.angle === t.angle;
450
+ }
451
+ /**
452
+ * String representation for debugging.
453
+ *
454
+ * @returns String in format "Rotation(angle°)"
455
+ */
456
+ toString() {
457
+ return `Rotation(${this.angle}°)`;
458
+ }
459
+ }
460
+ class A {
461
+ /**
462
+ * Unique identifier for this component.
463
+ * @readonly
464
+ */
465
+ id;
466
+ /**
467
+ * Component type (Battery, Switch, LED, etc.).
468
+ * @readonly
469
+ */
470
+ type;
471
+ /**
472
+ * Position on the 2D discrete grid.
473
+ * @readonly
474
+ */
475
+ position;
476
+ /**
477
+ * Orientation angle in degrees.
478
+ * @readonly
479
+ */
480
+ rotation;
481
+ /**
482
+ * Array of pin ENode UUIDs.
483
+ * Pin order is significant (index 0 is first pin, etc.).
484
+ * @readonly
485
+ */
486
+ pins;
487
+ /**
488
+ * Configuration parameters for this component instance.
489
+ *
490
+ * This map holds key-value pairs representing configurable settings
491
+ * The available configuration keys depend on the component type see ComponentTypeMetadata for details.
492
+ *
493
+ */
494
+ config;
495
+ /**
496
+ * Create a new component.
497
+ *
498
+ * **Note**: Typically components are created via `Circuit.addComponent()`
499
+ * which handles pin ENode creation automatically. This constructor is used
500
+ * internally by Circuit.
501
+ *
502
+ * @param type - Component type (Battery, Switch, LED, etc.)
503
+ * @param position - Grid position (integer x, y)
504
+ * @param rotation - Orientation angle (integer degrees)
505
+ * @param pins - Array of pin ENode UUIDs
506
+ *
507
+ * @example
508
+ * ```typescript
509
+ * // Usually created via Circuit:
510
+ * const component = circuit.addComponent(
511
+ * new Position(10, 20),
512
+ * new Rotation(90),
513
+ * ComponentType.Battery
514
+ * );
515
+ *
516
+ * // Direct construction (for deserialization):
517
+ * const component = new Component(
518
+ * ComponentType.Battery,
519
+ * new Position(10, 20),
520
+ * new Rotation(90),
521
+ * ['pin-id-1', 'pin-id-2']
522
+ * );
523
+ * ```
524
+ */
525
+ constructor(t, e, r, i) {
526
+ if (this.id = I(), this.type = t, this.position = e, this.rotation = r, new Set(i).size !== i.length) {
527
+ const o = i.filter((a, c) => i.indexOf(a) !== c);
528
+ throw new Error(
529
+ `Duplicate pin names are not allowed: ${[...new Set(o)].join(", ")}`
530
+ );
531
+ }
532
+ this.pins = i, this.config = new Map(E[t].config);
533
+ }
534
+ getPinLabel(t) {
535
+ const e = this.pins.indexOf(t);
536
+ if (e === -1)
537
+ return;
538
+ const r = N(this.type).pins.keys();
539
+ return Array.from(r)[e] || void 0;
540
+ }
541
+ setAllParameters(t) {
542
+ this.config = new Map(t);
543
+ }
544
+ setParameter(t, e) {
545
+ this.config.set(t, e);
546
+ }
547
+ /**
548
+ * Update the component's position.
549
+ *
550
+ * @param newPosition - The new position for the component
551
+ *
552
+ * @example
553
+ * ```typescript
554
+ * const component = circuit.getComponent(componentId);
555
+ * component.setPosition(new Position(15, 25));
556
+ * ```
557
+ */
558
+ setPosition(t) {
559
+ Object.defineProperty(this, "position", {
560
+ value: t,
561
+ writable: !1,
562
+ enumerable: !0,
563
+ configurable: !0
564
+ });
565
+ }
566
+ /**
567
+ * Update the component's rotation.
568
+ *
569
+ * @param newRotation - The new rotation for the component
570
+ *
571
+ * @example
572
+ * ```typescript
573
+ * const component = circuit.getComponent(componentId);
574
+ * component.setRotation(new Rotation(90));
575
+ * ```
576
+ */
577
+ setRotation(t) {
578
+ Object.defineProperty(this, "rotation", {
579
+ value: t,
580
+ writable: !1,
581
+ enumerable: !0,
582
+ configurable: !0
583
+ });
584
+ }
585
+ /**
586
+ * Serialize component to JSON.
587
+ *
588
+ * @returns Plain object representation
589
+ *
590
+ * @example
591
+ * ```typescript
592
+ * const json = component.toJSON();
593
+ * console.log(json);
594
+ * // {
595
+ * // id: "550e8400-...",
596
+ * // type: "battery",
597
+ * // position: { x: 10, y: 20 },
598
+ * // rotation: 90,
599
+ * // pins: ['pin-uuid-1', 'pin-uuid-2']
600
+ * // }
601
+ * ```
602
+ */
603
+ toJSON() {
604
+ return {
605
+ id: this.id,
606
+ type: this.type,
607
+ position: this.position.toJSON(),
608
+ rotation: this.rotation.toJSON(),
609
+ pins: [...this.pins],
610
+ config: Object.fromEntries(this.config)
611
+ };
612
+ }
613
+ /**
614
+ * Deserialize component from JSON.
615
+ *
616
+ * @param json - Component data
617
+ * @returns Component instance
618
+ *
619
+ * @example
620
+ * ```typescript
621
+ * const json = {
622
+ * id: "550e8400-...",
623
+ * type: "battery",
624
+ * position: { x: 10, y: 20 },
625
+ * rotation: 90,
626
+ * pins: ['1b4f6f3c-ce ....', '2c5e7g4d-df ...'],
627
+ * config: { "voltage": "5V" }
628
+ * };
629
+ *
630
+ * const component = Component.fromJSON(json);
631
+ * console.log(component.position.x); // 10
632
+ * ```
633
+ */
634
+ static fromJSON(t) {
635
+ const e = new A(
636
+ t.type,
637
+ v.fromJSON(t.position),
638
+ C.fromJSON(t.rotation),
639
+ t.pins
640
+ );
641
+ return e.config = new Map(Object.entries(t.config)), Object.defineProperty(e, "id", {
642
+ value: t.id,
643
+ writable: !1,
644
+ enumerable: !0,
645
+ configurable: !1
646
+ }), e;
647
+ }
648
+ }
649
+ class b {
650
+ /**
651
+ * Current simulation step number (starts at 0).
652
+ * @readonly
653
+ */
654
+ tick;
655
+ /**
656
+ * Electrical state for each ENode (component pins and branching points).
657
+ * Key: ENode UUID, Value: NodeElectricalState
658
+ * @readonly
659
+ */
660
+ nodeStates;
661
+ /**
662
+ * Electrical state for each Wire connecting ENodes.
663
+ * Key: Wire UUID, Value: NodeElectricalState
664
+ * @readonly
665
+ */
666
+ wireStates;
667
+ /**
668
+ * Component-specific state for each component.
669
+ * Key: Component UUID, Value: ComponentState subclass
670
+ * @readonly
671
+ */
672
+ componentStates;
673
+ /**
674
+ * Create a new simulation state snapshot.
675
+ *
676
+ * @param tick - Current simulation step number
677
+ */
678
+ constructor(t) {
679
+ if (t < 0 || !Number.isInteger(t))
680
+ throw new RangeError(`Tick must be a non-negative integer (got ${t})`);
681
+ this.tick = t, this.nodeStates = /* @__PURE__ */ new Map(), this.wireStates = /* @__PURE__ */ new Map(), this.componentStates = /* @__PURE__ */ new Map();
682
+ }
683
+ setTick(t) {
684
+ this.tick = t;
685
+ }
686
+ /**
687
+ * Create a deep copy of this state for historical storage.
688
+ *
689
+ * @returns Cloned SimulationState
690
+ */
691
+ clone() {
692
+ const t = new b(this.tick), e = /* @__PURE__ */ new Map();
693
+ for (const [o, a] of this.nodeStates.entries())
694
+ e.set(o, { ...a });
695
+ const r = /* @__PURE__ */ new Map();
696
+ for (const [o, a] of this.wireStates.entries())
697
+ r.set(o, { ...a });
698
+ const i = /* @__PURE__ */ new Map();
699
+ for (const [o, a] of this.componentStates.entries())
700
+ i.set(
701
+ o,
702
+ Object.assign(Object.create(Object.getPrototypeOf(a)), a)
703
+ );
704
+ return Object.defineProperty(t, "nodeStates", {
705
+ value: e,
706
+ writable: !1,
707
+ configurable: !1,
708
+ enumerable: !0
709
+ }), Object.defineProperty(t, "wireStates", {
710
+ value: r,
711
+ writable: !1,
712
+ configurable: !1,
713
+ enumerable: !0
714
+ }), Object.defineProperty(t, "componentStates", {
715
+ value: i,
716
+ writable: !1,
717
+ configurable: !1,
718
+ enumerable: !0
719
+ }), t;
720
+ }
721
+ }
722
+ class R {
723
+ currentState;
724
+ history;
725
+ historyEnabled;
726
+ historyLimit;
727
+ historyWriteIndex;
728
+ /**
729
+ * Create a new state controllerType.
730
+ *
731
+ * @param enableHistory - Whether to store state history (default: false)
732
+ * @param historyLimit - Maximum number of historical states to keep (default: 1000)
733
+ */
734
+ constructor(t = !1, e = 1e3) {
735
+ if (e < 1)
736
+ throw new RangeError(`historyLimit must be at least 1 (got ${e})`);
737
+ this.historyEnabled = t, this.historyLimit = e, this.currentState = new b(0), this.history = [], this.historyWriteIndex = 0;
738
+ }
739
+ /**
740
+ * Get the current simulation state.
741
+ *
742
+ * @returns Current state (mutable for simulation controller use)
743
+ */
744
+ getCurrentState() {
745
+ return this.currentState;
746
+ }
747
+ /**
748
+ * Get current tick number.
749
+ *
750
+ * @returns Current simulation tick
751
+ */
752
+ getCurrentTick() {
753
+ return this.currentState.tick;
754
+ }
755
+ /**
756
+ * Advance to next tick, optionally saving current state to history.
757
+ * Creates a new SimulationState for the next tick.
758
+ *
759
+ * @returns New current state for the next tick
760
+ */
761
+ advanceToNextTick() {
762
+ const t = this.currentState.tick + 1;
763
+ return this.historyEnabled && this.saveToHistory(this.currentState.clone()), this.currentState.tick = t, this.currentState;
764
+ }
765
+ /**
766
+ * Get a historical state by tick number.
767
+ * Only works if history is enabled.
768
+ *
769
+ * @param tick - Tick number to retrieve
770
+ * @returns State at that tick, or undefined if not available
771
+ */
772
+ getStateAtTick(t) {
773
+ if (this.historyEnabled)
774
+ return this.history.find((e) => e.tick === t);
775
+ }
776
+ /**
777
+ * Get all available historical states.
778
+ * Returns empty array if history is disabled.
779
+ *
780
+ * @returns Array of historical states, sorted by tick (oldest first)
781
+ */
782
+ getHistory() {
783
+ return this.historyEnabled ? [...this.history].sort((t, e) => t.tick - e.tick) : [];
784
+ }
785
+ /**
786
+ * Get the oldest tick number available in history.
787
+ *
788
+ * @returns Oldest tick number, or undefined if no history
789
+ */
790
+ getOldestTick() {
791
+ if (!(!this.historyEnabled || this.history.length === 0))
792
+ return Math.min(...this.history.map((t) => t.tick));
793
+ }
794
+ /**
795
+ * Get the newest tick number in history (not including current tick).
796
+ *
797
+ * @returns Newest historical tick, or undefined if no history
798
+ */
799
+ getNewestHistoricalTick() {
800
+ if (!(!this.historyEnabled || this.history.length === 0))
801
+ return Math.max(...this.history.map((t) => t.tick));
802
+ }
803
+ /**
804
+ * Clear all history.
805
+ */
806
+ clearHistory() {
807
+ this.history = [], this.historyWriteIndex = 0;
808
+ }
809
+ /**
810
+ * Reset to tick 0, clearing current state and all history.
811
+ */
812
+ reset() {
813
+ this.currentState = new b(0), this.clearHistory();
814
+ }
815
+ /**
816
+ * Check if history tracking is enabled.
817
+ *
818
+ * @returns True if history is enabled
819
+ */
820
+ isHistoryEnabled() {
821
+ return this.historyEnabled;
822
+ }
823
+ /**
824
+ * Get the configured history limit.
825
+ *
826
+ * @returns Maximum number of historical states
827
+ */
828
+ getHistoryLimit() {
829
+ return this.historyLimit;
830
+ }
831
+ /**
832
+ * Get current history size.
833
+ *
834
+ * @returns Number of states in history
835
+ */
836
+ getHistorySize() {
837
+ return this.history.length;
838
+ }
839
+ /**
840
+ * Save a state to history using circular buffer.
841
+ * Private helper for advanceToNextTick.
842
+ *
843
+ * @param state - State to save
844
+ */
845
+ saveToHistory(t) {
846
+ this.history.length < this.historyLimit ? this.history.push(t) : (this.history[this.historyWriteIndex] = t, this.historyWriteIndex = (this.historyWriteIndex + 1) % this.historyLimit);
847
+ }
848
+ }
849
+ class x {
850
+ heap;
851
+ /**
852
+ * Create a new empty event queue.
853
+ */
854
+ constructor() {
855
+ this.heap = [];
856
+ }
857
+ /**
858
+ * Schedule a future event.
859
+ * Inserted with O(log N) complexity using heap operations.
860
+ *
861
+ * @param event - Event to schedule
862
+ */
863
+ schedule(t) {
864
+ if (t.readyAtTick < t.scheduledAtTick)
865
+ throw new RangeError(
866
+ `readyAtTick (${t.readyAtTick}) cannot be before scheduledAtTick (${t.scheduledAtTick})`
867
+ );
868
+ this.heap.push(t), this.bubbleUp(this.heap.length - 1);
869
+ }
870
+ /**
871
+ * Get all events ready to fire at or before current tick.
872
+ * Returns events in FIFO order for same readyAtTick.
873
+ * Removes returned events from the queue.
874
+ *
875
+ * @param currentTick - Current simulation tick
876
+ * @returns Array of ready events (removed from queue)
877
+ */
878
+ getReadyEvents(t) {
879
+ const e = [];
880
+ for (; this.heap.length > 0 && this.heap[0].readyAtTick <= t; ) {
881
+ const r = this.extractMin();
882
+ r && e.push(r);
883
+ }
884
+ return e.sort((r, i) => r.readyAtTick === i.readyAtTick ? r.scheduledAtTick - i.scheduledAtTick : r.readyAtTick - i.readyAtTick), e;
885
+ }
886
+ /**
887
+ * Check if any events are pending.
888
+ *
889
+ * @returns True if queue contains events
890
+ */
891
+ hasEvents() {
892
+ return this.heap.length > 0;
893
+ }
894
+ /**
895
+ * Clear all pending events.
896
+ */
897
+ clear() {
898
+ this.heap = [];
899
+ }
900
+ /**
901
+ * Get number of pending events.
902
+ *
903
+ * @returns Event count
904
+ */
905
+ size() {
906
+ return this.heap.length;
907
+ }
908
+ bubbleUp(t) {
909
+ for (; t > 0; ) {
910
+ const e = Math.floor((t - 1) / 2);
911
+ if (this.heap[t].readyAtTick < this.heap[e].readyAtTick)
912
+ [this.heap[t], this.heap[e]] = [this.heap[e], this.heap[t]], t = e;
913
+ else
914
+ break;
915
+ }
916
+ }
917
+ bubbleDown(t) {
918
+ const e = this.heap.length;
919
+ for (; ; ) {
920
+ const r = 2 * t + 1, i = 2 * t + 2;
921
+ let o = t;
922
+ if (r < e && this.heap[r].readyAtTick < this.heap[o].readyAtTick && (o = r), i < e && this.heap[i].readyAtTick < this.heap[o].readyAtTick && (o = i), o !== t)
923
+ [this.heap[t], this.heap[o]] = [this.heap[o], this.heap[t]], t = o;
924
+ else
925
+ break;
926
+ }
927
+ }
928
+ extractMin() {
929
+ if (this.heap.length === 0)
930
+ return;
931
+ const t = this.heap[0], e = this.heap.pop();
932
+ return this.heap.length > 0 && e && (this.heap[0] = e, this.bubbleDown(0)), t;
933
+ }
934
+ }
935
+ class P {
936
+ dirtyComponents;
937
+ dirtyWires;
938
+ dirtyEnodes;
939
+ /**
940
+ * Create a new dirty tracker with no marked elements.
941
+ */
942
+ constructor() {
943
+ this.dirtyComponents = /* @__PURE__ */ new Set(), this.dirtyWires = /* @__PURE__ */ new Set(), this.dirtyEnodes = /* @__PURE__ */ new Set();
944
+ }
945
+ /**
946
+ * Mark a component as having changed state this tick.
947
+ *
948
+ * @param componentId - UUID of the component
949
+ */
950
+ markComponentDirty(t) {
951
+ this.dirtyComponents.add(t);
952
+ }
953
+ /**
954
+ * Mark a wire as having changed electrical state this tick.
955
+ *
956
+ * @param wireId - UUID of the wire
957
+ */
958
+ markWireDirty(t) {
959
+ this.dirtyWires.add(t);
960
+ }
961
+ /**
962
+ * Mark an ENode as having changed electrical state this tick.
963
+ *
964
+ * @param enodeId - UUID of the ENode
965
+ */
966
+ markEnodeDirty(t) {
967
+ this.dirtyEnodes.add(t);
968
+ }
969
+ /**
970
+ * Set the entire set of dirty components. Should be only used at CircuitRunner initialization.
971
+ * @param componentIds
972
+ */
973
+ setDirtyComponents(t) {
974
+ this.dirtyComponents = new Set(t);
975
+ }
976
+ /**
977
+ * Set the entire set of dirty components. Should be only used at CircuitRunner initialization.
978
+ * @param enodeIds
979
+ */
980
+ setDirtyEnodes(t) {
981
+ this.dirtyEnodes = new Set(t);
982
+ }
983
+ /**
984
+ * Set the entire set of dirty components. Should be only used at CircuitRunner initialization.
985
+ * @param wireIds
986
+ */
987
+ setDirtyWires(t) {
988
+ this.dirtyWires = new Set(t);
989
+ }
990
+ /**
991
+ * Get all dirty elements and clear the tracker.
992
+ * This is typically called at the end of a tick to collect changes.
993
+ *
994
+ * @returns Object containing sets of dirty component/wire/enode UUIDs
995
+ */
996
+ getDirtyElements() {
997
+ const t = {
998
+ components: new Set(this.dirtyComponents),
999
+ wires: new Set(this.dirtyWires),
1000
+ enodes: new Set(this.dirtyEnodes)
1001
+ };
1002
+ return this.clear(), t;
1003
+ }
1004
+ /**
1005
+ * Check if any elements are marked dirty.
1006
+ *
1007
+ * @returns True if at least one element is dirty
1008
+ */
1009
+ hasDirtyElements() {
1010
+ return this.dirtyComponents.size > 0 || this.dirtyWires.size > 0 || this.dirtyEnodes.size > 0;
1011
+ }
1012
+ /**
1013
+ * Clear all dirty markers without returning them.
1014
+ */
1015
+ clear() {
1016
+ this.dirtyComponents.clear(), this.dirtyWires.clear(), this.dirtyEnodes.clear();
1017
+ }
1018
+ /**
1019
+ * Get current count of dirty components (for debugging/metrics).
1020
+ *
1021
+ * @returns Number of dirty components
1022
+ */
1023
+ getDirtyComponentCount() {
1024
+ return this.dirtyComponents.size;
1025
+ }
1026
+ /**
1027
+ * Get current count of dirty wires (for debugging/metrics).
1028
+ *
1029
+ * @returns Number of dirty wires
1030
+ */
1031
+ getDirtyWireCount() {
1032
+ return this.dirtyWires.size;
1033
+ }
1034
+ /**
1035
+ * Get current count of dirty enodes (for debugging/metrics).
1036
+ *
1037
+ * @returns Number of dirty enodes
1038
+ */
1039
+ getDirtyEnodeCount() {
1040
+ return this.dirtyEnodes.size;
1041
+ }
1042
+ }
1043
+ const U = {
1044
+ /**
1045
+ * Minimum simulation speed in ticks per second
1046
+ */
1047
+ MIN_TPS: 1,
1048
+ /**
1049
+ * Maximum simulation speed in ticks per second
1050
+ */
1051
+ MAX_TPS: 50,
1052
+ /**
1053
+ * Default simulation speed in ticks per second
1054
+ */
1055
+ DEFAULT_TPS: 2,
1056
+ /**
1057
+ * Default tick interval in milliseconds (1000 / DEFAULT_TPS)
1058
+ */
1059
+ DEFAULT_INTERVAL_MS: 500
1060
+ }, $ = {
1061
+ /**
1062
+ * Default transitionSpan for relays and transistors in ticks (instant transition)
1063
+ */
1064
+ TRANSITION_SPAN_TICKS: 1,
1065
+ /**
1066
+ * Default transitionUserSpan for switches in milliseconds
1067
+ */
1068
+ TRANSITION_USER_SPAN_MS: 200
1069
+ };
1070
+ class J {
1071
+ circuit;
1072
+ stateManager;
1073
+ eventQueue;
1074
+ commands;
1075
+ dirtyTracker;
1076
+ behaviorRegistry;
1077
+ /**
1078
+ * Create a new circuit simulation runner.
1079
+ *
1080
+ * @param circuit - The circuit topology to simulate
1081
+ * @param behaviorRegistry - Registry of component behaviors
1082
+ * @param options - Simulation options (history settings)
1083
+ */
1084
+ constructor(t, e, r = {}) {
1085
+ this.circuit = t, this.behaviorRegistry = e;
1086
+ const i = r.enableHistory ?? !1, o = r.historyLimit ?? 1e3;
1087
+ this.stateManager = new R(i, o), this.eventQueue = new x(), this.commands = /* @__PURE__ */ new Map(), this.dirtyTracker = new P();
1088
+ try {
1089
+ this.initializeState();
1090
+ } catch (a) {
1091
+ throw console.error("Error during CircuitRunner initialization:", a), a;
1092
+ }
1093
+ }
1094
+ /**
1095
+ * Execute one simulation tick.
1096
+ * Process scheduled events, update state (electrical propagation), process user commands and advance tick.
1097
+ *
1098
+ * @returns the result of the tick execution
1099
+ */
1100
+ tick() {
1101
+ const t = this.eventQueue.size(), e = this.stateManager.getCurrentTick(), r = this.applyReadyEvents(e + 1), i = this.updateState(e + 1);
1102
+ i.firedEventCount = r.length;
1103
+ const o = this.processCommands();
1104
+ i.processedCommandCount = o.length, i.scheduledEventCount = this.eventQueue.size() + i.firedEventCount - t;
1105
+ for (const a of r)
1106
+ a.hasChanged && this.dirtyTracker.markComponentDirty(a.componentState.componentId);
1107
+ return i.componentUpdateCount = this.dirtyTracker.getDirtyComponentCount(), this.stateManager.advanceToNextTick(), i.endTick = this.stateManager.getCurrentTick(), i;
1108
+ }
1109
+ /**
1110
+ * Execute multiple simulation ticks.
1111
+ *
1112
+ * @param count - Number of ticks to execute
1113
+ * @returns an array of RunnerResult for each tick executed
1114
+ */
1115
+ tickN(t) {
1116
+ if (t < 1)
1117
+ throw new RangeError(`Tick count must be at least 1 (got ${t})`);
1118
+ const e = [];
1119
+ for (let r = 0; r < t; r++)
1120
+ e.push(this.tick());
1121
+ return e;
1122
+ }
1123
+ /**
1124
+ * Reset simulation to tick 0.
1125
+ * Clears all state, history, and scheduled events.
1126
+ */
1127
+ reset() {
1128
+ this.stateManager.reset(), this.eventQueue.clear(), this.dirtyTracker.clear(), this.initializeState();
1129
+ }
1130
+ /**
1131
+ * Get current simulation tick number.
1132
+ *
1133
+ * @returns Current tick
1134
+ */
1135
+ getCurrentTick() {
1136
+ return this.stateManager.getCurrentTick();
1137
+ }
1138
+ /**
1139
+ * Get current simulation state snapshot.
1140
+ *
1141
+ * @returns Current state (readonly)
1142
+ */
1143
+ getCurrentState() {
1144
+ return this.stateManager.getCurrentState();
1145
+ }
1146
+ /**
1147
+ * Get electrical state of a specific ENode.
1148
+ *
1149
+ * @param enodeId - UUID of the ENode
1150
+ * @returns Electrical state, or undefined if not found
1151
+ */
1152
+ getEnodeState(t) {
1153
+ return this.stateManager.getCurrentState().nodeStates.get(t);
1154
+ }
1155
+ /**
1156
+ * Get electrical state of a specific Wire.
1157
+ *
1158
+ * @param wireId - UUID of the Wire
1159
+ * @returns Electrical state, or undefined if not found
1160
+ */
1161
+ getWireState(t) {
1162
+ return this.stateManager.getCurrentState().wireStates.get(t);
1163
+ }
1164
+ /**
1165
+ * Get component state for a specific component.
1166
+ *
1167
+ * @param componentId - UUID of the component
1168
+ * @returns Component state, or undefined if not found
1169
+ */
1170
+ getComponentState(t) {
1171
+ return this.stateManager.getCurrentState().componentStates.get(t);
1172
+ }
1173
+ /**
1174
+ * Get historical state at a specific tick.
1175
+ * Only works if history is enabled.
1176
+ *
1177
+ * @param tick - Tick number to retrieve
1178
+ * @returns Historical state, or undefined if not available
1179
+ */
1180
+ getStateAtTick(t) {
1181
+ return this.stateManager.getStateAtTick(t);
1182
+ }
1183
+ /**
1184
+ * Check if a component is registered in behavior registry.
1185
+ *
1186
+ * @param componentType - Component type to check
1187
+ * @returns True if behavior is registered
1188
+ */
1189
+ hasBehavior(t) {
1190
+ return this.behaviorRegistry.has(t);
1191
+ }
1192
+ /**
1193
+ * Submit a user command to execute at the next tick.
1194
+ * only one command per component per tick is allowed.
1195
+ * Subsequent commands for the same component at this tick will be discarded.
1196
+ *
1197
+ * @param command - User command to submit
1198
+ */
1199
+ submitCommand(t) {
1200
+ if (!this.circuit.hasComponent(t.targetId))
1201
+ throw Error(`Cannot submit command for unknown component ID '${t.targetId}'`);
1202
+ return this.commands.has(t.targetId) ? !1 : (t.scheduledAtTick = this.getCurrentTick(), this.commands.set(t.targetId, t), !0);
1203
+ }
1204
+ /**
1205
+ * Process all scheduled user commands.
1206
+ * Mark changed components as Dirty and enqueue consequent scheduled events
1207
+ * Finally clears command queue after processing.
1208
+ *
1209
+ * @returns Array of BehaviorResult for each processed command
1210
+ */
1211
+ processCommands() {
1212
+ const t = this.stateManager.getCurrentState(), e = [];
1213
+ for (const r of this.commands.values()) {
1214
+ const i = this.circuit.getComponent(r.targetId), a = this.behaviorRegistry.get(i.type).onUserCommand(
1215
+ i,
1216
+ t.componentStates.get(i.id),
1217
+ r
1218
+ );
1219
+ for (const c of a.scheduledEvents)
1220
+ this.eventQueue.schedule(c);
1221
+ e.push(a), a.hasChanged && this.dirtyTracker.markComponentDirty(i.id);
1222
+ }
1223
+ return this.commands.clear(), e;
1224
+ }
1225
+ /**
1226
+ * Helper function to extract initializationOrder from component config.
1227
+ * Returns numeric order value, with empty string or null defaulting to 0.
1228
+ *
1229
+ * @param config - Component configuration map
1230
+ * @returns Order value (lower = processed first)
1231
+ */
1232
+ getInitializationOrder(t) {
1233
+ const e = t.get("initializationOrder");
1234
+ if (!e || e === "") return 0;
1235
+ const r = parseInt(e, 10);
1236
+ return isNaN(r) ? 0 : r;
1237
+ }
1238
+ /**
1239
+ * Initialize simulation state for all components.
1240
+ * Called on construction and reset.
1241
+ *
1242
+ * Uses order-based initialization to resolve feedback loops:
1243
+ * 1. Components are grouped by initializationOrder (higher = processed LAST)
1244
+ * 2. Within each group, components are sorted by UUID (ascending) for determinism
1245
+ * 3. Conductivity is propagated after each component
1246
+ *
1247
+ * In feedback circuits, the component processed LAST "wins" because earlier
1248
+ * components react to the initial symmetric state and open, while later
1249
+ * components see the updated (asymmetric) state and stay closed.
1250
+ */
1251
+ initializeState() {
1252
+ const t = this.stateManager.getCurrentState();
1253
+ for (const s of this.circuit.getAllComponents()) {
1254
+ if (s.pins.length < 1) continue;
1255
+ const h = this.behaviorRegistry.get(s.type);
1256
+ if (!h)
1257
+ continue;
1258
+ const u = h.createInitialState(s);
1259
+ t.componentStates.set(s.id, u), this.dirtyTracker.markComponentDirty(s.id);
1260
+ }
1261
+ for (const s of this.circuit.getAllENodes())
1262
+ t.nodeStates.set(s.id, {
1263
+ hasVoltage: s.source === S.Voltage,
1264
+ hasCurrent: s.source === S.Current,
1265
+ locked: !!s.source
1266
+ });
1267
+ for (const s of this.circuit.getAllWires())
1268
+ t.wireStates.set(s.id, {
1269
+ hasVoltage: !1,
1270
+ hasCurrent: !1,
1271
+ locked: !1
1272
+ });
1273
+ const e = this.circuit.getAllComponents(), r = /* @__PURE__ */ new Map();
1274
+ for (const s of e) {
1275
+ const h = this.getInitializationOrder(s.config), u = r.get(h) ?? [];
1276
+ u.push(s), r.set(h, u);
1277
+ }
1278
+ const i = Array.from(r.keys()).sort((s, h) => s - h);
1279
+ for (const s of i)
1280
+ r.get(s).sort((u, g) => u.id.localeCompare(g.id));
1281
+ let o = !0, a = 0;
1282
+ const c = 100;
1283
+ for (; o && a < c; ) {
1284
+ o = !1, a++;
1285
+ for (const s of i) {
1286
+ const h = r.get(s);
1287
+ for (const u of h) {
1288
+ const g = this.behaviorRegistry.get(u.type);
1289
+ if (!g) continue;
1290
+ const f = t.componentStates.get(u.id);
1291
+ if (!f) continue;
1292
+ this.propagateConductivity();
1293
+ const m = g.onPinsChange(
1294
+ u,
1295
+ f,
1296
+ t.nodeStates,
1297
+ 0
1298
+ );
1299
+ m.hasChanged && (o = !0, t.componentStates.set(
1300
+ u.id,
1301
+ m.componentState
1302
+ ));
1303
+ for (const y of m.scheduledEvents) {
1304
+ const d = g.onEventFiring(u, f, y);
1305
+ d.hasChanged && (o = !0, t.componentStates.set(
1306
+ u.id,
1307
+ d.componentState
1308
+ ));
1309
+ }
1310
+ }
1311
+ }
1312
+ }
1313
+ const l = this.updateState(0);
1314
+ return this.dirtyTracker.setDirtyComponents(
1315
+ /* @__PURE__ */ new Set([...this.circuit.getAllComponents().map((s) => s.id)])
1316
+ ), this.dirtyTracker.setDirtyEnodes(/* @__PURE__ */ new Set([...this.circuit.getAllENodes().map((s) => s.id)])), this.dirtyTracker.setDirtyWires(/* @__PURE__ */ new Set([...this.circuit.getAllWires().map((s) => s.id)])), l;
1317
+ }
1318
+ /**
1319
+ * Core method that orchestrates nodes, wires and components state updates
1320
+ * enqueue resulting events and update dirty tracker accordingly
1321
+ * @param targetTick - Tick at which to perform the update
1322
+ */
1323
+ updateState(t) {
1324
+ const e = this.stateManager.getCurrentState(), { updatedNodes: r, updatedWires: i } = this.propagateConductivity(), o = this.circuit.getComponentsOfPins(r), a = /* @__PURE__ */ new Set();
1325
+ let c = 0, l = Array.from(o);
1326
+ t === 0 && (l = l.sort((s, h) => {
1327
+ const u = this.circuit.getComponent(s), g = this.circuit.getComponent(h), f = this.getInitializationOrder(u.config), m = this.getInitializationOrder(g.config);
1328
+ return f !== m ? f - m : s.localeCompare(h);
1329
+ }));
1330
+ for (const s of l) {
1331
+ const h = this.circuit.getComponent(s), u = this.behaviorRegistry.get(h.type);
1332
+ if (!u)
1333
+ continue;
1334
+ const g = u.onPinsChange(
1335
+ h,
1336
+ e.componentStates.get(s),
1337
+ e.nodeStates,
1338
+ t
1339
+ );
1340
+ g.hasChanged && a.add(s);
1341
+ for (const f of g.scheduledEvents)
1342
+ this.eventQueue.schedule(f), c++;
1343
+ }
1344
+ return this.dirtyTracker.setDirtyComponents(a), this.dirtyTracker.setDirtyEnodes(r), this.dirtyTracker.setDirtyWires(i), {
1345
+ startTick: this.getCurrentTick(),
1346
+ endTick: this.getCurrentTick(),
1347
+ componentUpdateCount: a.size,
1348
+ nodeUpdateCount: r.size,
1349
+ wireUpdateCount: i.size,
1350
+ processedCommandCount: 0,
1351
+ scheduledEventCount: c,
1352
+ firedEventCount: 0
1353
+ };
1354
+ }
1355
+ /**
1356
+ * runs BFS (Breadth First Search) on voltage and current conductivity
1357
+ * to propagate voltage/current conductivity
1358
+ * and update enodes/wires electrical states throughout the circuit
1359
+ * update is performed in place and updated nodes and wires returned
1360
+ */
1361
+ propagateConductivity() {
1362
+ const t = this.stateManager.getCurrentState(), e = (c) => {
1363
+ const l = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Set(), h = this.circuit.getAllENodes().filter((d) => d.source == c).map((d) => d.id), u = /* @__PURE__ */ new Set([
1364
+ ...this.circuit.getAllENodes().filter((d) => !d.source).map((d) => d.id)
1365
+ ]), g = /* @__PURE__ */ new Set([...this.circuit.getAllWires().map((d) => d.id)]), { nodes: f, wires: m } = this.computeReachability(
1366
+ c,
1367
+ h,
1368
+ t.componentStates
1369
+ ), y = c == S.Voltage ? "hasVoltage" : "hasCurrent";
1370
+ for (const d of f) {
1371
+ const p = t.nodeStates.get(d);
1372
+ p && !p.locked && (p[y] || (p[y] = !0, l.add(d)), u.delete(d));
1373
+ }
1374
+ for (const d of u) {
1375
+ const p = t.nodeStates.get(d);
1376
+ p && !p.locked && p[y] && (p[y] = !1, l.add(d));
1377
+ }
1378
+ for (const d of m) {
1379
+ const p = t.wireStates.get(d);
1380
+ p && (p[y] || (p[y] = !0, s.add(d)), g.delete(d));
1381
+ }
1382
+ for (const d of g) {
1383
+ const p = t.wireStates.get(d);
1384
+ p && p[y] && (p[y] = !1, s.add(d));
1385
+ }
1386
+ return { updatedNodes: l, updatedWires: s };
1387
+ }, { updatedNodes: r, updatedWires: i } = e(S.Voltage), { updatedNodes: o, updatedWires: a } = e(S.Current);
1388
+ return {
1389
+ updatedNodes: /* @__PURE__ */ new Set([...r, ...o]),
1390
+ updatedWires: /* @__PURE__ */ new Set([...i, ...a])
1391
+ };
1392
+ }
1393
+ /**
1394
+ * given a conductivity conductivityType and a set of seed nodes, compute all reachable nodes and wires
1395
+ * depends on componentStates to determine if conductivity is allowed through components
1396
+ * this method doesn't mutate any state, it's a pure function
1397
+ * @param conductivityType
1398
+ * @param seeds
1399
+ * @param componentStates
1400
+ */
1401
+ computeReachability(t, e, r) {
1402
+ const i = /* @__PURE__ */ new Set(), o = /* @__PURE__ */ new Set(), a = [];
1403
+ for (const c of e)
1404
+ a.push(c), i.add(c);
1405
+ for (; a.length > 0; ) {
1406
+ const c = a.shift();
1407
+ for (const s of this.circuit.getWiresByNode(c)) {
1408
+ const h = s.node1 === c ? s.node2 : s.node1;
1409
+ i.has(h) || (i.add(h), a.push(h)), o.has(s.id) || o.add(s.id);
1410
+ }
1411
+ const l = this.circuit.getENode(c);
1412
+ if (l.type === M.Pin) {
1413
+ const s = this.circuit.getComponent(l.component), h = this.behaviorRegistry.get(s.type);
1414
+ if (!h)
1415
+ continue;
1416
+ const u = r.get(s.id);
1417
+ for (const g of s.pins) {
1418
+ if (g === c || i.has(g)) continue;
1419
+ h.allowConductivity(
1420
+ s,
1421
+ u,
1422
+ t,
1423
+ c,
1424
+ g
1425
+ ) && (i.add(g), a.push(g));
1426
+ }
1427
+ }
1428
+ }
1429
+ return { nodes: i, wires: o };
1430
+ }
1431
+ /**
1432
+ * Fire ready events and update current state accordingly
1433
+ * eventual subsequent events are enqueued
1434
+ *
1435
+ * @param targetTick - Tick at which to process events
1436
+ * @param events - Events to apply
1437
+ * @param state - Current simulation state
1438
+ * @param targetTick - Target tick for event processing
1439
+ */
1440
+ applyReadyEvents(t) {
1441
+ const e = this.stateManager.getCurrentState(), r = this.eventQueue.getReadyEvents(t), i = [];
1442
+ for (const o of r) {
1443
+ const a = this.circuit.getComponent(o.targetId), c = this.behaviorRegistry.get(a.type);
1444
+ if (!c)
1445
+ continue;
1446
+ const l = e.componentStates.get(a.id), s = c.onEventFiring(a, l, o);
1447
+ for (const h of s.scheduledEvents)
1448
+ this.eventQueue.schedule(h);
1449
+ i.push(s);
1450
+ }
1451
+ return i;
1452
+ }
1453
+ }
1454
+ export {
1455
+ k as C,
1456
+ P as D,
1457
+ S as E,
1458
+ v as P,
1459
+ C as R,
1460
+ b as S,
1461
+ $ as T,
1462
+ T as a,
1463
+ E as b,
1464
+ N as c,
1465
+ M as d,
1466
+ I as e,
1467
+ W as f,
1468
+ z as g,
1469
+ w as h,
1470
+ A as i,
1471
+ J as j,
1472
+ R as k,
1473
+ U as l,
1474
+ x as m,
1475
+ L as s
1476
+ };
1477
+ //# sourceMappingURL=CircuitRunner-CAeE31M5.js.map