schematex 0.5.0 → 0.5.2
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/README.md +15 -7
- package/dist/ai/ai-sdk.cjs +18 -12
- package/dist/ai/ai-sdk.cjs.map +1 -1
- package/dist/ai/ai-sdk.d.cts +4 -3
- package/dist/ai/ai-sdk.d.ts +4 -3
- package/dist/ai/ai-sdk.js +14 -8
- package/dist/ai/ai-sdk.js.map +1 -1
- package/dist/ai/index.cjs +15 -11
- package/dist/ai/index.d.cts +20 -153
- package/dist/ai/index.d.ts +20 -153
- package/dist/ai/index.js +3 -3
- package/dist/api-C5SECOxZ.d.cts +69 -0
- package/dist/api-Cr_MxttI.d.ts +69 -0
- package/dist/browser.cjs +23 -5
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +12 -3
- package/dist/browser.d.ts +12 -3
- package/dist/browser.js +13 -5
- package/dist/browser.js.map +1 -1
- package/dist/{chunk-3YUUC6RN.cjs → chunk-4QPDZJAL.cjs} +1421 -37
- package/dist/chunk-4QPDZJAL.cjs.map +1 -0
- package/dist/{chunk-NWPCY65Z.cjs → chunk-HK56GQQP.cjs} +544 -61
- package/dist/chunk-HK56GQQP.cjs.map +1 -0
- package/dist/{chunk-GTDQAN2Z.js → chunk-OK5ZS3LU.js} +1419 -38
- package/dist/chunk-OK5ZS3LU.js.map +1 -0
- package/dist/{chunk-XRCY75UV.cjs → chunk-QMTWG6JL.cjs} +947 -916
- package/dist/chunk-QMTWG6JL.cjs.map +1 -0
- package/dist/{chunk-HUPDIRBX.js → chunk-VCH7RI5H.js} +947 -916
- package/dist/chunk-VCH7RI5H.js.map +1 -0
- package/dist/{chunk-IM4RCUHA.js → chunk-VKPCR7BG.js} +544 -62
- package/dist/chunk-VKPCR7BG.js.map +1 -0
- package/dist/diagrams/blockdiagram/index.d.cts +1 -1
- package/dist/diagrams/blockdiagram/index.d.ts +1 -1
- package/dist/diagrams/circuit/index.cjs +7 -7
- package/dist/diagrams/circuit/index.d.cts +1 -1
- package/dist/diagrams/circuit/index.d.ts +1 -1
- package/dist/diagrams/circuit/index.js +1 -1
- package/dist/diagrams/ecomap/index.d.cts +1 -1
- package/dist/diagrams/ecomap/index.d.ts +1 -1
- package/dist/diagrams/entity/index.d.cts +1 -1
- package/dist/diagrams/entity/index.d.ts +1 -1
- package/dist/diagrams/fishbone/index.d.cts +1 -1
- package/dist/diagrams/fishbone/index.d.ts +1 -1
- package/dist/diagrams/flowchart/index.d.cts +2 -2
- package/dist/diagrams/flowchart/index.d.ts +2 -2
- package/dist/diagrams/genogram/index.d.cts +1 -1
- package/dist/diagrams/genogram/index.d.ts +1 -1
- package/dist/diagrams/ladder/index.d.cts +1 -1
- package/dist/diagrams/ladder/index.d.ts +1 -1
- package/dist/diagrams/logic/index.d.cts +1 -1
- package/dist/diagrams/logic/index.d.ts +1 -1
- package/dist/diagrams/orgchart/index.d.cts +1 -1
- package/dist/diagrams/orgchart/index.d.ts +1 -1
- package/dist/diagrams/pedigree/index.d.cts +1 -1
- package/dist/diagrams/pedigree/index.d.ts +1 -1
- package/dist/diagrams/phylo/index.d.cts +1 -1
- package/dist/diagrams/phylo/index.d.ts +1 -1
- package/dist/diagrams/sld/index.d.cts +1 -1
- package/dist/diagrams/sld/index.d.ts +1 -1
- package/dist/diagrams/sociogram/index.d.cts +1 -1
- package/dist/diagrams/sociogram/index.d.ts +1 -1
- package/dist/diagrams/timing/index.d.cts +1 -1
- package/dist/diagrams/timing/index.d.ts +1 -1
- package/dist/diagrams/venn/index.d.cts +1 -1
- package/dist/diagrams/venn/index.d.ts +1 -1
- package/dist/{index-C9A0h-CB.d.cts → index-BD2yDfQM.d.cts} +1 -1
- package/dist/{index-CJai_TEZ.d.ts → index-C30zQWZI.d.ts} +1 -1
- package/dist/index.cjs +24 -12
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/react.cjs +7 -9
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +3 -2
- package/dist/react.d.ts +3 -2
- package/dist/react.js +7 -9
- package/dist/react.js.map +1 -1
- package/dist/tools-BHWaJPOl.d.ts +153 -0
- package/dist/tools-DlpuE76u.d.cts +153 -0
- package/dist/{types-BOAsqHoU.d.cts → types-WTr9W5Ud.d.cts} +2 -2
- package/dist/{types-BOAsqHoU.d.ts → types-WTr9W5Ud.d.ts} +2 -2
- package/package.json +2 -2
- package/dist/api-C5UcmT7n.d.cts +0 -22
- package/dist/api-C5UcmT7n.d.ts +0 -22
- package/dist/chunk-3YUUC6RN.cjs.map +0 -1
- package/dist/chunk-GTDQAN2Z.js.map +0 -1
- package/dist/chunk-HUPDIRBX.js.map +0 -1
- package/dist/chunk-IM4RCUHA.js.map +0 -1
- package/dist/chunk-NWPCY65Z.cjs.map +0 -1
- package/dist/chunk-XRCY75UV.cjs.map +0 -1
|
@@ -2,644 +2,54 @@ import { resolveIndustrialTheme } from './chunk-2VNMKOUO.js';
|
|
|
2
2
|
import { matchQuotedTitle } from './chunk-5IKOLUWK.js';
|
|
3
3
|
import { text, svgRoot, title, desc, defs, el, group, circle, escapeXml } from './chunk-SYYBKDL7.js';
|
|
4
4
|
|
|
5
|
-
// src/diagrams/circuit/
|
|
6
|
-
var
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
// src/diagrams/circuit/symbols.ts
|
|
6
|
+
var BODY = 'class="schematex-circuit-body"';
|
|
7
|
+
var FILL = 'class="schematex-circuit-fill"';
|
|
8
|
+
var WIRE = 'class="schematex-circuit-wire"';
|
|
9
|
+
function lineWire(x1, y1, x2, y2) {
|
|
10
|
+
return `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" ${WIRE}/>`;
|
|
11
|
+
}
|
|
12
|
+
var resistor = {
|
|
13
|
+
length: 40,
|
|
14
|
+
anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
|
|
15
|
+
svg: () => `<path d="M 0,0 L 5,0 L 8,-8 L 12,8 L 16,-8 L 20,8 L 24,-8 L 28,8 L 32,-8 L 35,0 L 40,0" ${BODY}/>`
|
|
13
16
|
};
|
|
14
|
-
var
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
M: { type: "nmos", pins: ["d", "g", "s"] },
|
|
24
|
-
J: { type: "jfet_n", pins: ["d", "g", "s"] },
|
|
25
|
-
S: { type: "switch_spst", pins: ["start", "end"] },
|
|
26
|
-
F: { type: "fuse", pins: ["start", "end"] },
|
|
27
|
-
B: { type: "battery", pins: ["plus", "minus"] },
|
|
28
|
-
K: { type: "relay_coil", pins: ["start", "end"] },
|
|
29
|
-
U: { type: "generic_ic", pins: [] },
|
|
30
|
-
// pins declared via pins="..." attr
|
|
31
|
-
X: { type: "generic_ic", pins: [] },
|
|
32
|
-
W: { type: "wire", pins: ["start", "end"] },
|
|
33
|
-
// explicit wire (non-SPICE convention, common in EE textbooks)
|
|
34
|
-
T: { type: "terminal_block", pins: [] }
|
|
35
|
-
// pins via pins="..."
|
|
17
|
+
var capacitor = {
|
|
18
|
+
length: 20,
|
|
19
|
+
anchors: { start: { x: 0, y: 0 }, end: { x: 20, y: 0 } },
|
|
20
|
+
svg: () => [
|
|
21
|
+
lineWire(0, 0, 8, 0),
|
|
22
|
+
`<line x1="8" y1="-10" x2="8" y2="10" ${BODY}/>`,
|
|
23
|
+
`<line x1="12" y1="-10" x2="12" y2="10" ${BODY}/>`,
|
|
24
|
+
lineWire(12, 0, 20, 0)
|
|
25
|
+
].join("")
|
|
36
26
|
};
|
|
37
|
-
var
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
tb: "terminal_block",
|
|
49
|
-
junction_box: "terminal_block",
|
|
50
|
-
jbox: "terminal_block",
|
|
51
|
-
enclosure: "terminal_block"
|
|
27
|
+
var electrolytic_cap = {
|
|
28
|
+
length: 20,
|
|
29
|
+
anchors: { start: { x: 0, y: 0 }, end: { x: 20, y: 0 } },
|
|
30
|
+
svg: () => [
|
|
31
|
+
lineWire(0, 0, 8, 0),
|
|
32
|
+
`<line x1="8" y1="-10" x2="8" y2="10" ${BODY}/>`,
|
|
33
|
+
`<path d="M 12,-10 Q 16,0 12,10" fill="none" class="schematex-circuit-body"/>`,
|
|
34
|
+
lineWire(12, 0, 20, 0),
|
|
35
|
+
`<text x="5" y="-12" class="schematex-circuit-pol">\u2212</text>`,
|
|
36
|
+
`<text x="15" y="-12" class="schematex-circuit-pol">+</text>`
|
|
37
|
+
].join("")
|
|
52
38
|
};
|
|
53
|
-
var
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
function tokenize(line) {
|
|
58
|
-
const tokens = [];
|
|
59
|
-
let i = 0;
|
|
60
|
-
while (i < line.length) {
|
|
61
|
-
const ch = line[i];
|
|
62
|
-
if (ch === " " || ch === " ") {
|
|
63
|
-
i++;
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
if (ch === '"') {
|
|
67
|
-
const end = line.indexOf('"', i + 1);
|
|
68
|
-
tokens.push(line.slice(i, end < 0 ? line.length : end + 1));
|
|
69
|
-
i = end < 0 ? line.length : end + 1;
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
let j = i;
|
|
73
|
-
while (j < line.length && line[j] !== " " && line[j] !== " ") {
|
|
74
|
-
if (line[j] === "=" && line[j + 1] === '"') {
|
|
75
|
-
const end = line.indexOf('"', j + 2);
|
|
76
|
-
j = end < 0 ? line.length : end + 1;
|
|
77
|
-
break;
|
|
78
|
-
}
|
|
79
|
-
j++;
|
|
80
|
-
}
|
|
81
|
-
tokens.push(line.slice(i, j));
|
|
82
|
-
i = j;
|
|
83
|
-
}
|
|
84
|
-
return tokens;
|
|
85
|
-
}
|
|
86
|
-
function isKeyEqVal(tok) {
|
|
87
|
-
const eq = tok.indexOf("=");
|
|
88
|
-
return eq > 0 && /^[a-zA-Z_][\w]*$/.test(tok.slice(0, eq));
|
|
89
|
-
}
|
|
90
|
-
function parseKV(tok) {
|
|
91
|
-
const eq = tok.indexOf("=");
|
|
92
|
-
const key = tok.slice(0, eq).trim();
|
|
93
|
-
let val = tok.slice(eq + 1).trim();
|
|
94
|
-
if (val.startsWith('"') && val.endsWith('"')) val = val.slice(1, -1);
|
|
95
|
-
return [key, val];
|
|
96
|
-
}
|
|
97
|
-
function parseNetlist(body, title2) {
|
|
98
|
-
const components = [];
|
|
99
|
-
const netByName = /* @__PURE__ */ new Map();
|
|
100
|
-
const pinMap = {};
|
|
101
|
-
let autoGnd = 0;
|
|
102
|
-
const ensureNet = (name) => {
|
|
103
|
-
let n = netByName.get(name);
|
|
104
|
-
if (!n) {
|
|
105
|
-
n = { id: name, anchors: [] };
|
|
106
|
-
netByName.set(name, n);
|
|
107
|
-
}
|
|
108
|
-
return n;
|
|
109
|
-
};
|
|
110
|
-
const lines = body.split("\n").map((l) => l.replace(/\r$/, ""));
|
|
111
|
-
for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
|
|
112
|
-
const raw = lines[lineIdx];
|
|
113
|
-
const stripped = raw.replace(/#.*$/, "").trim();
|
|
114
|
-
if (!stripped) continue;
|
|
115
|
-
const tokens = tokenize(stripped);
|
|
116
|
-
if (tokens.length < 2) {
|
|
117
|
-
throw new NetlistParseError(
|
|
118
|
-
`Netlist line must have at least ID + one net: "${stripped}"`,
|
|
119
|
-
lineIdx + 1
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
const id = tokens[0];
|
|
123
|
-
if (!/^[a-zA-Z_][\w]*$/.test(id)) {
|
|
124
|
-
throw new NetlistParseError(`Invalid component id: "${id}"`, lineIdx + 1);
|
|
125
|
-
}
|
|
126
|
-
let cursor = 1;
|
|
127
|
-
const netRefs = [];
|
|
128
|
-
while (cursor < tokens.length && !isKeyEqVal(tokens[cursor])) {
|
|
129
|
-
netRefs.push(tokens[cursor]);
|
|
130
|
-
cursor++;
|
|
131
|
-
}
|
|
132
|
-
const kv = {};
|
|
133
|
-
for (; cursor < tokens.length; cursor++) {
|
|
134
|
-
if (!isKeyEqVal(tokens[cursor])) {
|
|
135
|
-
throw new NetlistParseError(
|
|
136
|
-
`Expected key=value after nets, got "${tokens[cursor]}"`,
|
|
137
|
-
lineIdx + 1
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
const [k, v] = parseKV(tokens[cursor]);
|
|
141
|
-
kv[k] = v;
|
|
142
|
-
}
|
|
143
|
-
const prefix = id[0].toUpperCase();
|
|
144
|
-
const defaults = PREFIX_MAP[prefix];
|
|
145
|
-
let cType;
|
|
146
|
-
let pinOrder;
|
|
147
|
-
if (kv.type) {
|
|
148
|
-
const t = kv.type.toLowerCase();
|
|
149
|
-
cType = TYPE_ALIASES[t] ?? t;
|
|
150
|
-
pinOrder = defaults?.pins ?? ["p1", "p2"];
|
|
151
|
-
} else if (defaults) {
|
|
152
|
-
cType = defaults.type;
|
|
153
|
-
pinOrder = defaults.pins;
|
|
154
|
-
} else if (isGroundRef(id)) {
|
|
155
|
-
cType = "ground";
|
|
156
|
-
pinOrder = ["start"];
|
|
157
|
-
} else {
|
|
158
|
-
throw new NetlistParseError(
|
|
159
|
-
`Cannot infer type from id "${id}". Either rename to a SPICE-prefix id (R*, C*, L*, D*, V*, I*, Q*, M*, J*, S*, F*, B*, K*, U*, X*, W*, T*) or pass an explicit \`type=<name>\` attribute (e.g. \`${id} ... type=resistor\`). Schematex circuit covers electrical schematics only \u2014 hydraulic/pneumatic prefixes (EV*, BOMBA*, TANK*, etc.) are not supported.`,
|
|
160
|
-
lineIdx + 1
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
const expectedPins = pinOrder.length;
|
|
164
|
-
let valueFromTail;
|
|
165
|
-
if (netRefs.length > expectedPins && expectedPins > 0) {
|
|
166
|
-
const tail = netRefs.slice(expectedPins);
|
|
167
|
-
netRefs.length = expectedPins;
|
|
168
|
-
const first = tail[0].toLowerCase();
|
|
169
|
-
if (TYPE_ALIASES[first]) {
|
|
170
|
-
cType = TYPE_ALIASES[first];
|
|
171
|
-
} else if (first === "npn" || first === "pnp" || first === "nmos" || first === "pmos" || first === "jfet_n" || first === "jfet_p" || first === "zener" || first === "schottky" || first === "led" || first === "photodiode") {
|
|
172
|
-
cType = first;
|
|
173
|
-
if (first === "pnp") pinOrder = ["c", "b", "e"];
|
|
174
|
-
if (first === "pmos" || first === "jfet_p") pinOrder = ["d", "g", "s"];
|
|
175
|
-
} else {
|
|
176
|
-
valueFromTail = tail.join(" ");
|
|
177
|
-
}
|
|
178
|
-
if (tail.length > 1 && !valueFromTail) {
|
|
179
|
-
valueFromTail = tail.slice(1).join(" ");
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
if (cType === "ground" || cType === "gnd_signal" || cType === "gnd_chassis" || cType === "gnd_digital") {
|
|
183
|
-
pinOrder = ["start"];
|
|
184
|
-
}
|
|
185
|
-
if (cType === "terminal_block") {
|
|
186
|
-
const pinsAttr = kv.pins ?? kv.terminals;
|
|
187
|
-
const labels = pinsAttr ? pinsAttr.split(",").map((s) => s.trim()).filter(Boolean) : Array.from({ length: Math.max(netRefs.length, 1) }, (_, i) => `t${i + 1}`);
|
|
188
|
-
pinOrder = labels.map(
|
|
189
|
-
(label, idx) => label.toLowerCase().replace(/[^a-z0-9]+/g, "_") || `t${idx + 1}`
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
if (netRefs.length < pinOrder.length) {
|
|
193
|
-
throw new NetlistParseError(
|
|
194
|
-
`Component ${id} (${cType}) expects ${pinOrder.length} nets, got ${netRefs.length}`,
|
|
195
|
-
lineIdx + 1
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
const pins = {};
|
|
199
|
-
for (let p = 0; p < pinOrder.length; p++) {
|
|
200
|
-
let net = netRefs[p];
|
|
201
|
-
if (isGroundRef(net)) net = "GND";
|
|
202
|
-
pins[pinOrder[p]] = net;
|
|
203
|
-
ensureNet(net).anchors.push(`${id}.${pinOrder[p]}`);
|
|
204
|
-
}
|
|
205
|
-
pinMap[id] = pins;
|
|
206
|
-
const comp = {
|
|
207
|
-
id,
|
|
208
|
-
componentType: cType,
|
|
209
|
-
direction: "right",
|
|
210
|
-
label: kv.label ?? id,
|
|
211
|
-
value: kv.value ?? valueFromTail,
|
|
212
|
-
attrs: {}
|
|
213
|
-
};
|
|
214
|
-
for (const [k, v] of Object.entries(kv)) {
|
|
215
|
-
if (k === "label" || k === "value" || k === "type") continue;
|
|
216
|
-
comp.attrs[k] = v;
|
|
217
|
-
}
|
|
218
|
-
components.push(comp);
|
|
219
|
-
}
|
|
220
|
-
if (netByName.has("GND")) {
|
|
221
|
-
const hasGroundSym = components.some(
|
|
222
|
-
(c) => c.componentType === "ground" || c.componentType === "gnd_signal" || c.componentType === "gnd_chassis" || c.componentType === "gnd_digital"
|
|
223
|
-
);
|
|
224
|
-
if (!hasGroundSym) {
|
|
225
|
-
const gId = `_GND${autoGnd++}`;
|
|
226
|
-
components.push({
|
|
227
|
-
id: gId,
|
|
228
|
-
componentType: "ground",
|
|
229
|
-
direction: "down",
|
|
230
|
-
label: void 0,
|
|
231
|
-
value: void 0,
|
|
232
|
-
attrs: { auto: "true" }
|
|
233
|
-
});
|
|
234
|
-
pinMap[gId] = { start: "GND" };
|
|
235
|
-
ensureNet("GND").anchors.push(`${gId}.start`);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
const nets = Array.from(netByName.values());
|
|
239
|
-
return {
|
|
240
|
-
type: "circuit",
|
|
241
|
-
title: title2,
|
|
242
|
-
components,
|
|
243
|
-
nets,
|
|
244
|
-
pinMap,
|
|
245
|
-
mode: "netlist"
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// src/diagrams/circuit/parser.ts
|
|
250
|
-
var CircuitParseError = class extends Error {
|
|
251
|
-
constructor(message) {
|
|
252
|
-
super(message);
|
|
253
|
-
this.name = "CircuitParseError";
|
|
254
|
-
}
|
|
39
|
+
var inductor = {
|
|
40
|
+
length: 40,
|
|
41
|
+
anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
|
|
42
|
+
svg: () => `<path d="M 0,0 L 5,0 A 5,5 0 0 1 15,0 A 5,5 0 0 1 25,0 A 5,5 0 0 1 35,0 L 40,0" fill="none" ${BODY}/>`
|
|
255
43
|
};
|
|
256
|
-
var
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
"
|
|
265
|
-
"fuse_slow",
|
|
266
|
-
"capacitor",
|
|
267
|
-
"electrolytic_cap",
|
|
268
|
-
"variable_cap",
|
|
269
|
-
"inductor",
|
|
270
|
-
"inductor_iron",
|
|
271
|
-
"inductor_ferrite",
|
|
272
|
-
"variable_inductor",
|
|
273
|
-
"ferrite_bead",
|
|
274
|
-
"crystal",
|
|
275
|
-
"transformer",
|
|
276
|
-
"diode",
|
|
277
|
-
"zener",
|
|
278
|
-
"schottky",
|
|
279
|
-
"led",
|
|
280
|
-
"photodiode",
|
|
281
|
-
"varactor",
|
|
282
|
-
"tvs_diode",
|
|
283
|
-
"bridge_rectifier",
|
|
284
|
-
"npn",
|
|
285
|
-
"pnp",
|
|
286
|
-
"darlington_npn",
|
|
287
|
-
"darlington_pnp",
|
|
288
|
-
"nmos",
|
|
289
|
-
"pmos",
|
|
290
|
-
"nmos_depletion",
|
|
291
|
-
"jfet_n",
|
|
292
|
-
"jfet_p",
|
|
293
|
-
"igbt",
|
|
294
|
-
"scr",
|
|
295
|
-
"triac",
|
|
296
|
-
"diac",
|
|
297
|
-
"phototransistor",
|
|
298
|
-
"optocoupler",
|
|
299
|
-
"opamp",
|
|
300
|
-
"comparator",
|
|
301
|
-
"schmitt_buffer",
|
|
302
|
-
"tri_state_buffer",
|
|
303
|
-
"instrumentation_amp",
|
|
304
|
-
"generic_ic",
|
|
305
|
-
"voltage_regulator",
|
|
306
|
-
"dc_dc_converter",
|
|
307
|
-
"555_timer",
|
|
308
|
-
"voltage_source",
|
|
309
|
-
"current_source",
|
|
310
|
-
"ac_source",
|
|
311
|
-
"battery",
|
|
312
|
-
"vcc",
|
|
313
|
-
"ground",
|
|
314
|
-
"gnd_signal",
|
|
315
|
-
"gnd_chassis",
|
|
316
|
-
"gnd_digital",
|
|
317
|
-
"switch_spst",
|
|
318
|
-
"switch_spdt",
|
|
319
|
-
"switch_dpdt",
|
|
320
|
-
"push_no",
|
|
321
|
-
"push_nc",
|
|
322
|
-
"relay_coil",
|
|
323
|
-
"relay_no",
|
|
324
|
-
"relay_nc",
|
|
325
|
-
"contactor",
|
|
326
|
-
"solenoid_valve",
|
|
327
|
-
"thermal_overload",
|
|
328
|
-
"disconnect_switch",
|
|
329
|
-
"motor",
|
|
330
|
-
"speaker",
|
|
331
|
-
"microphone",
|
|
332
|
-
"buzzer",
|
|
333
|
-
"ammeter",
|
|
334
|
-
"voltmeter",
|
|
335
|
-
"wattmeter",
|
|
336
|
-
"oscilloscope",
|
|
337
|
-
"wire",
|
|
338
|
-
"dot",
|
|
339
|
-
"label",
|
|
340
|
-
"port",
|
|
341
|
-
"test_point",
|
|
342
|
-
"no_connect",
|
|
343
|
-
"antenna"
|
|
344
|
-
]);
|
|
345
|
-
var ALIASES = {
|
|
346
|
-
vsource: "voltage_source",
|
|
347
|
-
isource: "current_source",
|
|
348
|
-
acsource: "ac_source",
|
|
349
|
-
ecap: "electrolytic_cap",
|
|
350
|
-
pot: "potentiometer",
|
|
351
|
-
xtal: "crystal",
|
|
352
|
-
xfmr: "transformer",
|
|
353
|
-
transistor: "npn",
|
|
354
|
-
bjt_npn: "npn",
|
|
355
|
-
bjt_pnp: "pnp",
|
|
356
|
-
mosfet_n: "nmos",
|
|
357
|
-
mosfet_p: "pmos",
|
|
358
|
-
gnd: "ground",
|
|
359
|
-
ic: "generic_ic",
|
|
360
|
-
reg: "voltage_regulator",
|
|
361
|
-
timer555: "555_timer",
|
|
362
|
-
therm: "thermistor_ntc",
|
|
363
|
-
ntc: "thermistor_ntc",
|
|
364
|
-
ptc: "thermistor_ptc",
|
|
365
|
-
ths: "thermistor_ntc",
|
|
366
|
-
// Industrial control aliases (IEC letter codes)
|
|
367
|
-
coil: "relay_coil",
|
|
368
|
-
relay: "relay_coil",
|
|
369
|
-
km: "contactor",
|
|
370
|
-
solenoid: "solenoid_valve",
|
|
371
|
-
ev: "solenoid_valve",
|
|
372
|
-
overload: "thermal_overload",
|
|
373
|
-
thermal: "thermal_overload",
|
|
374
|
-
disconnect: "disconnect_switch",
|
|
375
|
-
isolator: "disconnect_switch"
|
|
376
|
-
};
|
|
377
|
-
var EXTRA_TYPES = /* @__PURE__ */ new Set(["lamp"]);
|
|
378
|
-
function normalizeType(raw) {
|
|
379
|
-
const lower = raw.toLowerCase();
|
|
380
|
-
if (ALIASES[lower]) return ALIASES[lower];
|
|
381
|
-
if (COMPONENT_TYPES.has(lower)) {
|
|
382
|
-
return lower;
|
|
383
|
-
}
|
|
384
|
-
if (EXTRA_TYPES.has(lower)) {
|
|
385
|
-
return lower;
|
|
386
|
-
}
|
|
387
|
-
return null;
|
|
388
|
-
}
|
|
389
|
-
var DIRECTIONS = /* @__PURE__ */ new Set(["right", "left", "up", "down"]);
|
|
390
|
-
function parseAttrs(rest) {
|
|
391
|
-
const out = { attrs: {} };
|
|
392
|
-
const tokens = [];
|
|
393
|
-
let i = 0;
|
|
394
|
-
while (i < rest.length) {
|
|
395
|
-
const ch = rest[i];
|
|
396
|
-
if (ch === " " || ch === " ") {
|
|
397
|
-
i++;
|
|
398
|
-
continue;
|
|
399
|
-
}
|
|
400
|
-
if (ch === '"') {
|
|
401
|
-
const end = rest.indexOf('"', i + 1);
|
|
402
|
-
const tok = rest.slice(i, end < 0 ? rest.length : end + 1);
|
|
403
|
-
tokens.push(tok);
|
|
404
|
-
i = end < 0 ? rest.length : end + 1;
|
|
405
|
-
continue;
|
|
406
|
-
}
|
|
407
|
-
let j = i;
|
|
408
|
-
while (j < rest.length && rest[j] !== " " && rest[j] !== " ") {
|
|
409
|
-
if (rest[j] === "=" && rest[j + 1] === '"') {
|
|
410
|
-
const end = rest.indexOf('"', j + 2);
|
|
411
|
-
j = end < 0 ? rest.length : end + 1;
|
|
412
|
-
break;
|
|
413
|
-
}
|
|
414
|
-
j++;
|
|
415
|
-
}
|
|
416
|
-
tokens.push(rest.slice(i, j));
|
|
417
|
-
i = j;
|
|
418
|
-
}
|
|
419
|
-
for (const tok of tokens) {
|
|
420
|
-
if (!tok) continue;
|
|
421
|
-
if (DIRECTIONS.has(tok.toLowerCase())) {
|
|
422
|
-
out.direction = tok.toLowerCase();
|
|
423
|
-
continue;
|
|
424
|
-
}
|
|
425
|
-
const eq = tok.indexOf("=");
|
|
426
|
-
if (eq > 0) {
|
|
427
|
-
const key = tok.slice(0, eq).trim();
|
|
428
|
-
let val = tok.slice(eq + 1).trim();
|
|
429
|
-
if (val.startsWith('"') && val.endsWith('"')) {
|
|
430
|
-
val = val.slice(1, -1);
|
|
431
|
-
}
|
|
432
|
-
if (key === "label") out.label = val;
|
|
433
|
-
else if (key === "value") out.value = val;
|
|
434
|
-
else if (key === "at") out.at = val;
|
|
435
|
-
else if (key === "length") out.length = val;
|
|
436
|
-
else out.attrs[key] = val;
|
|
437
|
-
continue;
|
|
438
|
-
}
|
|
439
|
-
out.attrs[tok] = "true";
|
|
440
|
-
}
|
|
441
|
-
return out;
|
|
442
|
-
}
|
|
443
|
-
function parseCircuit(text2) {
|
|
444
|
-
const rawLines = text2.split("\n");
|
|
445
|
-
const firstMeaningful = rawLines.map((l) => l.replace(/#.*$/, "").trim()).find((l) => l.length > 0) ?? "";
|
|
446
|
-
if (/^circuit\b.*\bnetlist\s*$/i.test(firstMeaningful)) {
|
|
447
|
-
const netlistTitle = matchQuotedTitle(firstMeaningful);
|
|
448
|
-
let headerIdx = -1;
|
|
449
|
-
for (let i = 0; i < rawLines.length; i++) {
|
|
450
|
-
const s = rawLines[i].replace(/#.*$/, "").trim();
|
|
451
|
-
if (s.length > 0) {
|
|
452
|
-
headerIdx = i;
|
|
453
|
-
break;
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
const body = rawLines.slice(headerIdx + 1).join("\n");
|
|
457
|
-
return parseNetlist(body, netlistTitle);
|
|
458
|
-
}
|
|
459
|
-
const lines = text2.split("\n").map((l) => l.replace(/\r$/, ""));
|
|
460
|
-
let title2;
|
|
461
|
-
const components = [];
|
|
462
|
-
const nets = [];
|
|
463
|
-
const netByName = /* @__PURE__ */ new Map();
|
|
464
|
-
let autoId = 0;
|
|
465
|
-
let pendingAt;
|
|
466
|
-
const mkId = (prefix) => `${prefix}_${autoId++}`;
|
|
467
|
-
for (const rawLine of lines) {
|
|
468
|
-
const stripped = rawLine.replace(/#.*$/, "").trim();
|
|
469
|
-
if (!stripped) continue;
|
|
470
|
-
if (/^circuit\b/i.test(stripped)) {
|
|
471
|
-
const t = matchQuotedTitle(stripped);
|
|
472
|
-
if (t !== void 0) title2 = t;
|
|
473
|
-
continue;
|
|
474
|
-
}
|
|
475
|
-
const atMatch = stripped.match(/^at:\s*(.+)$/i);
|
|
476
|
-
if (atMatch) {
|
|
477
|
-
pendingAt = atMatch[1].trim();
|
|
478
|
-
continue;
|
|
479
|
-
}
|
|
480
|
-
const netDecl = stripped.match(/^net\s+([a-zA-Z_][\w]*)\s*$/i);
|
|
481
|
-
if (netDecl) {
|
|
482
|
-
const name = netDecl[1];
|
|
483
|
-
if (!netByName.has(name)) {
|
|
484
|
-
const n = { id: name, anchors: [] };
|
|
485
|
-
netByName.set(name, n);
|
|
486
|
-
nets.push(n);
|
|
487
|
-
}
|
|
488
|
-
continue;
|
|
489
|
-
}
|
|
490
|
-
const netDotMatch = stripped.match(/^net\s+([a-zA-Z_][\w]*)\s*:\s*dot\s*$/i);
|
|
491
|
-
if (netDotMatch) {
|
|
492
|
-
const name = netDotMatch[1];
|
|
493
|
-
let n = netByName.get(name);
|
|
494
|
-
if (!n) {
|
|
495
|
-
n = { id: name, anchors: [] };
|
|
496
|
-
netByName.set(name, n);
|
|
497
|
-
nets.push(n);
|
|
498
|
-
}
|
|
499
|
-
const id = mkId("dot");
|
|
500
|
-
components.push({
|
|
501
|
-
id,
|
|
502
|
-
componentType: "dot",
|
|
503
|
-
direction: "right",
|
|
504
|
-
at: pendingAt,
|
|
505
|
-
attrs: { net: name }
|
|
506
|
-
});
|
|
507
|
-
n.anchors.push(`${id}.end`);
|
|
508
|
-
pendingAt = `${id}.end`;
|
|
509
|
-
continue;
|
|
510
|
-
}
|
|
511
|
-
const labelMatch = stripped.match(/^label\s+"([^"]*)"(?:\s+(right|left|up|down))?\s*$/i);
|
|
512
|
-
if (labelMatch) {
|
|
513
|
-
const id = mkId("lbl");
|
|
514
|
-
components.push({
|
|
515
|
-
id,
|
|
516
|
-
componentType: "label",
|
|
517
|
-
direction: labelMatch[2]?.toLowerCase() ?? "right",
|
|
518
|
-
at: pendingAt,
|
|
519
|
-
label: labelMatch[1]
|
|
520
|
-
});
|
|
521
|
-
continue;
|
|
522
|
-
}
|
|
523
|
-
const wireMatch = stripped.match(/^wire(?:\s+(right|left|up|down))?(?:\s+(\d+)(?:px)?)?\s*$/i);
|
|
524
|
-
if (wireMatch) {
|
|
525
|
-
const id = mkId("w");
|
|
526
|
-
components.push({
|
|
527
|
-
id,
|
|
528
|
-
componentType: "wire",
|
|
529
|
-
direction: wireMatch[1]?.toLowerCase() ?? "right",
|
|
530
|
-
at: pendingAt,
|
|
531
|
-
attrs: wireMatch[2] ? { length: wireMatch[2] } : {}
|
|
532
|
-
});
|
|
533
|
-
pendingAt = `${id}.end`;
|
|
534
|
-
continue;
|
|
535
|
-
}
|
|
536
|
-
const bareMatch = stripped.match(/^([a-zA-Z_][\w]*)(\s+.*)?$/);
|
|
537
|
-
const colonMatch = stripped.match(/^([a-zA-Z_][\w]*)\s*:\s*([a-zA-Z_][\w]*)(\s+.*)?$/);
|
|
538
|
-
if (colonMatch) {
|
|
539
|
-
const id = colonMatch[1];
|
|
540
|
-
const typeStr = colonMatch[2];
|
|
541
|
-
const norm = normalizeType(typeStr);
|
|
542
|
-
if (!norm) {
|
|
543
|
-
throw new CircuitParseError(`Unknown component type: ${typeStr}`);
|
|
544
|
-
}
|
|
545
|
-
const rest = colonMatch[3] ?? "";
|
|
546
|
-
const parsed = parseAttrs(rest);
|
|
547
|
-
const comp = {
|
|
548
|
-
id,
|
|
549
|
-
componentType: norm,
|
|
550
|
-
direction: parsed.direction ?? "right",
|
|
551
|
-
at: parsed.at ?? pendingAt,
|
|
552
|
-
label: parsed.label,
|
|
553
|
-
value: parsed.value,
|
|
554
|
-
attrs: parsed.attrs
|
|
555
|
-
};
|
|
556
|
-
if (parsed.length) {
|
|
557
|
-
comp.attrs = { ...comp.attrs, length: parsed.length };
|
|
558
|
-
}
|
|
559
|
-
components.push(comp);
|
|
560
|
-
pendingAt = `${id}.end`;
|
|
561
|
-
continue;
|
|
562
|
-
}
|
|
563
|
-
if (bareMatch) {
|
|
564
|
-
const typeStr = bareMatch[1];
|
|
565
|
-
const norm = normalizeType(typeStr);
|
|
566
|
-
if (!norm) {
|
|
567
|
-
continue;
|
|
568
|
-
}
|
|
569
|
-
const rest = bareMatch[2] ?? "";
|
|
570
|
-
const parsed = parseAttrs(rest);
|
|
571
|
-
const id = mkId(norm);
|
|
572
|
-
const comp = {
|
|
573
|
-
id,
|
|
574
|
-
componentType: norm,
|
|
575
|
-
direction: parsed.direction ?? "right",
|
|
576
|
-
at: parsed.at ?? pendingAt,
|
|
577
|
-
label: parsed.label,
|
|
578
|
-
value: parsed.value,
|
|
579
|
-
attrs: parsed.attrs
|
|
580
|
-
};
|
|
581
|
-
components.push(comp);
|
|
582
|
-
pendingAt = `${id}.end`;
|
|
583
|
-
continue;
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
return {
|
|
587
|
-
type: "circuit",
|
|
588
|
-
title: title2,
|
|
589
|
-
components,
|
|
590
|
-
nets,
|
|
591
|
-
mode: "positional"
|
|
592
|
-
};
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
// src/diagrams/circuit/symbols.ts
|
|
596
|
-
var BODY = 'class="schematex-circuit-body"';
|
|
597
|
-
var FILL = 'class="schematex-circuit-fill"';
|
|
598
|
-
var WIRE = 'class="schematex-circuit-wire"';
|
|
599
|
-
function lineWire(x1, y1, x2, y2) {
|
|
600
|
-
return `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" ${WIRE}/>`;
|
|
601
|
-
}
|
|
602
|
-
var resistor = {
|
|
603
|
-
length: 40,
|
|
604
|
-
anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
|
|
605
|
-
svg: () => `<path d="M 0,0 L 5,0 L 8,-8 L 12,8 L 16,-8 L 20,8 L 24,-8 L 28,8 L 32,-8 L 35,0 L 40,0" ${BODY}/>`
|
|
606
|
-
};
|
|
607
|
-
var capacitor = {
|
|
608
|
-
length: 20,
|
|
609
|
-
anchors: { start: { x: 0, y: 0 }, end: { x: 20, y: 0 } },
|
|
610
|
-
svg: () => [
|
|
611
|
-
lineWire(0, 0, 8, 0),
|
|
612
|
-
`<line x1="8" y1="-10" x2="8" y2="10" ${BODY}/>`,
|
|
613
|
-
`<line x1="12" y1="-10" x2="12" y2="10" ${BODY}/>`,
|
|
614
|
-
lineWire(12, 0, 20, 0)
|
|
615
|
-
].join("")
|
|
616
|
-
};
|
|
617
|
-
var electrolytic_cap = {
|
|
618
|
-
length: 20,
|
|
619
|
-
anchors: { start: { x: 0, y: 0 }, end: { x: 20, y: 0 } },
|
|
620
|
-
svg: () => [
|
|
621
|
-
lineWire(0, 0, 8, 0),
|
|
622
|
-
`<line x1="8" y1="-10" x2="8" y2="10" ${BODY}/>`,
|
|
623
|
-
`<path d="M 12,-10 Q 16,0 12,10" fill="none" class="schematex-circuit-body"/>`,
|
|
624
|
-
lineWire(12, 0, 20, 0),
|
|
625
|
-
`<text x="5" y="-12" class="schematex-circuit-pol">\u2212</text>`,
|
|
626
|
-
`<text x="15" y="-12" class="schematex-circuit-pol">+</text>`
|
|
627
|
-
].join("")
|
|
628
|
-
};
|
|
629
|
-
var inductor = {
|
|
630
|
-
length: 40,
|
|
631
|
-
anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
|
|
632
|
-
svg: () => `<path d="M 0,0 L 5,0 A 5,5 0 0 1 15,0 A 5,5 0 0 1 25,0 A 5,5 0 0 1 35,0 L 40,0" fill="none" ${BODY}/>`
|
|
633
|
-
};
|
|
634
|
-
var fuse = {
|
|
635
|
-
length: 30,
|
|
636
|
-
anchors: { start: { x: 0, y: 0 }, end: { x: 30, y: 0 } },
|
|
637
|
-
svg: () => [
|
|
638
|
-
lineWire(0, 0, 5, 0),
|
|
639
|
-
`<rect x="5" y="-5" width="20" height="10" rx="5" fill="none" ${BODY}/>`,
|
|
640
|
-
`<line x1="5" y1="0" x2="25" y2="0" ${BODY}/>`,
|
|
641
|
-
lineWire(25, 0, 30, 0)
|
|
642
|
-
].join("")
|
|
44
|
+
var fuse = {
|
|
45
|
+
length: 30,
|
|
46
|
+
anchors: { start: { x: 0, y: 0 }, end: { x: 30, y: 0 } },
|
|
47
|
+
svg: () => [
|
|
48
|
+
lineWire(0, 0, 5, 0),
|
|
49
|
+
`<rect x="5" y="-5" width="20" height="10" rx="5" fill="none" ${BODY}/>`,
|
|
50
|
+
`<line x1="5" y1="0" x2="25" y2="0" ${BODY}/>`,
|
|
51
|
+
lineWire(25, 0, 30, 0)
|
|
52
|
+
].join("")
|
|
643
53
|
};
|
|
644
54
|
var crystal = {
|
|
645
55
|
length: 40,
|
|
@@ -654,6 +64,7 @@ var crystal = {
|
|
|
654
64
|
};
|
|
655
65
|
var transformer = {
|
|
656
66
|
length: 60,
|
|
67
|
+
netlistPins: ["p1", "p2", "s1", "s2"],
|
|
657
68
|
anchors: {
|
|
658
69
|
start: { x: 0, y: 0 },
|
|
659
70
|
end: { x: 60, y: 0 },
|
|
@@ -676,6 +87,7 @@ var transformer = {
|
|
|
676
87
|
};
|
|
677
88
|
var voltage_source = {
|
|
678
89
|
length: 40,
|
|
90
|
+
netlistPins: ["plus", "minus"],
|
|
679
91
|
// plus/minus aliases: end = +, start = − (matches on-symbol label)
|
|
680
92
|
anchors: {
|
|
681
93
|
start: { x: 0, y: 0 },
|
|
@@ -693,6 +105,7 @@ var voltage_source = {
|
|
|
693
105
|
};
|
|
694
106
|
var current_source = {
|
|
695
107
|
length: 40,
|
|
108
|
+
netlistPins: ["plus", "minus"],
|
|
696
109
|
anchors: {
|
|
697
110
|
start: { x: 0, y: 0 },
|
|
698
111
|
end: { x: 40, y: 0 },
|
|
@@ -709,6 +122,7 @@ var current_source = {
|
|
|
709
122
|
};
|
|
710
123
|
var ac_source = {
|
|
711
124
|
length: 40,
|
|
125
|
+
netlistPins: ["plus", "minus"],
|
|
712
126
|
anchors: {
|
|
713
127
|
start: { x: 0, y: 0 },
|
|
714
128
|
end: { x: 40, y: 0 },
|
|
@@ -724,6 +138,7 @@ var ac_source = {
|
|
|
724
138
|
};
|
|
725
139
|
var battery = {
|
|
726
140
|
length: 24,
|
|
141
|
+
netlistPins: ["plus", "minus"],
|
|
727
142
|
anchors: {
|
|
728
143
|
start: { x: 0, y: 0 },
|
|
729
144
|
end: { x: 24, y: 0 },
|
|
@@ -824,6 +239,7 @@ var photodiode = {
|
|
|
824
239
|
};
|
|
825
240
|
var npn = {
|
|
826
241
|
length: 40,
|
|
242
|
+
netlistPins: ["c", "b", "e"],
|
|
827
243
|
// base at left, collector up-right, emitter down-right
|
|
828
244
|
anchors: {
|
|
829
245
|
start: { x: 0, y: 0 },
|
|
@@ -849,6 +265,7 @@ var npn = {
|
|
|
849
265
|
};
|
|
850
266
|
var pnp = {
|
|
851
267
|
length: 40,
|
|
268
|
+
netlistPins: ["c", "b", "e"],
|
|
852
269
|
anchors: {
|
|
853
270
|
start: { x: 0, y: 0 },
|
|
854
271
|
end: { x: 40, y: -16 },
|
|
@@ -873,6 +290,7 @@ var pnp = {
|
|
|
873
290
|
};
|
|
874
291
|
var nmos = {
|
|
875
292
|
length: 40,
|
|
293
|
+
netlistPins: ["d", "g", "s"],
|
|
876
294
|
anchors: {
|
|
877
295
|
start: { x: 0, y: 0 },
|
|
878
296
|
end: { x: 40, y: -16 },
|
|
@@ -898,6 +316,7 @@ var nmos = {
|
|
|
898
316
|
};
|
|
899
317
|
var pmos = {
|
|
900
318
|
length: 40,
|
|
319
|
+
netlistPins: ["d", "g", "s"],
|
|
901
320
|
anchors: {
|
|
902
321
|
start: { x: 0, y: 0 },
|
|
903
322
|
end: { x: 40, y: -16 },
|
|
@@ -923,12 +342,16 @@ var pmos = {
|
|
|
923
342
|
};
|
|
924
343
|
var jfet_n = {
|
|
925
344
|
length: 40,
|
|
345
|
+
netlistPins: ["d", "g", "s"],
|
|
926
346
|
anchors: {
|
|
927
347
|
start: { x: 0, y: 0 },
|
|
928
348
|
end: { x: 40, y: -16 },
|
|
929
349
|
gate: { x: 0, y: 0 },
|
|
930
350
|
drain: { x: 40, y: -16 },
|
|
931
|
-
source: { x: 40, y: 16 }
|
|
351
|
+
source: { x: 40, y: 16 },
|
|
352
|
+
g: { x: 0, y: 0 },
|
|
353
|
+
d: { x: 40, y: -16 },
|
|
354
|
+
s: { x: 40, y: 16 }
|
|
932
355
|
},
|
|
933
356
|
svg: () => [
|
|
934
357
|
lineWire(0, 0, 14, 0),
|
|
@@ -942,12 +365,16 @@ var jfet_n = {
|
|
|
942
365
|
};
|
|
943
366
|
var jfet_p = {
|
|
944
367
|
length: 40,
|
|
368
|
+
netlistPins: ["d", "g", "s"],
|
|
945
369
|
anchors: {
|
|
946
370
|
start: { x: 0, y: 0 },
|
|
947
371
|
end: { x: 40, y: -16 },
|
|
948
372
|
gate: { x: 0, y: 0 },
|
|
949
373
|
drain: { x: 40, y: -16 },
|
|
950
|
-
source: { x: 40, y: 16 }
|
|
374
|
+
source: { x: 40, y: 16 },
|
|
375
|
+
g: { x: 0, y: 0 },
|
|
376
|
+
d: { x: 40, y: -16 },
|
|
377
|
+
s: { x: 40, y: 16 }
|
|
951
378
|
},
|
|
952
379
|
svg: () => [
|
|
953
380
|
lineWire(0, 0, 14, 0),
|
|
@@ -961,6 +388,7 @@ var jfet_p = {
|
|
|
961
388
|
};
|
|
962
389
|
var opamp = {
|
|
963
390
|
length: 50,
|
|
391
|
+
netlistPins: ["plus", "minus", "out"],
|
|
964
392
|
anchors: {
|
|
965
393
|
start: { x: 0, y: 0 },
|
|
966
394
|
end: { x: 50, y: 0 },
|
|
@@ -978,6 +406,7 @@ var opamp = {
|
|
|
978
406
|
};
|
|
979
407
|
var comparator = {
|
|
980
408
|
length: 50,
|
|
409
|
+
netlistPins: ["plus", "minus", "out"],
|
|
981
410
|
anchors: {
|
|
982
411
|
start: { x: 0, y: 0 },
|
|
983
412
|
end: { x: 50, y: 0 },
|
|
@@ -1126,6 +555,7 @@ var variable_inductor = {
|
|
|
1126
555
|
};
|
|
1127
556
|
var switch_spdt = {
|
|
1128
557
|
length: 50,
|
|
558
|
+
netlistPins: ["common", "nc", "no"],
|
|
1129
559
|
anchors: {
|
|
1130
560
|
start: { x: 0, y: 0 },
|
|
1131
561
|
end: { x: 50, y: 0 },
|
|
@@ -1200,292 +630,893 @@ var antenna = {
|
|
|
1200
630
|
`<line x1="0" y1="-10" x2="10" y2="-24" ${BODY}/>`
|
|
1201
631
|
].join("")
|
|
1202
632
|
};
|
|
1203
|
-
function icSymbol(defaultLeft, defaultRight, bodyLabel) {
|
|
1204
|
-
const BODY_W = 80;
|
|
1205
|
-
return {
|
|
1206
|
-
length: BODY_W,
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
const
|
|
1214
|
-
const
|
|
1215
|
-
const
|
|
1216
|
-
const
|
|
1217
|
-
const
|
|
1218
|
-
const
|
|
1219
|
-
parts
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
parts.push(`<
|
|
633
|
+
function icSymbol(defaultLeft, defaultRight, bodyLabel) {
|
|
634
|
+
const BODY_W = 80;
|
|
635
|
+
return {
|
|
636
|
+
length: BODY_W,
|
|
637
|
+
netlistPins: [],
|
|
638
|
+
anchors: {
|
|
639
|
+
start: { x: 0, y: 0 },
|
|
640
|
+
end: { x: BODY_W, y: 0 }
|
|
641
|
+
},
|
|
642
|
+
svg: (_label, _value, attrs) => {
|
|
643
|
+
const left = attrs?.pins_left ? attrs.pins_left.split(",").map((s) => s.trim()) : defaultLeft;
|
|
644
|
+
const right = attrs?.pins_right ? attrs.pins_right.split(",").map((s) => s.trim()) : defaultRight;
|
|
645
|
+
const n = Math.max(left.length, right.length, 2);
|
|
646
|
+
const pitch = 16;
|
|
647
|
+
const bodyH = pitch * (n + 1);
|
|
648
|
+
const topY = -bodyH / 2;
|
|
649
|
+
const parts = [];
|
|
650
|
+
parts.push(`<rect x="0" y="${topY}" width="${BODY_W}" height="${bodyH}" fill="white" ${BODY}/>`);
|
|
651
|
+
const labelText = attrs?.ic_label ?? bodyLabel ?? "";
|
|
652
|
+
if (labelText) {
|
|
653
|
+
parts.push(`<text x="${BODY_W / 2}" y="3" text-anchor="middle" class="schematex-circuit-meter">${labelText}</text>`);
|
|
654
|
+
}
|
|
655
|
+
for (let i = 0; i < left.length; i++) {
|
|
656
|
+
const y = topY + pitch * (i + 1);
|
|
657
|
+
parts.push(`<line x1="-8" y1="${y}" x2="0" y2="${y}" ${WIRE}/>`);
|
|
658
|
+
parts.push(`<text x="4" y="${y + 3}" class="schematex-circuit-pol">${left[i]}</text>`);
|
|
659
|
+
}
|
|
660
|
+
for (let i = 0; i < right.length; i++) {
|
|
661
|
+
const y = topY + pitch * (i + 1);
|
|
662
|
+
parts.push(`<line x1="${BODY_W}" y1="${y}" x2="${BODY_W + 8}" y2="${y}" ${WIRE}/>`);
|
|
663
|
+
parts.push(`<text x="${BODY_W - 4}" y="${y + 3}" text-anchor="end" class="schematex-circuit-pol">${right[i]}</text>`);
|
|
664
|
+
}
|
|
665
|
+
return parts.join("");
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
var generic_ic = icSymbol(
|
|
670
|
+
["1", "2", "3", "4"],
|
|
671
|
+
["8", "7", "6", "5"],
|
|
672
|
+
"IC"
|
|
673
|
+
);
|
|
674
|
+
var timer_555 = (() => {
|
|
675
|
+
const left = ["GND", "TRG", "OUT", "RST"];
|
|
676
|
+
const right = ["VCC", "DIS", "THR", "CTL"];
|
|
677
|
+
const sym = icSymbol(left, right, "555");
|
|
678
|
+
const BODY_W = 80;
|
|
679
|
+
const pitch = 16;
|
|
680
|
+
const n = 4;
|
|
681
|
+
const topY = -80 / 2;
|
|
682
|
+
const anchors = {
|
|
683
|
+
start: { x: 0, y: 0 },
|
|
684
|
+
end: { x: BODY_W, y: 0 }
|
|
685
|
+
};
|
|
686
|
+
const leftNames = ["gnd", "trg", "out", "rst"];
|
|
687
|
+
const rightNames = ["vcc", "dis", "thr", "ctl"];
|
|
688
|
+
for (let i = 0; i < n; i++) {
|
|
689
|
+
const y = topY + pitch * (i + 1);
|
|
690
|
+
anchors[leftNames[i]] = { x: -8, y };
|
|
691
|
+
anchors[rightNames[i]] = { x: BODY_W + 8, y };
|
|
692
|
+
}
|
|
693
|
+
return { ...sym, anchors };
|
|
694
|
+
})();
|
|
695
|
+
var voltage_regulator = {
|
|
696
|
+
length: 60,
|
|
697
|
+
netlistPins: ["in", "gnd", "out"],
|
|
698
|
+
anchors: {
|
|
699
|
+
start: { x: 0, y: 0 },
|
|
700
|
+
end: { x: 60, y: 0 },
|
|
701
|
+
in: { x: 0, y: 0 },
|
|
702
|
+
out: { x: 60, y: 0 },
|
|
703
|
+
gnd: { x: 30, y: 20 }
|
|
704
|
+
},
|
|
705
|
+
svg: (_l, _v, attrs) => [
|
|
706
|
+
lineWire(0, 0, 10, 0),
|
|
707
|
+
`<rect x="10" y="-15" width="40" height="30" fill="white" ${BODY}/>`,
|
|
708
|
+
`<text x="30" y="4" text-anchor="middle" class="schematex-circuit-meter">${attrs?.model ?? "REG"}</text>`,
|
|
709
|
+
lineWire(50, 0, 60, 0),
|
|
710
|
+
lineWire(30, 15, 30, 20)
|
|
711
|
+
].join("")
|
|
712
|
+
};
|
|
713
|
+
var potentiometer = {
|
|
714
|
+
length: 50,
|
|
715
|
+
netlistPins: ["start", "wiper", "end"],
|
|
716
|
+
anchors: {
|
|
717
|
+
start: { x: 0, y: 0 },
|
|
718
|
+
end: { x: 50, y: 0 },
|
|
719
|
+
wiper: { x: 25, y: -22 }
|
|
720
|
+
},
|
|
721
|
+
svg: () => [
|
|
722
|
+
`<path d="M 0,0 L 10,0 L 13,-8 L 18,8 L 23,-8 L 28,8 L 33,-8 L 38,8 L 40,0 L 50,0" ${BODY}/>`,
|
|
723
|
+
`<line x1="25" y1="-22" x2="25" y2="-10" ${BODY}/>`,
|
|
724
|
+
`<polygon points="25,-10 21,-16 29,-16" ${FILL}/>`
|
|
725
|
+
].join("")
|
|
726
|
+
};
|
|
727
|
+
var terminal_block = {
|
|
728
|
+
length: 80,
|
|
729
|
+
netlistPins: [],
|
|
730
|
+
anchors: { start: { x: 0, y: 0 }, end: { x: 80, y: 0 } },
|
|
731
|
+
svg: (label, _value, attrs) => {
|
|
732
|
+
const pinsAttr = attrs?.pins ?? attrs?.terminals ?? "1,2,3,4";
|
|
733
|
+
const pins = pinsAttr.split(",").map((s) => s.trim()).filter(Boolean);
|
|
734
|
+
const n = Math.max(pins.length, 1);
|
|
735
|
+
const BODY_W = 80;
|
|
736
|
+
const pitch = 18;
|
|
737
|
+
const bodyH = pitch * (n + 1);
|
|
738
|
+
const topY = -bodyH / 2;
|
|
739
|
+
const parts = [];
|
|
740
|
+
parts.push(
|
|
741
|
+
`<rect x="0" y="${topY}" width="${BODY_W}" height="${bodyH}" rx="3" fill="white" stroke-width="2" ${BODY}/>`
|
|
742
|
+
);
|
|
743
|
+
if (label) {
|
|
744
|
+
parts.push(
|
|
745
|
+
`<text x="${BODY_W / 2}" y="${topY + 14}" text-anchor="middle" class="schematex-circuit-meter">${label}</text>`
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
for (let i = 0; i < n; i++) {
|
|
749
|
+
const y = topY + pitch * (i + 1) + (label ? 8 : 0);
|
|
750
|
+
parts.push(`<line x1="-8" y1="${y}" x2="0" y2="${y}" ${WIRE}/>`);
|
|
751
|
+
parts.push(`<circle cx="6" cy="${y}" r="2" ${FILL}/>`);
|
|
752
|
+
parts.push(
|
|
753
|
+
`<text x="12" y="${y + 3}" class="schematex-circuit-pol">${pins[i] ?? `T${i + 1}`}</text>`
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
return parts.join("");
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
var relay_coil = {
|
|
760
|
+
length: 40,
|
|
761
|
+
anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
|
|
762
|
+
svg: () => [
|
|
763
|
+
lineWire(0, 0, 8, 0),
|
|
764
|
+
`<rect x="8" y="-10" width="24" height="20" fill="white" ${BODY}/>`,
|
|
765
|
+
// Diagonal slash inside the box = coil winding indicator
|
|
766
|
+
`<line x1="8" y1="-10" x2="32" y2="10" ${BODY}/>`,
|
|
767
|
+
lineWire(32, 0, 40, 0)
|
|
768
|
+
].join("")
|
|
769
|
+
};
|
|
770
|
+
var relay_no = {
|
|
771
|
+
length: 40,
|
|
772
|
+
anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
|
|
773
|
+
svg: () => [
|
|
774
|
+
lineWire(0, 0, 10, 0),
|
|
775
|
+
`<line x1="10" y1="0" x2="30" y2="-10" ${BODY}/>`,
|
|
776
|
+
`<circle cx="10" cy="0" r="2" ${FILL}/>`,
|
|
777
|
+
`<circle cx="30" cy="0" r="2" ${FILL}/>`,
|
|
778
|
+
// Small dashed bar above to denote actuator-driven (vs hand switch)
|
|
779
|
+
`<line x1="14" y1="-14" x2="26" y2="-14" stroke-dasharray="2,2" ${BODY}/>`,
|
|
780
|
+
lineWire(30, 0, 40, 0)
|
|
781
|
+
].join("")
|
|
782
|
+
};
|
|
783
|
+
var relay_nc = {
|
|
784
|
+
length: 40,
|
|
785
|
+
anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
|
|
786
|
+
svg: () => [
|
|
787
|
+
lineWire(0, 0, 10, 0),
|
|
788
|
+
`<line x1="10" y1="-10" x2="30" y2="-10" ${BODY}/>`,
|
|
789
|
+
// NC slash: line crosses both contact endpoints (closed by default)
|
|
790
|
+
`<line x1="8" y1="2" x2="32" y2="-12" ${BODY}/>`,
|
|
791
|
+
`<circle cx="10" cy="0" r="2" ${FILL}/>`,
|
|
792
|
+
`<circle cx="30" cy="0" r="2" ${FILL}/>`,
|
|
793
|
+
`<line x1="14" y1="-14" x2="26" y2="-14" stroke-dasharray="2,2" ${BODY}/>`,
|
|
794
|
+
lineWire(30, 0, 40, 0)
|
|
795
|
+
].join("")
|
|
796
|
+
};
|
|
797
|
+
var contactor = {
|
|
798
|
+
length: 44,
|
|
799
|
+
anchors: { start: { x: 0, y: 0 }, end: { x: 44, y: 0 } },
|
|
800
|
+
svg: () => [
|
|
801
|
+
lineWire(0, 0, 10, 0),
|
|
802
|
+
// Two parallel diagonal contacts = double-break (typical of contactors)
|
|
803
|
+
`<line x1="10" y1="0" x2="30" y2="-10" stroke-width="2.5" ${BODY}/>`,
|
|
804
|
+
`<line x1="14" y1="2" x2="34" y2="-8" stroke-width="2.5" ${BODY}/>`,
|
|
805
|
+
`<circle cx="10" cy="0" r="2" ${FILL}/>`,
|
|
806
|
+
`<circle cx="34" cy="0" r="2" ${FILL}/>`,
|
|
807
|
+
// Solid bar above = electromagnetic-driven actuator
|
|
808
|
+
`<line x1="14" y1="-14" x2="30" y2="-14" stroke-width="2" ${BODY}/>`,
|
|
809
|
+
lineWire(34, 0, 44, 0)
|
|
810
|
+
].join("")
|
|
811
|
+
};
|
|
812
|
+
var solenoid_valve = {
|
|
813
|
+
length: 50,
|
|
814
|
+
anchors: { start: { x: 0, y: 0 }, end: { x: 50, y: 0 } },
|
|
815
|
+
svg: () => [
|
|
816
|
+
lineWire(0, 0, 8, 0),
|
|
817
|
+
// Solenoid box on top
|
|
818
|
+
`<rect x="8" y="-22" width="14" height="12" fill="white" ${BODY}/>`,
|
|
819
|
+
`<line x1="8" y1="-22" x2="22" y2="-10" ${BODY}/>`,
|
|
820
|
+
// Valve body (triangle pair = IEC valve symbol)
|
|
821
|
+
`<polygon points="22,0 38,-8 38,8" fill="white" ${BODY}/>`,
|
|
822
|
+
`<polygon points="22,0 8,-8 8,8" fill="white" ${BODY}/>`,
|
|
823
|
+
// Connect solenoid to valve body (actuator line)
|
|
824
|
+
`<line x1="15" y1="-10" x2="22" y2="0" ${BODY}/>`,
|
|
825
|
+
lineWire(38, 0, 50, 0)
|
|
826
|
+
].join("")
|
|
827
|
+
};
|
|
828
|
+
var thermal_overload = {
|
|
829
|
+
length: 40,
|
|
830
|
+
anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
|
|
831
|
+
svg: () => [
|
|
832
|
+
lineWire(0, 0, 8, 0),
|
|
833
|
+
`<rect x="8" y="-12" width="24" height="24" fill="white" ${BODY}/>`,
|
|
834
|
+
// Bimetal element: bent line inside box
|
|
835
|
+
`<path d="M 12,-6 L 16,4 L 20,-6 L 24,4 L 28,-6" fill="none" ${BODY}/>`,
|
|
836
|
+
lineWire(32, 0, 40, 0)
|
|
837
|
+
].join("")
|
|
838
|
+
};
|
|
839
|
+
var disconnect_switch = {
|
|
840
|
+
length: 48,
|
|
841
|
+
anchors: { start: { x: 0, y: 0 }, end: { x: 48, y: 0 } },
|
|
842
|
+
svg: () => [
|
|
843
|
+
lineWire(0, 0, 10, 0),
|
|
844
|
+
`<line x1="10" y1="0" x2="34" y2="-12" ${BODY}/>`,
|
|
845
|
+
`<circle cx="10" cy="0" r="2" ${FILL}/>`,
|
|
846
|
+
// Hollow square at top of arm = visible-isolation indicator
|
|
847
|
+
`<rect x="30" y="-16" width="8" height="8" fill="white" ${BODY}/>`,
|
|
848
|
+
`<circle cx="38" cy="0" r="2" ${FILL}/>`,
|
|
849
|
+
lineWire(38, 0, 48, 0)
|
|
850
|
+
].join("")
|
|
851
|
+
};
|
|
852
|
+
var SYMBOLS = {
|
|
853
|
+
resistor,
|
|
854
|
+
capacitor,
|
|
855
|
+
electrolytic_cap,
|
|
856
|
+
inductor,
|
|
857
|
+
fuse,
|
|
858
|
+
crystal,
|
|
859
|
+
transformer,
|
|
860
|
+
voltage_source,
|
|
861
|
+
current_source,
|
|
862
|
+
ac_source,
|
|
863
|
+
battery,
|
|
864
|
+
ground,
|
|
865
|
+
gnd_signal,
|
|
866
|
+
vcc,
|
|
867
|
+
diode,
|
|
868
|
+
zener,
|
|
869
|
+
schottky,
|
|
870
|
+
led,
|
|
871
|
+
photodiode,
|
|
872
|
+
npn,
|
|
873
|
+
pnp,
|
|
874
|
+
nmos,
|
|
875
|
+
pmos,
|
|
876
|
+
jfet_n,
|
|
877
|
+
jfet_p,
|
|
878
|
+
opamp,
|
|
879
|
+
comparator,
|
|
880
|
+
switch_spst,
|
|
881
|
+
push_no,
|
|
882
|
+
ammeter,
|
|
883
|
+
voltmeter,
|
|
884
|
+
wattmeter,
|
|
885
|
+
motor,
|
|
886
|
+
speaker,
|
|
887
|
+
microphone,
|
|
888
|
+
buzzer,
|
|
889
|
+
potentiometer,
|
|
890
|
+
rheostat,
|
|
891
|
+
thermistor_ntc,
|
|
892
|
+
thermistor_ptc,
|
|
893
|
+
ldr,
|
|
894
|
+
variable_cap,
|
|
895
|
+
variable_inductor,
|
|
896
|
+
switch_spdt,
|
|
897
|
+
push_nc,
|
|
898
|
+
gnd_chassis,
|
|
899
|
+
gnd_digital,
|
|
900
|
+
test_point,
|
|
901
|
+
no_connect,
|
|
902
|
+
antenna,
|
|
903
|
+
generic_ic,
|
|
904
|
+
terminal_block,
|
|
905
|
+
"555_timer": timer_555,
|
|
906
|
+
voltage_regulator,
|
|
907
|
+
// Industrial control / power electrical (IEC 60617)
|
|
908
|
+
relay_coil,
|
|
909
|
+
relay_no,
|
|
910
|
+
relay_nc,
|
|
911
|
+
contactor,
|
|
912
|
+
solenoid_valve,
|
|
913
|
+
thermal_overload,
|
|
914
|
+
disconnect_switch
|
|
915
|
+
// Lamp reuses buzzer slot? No, needs its own entry but our CircuitComponentType
|
|
916
|
+
// doesn't have "lamp". We map it via parser alias to "buzzer" or add specifically.
|
|
917
|
+
};
|
|
918
|
+
var EXTRA_SYMBOLS = {
|
|
919
|
+
lamp
|
|
920
|
+
};
|
|
921
|
+
function getSymbol(t) {
|
|
922
|
+
return SYMBOLS[t] ?? EXTRA_SYMBOLS[t];
|
|
923
|
+
}
|
|
924
|
+
function getNetlistPinOrder(t) {
|
|
925
|
+
const sym = getSymbol(t);
|
|
926
|
+
if (!sym) return void 0;
|
|
927
|
+
if (sym.netlistPins) return [...sym.netlistPins];
|
|
928
|
+
if (sym.anchors.start && sym.anchors.end) return ["start", "end"];
|
|
929
|
+
return Object.keys(sym.anchors);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// src/diagrams/circuit/netlist.ts
|
|
933
|
+
var NetlistParseError = class extends Error {
|
|
934
|
+
constructor(message, line) {
|
|
935
|
+
super(message);
|
|
936
|
+
this.line = line;
|
|
937
|
+
this.name = "NetlistParseError";
|
|
938
|
+
}
|
|
939
|
+
line;
|
|
940
|
+
};
|
|
941
|
+
var PREFIX_MAP = {
|
|
942
|
+
R: { type: "resistor", pins: ["start", "end"] },
|
|
943
|
+
C: { type: "capacitor", pins: ["start", "end"] },
|
|
944
|
+
L: { type: "inductor", pins: ["start", "end"] },
|
|
945
|
+
D: { type: "diode", pins: ["start", "end"] },
|
|
946
|
+
// anode → start, cathode → end
|
|
947
|
+
V: { type: "voltage_source", pins: ["plus", "minus"] },
|
|
948
|
+
I: { type: "current_source", pins: ["plus", "minus"] },
|
|
949
|
+
Q: { type: "npn", pins: ["c", "b", "e"] },
|
|
950
|
+
M: { type: "nmos", pins: ["d", "g", "s"] },
|
|
951
|
+
J: { type: "jfet_n", pins: ["d", "g", "s"] },
|
|
952
|
+
S: { type: "switch_spst", pins: ["start", "end"] },
|
|
953
|
+
F: { type: "fuse", pins: ["start", "end"] },
|
|
954
|
+
B: { type: "battery", pins: ["plus", "minus"] },
|
|
955
|
+
K: { type: "relay_coil", pins: ["start", "end"] },
|
|
956
|
+
U: { type: "generic_ic", pins: [] },
|
|
957
|
+
// pins declared via pins="..." attr
|
|
958
|
+
X: { type: "generic_ic", pins: [] },
|
|
959
|
+
W: { type: "wire", pins: ["start", "end"] },
|
|
960
|
+
// explicit wire (non-SPICE convention, common in EE textbooks)
|
|
961
|
+
T: { type: "terminal_block", pins: [] }
|
|
962
|
+
// pins via pins="..."
|
|
963
|
+
};
|
|
964
|
+
var TYPE_ALIASES = {
|
|
965
|
+
vsource: "voltage_source",
|
|
966
|
+
isource: "current_source",
|
|
967
|
+
acsource: "ac_source",
|
|
968
|
+
ecap: "electrolytic_cap",
|
|
969
|
+
pot: "potentiometer",
|
|
970
|
+
gnd: "ground",
|
|
971
|
+
ic: "generic_ic",
|
|
972
|
+
reg: "voltage_regulator",
|
|
973
|
+
timer555: "555_timer",
|
|
974
|
+
transistor: "npn",
|
|
975
|
+
tb: "terminal_block",
|
|
976
|
+
junction_box: "terminal_block",
|
|
977
|
+
jbox: "terminal_block",
|
|
978
|
+
enclosure: "terminal_block"
|
|
979
|
+
};
|
|
980
|
+
var GROUND_REF = /^(0|gnd|ground|earth|pe|agnd|dgnd|gnda|gndd|vss|com)(_\w+|\d+)?$/i;
|
|
981
|
+
function isGroundRef(s) {
|
|
982
|
+
return GROUND_REF.test(s);
|
|
983
|
+
}
|
|
984
|
+
function tokenize(line) {
|
|
985
|
+
const tokens = [];
|
|
986
|
+
let i = 0;
|
|
987
|
+
while (i < line.length) {
|
|
988
|
+
const ch = line[i];
|
|
989
|
+
if (ch === " " || ch === " ") {
|
|
990
|
+
i++;
|
|
991
|
+
continue;
|
|
992
|
+
}
|
|
993
|
+
if (ch === '"') {
|
|
994
|
+
const end = line.indexOf('"', i + 1);
|
|
995
|
+
tokens.push(line.slice(i, end < 0 ? line.length : end + 1));
|
|
996
|
+
i = end < 0 ? line.length : end + 1;
|
|
997
|
+
continue;
|
|
998
|
+
}
|
|
999
|
+
let j = i;
|
|
1000
|
+
while (j < line.length && line[j] !== " " && line[j] !== " ") {
|
|
1001
|
+
if (line[j] === "=" && line[j + 1] === '"') {
|
|
1002
|
+
const end = line.indexOf('"', j + 2);
|
|
1003
|
+
j = end < 0 ? line.length : end + 1;
|
|
1004
|
+
break;
|
|
1005
|
+
}
|
|
1006
|
+
j++;
|
|
1007
|
+
}
|
|
1008
|
+
tokens.push(line.slice(i, j));
|
|
1009
|
+
i = j;
|
|
1010
|
+
}
|
|
1011
|
+
return tokens;
|
|
1012
|
+
}
|
|
1013
|
+
function isKeyEqVal(tok) {
|
|
1014
|
+
const eq = tok.indexOf("=");
|
|
1015
|
+
return eq > 0 && /^[a-zA-Z_][\w]*$/.test(tok.slice(0, eq));
|
|
1016
|
+
}
|
|
1017
|
+
function parseKV(tok) {
|
|
1018
|
+
const eq = tok.indexOf("=");
|
|
1019
|
+
const key = tok.slice(0, eq).trim();
|
|
1020
|
+
let val = tok.slice(eq + 1).trim();
|
|
1021
|
+
if (val.startsWith('"') && val.endsWith('"')) val = val.slice(1, -1);
|
|
1022
|
+
return [key, val];
|
|
1023
|
+
}
|
|
1024
|
+
function parseNetlist(body, title2) {
|
|
1025
|
+
const components = [];
|
|
1026
|
+
const netByName = /* @__PURE__ */ new Map();
|
|
1027
|
+
const pinMap = {};
|
|
1028
|
+
let autoGnd = 0;
|
|
1029
|
+
const ensureNet = (name) => {
|
|
1030
|
+
let n = netByName.get(name);
|
|
1031
|
+
if (!n) {
|
|
1032
|
+
n = { id: name, anchors: [] };
|
|
1033
|
+
netByName.set(name, n);
|
|
1034
|
+
}
|
|
1035
|
+
return n;
|
|
1036
|
+
};
|
|
1037
|
+
const lines = body.split("\n").map((l) => l.replace(/\r$/, ""));
|
|
1038
|
+
for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
|
|
1039
|
+
const raw = lines[lineIdx];
|
|
1040
|
+
const stripped = raw.replace(/#.*$/, "").trim();
|
|
1041
|
+
if (!stripped) continue;
|
|
1042
|
+
const tokens = tokenize(stripped);
|
|
1043
|
+
if (tokens.length < 2) {
|
|
1044
|
+
throw new NetlistParseError(
|
|
1045
|
+
`Netlist line must have at least ID + one net: "${stripped}"`,
|
|
1046
|
+
lineIdx + 1
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
const id = tokens[0];
|
|
1050
|
+
if (!/^[a-zA-Z_][\w]*$/.test(id)) {
|
|
1051
|
+
throw new NetlistParseError(`Invalid component id: "${id}"`, lineIdx + 1);
|
|
1052
|
+
}
|
|
1053
|
+
let cursor = 1;
|
|
1054
|
+
const netRefs = [];
|
|
1055
|
+
while (cursor < tokens.length && !isKeyEqVal(tokens[cursor])) {
|
|
1056
|
+
netRefs.push(tokens[cursor]);
|
|
1057
|
+
cursor++;
|
|
1058
|
+
}
|
|
1059
|
+
const kv = {};
|
|
1060
|
+
for (; cursor < tokens.length; cursor++) {
|
|
1061
|
+
if (!isKeyEqVal(tokens[cursor])) {
|
|
1062
|
+
throw new NetlistParseError(
|
|
1063
|
+
`Expected key=value after nets, got "${tokens[cursor]}"`,
|
|
1064
|
+
lineIdx + 1
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
const [k, v] = parseKV(tokens[cursor]);
|
|
1068
|
+
kv[k] = v;
|
|
1069
|
+
}
|
|
1070
|
+
const prefix = id[0].toUpperCase();
|
|
1071
|
+
const defaults = PREFIX_MAP[prefix];
|
|
1072
|
+
let cType;
|
|
1073
|
+
let pinOrder;
|
|
1074
|
+
if (kv.type) {
|
|
1075
|
+
const t = kv.type.toLowerCase();
|
|
1076
|
+
cType = TYPE_ALIASES[t] ?? t;
|
|
1077
|
+
pinOrder = getNetlistPinOrder(cType) ?? defaults?.pins ?? ["start", "end"];
|
|
1078
|
+
} else if (defaults) {
|
|
1079
|
+
cType = defaults.type;
|
|
1080
|
+
pinOrder = defaults.pins;
|
|
1081
|
+
} else if (isGroundRef(id)) {
|
|
1082
|
+
cType = "ground";
|
|
1083
|
+
pinOrder = ["start"];
|
|
1084
|
+
} else {
|
|
1085
|
+
throw new NetlistParseError(
|
|
1086
|
+
`Cannot infer type from id "${id}". Either rename to a SPICE-prefix id (R*, C*, L*, D*, V*, I*, Q*, M*, J*, S*, F*, B*, K*, U*, X*, W*, T*) or pass an explicit \`type=<name>\` attribute (e.g. \`${id} ... type=resistor\`). Schematex circuit covers electrical schematics only \u2014 hydraulic/pneumatic prefixes (EV*, BOMBA*, TANK*, etc.) are not supported.`,
|
|
1087
|
+
lineIdx + 1
|
|
1088
|
+
);
|
|
1089
|
+
}
|
|
1090
|
+
const expectedPins = pinOrder.length;
|
|
1091
|
+
let valueFromTail;
|
|
1092
|
+
if (netRefs.length > expectedPins && expectedPins > 0) {
|
|
1093
|
+
const tail = netRefs.slice(expectedPins);
|
|
1094
|
+
netRefs.length = expectedPins;
|
|
1095
|
+
const first = tail[0].toLowerCase();
|
|
1096
|
+
if (TYPE_ALIASES[first]) {
|
|
1097
|
+
cType = TYPE_ALIASES[first];
|
|
1098
|
+
pinOrder = getNetlistPinOrder(cType) ?? pinOrder;
|
|
1099
|
+
} else if (first === "npn" || first === "pnp" || first === "nmos" || first === "pmos" || first === "jfet_n" || first === "jfet_p" || first === "zener" || first === "schottky" || first === "led" || first === "photodiode") {
|
|
1100
|
+
cType = first;
|
|
1101
|
+
pinOrder = getNetlistPinOrder(cType) ?? pinOrder;
|
|
1102
|
+
} else {
|
|
1103
|
+
valueFromTail = tail.join(" ");
|
|
1104
|
+
}
|
|
1105
|
+
if (tail.length > 1 && !valueFromTail) {
|
|
1106
|
+
valueFromTail = tail.slice(1).join(" ");
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
if (cType === "ground" || cType === "gnd_signal" || cType === "gnd_chassis" || cType === "gnd_digital") {
|
|
1110
|
+
pinOrder = ["start"];
|
|
1111
|
+
}
|
|
1112
|
+
if (cType === "terminal_block") {
|
|
1113
|
+
const pinsAttr = kv.pins ?? kv.terminals;
|
|
1114
|
+
const labels = pinsAttr ? pinsAttr.split(",").map((s) => s.trim()).filter(Boolean) : Array.from({ length: Math.max(netRefs.length, 1) }, (_, i) => `t${i + 1}`);
|
|
1115
|
+
pinOrder = labels.map(
|
|
1116
|
+
(label, idx) => label.toLowerCase().replace(/[^a-z0-9]+/g, "_") || `t${idx + 1}`
|
|
1117
|
+
);
|
|
1118
|
+
}
|
|
1119
|
+
if (netRefs.length < pinOrder.length) {
|
|
1120
|
+
throw new NetlistParseError(
|
|
1121
|
+
`Component ${id} (${cType}) expects ${pinOrder.length} nets, got ${netRefs.length}`,
|
|
1122
|
+
lineIdx + 1
|
|
1123
|
+
);
|
|
1124
|
+
}
|
|
1125
|
+
const pins = {};
|
|
1126
|
+
for (let p = 0; p < pinOrder.length; p++) {
|
|
1127
|
+
let net = netRefs[p];
|
|
1128
|
+
if (isGroundRef(net)) net = "GND";
|
|
1129
|
+
pins[pinOrder[p]] = net;
|
|
1130
|
+
ensureNet(net).anchors.push(`${id}.${pinOrder[p]}`);
|
|
1131
|
+
}
|
|
1132
|
+
pinMap[id] = pins;
|
|
1133
|
+
const comp = {
|
|
1134
|
+
id,
|
|
1135
|
+
componentType: cType,
|
|
1136
|
+
direction: "right",
|
|
1137
|
+
label: kv.label ?? id,
|
|
1138
|
+
value: kv.value ?? valueFromTail,
|
|
1139
|
+
attrs: {}
|
|
1140
|
+
};
|
|
1141
|
+
for (const [k, v] of Object.entries(kv)) {
|
|
1142
|
+
if (k === "label" || k === "value" || k === "type") continue;
|
|
1143
|
+
comp.attrs[k] = v;
|
|
1144
|
+
}
|
|
1145
|
+
components.push(comp);
|
|
1146
|
+
}
|
|
1147
|
+
if (netByName.has("GND")) {
|
|
1148
|
+
const hasGroundSym = components.some(
|
|
1149
|
+
(c) => c.componentType === "ground" || c.componentType === "gnd_signal" || c.componentType === "gnd_chassis" || c.componentType === "gnd_digital"
|
|
1150
|
+
);
|
|
1151
|
+
if (!hasGroundSym) {
|
|
1152
|
+
const gId = `_GND${autoGnd++}`;
|
|
1153
|
+
components.push({
|
|
1154
|
+
id: gId,
|
|
1155
|
+
componentType: "ground",
|
|
1156
|
+
direction: "down",
|
|
1157
|
+
label: void 0,
|
|
1158
|
+
value: void 0,
|
|
1159
|
+
attrs: { auto: "true" }
|
|
1160
|
+
});
|
|
1161
|
+
pinMap[gId] = { start: "GND" };
|
|
1162
|
+
ensureNet("GND").anchors.push(`${gId}.start`);
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
const nets = Array.from(netByName.values());
|
|
1166
|
+
return {
|
|
1167
|
+
type: "circuit",
|
|
1168
|
+
title: title2,
|
|
1169
|
+
components,
|
|
1170
|
+
nets,
|
|
1171
|
+
pinMap,
|
|
1172
|
+
mode: "netlist"
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
// src/diagrams/circuit/parser.ts
|
|
1177
|
+
var CircuitParseError = class extends Error {
|
|
1178
|
+
constructor(message) {
|
|
1179
|
+
super(message);
|
|
1180
|
+
this.name = "CircuitParseError";
|
|
1181
|
+
}
|
|
1182
|
+
};
|
|
1183
|
+
var COMPONENT_TYPES = /* @__PURE__ */ new Set([
|
|
1184
|
+
"resistor",
|
|
1185
|
+
"potentiometer",
|
|
1186
|
+
"rheostat",
|
|
1187
|
+
"thermistor_ntc",
|
|
1188
|
+
"thermistor_ptc",
|
|
1189
|
+
"ldr",
|
|
1190
|
+
"varistor",
|
|
1191
|
+
"fuse",
|
|
1192
|
+
"fuse_slow",
|
|
1193
|
+
"capacitor",
|
|
1194
|
+
"electrolytic_cap",
|
|
1195
|
+
"variable_cap",
|
|
1196
|
+
"inductor",
|
|
1197
|
+
"inductor_iron",
|
|
1198
|
+
"inductor_ferrite",
|
|
1199
|
+
"variable_inductor",
|
|
1200
|
+
"ferrite_bead",
|
|
1201
|
+
"crystal",
|
|
1202
|
+
"transformer",
|
|
1203
|
+
"diode",
|
|
1204
|
+
"zener",
|
|
1205
|
+
"schottky",
|
|
1206
|
+
"led",
|
|
1207
|
+
"photodiode",
|
|
1208
|
+
"varactor",
|
|
1209
|
+
"tvs_diode",
|
|
1210
|
+
"bridge_rectifier",
|
|
1211
|
+
"npn",
|
|
1212
|
+
"pnp",
|
|
1213
|
+
"darlington_npn",
|
|
1214
|
+
"darlington_pnp",
|
|
1215
|
+
"nmos",
|
|
1216
|
+
"pmos",
|
|
1217
|
+
"nmos_depletion",
|
|
1218
|
+
"jfet_n",
|
|
1219
|
+
"jfet_p",
|
|
1220
|
+
"igbt",
|
|
1221
|
+
"scr",
|
|
1222
|
+
"triac",
|
|
1223
|
+
"diac",
|
|
1224
|
+
"phototransistor",
|
|
1225
|
+
"optocoupler",
|
|
1226
|
+
"opamp",
|
|
1227
|
+
"comparator",
|
|
1228
|
+
"schmitt_buffer",
|
|
1229
|
+
"tri_state_buffer",
|
|
1230
|
+
"instrumentation_amp",
|
|
1231
|
+
"generic_ic",
|
|
1232
|
+
"voltage_regulator",
|
|
1233
|
+
"dc_dc_converter",
|
|
1234
|
+
"555_timer",
|
|
1235
|
+
"voltage_source",
|
|
1236
|
+
"current_source",
|
|
1237
|
+
"ac_source",
|
|
1238
|
+
"battery",
|
|
1239
|
+
"vcc",
|
|
1240
|
+
"ground",
|
|
1241
|
+
"gnd_signal",
|
|
1242
|
+
"gnd_chassis",
|
|
1243
|
+
"gnd_digital",
|
|
1244
|
+
"switch_spst",
|
|
1245
|
+
"switch_spdt",
|
|
1246
|
+
"switch_dpdt",
|
|
1247
|
+
"push_no",
|
|
1248
|
+
"push_nc",
|
|
1249
|
+
"relay_coil",
|
|
1250
|
+
"relay_no",
|
|
1251
|
+
"relay_nc",
|
|
1252
|
+
"contactor",
|
|
1253
|
+
"solenoid_valve",
|
|
1254
|
+
"thermal_overload",
|
|
1255
|
+
"disconnect_switch",
|
|
1256
|
+
"motor",
|
|
1257
|
+
"speaker",
|
|
1258
|
+
"microphone",
|
|
1259
|
+
"buzzer",
|
|
1260
|
+
"ammeter",
|
|
1261
|
+
"voltmeter",
|
|
1262
|
+
"wattmeter",
|
|
1263
|
+
"oscilloscope",
|
|
1264
|
+
"wire",
|
|
1265
|
+
"dot",
|
|
1266
|
+
"label",
|
|
1267
|
+
"port",
|
|
1268
|
+
"test_point",
|
|
1269
|
+
"no_connect",
|
|
1270
|
+
"antenna"
|
|
1271
|
+
]);
|
|
1272
|
+
var ALIASES = {
|
|
1273
|
+
vsource: "voltage_source",
|
|
1274
|
+
isource: "current_source",
|
|
1275
|
+
acsource: "ac_source",
|
|
1276
|
+
ecap: "electrolytic_cap",
|
|
1277
|
+
pot: "potentiometer",
|
|
1278
|
+
xtal: "crystal",
|
|
1279
|
+
xfmr: "transformer",
|
|
1280
|
+
transistor: "npn",
|
|
1281
|
+
bjt_npn: "npn",
|
|
1282
|
+
bjt_pnp: "pnp",
|
|
1283
|
+
mosfet_n: "nmos",
|
|
1284
|
+
mosfet_p: "pmos",
|
|
1285
|
+
gnd: "ground",
|
|
1286
|
+
ic: "generic_ic",
|
|
1287
|
+
reg: "voltage_regulator",
|
|
1288
|
+
timer555: "555_timer",
|
|
1289
|
+
therm: "thermistor_ntc",
|
|
1290
|
+
ntc: "thermistor_ntc",
|
|
1291
|
+
ptc: "thermistor_ptc",
|
|
1292
|
+
ths: "thermistor_ntc",
|
|
1293
|
+
// Industrial control aliases (IEC letter codes)
|
|
1294
|
+
coil: "relay_coil",
|
|
1295
|
+
relay: "relay_coil",
|
|
1296
|
+
km: "contactor",
|
|
1297
|
+
solenoid: "solenoid_valve",
|
|
1298
|
+
ev: "solenoid_valve",
|
|
1299
|
+
overload: "thermal_overload",
|
|
1300
|
+
thermal: "thermal_overload",
|
|
1301
|
+
disconnect: "disconnect_switch",
|
|
1302
|
+
isolator: "disconnect_switch"
|
|
1303
|
+
};
|
|
1304
|
+
var EXTRA_TYPES = /* @__PURE__ */ new Set(["lamp"]);
|
|
1305
|
+
function normalizeType(raw) {
|
|
1306
|
+
const lower = raw.toLowerCase();
|
|
1307
|
+
if (ALIASES[lower]) return ALIASES[lower];
|
|
1308
|
+
if (COMPONENT_TYPES.has(lower)) {
|
|
1309
|
+
return lower;
|
|
1310
|
+
}
|
|
1311
|
+
if (EXTRA_TYPES.has(lower)) {
|
|
1312
|
+
return lower;
|
|
1313
|
+
}
|
|
1314
|
+
return null;
|
|
1315
|
+
}
|
|
1316
|
+
var DIRECTIONS = /* @__PURE__ */ new Set(["right", "left", "up", "down"]);
|
|
1317
|
+
function parseAttrs(rest) {
|
|
1318
|
+
const out = { attrs: {} };
|
|
1319
|
+
const tokens = [];
|
|
1320
|
+
let i = 0;
|
|
1321
|
+
while (i < rest.length) {
|
|
1322
|
+
const ch = rest[i];
|
|
1323
|
+
if (ch === " " || ch === " ") {
|
|
1324
|
+
i++;
|
|
1325
|
+
continue;
|
|
1326
|
+
}
|
|
1327
|
+
if (ch === '"') {
|
|
1328
|
+
const end = rest.indexOf('"', i + 1);
|
|
1329
|
+
const tok = rest.slice(i, end < 0 ? rest.length : end + 1);
|
|
1330
|
+
tokens.push(tok);
|
|
1331
|
+
i = end < 0 ? rest.length : end + 1;
|
|
1332
|
+
continue;
|
|
1333
|
+
}
|
|
1334
|
+
let j = i;
|
|
1335
|
+
while (j < rest.length && rest[j] !== " " && rest[j] !== " ") {
|
|
1336
|
+
if (rest[j] === "=" && rest[j + 1] === '"') {
|
|
1337
|
+
const end = rest.indexOf('"', j + 2);
|
|
1338
|
+
j = end < 0 ? rest.length : end + 1;
|
|
1339
|
+
break;
|
|
1228
1340
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1341
|
+
j++;
|
|
1342
|
+
}
|
|
1343
|
+
tokens.push(rest.slice(i, j));
|
|
1344
|
+
i = j;
|
|
1345
|
+
}
|
|
1346
|
+
for (const tok of tokens) {
|
|
1347
|
+
if (!tok) continue;
|
|
1348
|
+
if (DIRECTIONS.has(tok.toLowerCase())) {
|
|
1349
|
+
out.direction = tok.toLowerCase();
|
|
1350
|
+
continue;
|
|
1351
|
+
}
|
|
1352
|
+
const eq = tok.indexOf("=");
|
|
1353
|
+
if (eq > 0) {
|
|
1354
|
+
const key = tok.slice(0, eq).trim();
|
|
1355
|
+
let val = tok.slice(eq + 1).trim();
|
|
1356
|
+
if (val.startsWith('"') && val.endsWith('"')) {
|
|
1357
|
+
val = val.slice(1, -1);
|
|
1233
1358
|
}
|
|
1234
|
-
|
|
1359
|
+
if (key === "label") out.label = val;
|
|
1360
|
+
else if (key === "value") out.value = val;
|
|
1361
|
+
else if (key === "at") out.at = val;
|
|
1362
|
+
else if (key === "length") out.length = val;
|
|
1363
|
+
else out.attrs[key] = val;
|
|
1364
|
+
continue;
|
|
1235
1365
|
}
|
|
1236
|
-
|
|
1366
|
+
out.attrs[tok] = "true";
|
|
1367
|
+
}
|
|
1368
|
+
return out;
|
|
1237
1369
|
}
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
);
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
end: { x: BODY_W, y: 0 }
|
|
1254
|
-
};
|
|
1255
|
-
const leftNames = ["gnd", "trg", "out", "rst"];
|
|
1256
|
-
const rightNames = ["vcc", "dis", "thr", "ctl"];
|
|
1257
|
-
for (let i = 0; i < n; i++) {
|
|
1258
|
-
const y = topY + pitch * (i + 1);
|
|
1259
|
-
anchors[leftNames[i]] = { x: -8, y };
|
|
1260
|
-
anchors[rightNames[i]] = { x: BODY_W + 8, y };
|
|
1370
|
+
function parseCircuit(text2) {
|
|
1371
|
+
const rawLines = text2.split("\n");
|
|
1372
|
+
const firstMeaningful = rawLines.map((l) => l.replace(/#.*$/, "").trim()).find((l) => l.length > 0) ?? "";
|
|
1373
|
+
if (/^circuit\b.*\bnetlist\s*$/i.test(firstMeaningful)) {
|
|
1374
|
+
const netlistTitle = matchQuotedTitle(firstMeaningful);
|
|
1375
|
+
let headerIdx = -1;
|
|
1376
|
+
for (let i = 0; i < rawLines.length; i++) {
|
|
1377
|
+
const s = rawLines[i].replace(/#.*$/, "").trim();
|
|
1378
|
+
if (s.length > 0) {
|
|
1379
|
+
headerIdx = i;
|
|
1380
|
+
break;
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
const body = rawLines.slice(headerIdx + 1).join("\n");
|
|
1384
|
+
return parseNetlist(body, netlistTitle);
|
|
1261
1385
|
}
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1386
|
+
const lines = text2.split("\n").map((l) => l.replace(/\r$/, ""));
|
|
1387
|
+
let title2;
|
|
1388
|
+
const components = [];
|
|
1389
|
+
const nets = [];
|
|
1390
|
+
const netByName = /* @__PURE__ */ new Map();
|
|
1391
|
+
let autoId = 0;
|
|
1392
|
+
let pendingAt;
|
|
1393
|
+
const mkId = (prefix) => `${prefix}_${autoId++}`;
|
|
1394
|
+
for (const rawLine of lines) {
|
|
1395
|
+
const stripped = rawLine.replace(/#.*$/, "").trim();
|
|
1396
|
+
if (!stripped) continue;
|
|
1397
|
+
if (/^circuit\b/i.test(stripped)) {
|
|
1398
|
+
const t = matchQuotedTitle(stripped);
|
|
1399
|
+
if (t !== void 0) title2 = t;
|
|
1400
|
+
continue;
|
|
1401
|
+
}
|
|
1402
|
+
const atMatch = stripped.match(/^at:\s*(.+)$/i);
|
|
1403
|
+
if (atMatch) {
|
|
1404
|
+
pendingAt = atMatch[1].trim();
|
|
1405
|
+
continue;
|
|
1406
|
+
}
|
|
1407
|
+
const netDecl = stripped.match(/^net\s+([a-zA-Z_][\w]*)\s*$/i);
|
|
1408
|
+
if (netDecl) {
|
|
1409
|
+
const name = netDecl[1];
|
|
1410
|
+
if (!netByName.has(name)) {
|
|
1411
|
+
const n = { id: name, anchors: [] };
|
|
1412
|
+
netByName.set(name, n);
|
|
1413
|
+
nets.push(n);
|
|
1414
|
+
}
|
|
1415
|
+
continue;
|
|
1416
|
+
}
|
|
1417
|
+
const netDotMatch = stripped.match(/^net\s+([a-zA-Z_][\w]*)\s*:\s*dot\s*$/i);
|
|
1418
|
+
if (netDotMatch) {
|
|
1419
|
+
const name = netDotMatch[1];
|
|
1420
|
+
let n = netByName.get(name);
|
|
1421
|
+
if (!n) {
|
|
1422
|
+
n = { id: name, anchors: [] };
|
|
1423
|
+
netByName.set(name, n);
|
|
1424
|
+
nets.push(n);
|
|
1425
|
+
}
|
|
1426
|
+
const id = mkId("dot");
|
|
1427
|
+
components.push({
|
|
1428
|
+
id,
|
|
1429
|
+
componentType: "dot",
|
|
1430
|
+
direction: "right",
|
|
1431
|
+
at: pendingAt,
|
|
1432
|
+
attrs: { net: name }
|
|
1433
|
+
});
|
|
1434
|
+
n.anchors.push(`${id}.end`);
|
|
1435
|
+
pendingAt = `${id}.end`;
|
|
1436
|
+
continue;
|
|
1313
1437
|
}
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1438
|
+
const labelMatch = stripped.match(/^label\s+"([^"]*)"(?:\s+(right|left|up|down))?\s*$/i);
|
|
1439
|
+
if (labelMatch) {
|
|
1440
|
+
const id = mkId("lbl");
|
|
1441
|
+
components.push({
|
|
1442
|
+
id,
|
|
1443
|
+
componentType: "label",
|
|
1444
|
+
direction: labelMatch[2]?.toLowerCase() ?? "right",
|
|
1445
|
+
at: pendingAt,
|
|
1446
|
+
label: labelMatch[1]
|
|
1447
|
+
});
|
|
1448
|
+
continue;
|
|
1449
|
+
}
|
|
1450
|
+
const wireMatch = stripped.match(/^wire(?:\s+(right|left|up|down))?(?:\s+(\d+)(?:px)?)?\s*$/i);
|
|
1451
|
+
if (wireMatch) {
|
|
1452
|
+
const id = mkId("w");
|
|
1453
|
+
components.push({
|
|
1454
|
+
id,
|
|
1455
|
+
componentType: "wire",
|
|
1456
|
+
direction: wireMatch[1]?.toLowerCase() ?? "right",
|
|
1457
|
+
at: pendingAt,
|
|
1458
|
+
attrs: wireMatch[2] ? { length: wireMatch[2] } : {}
|
|
1459
|
+
});
|
|
1460
|
+
pendingAt = `${id}.end`;
|
|
1461
|
+
continue;
|
|
1462
|
+
}
|
|
1463
|
+
const bareMatch = stripped.match(/^([a-zA-Z_][\w]*)(\s+.*)?$/);
|
|
1464
|
+
const colonMatch = stripped.match(/^([a-zA-Z_][\w]*)\s*:\s*([a-zA-Z_][\w]*)(\s+.*)?$/);
|
|
1465
|
+
if (colonMatch) {
|
|
1466
|
+
const id = colonMatch[1];
|
|
1467
|
+
const typeStr = colonMatch[2];
|
|
1468
|
+
const norm = normalizeType(typeStr);
|
|
1469
|
+
if (!norm) {
|
|
1470
|
+
throw new CircuitParseError(`Unknown component type: ${typeStr}`);
|
|
1471
|
+
}
|
|
1472
|
+
const rest = colonMatch[3] ?? "";
|
|
1473
|
+
const parsed = parseAttrs(rest);
|
|
1474
|
+
const comp = {
|
|
1475
|
+
id,
|
|
1476
|
+
componentType: norm,
|
|
1477
|
+
direction: parsed.direction ?? "right",
|
|
1478
|
+
at: parsed.at ?? pendingAt,
|
|
1479
|
+
label: parsed.label,
|
|
1480
|
+
value: parsed.value,
|
|
1481
|
+
attrs: parsed.attrs
|
|
1482
|
+
};
|
|
1483
|
+
if (parsed.length) {
|
|
1484
|
+
comp.attrs = { ...comp.attrs, length: parsed.length };
|
|
1485
|
+
}
|
|
1486
|
+
components.push(comp);
|
|
1487
|
+
pendingAt = `${id}.end`;
|
|
1488
|
+
continue;
|
|
1489
|
+
}
|
|
1490
|
+
if (bareMatch) {
|
|
1491
|
+
const typeStr = bareMatch[1];
|
|
1492
|
+
const norm = normalizeType(typeStr);
|
|
1493
|
+
if (!norm) {
|
|
1494
|
+
continue;
|
|
1495
|
+
}
|
|
1496
|
+
const rest = bareMatch[2] ?? "";
|
|
1497
|
+
const parsed = parseAttrs(rest);
|
|
1498
|
+
const id = mkId(norm);
|
|
1499
|
+
const comp = {
|
|
1500
|
+
id,
|
|
1501
|
+
componentType: norm,
|
|
1502
|
+
direction: parsed.direction ?? "right",
|
|
1503
|
+
at: parsed.at ?? pendingAt,
|
|
1504
|
+
label: parsed.label,
|
|
1505
|
+
value: parsed.value,
|
|
1506
|
+
attrs: parsed.attrs
|
|
1507
|
+
};
|
|
1508
|
+
components.push(comp);
|
|
1509
|
+
pendingAt = `${id}.end`;
|
|
1510
|
+
continue;
|
|
1321
1511
|
}
|
|
1322
|
-
return parts.join("");
|
|
1323
1512
|
}
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
// Diagonal slash inside the box = coil winding indicator
|
|
1332
|
-
`<line x1="8" y1="-10" x2="32" y2="10" ${BODY}/>`,
|
|
1333
|
-
lineWire(32, 0, 40, 0)
|
|
1334
|
-
].join("")
|
|
1335
|
-
};
|
|
1336
|
-
var relay_no = {
|
|
1337
|
-
length: 40,
|
|
1338
|
-
anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
|
|
1339
|
-
svg: () => [
|
|
1340
|
-
lineWire(0, 0, 10, 0),
|
|
1341
|
-
`<line x1="10" y1="0" x2="30" y2="-10" ${BODY}/>`,
|
|
1342
|
-
`<circle cx="10" cy="0" r="2" ${FILL}/>`,
|
|
1343
|
-
`<circle cx="30" cy="0" r="2" ${FILL}/>`,
|
|
1344
|
-
// Small dashed bar above to denote actuator-driven (vs hand switch)
|
|
1345
|
-
`<line x1="14" y1="-14" x2="26" y2="-14" stroke-dasharray="2,2" ${BODY}/>`,
|
|
1346
|
-
lineWire(30, 0, 40, 0)
|
|
1347
|
-
].join("")
|
|
1348
|
-
};
|
|
1349
|
-
var relay_nc = {
|
|
1350
|
-
length: 40,
|
|
1351
|
-
anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
|
|
1352
|
-
svg: () => [
|
|
1353
|
-
lineWire(0, 0, 10, 0),
|
|
1354
|
-
`<line x1="10" y1="-10" x2="30" y2="-10" ${BODY}/>`,
|
|
1355
|
-
// NC slash: line crosses both contact endpoints (closed by default)
|
|
1356
|
-
`<line x1="8" y1="2" x2="32" y2="-12" ${BODY}/>`,
|
|
1357
|
-
`<circle cx="10" cy="0" r="2" ${FILL}/>`,
|
|
1358
|
-
`<circle cx="30" cy="0" r="2" ${FILL}/>`,
|
|
1359
|
-
`<line x1="14" y1="-14" x2="26" y2="-14" stroke-dasharray="2,2" ${BODY}/>`,
|
|
1360
|
-
lineWire(30, 0, 40, 0)
|
|
1361
|
-
].join("")
|
|
1362
|
-
};
|
|
1363
|
-
var contactor = {
|
|
1364
|
-
length: 44,
|
|
1365
|
-
anchors: { start: { x: 0, y: 0 }, end: { x: 44, y: 0 } },
|
|
1366
|
-
svg: () => [
|
|
1367
|
-
lineWire(0, 0, 10, 0),
|
|
1368
|
-
// Two parallel diagonal contacts = double-break (typical of contactors)
|
|
1369
|
-
`<line x1="10" y1="0" x2="30" y2="-10" stroke-width="2.5" ${BODY}/>`,
|
|
1370
|
-
`<line x1="14" y1="2" x2="34" y2="-8" stroke-width="2.5" ${BODY}/>`,
|
|
1371
|
-
`<circle cx="10" cy="0" r="2" ${FILL}/>`,
|
|
1372
|
-
`<circle cx="34" cy="0" r="2" ${FILL}/>`,
|
|
1373
|
-
// Solid bar above = electromagnetic-driven actuator
|
|
1374
|
-
`<line x1="14" y1="-14" x2="30" y2="-14" stroke-width="2" ${BODY}/>`,
|
|
1375
|
-
lineWire(34, 0, 44, 0)
|
|
1376
|
-
].join("")
|
|
1377
|
-
};
|
|
1378
|
-
var solenoid_valve = {
|
|
1379
|
-
length: 50,
|
|
1380
|
-
anchors: { start: { x: 0, y: 0 }, end: { x: 50, y: 0 } },
|
|
1381
|
-
svg: () => [
|
|
1382
|
-
lineWire(0, 0, 8, 0),
|
|
1383
|
-
// Solenoid box on top
|
|
1384
|
-
`<rect x="8" y="-22" width="14" height="12" fill="white" ${BODY}/>`,
|
|
1385
|
-
`<line x1="8" y1="-22" x2="22" y2="-10" ${BODY}/>`,
|
|
1386
|
-
// Valve body (triangle pair = IEC valve symbol)
|
|
1387
|
-
`<polygon points="22,0 38,-8 38,8" fill="white" ${BODY}/>`,
|
|
1388
|
-
`<polygon points="22,0 8,-8 8,8" fill="white" ${BODY}/>`,
|
|
1389
|
-
// Connect solenoid to valve body (actuator line)
|
|
1390
|
-
`<line x1="15" y1="-10" x2="22" y2="0" ${BODY}/>`,
|
|
1391
|
-
lineWire(38, 0, 50, 0)
|
|
1392
|
-
].join("")
|
|
1393
|
-
};
|
|
1394
|
-
var thermal_overload = {
|
|
1395
|
-
length: 40,
|
|
1396
|
-
anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
|
|
1397
|
-
svg: () => [
|
|
1398
|
-
lineWire(0, 0, 8, 0),
|
|
1399
|
-
`<rect x="8" y="-12" width="24" height="24" fill="white" ${BODY}/>`,
|
|
1400
|
-
// Bimetal element: bent line inside box
|
|
1401
|
-
`<path d="M 12,-6 L 16,4 L 20,-6 L 24,4 L 28,-6" fill="none" ${BODY}/>`,
|
|
1402
|
-
lineWire(32, 0, 40, 0)
|
|
1403
|
-
].join("")
|
|
1404
|
-
};
|
|
1405
|
-
var disconnect_switch = {
|
|
1406
|
-
length: 48,
|
|
1407
|
-
anchors: { start: { x: 0, y: 0 }, end: { x: 48, y: 0 } },
|
|
1408
|
-
svg: () => [
|
|
1409
|
-
lineWire(0, 0, 10, 0),
|
|
1410
|
-
`<line x1="10" y1="0" x2="34" y2="-12" ${BODY}/>`,
|
|
1411
|
-
`<circle cx="10" cy="0" r="2" ${FILL}/>`,
|
|
1412
|
-
// Hollow square at top of arm = visible-isolation indicator
|
|
1413
|
-
`<rect x="30" y="-16" width="8" height="8" fill="white" ${BODY}/>`,
|
|
1414
|
-
`<circle cx="38" cy="0" r="2" ${FILL}/>`,
|
|
1415
|
-
lineWire(38, 0, 48, 0)
|
|
1416
|
-
].join("")
|
|
1417
|
-
};
|
|
1418
|
-
var SYMBOLS = {
|
|
1419
|
-
resistor,
|
|
1420
|
-
capacitor,
|
|
1421
|
-
electrolytic_cap,
|
|
1422
|
-
inductor,
|
|
1423
|
-
fuse,
|
|
1424
|
-
crystal,
|
|
1425
|
-
transformer,
|
|
1426
|
-
voltage_source,
|
|
1427
|
-
current_source,
|
|
1428
|
-
ac_source,
|
|
1429
|
-
battery,
|
|
1430
|
-
ground,
|
|
1431
|
-
gnd_signal,
|
|
1432
|
-
vcc,
|
|
1433
|
-
diode,
|
|
1434
|
-
zener,
|
|
1435
|
-
schottky,
|
|
1436
|
-
led,
|
|
1437
|
-
photodiode,
|
|
1438
|
-
npn,
|
|
1439
|
-
pnp,
|
|
1440
|
-
nmos,
|
|
1441
|
-
pmos,
|
|
1442
|
-
jfet_n,
|
|
1443
|
-
jfet_p,
|
|
1444
|
-
opamp,
|
|
1445
|
-
comparator,
|
|
1446
|
-
switch_spst,
|
|
1447
|
-
push_no,
|
|
1448
|
-
ammeter,
|
|
1449
|
-
voltmeter,
|
|
1450
|
-
wattmeter,
|
|
1451
|
-
motor,
|
|
1452
|
-
speaker,
|
|
1453
|
-
microphone,
|
|
1454
|
-
buzzer,
|
|
1455
|
-
potentiometer,
|
|
1456
|
-
rheostat,
|
|
1457
|
-
thermistor_ntc,
|
|
1458
|
-
thermistor_ptc,
|
|
1459
|
-
ldr,
|
|
1460
|
-
variable_cap,
|
|
1461
|
-
variable_inductor,
|
|
1462
|
-
switch_spdt,
|
|
1463
|
-
push_nc,
|
|
1464
|
-
gnd_chassis,
|
|
1465
|
-
gnd_digital,
|
|
1466
|
-
test_point,
|
|
1467
|
-
no_connect,
|
|
1468
|
-
antenna,
|
|
1469
|
-
generic_ic,
|
|
1470
|
-
terminal_block,
|
|
1471
|
-
"555_timer": timer_555,
|
|
1472
|
-
voltage_regulator,
|
|
1473
|
-
// Industrial control / power electrical (IEC 60617)
|
|
1474
|
-
relay_coil,
|
|
1475
|
-
relay_no,
|
|
1476
|
-
relay_nc,
|
|
1477
|
-
contactor,
|
|
1478
|
-
solenoid_valve,
|
|
1479
|
-
thermal_overload,
|
|
1480
|
-
disconnect_switch
|
|
1481
|
-
// Lamp reuses buzzer slot? No, needs its own entry but our CircuitComponentType
|
|
1482
|
-
// doesn't have "lamp". We map it via parser alias to "buzzer" or add specifically.
|
|
1483
|
-
};
|
|
1484
|
-
var EXTRA_SYMBOLS = {
|
|
1485
|
-
lamp
|
|
1486
|
-
};
|
|
1487
|
-
function getSymbol(t) {
|
|
1488
|
-
return SYMBOLS[t] ?? EXTRA_SYMBOLS[t];
|
|
1513
|
+
return {
|
|
1514
|
+
type: "circuit",
|
|
1515
|
+
title: title2,
|
|
1516
|
+
components,
|
|
1517
|
+
nets,
|
|
1518
|
+
mode: "positional"
|
|
1519
|
+
};
|
|
1489
1520
|
}
|
|
1490
1521
|
|
|
1491
1522
|
// src/diagrams/circuit/layout.ts
|
|
@@ -2010,5 +2041,5 @@ var circuit = {
|
|
|
2010
2041
|
};
|
|
2011
2042
|
|
|
2012
2043
|
export { circuit, layoutCircuit, layoutCircuitNetlist, parseCircuit, parseNetlist, renderCircuit };
|
|
2013
|
-
//# sourceMappingURL=chunk-
|
|
2014
|
-
//# sourceMappingURL=chunk-
|
|
2044
|
+
//# sourceMappingURL=chunk-VCH7RI5H.js.map
|
|
2045
|
+
//# sourceMappingURL=chunk-VCH7RI5H.js.map
|