circuitscript 0.0.13 → 0.0.15
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/.gitlab-ci.yml +22 -19
- package/__tests__/helpers.ts +12 -0
- package/__tests__/renderData/script1.cst.svg +1 -1
- package/__tests__/renderData/script2.cst.svg +1 -1
- package/__tests__/renderData/script3.cst.svg +1 -1
- package/__tests__/renderData/script4.cst +27 -5
- package/__tests__/renderData/script4.cst.svg +1 -1
- package/__tests__/renderData/script5.cst.svg +1 -1
- package/__tests__/testParse.ts +37 -2
- package/build/src/draw_symbols.js +209 -67
- package/build/src/geometry.js +35 -4
- package/build/src/globals.js +2 -1
- package/build/src/helpers.js +73 -0
- package/build/src/layout.js +30 -17
- package/build/src/main.js +2 -69
- package/build/src/regenerate-tests.js +2 -2
- package/build/src/sizing.js +4 -10
- package/build/src/visitor.js +12 -4
- package/examples/example_arduino_uno.cst +390 -120
- package/examples/lib.cst +25 -28
- package/libs/lib.cst +23 -28
- package/package.json +1 -1
- package/src/draw_symbols.ts +273 -71
- package/src/geometry.ts +48 -6
- package/src/globals.ts +3 -1
- package/src/helpers.ts +114 -0
- package/src/layout.ts +42 -21
- package/src/main.ts +2 -112
- package/src/regenerate-tests.ts +2 -2
- package/src/sizing.ts +5 -8
- package/src/visitor.ts +16 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SymbolPinSide,
|
|
1
|
+
import { SymbolPinSide, defaultFont } from "./globals.js";
|
|
2
2
|
import { Geometry, GeometryProp, HorizontalAlign, Label, VerticalAlign } from "./geometry.js";
|
|
3
3
|
const defaultSymbolLineColor = '#333';
|
|
4
4
|
const defaultSymbolLineWidth = 2;
|
|
@@ -98,7 +98,7 @@ export class SymbolGraphic {
|
|
|
98
98
|
const labels = this.drawing.getLabels();
|
|
99
99
|
labels.forEach(label => {
|
|
100
100
|
const tmpLabel = label;
|
|
101
|
-
const { fontSize = 10, anchor = HorizontalAlign.Left, vanchor = VerticalAlign.Bottom, fontWeight = 'regular', } = tmpLabel.style ?? {};
|
|
101
|
+
const { fontSize = 10, anchor = HorizontalAlign.Left, vanchor = VerticalAlign.Bottom, fontWeight = 'regular', angle: labelAngle = 0, } = tmpLabel.style ?? {};
|
|
102
102
|
let anchorStyle = 'start';
|
|
103
103
|
let dominantBaseline = 'auto';
|
|
104
104
|
let useAnchor = anchor;
|
|
@@ -129,25 +129,32 @@ export class SymbolGraphic {
|
|
|
129
129
|
break;
|
|
130
130
|
}
|
|
131
131
|
const position = tmpLabel.getLabelPosition();
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
const text = group.text(tmpLabel.text)
|
|
132
|
+
const useFont = defaultFont;
|
|
133
|
+
const textContainer = group.group();
|
|
134
|
+
const text = textContainer.text(tmpLabel.text)
|
|
137
135
|
.fill('#333')
|
|
138
136
|
.font({
|
|
139
137
|
family: useFont,
|
|
140
138
|
size: fontSize,
|
|
141
139
|
anchor: anchorStyle,
|
|
142
140
|
'dominant-baseline': dominantBaseline,
|
|
141
|
+
weight: fontWeight,
|
|
143
142
|
});
|
|
143
|
+
let translateX, translateY;
|
|
144
|
+
let useRotateAngle = 0;
|
|
144
145
|
if (isRotation180) {
|
|
145
|
-
|
|
146
|
+
translateX = -position[0];
|
|
147
|
+
translateY = position[1];
|
|
148
|
+
useRotateAngle = 0;
|
|
146
149
|
}
|
|
147
150
|
else {
|
|
148
|
-
|
|
149
|
-
|
|
151
|
+
translateX = position[0];
|
|
152
|
+
translateY = position[1];
|
|
153
|
+
useRotateAngle = this.angle;
|
|
150
154
|
}
|
|
155
|
+
text.rotate(labelAngle);
|
|
156
|
+
textContainer.translate(translateX, translateY)
|
|
157
|
+
.rotate(useRotateAngle, -translateX, -translateY);
|
|
151
158
|
});
|
|
152
159
|
}
|
|
153
160
|
flipTextAnchor(value) {
|
|
@@ -208,67 +215,183 @@ export class SymbolPlaceholder extends SymbolGraphic {
|
|
|
208
215
|
drawing.clear();
|
|
209
216
|
drawing.angle = this._angle;
|
|
210
217
|
const commands = drawing.getCommands();
|
|
211
|
-
drawing.log(drawing.id, 'angle: ', this._angle, "commands:", commands.length);
|
|
218
|
+
drawing.log('id: ', drawing.id, 'angle: ', this._angle, "commands:", commands.length);
|
|
212
219
|
commands.forEach(([commandName, positionParams, keywordParams]) => {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
220
|
+
switch (commandName) {
|
|
221
|
+
case PlaceHolderCommands.rect:
|
|
222
|
+
drawing.log('add rect', ...positionParams);
|
|
223
|
+
drawing.addRect(...positionParams);
|
|
224
|
+
break;
|
|
225
|
+
case PlaceHolderCommands.hline:
|
|
226
|
+
drawing.log('add hline', ...positionParams);
|
|
227
|
+
drawing.addHLine(...positionParams);
|
|
228
|
+
break;
|
|
229
|
+
case PlaceHolderCommands.vline:
|
|
230
|
+
drawing.log('add vline', ...positionParams);
|
|
231
|
+
drawing.addVLine(...positionParams);
|
|
232
|
+
break;
|
|
233
|
+
case PlaceHolderCommands.line:
|
|
234
|
+
drawing.log('add line', ...positionParams);
|
|
235
|
+
drawing.addLine(...positionParams);
|
|
236
|
+
break;
|
|
237
|
+
case PlaceHolderCommands.path:
|
|
238
|
+
drawing.addPath(...positionParams);
|
|
239
|
+
break;
|
|
240
|
+
case PlaceHolderCommands.lineWidth:
|
|
241
|
+
drawing.addSetLineWidth(...positionParams);
|
|
242
|
+
break;
|
|
243
|
+
case PlaceHolderCommands.fill:
|
|
244
|
+
drawing.addSetFillColor(...positionParams);
|
|
245
|
+
break;
|
|
246
|
+
case PlaceHolderCommands.lineColor:
|
|
247
|
+
drawing.addSetLineColor(...positionParams);
|
|
248
|
+
break;
|
|
249
|
+
case PlaceHolderCommands.arc:
|
|
250
|
+
drawing.addArc(...positionParams);
|
|
251
|
+
break;
|
|
252
|
+
case PlaceHolderCommands.circle:
|
|
253
|
+
drawing.addArc(...positionParams, 0, 360);
|
|
254
|
+
break;
|
|
255
|
+
case PlaceHolderCommands.triangle:
|
|
256
|
+
drawing.addTriangle(...positionParams);
|
|
257
|
+
break;
|
|
258
|
+
case PlaceHolderCommands.pin:
|
|
259
|
+
case PlaceHolderCommands.hpin:
|
|
260
|
+
case PlaceHolderCommands.vpin:
|
|
261
|
+
{
|
|
262
|
+
this.drawPinParams(drawing, commandName, keywordParams, positionParams);
|
|
263
|
+
break;
|
|
239
264
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
else if (commandName === 'arc') {
|
|
262
|
-
drawing.addArc(...positionParams);
|
|
265
|
+
case PlaceHolderCommands.label: {
|
|
266
|
+
const keywords = ['fontSize', 'anchor', 'vanchor', 'angle'];
|
|
267
|
+
const style = {};
|
|
268
|
+
keywords.forEach(item => {
|
|
269
|
+
if (keywordParams.has(item)) {
|
|
270
|
+
style[item] = keywordParams.get(item);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
positionParams = [...positionParams];
|
|
274
|
+
positionParams.push(style);
|
|
275
|
+
const labelId = positionParams[0];
|
|
276
|
+
const tmpPositionParams = [...positionParams];
|
|
277
|
+
const tmpLabelValue = this.getLabelValue(labelId);
|
|
278
|
+
if (tmpLabelValue !== undefined) {
|
|
279
|
+
tmpPositionParams[3] = tmpLabelValue;
|
|
280
|
+
}
|
|
281
|
+
drawing.log('add label', JSON.stringify(tmpPositionParams));
|
|
282
|
+
drawing.addLabelId(...tmpPositionParams);
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
263
285
|
}
|
|
264
286
|
});
|
|
265
287
|
drawing.log("=== end generate drawing ===");
|
|
266
288
|
}
|
|
289
|
+
drawPinParams(drawing, commandName, keywordParams, positionParams) {
|
|
290
|
+
drawing.log('add pin', ...positionParams);
|
|
291
|
+
const keywordDisplayPinId = 'display_pin_id';
|
|
292
|
+
let displayPinId = true;
|
|
293
|
+
if (keywordParams.has(keywordDisplayPinId)) {
|
|
294
|
+
if (keywordParams.get(keywordDisplayPinId) === 0) {
|
|
295
|
+
displayPinId = false;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
let pinNameParam = null;
|
|
299
|
+
if (typeof positionParams[1] === 'string') {
|
|
300
|
+
pinNameParam = positionParams[1];
|
|
301
|
+
positionParams = [positionParams[0], ...positionParams.slice(2)];
|
|
302
|
+
}
|
|
303
|
+
const startX = positionParams[1];
|
|
304
|
+
const startY = positionParams[2];
|
|
305
|
+
if (commandName === PlaceHolderCommands.vpin) {
|
|
306
|
+
const magnitude = positionParams[3];
|
|
307
|
+
positionParams = [
|
|
308
|
+
positionParams[0],
|
|
309
|
+
startX,
|
|
310
|
+
startY,
|
|
311
|
+
startX,
|
|
312
|
+
startY + magnitude
|
|
313
|
+
];
|
|
314
|
+
}
|
|
315
|
+
else if (commandName === PlaceHolderCommands.hpin) {
|
|
316
|
+
const magnitude = positionParams[3];
|
|
317
|
+
positionParams = [
|
|
318
|
+
positionParams[0],
|
|
319
|
+
startX,
|
|
320
|
+
startY,
|
|
321
|
+
startX + magnitude,
|
|
322
|
+
startY
|
|
323
|
+
];
|
|
324
|
+
}
|
|
325
|
+
drawing.addPin(...positionParams);
|
|
326
|
+
const latestPin = this.drawing.pins[this.drawing.pins.length - 1];
|
|
327
|
+
const [pinId, , angle] = latestPin;
|
|
328
|
+
const [, , , endX, endY] = positionParams;
|
|
329
|
+
let pinNameAlignment = HorizontalAlign.Left;
|
|
330
|
+
let pinNameOffsetX = 4;
|
|
331
|
+
let pinIdOffsetX = 0;
|
|
332
|
+
let pinIdAlignment = HorizontalAlign.Left;
|
|
333
|
+
let pinIdVAlignment = VerticalAlign.Bottom;
|
|
334
|
+
let pinIdOffsetY = -2;
|
|
335
|
+
switch (angle) {
|
|
336
|
+
case 0:
|
|
337
|
+
pinNameAlignment = HorizontalAlign.Left;
|
|
338
|
+
pinNameOffsetX = 4;
|
|
339
|
+
pinIdAlignment = HorizontalAlign.Right;
|
|
340
|
+
pinIdOffsetX = -2;
|
|
341
|
+
break;
|
|
342
|
+
case 90:
|
|
343
|
+
case 180:
|
|
344
|
+
pinNameAlignment = HorizontalAlign.Right;
|
|
345
|
+
pinNameOffsetX = -4;
|
|
346
|
+
pinIdAlignment = HorizontalAlign.Left;
|
|
347
|
+
pinIdOffsetX = 2;
|
|
348
|
+
break;
|
|
349
|
+
case 270:
|
|
350
|
+
pinNameAlignment = HorizontalAlign.Left;
|
|
351
|
+
pinNameOffsetX = 4;
|
|
352
|
+
pinIdAlignment = HorizontalAlign.Left;
|
|
353
|
+
pinIdOffsetX = 2;
|
|
354
|
+
pinIdOffsetY = 2;
|
|
355
|
+
pinIdVAlignment = VerticalAlign.Top;
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
if (angle === 0 || angle === 90 || angle === 180 || angle === 270) {
|
|
359
|
+
const usePinName = pinNameParam ?? "";
|
|
360
|
+
usePinName !== "" && drawing.addLabel(endX + pinNameOffsetX, endY, usePinName, {
|
|
361
|
+
fontSize: 10,
|
|
362
|
+
anchor: pinNameAlignment,
|
|
363
|
+
vanchor: VerticalAlign.Middle,
|
|
364
|
+
});
|
|
365
|
+
displayPinId && drawing.addLabel(endX + pinIdOffsetX, endY + pinIdOffsetY, pinId.toString(), {
|
|
366
|
+
fontSize: 8,
|
|
367
|
+
anchor: pinIdAlignment,
|
|
368
|
+
vanchor: pinIdVAlignment,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
267
372
|
constructor(drawing) {
|
|
268
373
|
super();
|
|
269
374
|
this.drawing = drawing;
|
|
270
375
|
}
|
|
271
376
|
}
|
|
377
|
+
export var PlaceHolderCommands;
|
|
378
|
+
(function (PlaceHolderCommands) {
|
|
379
|
+
PlaceHolderCommands["arc"] = "arc";
|
|
380
|
+
PlaceHolderCommands["circle"] = "circle";
|
|
381
|
+
PlaceHolderCommands["rect"] = "rect";
|
|
382
|
+
PlaceHolderCommands["triangle"] = "triangle";
|
|
383
|
+
PlaceHolderCommands["pin"] = "pin";
|
|
384
|
+
PlaceHolderCommands["hpin"] = "hpin";
|
|
385
|
+
PlaceHolderCommands["vpin"] = "vpin";
|
|
386
|
+
PlaceHolderCommands["hline"] = "hline";
|
|
387
|
+
PlaceHolderCommands["vline"] = "vline";
|
|
388
|
+
PlaceHolderCommands["line"] = "line";
|
|
389
|
+
PlaceHolderCommands["label"] = "label";
|
|
390
|
+
PlaceHolderCommands["path"] = "path";
|
|
391
|
+
PlaceHolderCommands["lineWidth"] = "lineWidth";
|
|
392
|
+
PlaceHolderCommands["fill"] = "fill";
|
|
393
|
+
PlaceHolderCommands["lineColor"] = "lineColor";
|
|
394
|
+
})(PlaceHolderCommands || (PlaceHolderCommands = {}));
|
|
272
395
|
export class SymbolCustom extends SymbolGraphic {
|
|
273
396
|
pinDefinition = [];
|
|
274
397
|
bodyWidth = 100;
|
|
@@ -297,7 +420,6 @@ export class SymbolCustom extends SymbolGraphic {
|
|
|
297
420
|
drawing.angle = this._angle;
|
|
298
421
|
const bodyWidth = this.bodyWidth;
|
|
299
422
|
const bodyHeight = (1 + Math.max(maxLeftPins, maxRightPins)) * this.pinSpacing;
|
|
300
|
-
drawing.addSetFillColor(bodyColor);
|
|
301
423
|
drawing.addRect(0, 0, bodyWidth, bodyHeight);
|
|
302
424
|
const leftPinStart = -bodyWidth / 2;
|
|
303
425
|
const rightPinStart = bodyWidth / 2;
|
|
@@ -333,15 +455,20 @@ export class SymbolCustom extends SymbolGraphic {
|
|
|
333
455
|
});
|
|
334
456
|
});
|
|
335
457
|
const instanceName = this.getLabelValue("refdes");
|
|
336
|
-
const MPN = this.getLabelValue("MPN");
|
|
337
458
|
instanceName && drawing.addLabel(-bodyWidth / 2, -bodyHeight / 2 - 4, instanceName, {
|
|
338
459
|
fontSize: 10,
|
|
339
460
|
anchor: HorizontalAlign.Left,
|
|
340
461
|
});
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
462
|
+
const acceptedMPNKeys = ['MPN', 'mpn', 'manufacturer_pn'];
|
|
463
|
+
acceptedMPNKeys.some(key => {
|
|
464
|
+
const labelValue = this.getLabelValue(key);
|
|
465
|
+
if (labelValue !== undefined) {
|
|
466
|
+
drawing.addLabel(-bodyWidth / 2, bodyHeight / 2 + 4, labelValue, {
|
|
467
|
+
fontSize: 10,
|
|
468
|
+
anchor: HorizontalAlign.Left,
|
|
469
|
+
vanchor: VerticalAlign.Top,
|
|
470
|
+
});
|
|
471
|
+
}
|
|
345
472
|
});
|
|
346
473
|
this.drawing = drawing;
|
|
347
474
|
this._cacheLeftPins = leftPins;
|
|
@@ -414,6 +541,21 @@ export class SymbolDrawing {
|
|
|
414
541
|
]));
|
|
415
542
|
return this;
|
|
416
543
|
}
|
|
544
|
+
addTriangle(startX, startY, endX, endY, width) {
|
|
545
|
+
const line = Geometry.line(startX, startY, endX, endY);
|
|
546
|
+
const normLine = line.norm;
|
|
547
|
+
const dx1 = normLine.x * width / 2;
|
|
548
|
+
const dy1 = normLine.y * width / 2;
|
|
549
|
+
const dx2 = normLine.x * -width / 2;
|
|
550
|
+
const dy2 = normLine.y * -width / 2;
|
|
551
|
+
this.items.push(Geometry.polygon([
|
|
552
|
+
[dx1 + startX, dy1 + startY],
|
|
553
|
+
[dx2 + startX, dy2 + startY],
|
|
554
|
+
[endX, endY],
|
|
555
|
+
[dx1 + startX, dy1 + startY],
|
|
556
|
+
]));
|
|
557
|
+
return this;
|
|
558
|
+
}
|
|
417
559
|
addRect2(x, y, x2, y2) {
|
|
418
560
|
this.items.push(Geometry.polygon([
|
|
419
561
|
[x, y],
|
|
@@ -589,7 +731,7 @@ export class SymbolDrawingCommands extends SymbolDrawing {
|
|
|
589
731
|
}
|
|
590
732
|
clone() {
|
|
591
733
|
const tmpCommands = this.commands.map(item => {
|
|
592
|
-
if (item[0] ===
|
|
734
|
+
if (item[0] === PlaceHolderCommands.label) {
|
|
593
735
|
const commandName = item[0];
|
|
594
736
|
const positionParams = item[1];
|
|
595
737
|
const keywordParams = item[2];
|
package/build/src/geometry.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Flatten from '@flatten-js/core';
|
|
2
2
|
import { measureTextSize2 } from './sizing.js';
|
|
3
3
|
import { defaultFont } from './globals.js';
|
|
4
|
+
import { NumericValue } from './objects/ParamDefinition.js';
|
|
4
5
|
export class Label extends Flatten.Polygon {
|
|
5
6
|
id;
|
|
6
7
|
text;
|
|
@@ -24,8 +25,22 @@ export class Label extends Flatten.Polygon {
|
|
|
24
25
|
this.textMeasurementBounds = bounds;
|
|
25
26
|
}
|
|
26
27
|
static fromPoint(id, x, y, text, style) {
|
|
28
|
+
let useText;
|
|
29
|
+
if (typeof text === 'number') {
|
|
30
|
+
useText = text.toString();
|
|
31
|
+
}
|
|
32
|
+
else if (typeof text === 'object'
|
|
33
|
+
&& text instanceof NumericValue) {
|
|
34
|
+
useText = text.toDisplayString();
|
|
35
|
+
}
|
|
36
|
+
else if (typeof text === 'string') {
|
|
37
|
+
useText = text;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
throw 'Invalid string passed into label';
|
|
41
|
+
}
|
|
27
42
|
const { fontSize = 10, anchor = HorizontalAlign.Left, vanchor = VerticalAlign.Bottom, fontWeight = 'regular', } = style ?? {};
|
|
28
|
-
const { width, height, box } = measureTextSize2(
|
|
43
|
+
const { width, height, box } = measureTextSize2(useText, defaultFont, fontSize, fontWeight, anchor, vanchor);
|
|
29
44
|
const polygonCoords = [
|
|
30
45
|
[box.x, box.y],
|
|
31
46
|
[box.x2, box.y],
|
|
@@ -34,7 +49,7 @@ export class Label extends Flatten.Polygon {
|
|
|
34
49
|
[box.x, box.y],
|
|
35
50
|
];
|
|
36
51
|
const polygon = new Flatten.Polygon(polygonCoords);
|
|
37
|
-
return new Label(id,
|
|
52
|
+
return new Label(id, useText, [x, y], polygon, style, box);
|
|
38
53
|
}
|
|
39
54
|
rotate(angle, origin) {
|
|
40
55
|
const polygonRotate = super.rotate(angle, origin);
|
|
@@ -56,6 +71,9 @@ export class Geometry {
|
|
|
56
71
|
static point(x, y) {
|
|
57
72
|
return new Flatten.Point(x, y);
|
|
58
73
|
}
|
|
74
|
+
static line(x1, y1, x2, y2) {
|
|
75
|
+
return new Flatten.Line(Geometry.point(x1, y1), Geometry.point(x2, y2));
|
|
76
|
+
}
|
|
59
77
|
static label(id, x, y, text, style) {
|
|
60
78
|
return Label.fromPoint(id, x, y, text, style);
|
|
61
79
|
}
|
|
@@ -99,6 +117,11 @@ export class Geometry {
|
|
|
99
117
|
let maxY = Number.NEGATIVE_INFINITY;
|
|
100
118
|
features.forEach(feature => {
|
|
101
119
|
const box = feature.box;
|
|
120
|
+
if (feature instanceof Label
|
|
121
|
+
&& typeof feature.text === 'string'
|
|
122
|
+
&& feature.text.trim().length === 0) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
102
125
|
if (box.xmin === undefined) {
|
|
103
126
|
throw "Invalid box!";
|
|
104
127
|
}
|
|
@@ -126,6 +149,7 @@ export class Geometry {
|
|
|
126
149
|
}
|
|
127
150
|
console.log('unknown type', feature);
|
|
128
151
|
}
|
|
152
|
+
static FullCircleRadians = 2 * Math.PI;
|
|
129
153
|
static featuresToPath(items) {
|
|
130
154
|
const paths = [];
|
|
131
155
|
let isClosedPolygon = false;
|
|
@@ -138,11 +162,18 @@ export class Geometry {
|
|
|
138
162
|
const x = item.center.x;
|
|
139
163
|
const y = item.center.y;
|
|
140
164
|
const radius = item.r;
|
|
165
|
+
let useEndAngle = item.endAngle;
|
|
166
|
+
let extraEnd = '';
|
|
167
|
+
if (item.startAngle === 0 && item.endAngle === Geometry.FullCircleRadians) {
|
|
168
|
+
useEndAngle = 359.9999 * Math.PI / 180;
|
|
169
|
+
isClosedPolygon = true;
|
|
170
|
+
extraEnd = ' Z';
|
|
171
|
+
}
|
|
141
172
|
const startPoint = getArcPointRadians(x, y, radius, item.startAngle);
|
|
142
|
-
const endPoint = getArcPointRadians(x, y, radius,
|
|
173
|
+
const endPoint = getArcPointRadians(x, y, radius, useEndAngle);
|
|
143
174
|
paths.push('M ' + startPoint[0] + ' ' + startPoint[1]
|
|
144
175
|
+ 'A ' + radius + ' ' + radius + ' 0 1 1 '
|
|
145
|
-
+ endPoint[0] + ' ' + endPoint[1]);
|
|
176
|
+
+ endPoint[0] + ' ' + endPoint[1] + extraEnd);
|
|
146
177
|
}
|
|
147
178
|
else {
|
|
148
179
|
const coords = Geometry.getCoords(item);
|
package/build/src/globals.js
CHANGED
|
@@ -26,7 +26,8 @@ export var SymbolPinSide;
|
|
|
26
26
|
})(SymbolPinSide || (SymbolPinSide = {}));
|
|
27
27
|
export const portWidth = 20;
|
|
28
28
|
export const portHeight = 2;
|
|
29
|
-
export const defaultFont = '
|
|
29
|
+
export const defaultFont = 'Open Sans-Regular, Arial';
|
|
30
|
+
export const defaultFontBold = 'Open Sans-Bold, Arial-Bold, Arial';
|
|
30
31
|
export const defaultFontSize = 10;
|
|
31
32
|
export const bodyColor = '#FFFEAF';
|
|
32
33
|
export const junctionSize = 5;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { writeFileSync } from "fs";
|
|
2
|
+
import { generateKiCADNetList } from "./export.js";
|
|
3
|
+
import { LayoutEngine } from "./layout.js";
|
|
4
|
+
import { SequenceAction } from "./objects/ExecutionScope.js";
|
|
5
|
+
import { parseFileWithVisitor } from "./parser.js";
|
|
6
|
+
import { generateSVG2 } from "./render.js";
|
|
7
|
+
import { SimpleStopwatch } from "./utils.js";
|
|
8
|
+
import { MainVisitor } from "./visitor.js";
|
|
9
|
+
export function renderScript(scriptData, outputPath, options) {
|
|
10
|
+
const { currentDirectory = null, defaultLibsPath, dumpNets = false, dumpData = false, kicadNetlistPath = null, showStats = false } = options;
|
|
11
|
+
const visitor = new MainVisitor(true);
|
|
12
|
+
visitor.onImportFile = visitor.createImportFileHandler(currentDirectory, defaultLibsPath);
|
|
13
|
+
visitor.print('reading file');
|
|
14
|
+
visitor.print('done reading file');
|
|
15
|
+
const { tree, parser, hasParseError, hasError, parserTimeTaken, lexerTimeTaken } = parseFileWithVisitor(visitor, scriptData);
|
|
16
|
+
showStats && console.log('Lexing took:', lexerTimeTaken);
|
|
17
|
+
showStats && console.log('Parsing took:', parserTimeTaken);
|
|
18
|
+
dumpNets && console.log(visitor.dumpNets());
|
|
19
|
+
dumpData && writeFileSync('dump/tree.lisp', tree.toStringTree(null, parser));
|
|
20
|
+
dumpData && writeFileSync('dump/raw-parser.txt', visitor.logger.dump());
|
|
21
|
+
if (hasError || hasParseError) {
|
|
22
|
+
console.log('Error while parsing');
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
visitor.annotateComponents();
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
console.log('Error during annotation: ', err);
|
|
30
|
+
}
|
|
31
|
+
if (kicadNetlistPath) {
|
|
32
|
+
const kicadNetList = generateKiCADNetList(visitor.getNetList());
|
|
33
|
+
writeFileSync(kicadNetlistPath, kicadNetList);
|
|
34
|
+
console.log('Generated KiCad netlist file');
|
|
35
|
+
}
|
|
36
|
+
const { sequence, nets } = visitor.getGraph();
|
|
37
|
+
const tmpSequence = sequence.map(item => {
|
|
38
|
+
const tmp = [...item];
|
|
39
|
+
const action = tmp[0];
|
|
40
|
+
if (action === SequenceAction.Wire) {
|
|
41
|
+
tmp[2] = tmp[2].map(item2 => {
|
|
42
|
+
return [item2.direction, item2.value].join(",");
|
|
43
|
+
}).join(" ");
|
|
44
|
+
}
|
|
45
|
+
else if (action === SequenceAction.Frame) {
|
|
46
|
+
tmp[1] = item[1].frameId;
|
|
47
|
+
}
|
|
48
|
+
else if (action !== SequenceAction.WireJump) {
|
|
49
|
+
tmp[1] = item[1].instanceName;
|
|
50
|
+
}
|
|
51
|
+
return tmp.join(" | ");
|
|
52
|
+
});
|
|
53
|
+
dumpData && writeFileSync('dump/raw-sequence.txt', tmpSequence.join('\n'));
|
|
54
|
+
let svgOutput = null;
|
|
55
|
+
try {
|
|
56
|
+
const layoutEngine = new LayoutEngine();
|
|
57
|
+
const layoutTimer = new SimpleStopwatch();
|
|
58
|
+
const graph = layoutEngine.runLayout(sequence, nets);
|
|
59
|
+
layoutEngine.printWarnings();
|
|
60
|
+
showStats && console.log('Layout took:', layoutTimer.lap());
|
|
61
|
+
dumpData && writeFileSync('dump/raw-layout.txt', layoutEngine.logger.dump());
|
|
62
|
+
const generateSvgTimer = new SimpleStopwatch();
|
|
63
|
+
svgOutput = generateSVG2(graph);
|
|
64
|
+
showStats && console.log('Render took:', generateSvgTimer.lap());
|
|
65
|
+
if (outputPath) {
|
|
66
|
+
writeFileSync(outputPath, svgOutput);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
console.log('Error during render: ', err);
|
|
71
|
+
}
|
|
72
|
+
return svgOutput;
|
|
73
|
+
}
|
package/build/src/layout.js
CHANGED
|
@@ -389,9 +389,6 @@ export class LayoutEngine {
|
|
|
389
389
|
if (tmpSymbol instanceof SymbolCustom && widthProp) {
|
|
390
390
|
tmpSymbol.bodyWidth = widthProp;
|
|
391
391
|
}
|
|
392
|
-
if (component.assignedRefDes !== null) {
|
|
393
|
-
tmpSymbol.setLabelValue("refdes", component.assignedRefDes);
|
|
394
|
-
}
|
|
395
392
|
if (!didSetAngle && component.parameters.has('_addDirection')) {
|
|
396
393
|
tmpSymbol.refreshDrawing(false);
|
|
397
394
|
tmpSymbol.angle = calculateSymbolAngle(tmpSymbol, component.parameters.get('_addPin'), component.parameters.get('_addDirection'));
|
|
@@ -433,7 +430,20 @@ export class LayoutEngine {
|
|
|
433
430
|
graph.setEdge(previousNode, wireName, makeEdgeValue(previousNode, previousPin, wireName, 0, i));
|
|
434
431
|
previousNode = wireName;
|
|
435
432
|
previousPin = 1;
|
|
436
|
-
|
|
433
|
+
const wireSegmentsInfo = wireSegments.map(item => {
|
|
434
|
+
const tmp = {
|
|
435
|
+
direction: item.direction,
|
|
436
|
+
value: item.value,
|
|
437
|
+
};
|
|
438
|
+
if (item.valueXY) {
|
|
439
|
+
tmp.valueXY = item.valueXY;
|
|
440
|
+
}
|
|
441
|
+
if (item.until) {
|
|
442
|
+
tmp.until = [item.until[0].toString(), item.until[1]];
|
|
443
|
+
}
|
|
444
|
+
return tmp;
|
|
445
|
+
});
|
|
446
|
+
this.print(SequenceAction.Wire, wireId, JSON.stringify(wireSegmentsInfo));
|
|
437
447
|
}
|
|
438
448
|
else if (action === SequenceAction.WireJump) {
|
|
439
449
|
this.print(...sequence[i]);
|
|
@@ -881,20 +891,23 @@ function applyComponentParamsToSymbol(typeProp, component, symbol) {
|
|
|
881
891
|
if (typeProp === 'net') {
|
|
882
892
|
symbol.setLabelValue("net_name", component.parameters.get(ParamKeys.net_name));
|
|
883
893
|
}
|
|
884
|
-
if (component.
|
|
885
|
-
|
|
886
|
-
const tmpValue = component.parameters.get('value');
|
|
887
|
-
if (typeof tmpValue == 'object' && (tmpValue instanceof NumericValue)) {
|
|
888
|
-
displayString = tmpValue.toDisplayString();
|
|
889
|
-
}
|
|
890
|
-
else {
|
|
891
|
-
displayString = tmpValue;
|
|
892
|
-
}
|
|
893
|
-
symbol.setLabelValue('value', displayString);
|
|
894
|
+
if (component.assignedRefDes !== null) {
|
|
895
|
+
symbol.setLabelValue("refdes", component.assignedRefDes);
|
|
894
896
|
}
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
897
|
+
for (const [key, value] of component.parameters) {
|
|
898
|
+
if (key !== 'refdes' && key !== 'net_name') {
|
|
899
|
+
let useValue;
|
|
900
|
+
if (typeof value == 'object' && (value instanceof NumericValue)) {
|
|
901
|
+
useValue = value.toDisplayString();
|
|
902
|
+
}
|
|
903
|
+
else if (typeof value === 'number') {
|
|
904
|
+
useValue = value.toString();
|
|
905
|
+
}
|
|
906
|
+
else if (typeof value === 'string') {
|
|
907
|
+
useValue = value;
|
|
908
|
+
}
|
|
909
|
+
symbol.setLabelValue(key, useValue);
|
|
910
|
+
}
|
|
898
911
|
}
|
|
899
912
|
}
|
|
900
913
|
function calculateSymbolAngle(symbol, pin, direction) {
|