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.
- package/LICENSE +21 -0
- package/README.md +6 -0
- package/dist/CircuitRunner-CAeE31M5.js +1477 -0
- package/dist/CircuitRunner-CAeE31M5.js.map +1 -0
- package/dist/core/index.d.ts +47 -0
- package/dist/core/index.js +1683 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index.d.ts +5621 -0
- package/dist/index.js +71 -0
- package/dist/index.js.map +1 -0
- package/dist/scene/index.d.ts +42 -0
- package/dist/scene/index.js +27 -0
- package/dist/scene/index.js.map +1 -0
- package/dist/setup-FtJmrLda.js +8302 -0
- package/dist/setup-FtJmrLda.js.map +1 -0
- package/package.json +94 -0
|
@@ -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
|