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