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
package/src/draw_symbols.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { G } from "@svgdotjs/svg.js";
|
|
2
2
|
|
|
3
|
-
import { SymbolPinSide, bodyColor, defaultFont } from "./globals.js";
|
|
3
|
+
import { SymbolPinSide, bodyColor, defaultFontBold, defaultFont } from "./globals.js";
|
|
4
4
|
import { Feature, Geometry, GeometryProp, HorizontalAlign, Label, LabelStyle, VerticalAlign } from "./geometry.js";
|
|
5
5
|
import { Logger } from "./logger.js";
|
|
6
6
|
|
|
@@ -154,6 +154,7 @@ export abstract class SymbolGraphic {
|
|
|
154
154
|
anchor = HorizontalAlign.Left,
|
|
155
155
|
vanchor = VerticalAlign.Bottom,
|
|
156
156
|
fontWeight = 'regular',
|
|
157
|
+
angle: labelAngle = 0,
|
|
157
158
|
} = tmpLabel.style ?? {};
|
|
158
159
|
|
|
159
160
|
let anchorStyle = 'start';
|
|
@@ -196,28 +197,38 @@ export abstract class SymbolGraphic {
|
|
|
196
197
|
}
|
|
197
198
|
|
|
198
199
|
const position = tmpLabel.getLabelPosition();
|
|
200
|
+
const useFont = defaultFont;
|
|
199
201
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
useFont = 'Inter-Bold';
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const text = group.text(tmpLabel.text)
|
|
202
|
+
const textContainer = group.group();
|
|
203
|
+
const text = textContainer.text(tmpLabel.text)
|
|
206
204
|
.fill('#333')
|
|
207
205
|
.font({
|
|
208
206
|
family: useFont,
|
|
209
207
|
size: fontSize,
|
|
210
208
|
anchor: anchorStyle,
|
|
211
209
|
'dominant-baseline': dominantBaseline,
|
|
212
|
-
|
|
210
|
+
weight: fontWeight,
|
|
213
211
|
});
|
|
214
212
|
|
|
213
|
+
let translateX: number, translateY: number;
|
|
214
|
+
let useRotateAngle = 0;
|
|
215
|
+
|
|
215
216
|
if (isRotation180){
|
|
216
|
-
|
|
217
|
+
translateX = -position[0];
|
|
218
|
+
translateY = position[1];
|
|
219
|
+
useRotateAngle = 0;
|
|
217
220
|
} else {
|
|
218
|
-
|
|
219
|
-
|
|
221
|
+
translateX = position[0];
|
|
222
|
+
translateY = position[1];
|
|
223
|
+
useRotateAngle = this.angle;
|
|
220
224
|
}
|
|
225
|
+
|
|
226
|
+
text.rotate(labelAngle);
|
|
227
|
+
textContainer.translate(translateX, translateY)
|
|
228
|
+
.rotate(useRotateAngle, -translateX, -translateY);
|
|
229
|
+
|
|
230
|
+
// For debug, show the origin of the text container
|
|
231
|
+
// textContainer.circle(2).fill('red');
|
|
221
232
|
});
|
|
222
233
|
}
|
|
223
234
|
|
|
@@ -293,73 +304,233 @@ export class SymbolPlaceholder extends SymbolGraphic {
|
|
|
293
304
|
drawing.angle = this._angle;
|
|
294
305
|
const commands = drawing.getCommands();
|
|
295
306
|
|
|
296
|
-
drawing.log(drawing.id, 'angle: ', this._angle, "commands:", commands.length);
|
|
307
|
+
drawing.log('id: ', drawing.id, 'angle: ', this._angle, "commands:", commands.length);
|
|
297
308
|
|
|
298
309
|
commands.forEach(([commandName, positionParams, keywordParams]) => {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
drawing.log('add pin', ...positionParams);
|
|
305
|
-
drawing.addPin(...positionParams);
|
|
306
|
-
|
|
307
|
-
} else if (commandName === 'hline') {
|
|
308
|
-
drawing.log('add hline', ...positionParams);
|
|
309
|
-
drawing.addHLine(...positionParams);
|
|
310
|
-
|
|
311
|
-
} else if (commandName === 'vline') {
|
|
312
|
-
drawing.log('add vline', ...positionParams);
|
|
313
|
-
drawing.addVLine(...positionParams);
|
|
314
|
-
|
|
315
|
-
} else if (commandName === 'line') {
|
|
316
|
-
drawing.log('add line', ...positionParams);
|
|
317
|
-
drawing.addLine(...positionParams);
|
|
318
|
-
|
|
319
|
-
} else if (commandName === 'label') {
|
|
320
|
-
const keywords = ['fontSize', 'anchor', 'vanchor'];
|
|
321
|
-
|
|
322
|
-
// Create the style object
|
|
323
|
-
const style = {};
|
|
324
|
-
keywords.forEach(item => {
|
|
325
|
-
if (keywordParams.has(item)){
|
|
326
|
-
style[item] = keywordParams.get(item);
|
|
327
|
-
}
|
|
328
|
-
});
|
|
310
|
+
switch (commandName) {
|
|
311
|
+
case PlaceHolderCommands.rect:
|
|
312
|
+
drawing.log('add rect', ...positionParams);
|
|
313
|
+
drawing.addRect(...positionParams);
|
|
314
|
+
break;
|
|
329
315
|
|
|
330
|
-
|
|
331
|
-
|
|
316
|
+
case PlaceHolderCommands.hline:
|
|
317
|
+
drawing.log('add hline', ...positionParams);
|
|
318
|
+
drawing.addHLine(...positionParams);
|
|
319
|
+
break;
|
|
332
320
|
|
|
333
|
-
|
|
321
|
+
case PlaceHolderCommands.vline:
|
|
322
|
+
drawing.log('add vline', ...positionParams);
|
|
323
|
+
drawing.addVLine(...positionParams);
|
|
324
|
+
break;
|
|
334
325
|
|
|
335
|
-
|
|
336
|
-
|
|
326
|
+
case PlaceHolderCommands.line:
|
|
327
|
+
drawing.log('add line', ...positionParams);
|
|
328
|
+
drawing.addLine(...positionParams);
|
|
329
|
+
break;
|
|
337
330
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
331
|
+
case PlaceHolderCommands.path:
|
|
332
|
+
drawing.addPath(...positionParams);
|
|
333
|
+
break;
|
|
334
|
+
|
|
335
|
+
case PlaceHolderCommands.lineWidth:
|
|
336
|
+
drawing.addSetLineWidth(...positionParams);
|
|
337
|
+
break;
|
|
338
|
+
|
|
339
|
+
case PlaceHolderCommands.fill:
|
|
340
|
+
drawing.addSetFillColor(...positionParams);
|
|
341
|
+
break;
|
|
342
|
+
|
|
343
|
+
case PlaceHolderCommands.lineColor:
|
|
344
|
+
drawing.addSetLineColor(...positionParams);
|
|
345
|
+
break;
|
|
346
|
+
|
|
347
|
+
case PlaceHolderCommands.arc:
|
|
348
|
+
drawing.addArc(...positionParams);
|
|
349
|
+
break;
|
|
350
|
+
|
|
351
|
+
case PlaceHolderCommands.circle:
|
|
352
|
+
// circle params: center x, center y, radius
|
|
353
|
+
drawing.addArc(...positionParams, 0, 360);
|
|
354
|
+
break;
|
|
355
|
+
|
|
356
|
+
case PlaceHolderCommands.triangle:
|
|
357
|
+
drawing.addTriangle(...positionParams);
|
|
358
|
+
break;
|
|
359
|
+
|
|
360
|
+
case PlaceHolderCommands.pin:
|
|
361
|
+
case PlaceHolderCommands.hpin:
|
|
362
|
+
case PlaceHolderCommands.vpin:
|
|
363
|
+
{
|
|
364
|
+
this.drawPinParams(drawing, commandName,
|
|
365
|
+
keywordParams, positionParams);
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
case PlaceHolderCommands.label: {
|
|
370
|
+
const keywords = ['fontSize', 'anchor', 'vanchor', 'angle'];
|
|
371
|
+
|
|
372
|
+
// Create the style object
|
|
373
|
+
const style = {};
|
|
374
|
+
keywords.forEach(item => {
|
|
375
|
+
if (keywordParams.has(item)) {
|
|
376
|
+
style[item] = keywordParams.get(item);
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
positionParams = [...positionParams];
|
|
381
|
+
positionParams.push(style);
|
|
382
|
+
|
|
383
|
+
const labelId = positionParams[0];
|
|
384
|
+
|
|
385
|
+
const tmpPositionParams = [...positionParams];
|
|
386
|
+
|
|
387
|
+
const tmpLabelValue = this.getLabelValue(labelId);
|
|
388
|
+
if (tmpLabelValue !== undefined) {
|
|
389
|
+
tmpPositionParams[3] = tmpLabelValue;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
drawing.log('add label', JSON.stringify(tmpPositionParams));
|
|
393
|
+
drawing.addLabelId(...tmpPositionParams);
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
351
396
|
}
|
|
352
397
|
});
|
|
353
398
|
|
|
354
399
|
drawing.log("=== end generate drawing ===");
|
|
355
400
|
}
|
|
356
401
|
|
|
402
|
+
drawPinParams(drawing: SymbolDrawingCommands,
|
|
403
|
+
commandName: string, keywordParams: Map<string, any>,
|
|
404
|
+
positionParams: any[]): void {
|
|
405
|
+
|
|
406
|
+
drawing.log('add pin', ...positionParams);
|
|
407
|
+
|
|
408
|
+
const keywordDisplayPinId = 'display_pin_id';
|
|
409
|
+
let displayPinId = true;
|
|
410
|
+
|
|
411
|
+
if (keywordParams.has(keywordDisplayPinId)) {
|
|
412
|
+
if (keywordParams.get(keywordDisplayPinId) === 0) {
|
|
413
|
+
displayPinId = false;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
let pinNameParam: string | null = null;
|
|
418
|
+
if (typeof positionParams[1] === 'string') {
|
|
419
|
+
// If the type of the second position is a string, then
|
|
420
|
+
// use the string value as the pin name
|
|
421
|
+
pinNameParam = positionParams[1];
|
|
422
|
+
positionParams = [positionParams[0], ...positionParams.slice(2)];
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// create the next point
|
|
426
|
+
const startX = positionParams[1];
|
|
427
|
+
const startY = positionParams[2];
|
|
428
|
+
|
|
429
|
+
if (commandName === PlaceHolderCommands.vpin) {
|
|
430
|
+
const magnitude = positionParams[3];
|
|
431
|
+
positionParams = [
|
|
432
|
+
positionParams[0],
|
|
433
|
+
startX,
|
|
434
|
+
startY,
|
|
435
|
+
startX,
|
|
436
|
+
startY + magnitude
|
|
437
|
+
];
|
|
438
|
+
} else if (commandName === PlaceHolderCommands.hpin) {
|
|
439
|
+
const magnitude = positionParams[3];
|
|
440
|
+
positionParams = [
|
|
441
|
+
positionParams[0],
|
|
442
|
+
startX,
|
|
443
|
+
startY,
|
|
444
|
+
startX + magnitude,
|
|
445
|
+
startY
|
|
446
|
+
];
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
drawing.addPin(...positionParams);
|
|
450
|
+
|
|
451
|
+
// Add a label for the pinId and pinName
|
|
452
|
+
const latestPin = this.drawing.pins[this.drawing.pins.length - 1];
|
|
453
|
+
const [pinId, , angle] = latestPin;
|
|
454
|
+
const [, , , endX, endY] = positionParams;
|
|
455
|
+
|
|
456
|
+
let pinNameAlignment = HorizontalAlign.Left;
|
|
457
|
+
let pinNameOffsetX = 4;
|
|
458
|
+
|
|
459
|
+
let pinIdOffsetX = 0;
|
|
460
|
+
let pinIdAlignment = HorizontalAlign.Left;
|
|
461
|
+
|
|
462
|
+
let pinIdVAlignment = VerticalAlign.Bottom;
|
|
463
|
+
let pinIdOffsetY = -2;
|
|
464
|
+
|
|
465
|
+
switch (angle) {
|
|
466
|
+
case 0:
|
|
467
|
+
pinNameAlignment = HorizontalAlign.Left;
|
|
468
|
+
pinNameOffsetX = 4;
|
|
469
|
+
pinIdAlignment = HorizontalAlign.Right;
|
|
470
|
+
pinIdOffsetX = -2;
|
|
471
|
+
break;
|
|
472
|
+
case 90:
|
|
473
|
+
case 180:
|
|
474
|
+
pinNameAlignment = HorizontalAlign.Right;
|
|
475
|
+
pinNameOffsetX = -4;
|
|
476
|
+
pinIdAlignment = HorizontalAlign.Left;
|
|
477
|
+
pinIdOffsetX = 2;
|
|
478
|
+
break;
|
|
479
|
+
case 270:
|
|
480
|
+
pinNameAlignment = HorizontalAlign.Left;
|
|
481
|
+
pinNameOffsetX = 4;
|
|
482
|
+
pinIdAlignment = HorizontalAlign.Left;
|
|
483
|
+
pinIdOffsetX = 2;
|
|
484
|
+
pinIdOffsetY = 2;
|
|
485
|
+
pinIdVAlignment = VerticalAlign.Top;
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (angle === 0 || angle === 90 || angle === 180 || angle === 270) {
|
|
490
|
+
const usePinName = pinNameParam ?? "";
|
|
491
|
+
|
|
492
|
+
// Draw the pinName
|
|
493
|
+
usePinName !== "" && drawing.addLabel(
|
|
494
|
+
endX + pinNameOffsetX, endY, usePinName, {
|
|
495
|
+
fontSize: 10,
|
|
496
|
+
anchor: pinNameAlignment,
|
|
497
|
+
vanchor: VerticalAlign.Middle,
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
// Draw pin Id
|
|
501
|
+
displayPinId && drawing.addLabel(
|
|
502
|
+
endX + pinIdOffsetX, endY + pinIdOffsetY, pinId.toString(), {
|
|
503
|
+
fontSize: 8,
|
|
504
|
+
anchor: pinIdAlignment,
|
|
505
|
+
vanchor: pinIdVAlignment,
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
357
510
|
constructor(drawing: SymbolDrawing) {
|
|
358
511
|
super();
|
|
359
512
|
this.drawing = drawing;
|
|
360
513
|
}
|
|
361
514
|
}
|
|
362
515
|
|
|
516
|
+
export enum PlaceHolderCommands {
|
|
517
|
+
arc = 'arc',
|
|
518
|
+
circle = 'circle',
|
|
519
|
+
rect = 'rect',
|
|
520
|
+
triangle = 'triangle',
|
|
521
|
+
pin = 'pin',
|
|
522
|
+
hpin = 'hpin',
|
|
523
|
+
vpin = 'vpin',
|
|
524
|
+
hline = 'hline',
|
|
525
|
+
vline = 'vline',
|
|
526
|
+
line = 'line',
|
|
527
|
+
label = 'label',
|
|
528
|
+
path = 'path',
|
|
529
|
+
lineWidth = 'lineWidth',
|
|
530
|
+
fill = 'fill',
|
|
531
|
+
lineColor = 'lineColor'
|
|
532
|
+
}
|
|
533
|
+
|
|
363
534
|
export class SymbolCustom extends SymbolGraphic {
|
|
364
535
|
|
|
365
536
|
pinDefinition: SymbolPinDefintion[] = [];
|
|
@@ -407,7 +578,7 @@ export class SymbolCustom extends SymbolGraphic {
|
|
|
407
578
|
const bodyWidth = this.bodyWidth;
|
|
408
579
|
const bodyHeight = (1 + Math.max(maxLeftPins, maxRightPins)) * this.pinSpacing;
|
|
409
580
|
|
|
410
|
-
drawing.addSetFillColor(bodyColor);
|
|
581
|
+
// drawing.addSetFillColor(bodyColor);
|
|
411
582
|
|
|
412
583
|
drawing.addRect(0, 0, bodyWidth, bodyHeight);
|
|
413
584
|
|
|
@@ -455,18 +626,23 @@ export class SymbolCustom extends SymbolGraphic {
|
|
|
455
626
|
});
|
|
456
627
|
|
|
457
628
|
const instanceName = this.getLabelValue("refdes");
|
|
458
|
-
const MPN = this.getLabelValue("MPN");
|
|
459
|
-
|
|
460
629
|
instanceName && drawing.addLabel(-bodyWidth/2, -bodyHeight/2 - 4, instanceName, {
|
|
461
630
|
fontSize: 10,
|
|
462
631
|
anchor: HorizontalAlign.Left,
|
|
463
632
|
});
|
|
464
633
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
634
|
+
const acceptedMPNKeys = ['MPN', 'mpn', 'manufacturer_pn'];
|
|
635
|
+
|
|
636
|
+
acceptedMPNKeys.some(key => {
|
|
637
|
+
const labelValue = this.getLabelValue(key);
|
|
638
|
+
if (labelValue !== undefined){
|
|
639
|
+
drawing.addLabel(-bodyWidth/2, bodyHeight/2 + 4, labelValue, {
|
|
640
|
+
fontSize: 10,
|
|
641
|
+
anchor: HorizontalAlign.Left,
|
|
642
|
+
vanchor: VerticalAlign.Top,
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
});
|
|
470
646
|
|
|
471
647
|
this.drawing = drawing;
|
|
472
648
|
this._cacheLeftPins = leftPins;
|
|
@@ -516,10 +692,13 @@ export class SymbolDrawing {
|
|
|
516
692
|
addPin(pinId: number, startX: number, startY: number,
|
|
517
693
|
endX: number, endY: number): SymbolDrawing {
|
|
518
694
|
|
|
519
|
-
// Determine the pin angle based on
|
|
695
|
+
// Determine the pin angle based on vector with start point
|
|
696
|
+
// going to end point. The angle is relative to the x-axis.
|
|
697
|
+
// 270
|
|
698
|
+
// 180 -- start --> 0
|
|
699
|
+
// 90
|
|
520
700
|
|
|
521
701
|
let angle = 0;
|
|
522
|
-
|
|
523
702
|
if (startX === endX) {
|
|
524
703
|
if (startY > endY) {
|
|
525
704
|
angle = 270;
|
|
@@ -574,6 +753,29 @@ export class SymbolDrawing {
|
|
|
574
753
|
return this;
|
|
575
754
|
}
|
|
576
755
|
|
|
756
|
+
addTriangle(startX: number, startY: number, endX: number, endY: number, width: number): SymbolDrawing {
|
|
757
|
+
const line = Geometry.line(
|
|
758
|
+
startX, startY,
|
|
759
|
+
endX, endY);
|
|
760
|
+
|
|
761
|
+
const normLine = line.norm;
|
|
762
|
+
const dx1 = normLine.x * width / 2;
|
|
763
|
+
const dy1 = normLine.y * width / 2;
|
|
764
|
+
const dx2 = normLine.x * -width / 2;
|
|
765
|
+
const dy2 = normLine.y * -width / 2;
|
|
766
|
+
|
|
767
|
+
this.items.push(
|
|
768
|
+
Geometry.polygon([
|
|
769
|
+
[dx1 + startX, dy1 + startY],
|
|
770
|
+
[dx2 + startX, dy2 + startY],
|
|
771
|
+
[endX, endY],
|
|
772
|
+
[dx1 + startX, dy1 + startY],
|
|
773
|
+
])
|
|
774
|
+
);
|
|
775
|
+
|
|
776
|
+
return this;
|
|
777
|
+
}
|
|
778
|
+
|
|
577
779
|
addRect2(x: number, y: number, x2: number, y2: number): SymbolDrawing {
|
|
578
780
|
this.items.push(
|
|
579
781
|
Geometry.polygon([
|
|
@@ -824,7 +1026,7 @@ export class SymbolDrawingCommands extends SymbolDrawing {
|
|
|
824
1026
|
clone(): SymbolDrawingCommands {
|
|
825
1027
|
// Force a deep clone
|
|
826
1028
|
const tmpCommands: SubExpressionCommand[] = this.commands.map(item => {
|
|
827
|
-
if (item[0] ===
|
|
1029
|
+
if (item[0] === PlaceHolderCommands.label) {
|
|
828
1030
|
const commandName = item[0];
|
|
829
1031
|
const positionParams = item[1];
|
|
830
1032
|
const keywordParams = item[2];
|
package/src/geometry.ts
CHANGED
|
@@ -2,10 +2,12 @@ import Flatten from '@flatten-js/core'
|
|
|
2
2
|
import { measureTextSize2 } from './sizing.js';
|
|
3
3
|
import { defaultFont } from './globals.js';
|
|
4
4
|
import { Box } from '@svgdotjs/svg.js';
|
|
5
|
+
import { NumericValue } from './objects/ParamDefinition.js';
|
|
5
6
|
|
|
6
7
|
export type Segment = Flatten.Segment;
|
|
7
8
|
export type Polygon = Flatten.Polygon;
|
|
8
9
|
export type Multiline = Flatten.Multiline;
|
|
10
|
+
export type Line = Flatten.Line;
|
|
9
11
|
|
|
10
12
|
export type Arc = Flatten.Arc;
|
|
11
13
|
|
|
@@ -15,6 +17,7 @@ export type LabelStyle = {
|
|
|
15
17
|
font?: string,
|
|
16
18
|
fontSize?: number,
|
|
17
19
|
fontWeight?: string,
|
|
20
|
+
angle?: number,
|
|
18
21
|
|
|
19
22
|
anchor?: HorizontalAlign.Left | HorizontalAlign.Middle | HorizontalAlign.Right, // Horizontal anchor
|
|
20
23
|
vanchor?: VerticalAlign.Top | VerticalAlign.Middle | VerticalAlign.Bottom, // Vertical anchor
|
|
@@ -62,15 +65,28 @@ export class Label extends Flatten.Polygon {
|
|
|
62
65
|
static fromPoint(id: string, x: number, y: number,
|
|
63
66
|
text: string, style: LabelStyle): Label {
|
|
64
67
|
|
|
68
|
+
let useText: string;
|
|
69
|
+
if (typeof text === 'number'){
|
|
70
|
+
useText = (text as number).toString();
|
|
71
|
+
} else if (typeof text === 'object'
|
|
72
|
+
&& text instanceof NumericValue) {
|
|
73
|
+
useText = (text as NumericValue).toDisplayString();
|
|
74
|
+
} else if (typeof text === 'string'){
|
|
75
|
+
useText = text;
|
|
76
|
+
} else {
|
|
77
|
+
throw 'Invalid string passed into label';
|
|
78
|
+
}
|
|
79
|
+
|
|
65
80
|
const { fontSize = 10,
|
|
66
81
|
anchor = HorizontalAlign.Left,
|
|
67
82
|
vanchor = VerticalAlign.Bottom,
|
|
68
83
|
fontWeight = 'regular',
|
|
69
84
|
} = style ?? {};
|
|
70
85
|
|
|
71
|
-
// Determine the size of the text
|
|
86
|
+
// Determine the size of the text, this is needed to determine the
|
|
87
|
+
// bounding box of the text for layout purposes.
|
|
72
88
|
const { width, height, box } =
|
|
73
|
-
measureTextSize2(
|
|
89
|
+
measureTextSize2(useText, defaultFont, fontSize, fontWeight,
|
|
74
90
|
anchor, vanchor);
|
|
75
91
|
|
|
76
92
|
// const polygonCoords =
|
|
@@ -87,7 +103,7 @@ export class Label extends Flatten.Polygon {
|
|
|
87
103
|
const polygon = new Flatten.Polygon(polygonCoords);
|
|
88
104
|
|
|
89
105
|
// Create the bounds of the label
|
|
90
|
-
return new Label(id,
|
|
106
|
+
return new Label(id, useText, [x, y], polygon, style, box);
|
|
91
107
|
}
|
|
92
108
|
|
|
93
109
|
rotate(angle: number, origin: Flatten.Point): Label {
|
|
@@ -116,6 +132,13 @@ export class Geometry {
|
|
|
116
132
|
return new Flatten.Point(x, y);
|
|
117
133
|
}
|
|
118
134
|
|
|
135
|
+
static line(x1: number, y1: number, x2: number, y2: number): Flatten.Line {
|
|
136
|
+
return new Flatten.Line(
|
|
137
|
+
Geometry.point(x1, y1),
|
|
138
|
+
Geometry.point(x2, y2)
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
|
|
119
142
|
static label(id: string, x: number, y: number, text: string, style: LabelStyle): Label {
|
|
120
143
|
return Label.fromPoint(id, x, y, text, style);
|
|
121
144
|
}
|
|
@@ -187,7 +210,14 @@ export class Geometry {
|
|
|
187
210
|
|
|
188
211
|
features.forEach(feature => {
|
|
189
212
|
const box = feature.box;
|
|
190
|
-
|
|
213
|
+
|
|
214
|
+
if (feature instanceof Label
|
|
215
|
+
&& typeof feature.text === 'string'
|
|
216
|
+
&& feature.text.trim().length === 0) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (box.xmin === undefined) {
|
|
191
221
|
throw "Invalid box!";
|
|
192
222
|
}
|
|
193
223
|
|
|
@@ -218,6 +248,8 @@ export class Geometry {
|
|
|
218
248
|
console.log('unknown type', feature);
|
|
219
249
|
}
|
|
220
250
|
|
|
251
|
+
static FullCircleRadians = 2 * Math.PI;
|
|
252
|
+
|
|
221
253
|
static featuresToPath(items: Feature[]):
|
|
222
254
|
{ path: string, isClosedPolygon: boolean } {
|
|
223
255
|
|
|
@@ -237,16 +269,26 @@ export class Geometry {
|
|
|
237
269
|
const y = item.center.y;
|
|
238
270
|
const radius = item.r as number;
|
|
239
271
|
|
|
272
|
+
let useEndAngle = item.endAngle;
|
|
273
|
+
let extraEnd = '';
|
|
274
|
+
if (item.startAngle === 0 && item.endAngle === Geometry.FullCircleRadians){
|
|
275
|
+
// detect as a circle and close the polygon
|
|
276
|
+
useEndAngle = 359.9999 * Math.PI/ 180;
|
|
277
|
+
isClosedPolygon = true;
|
|
278
|
+
extraEnd = ' Z'; // close the circle
|
|
279
|
+
}
|
|
280
|
+
|
|
240
281
|
// Assume angle is clockwise for now
|
|
241
282
|
const startPoint = getArcPointRadians(x, y, radius,
|
|
242
283
|
item.startAngle);
|
|
243
284
|
|
|
244
285
|
const endPoint = getArcPointRadians(x, y, radius,
|
|
245
|
-
|
|
286
|
+
useEndAngle);
|
|
246
287
|
|
|
247
288
|
paths.push('M ' + startPoint[0] + ' ' + startPoint[1]
|
|
248
289
|
+ 'A ' + radius + ' ' + radius + ' 0 1 1 '
|
|
249
|
-
+ endPoint[0] + ' ' + endPoint[1]);
|
|
290
|
+
+ endPoint[0] + ' ' + endPoint[1] + extraEnd);
|
|
291
|
+
|
|
250
292
|
|
|
251
293
|
} else {
|
|
252
294
|
const coords = Geometry.getCoords(item);
|
package/src/globals.ts
CHANGED
|
@@ -33,7 +33,9 @@ export enum SymbolPinSide {
|
|
|
33
33
|
export const portWidth = 20;
|
|
34
34
|
export const portHeight = 2;
|
|
35
35
|
|
|
36
|
-
export const defaultFont = '
|
|
36
|
+
export const defaultFont = 'Open Sans-Regular, Arial';
|
|
37
|
+
export const defaultFontBold = 'Open Sans-Bold, Arial-Bold, Arial';
|
|
38
|
+
|
|
37
39
|
export const defaultFontSize = 10;
|
|
38
40
|
|
|
39
41
|
export const bodyColor = '#FFFEAF';
|
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
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
|
+
|
|
10
|
+
export function renderScript(scriptData: string, outputPath: string, options): string {
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
currentDirectory = null,
|
|
14
|
+
defaultLibsPath,
|
|
15
|
+
dumpNets = false,
|
|
16
|
+
dumpData = false,
|
|
17
|
+
kicadNetlistPath = null,
|
|
18
|
+
showStats = false} = options;
|
|
19
|
+
|
|
20
|
+
const visitor = new MainVisitor(true);
|
|
21
|
+
|
|
22
|
+
visitor.onImportFile = visitor.createImportFileHandler(currentDirectory, defaultLibsPath);
|
|
23
|
+
|
|
24
|
+
visitor.print('reading file');
|
|
25
|
+
visitor.print('done reading file');
|
|
26
|
+
|
|
27
|
+
const { tree, parser,
|
|
28
|
+
hasParseError, hasError,
|
|
29
|
+
parserTimeTaken,
|
|
30
|
+
lexerTimeTaken } = parseFileWithVisitor(visitor, scriptData);
|
|
31
|
+
|
|
32
|
+
showStats && console.log('Lexing took:', lexerTimeTaken);
|
|
33
|
+
showStats && console.log('Parsing took:', parserTimeTaken);
|
|
34
|
+
dumpNets && console.log(visitor.dumpNets());
|
|
35
|
+
|
|
36
|
+
dumpData && writeFileSync('dump/tree.lisp', tree.toStringTree(null, parser));
|
|
37
|
+
dumpData && writeFileSync('dump/raw-parser.txt', visitor.logger.dump());
|
|
38
|
+
|
|
39
|
+
if (hasError || hasParseError) {
|
|
40
|
+
console.log('Error while parsing');
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
visitor.annotateComponents();
|
|
46
|
+
} catch (err) {
|
|
47
|
+
console.log('Error during annotation: ', err);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (kicadNetlistPath) {
|
|
51
|
+
const kicadNetList = generateKiCADNetList(visitor.getNetList());
|
|
52
|
+
writeFileSync(kicadNetlistPath, kicadNetList);
|
|
53
|
+
console.log('Generated KiCad netlist file');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
// await writeFile('dump/raw-netlist.json', JSON.stringify(visitor.dump2(), null, 2));
|
|
58
|
+
|
|
59
|
+
const { sequence, nets } = visitor.getGraph();
|
|
60
|
+
|
|
61
|
+
// const tmpInstances = visitor.getExecutor().scope.instances;
|
|
62
|
+
// for (const [instanceName, instance] of tmpInstances){
|
|
63
|
+
// console.log(instanceName);
|
|
64
|
+
// console.log(instance.pinNets);
|
|
65
|
+
// }
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
const tmpSequence = sequence.map(item => {
|
|
69
|
+
const tmp = [...item];
|
|
70
|
+
|
|
71
|
+
const action = tmp[0];
|
|
72
|
+
|
|
73
|
+
if (action === SequenceAction.Wire) {
|
|
74
|
+
tmp[2] = tmp[2].map(item2 => {
|
|
75
|
+
return [item2.direction, item2.value].join(",");
|
|
76
|
+
}).join(" ");
|
|
77
|
+
} else if (action === SequenceAction.Frame) {
|
|
78
|
+
tmp[1] = item[1].frameId;
|
|
79
|
+
|
|
80
|
+
} else if (action !== SequenceAction.WireJump) {
|
|
81
|
+
tmp[1] = item[1].instanceName;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return tmp.join(" | ");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
dumpData && writeFileSync('dump/raw-sequence.txt', tmpSequence.join('\n'));
|
|
88
|
+
let svgOutput: string = null;
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const layoutEngine = new LayoutEngine();
|
|
92
|
+
const layoutTimer = new SimpleStopwatch();
|
|
93
|
+
|
|
94
|
+
const graph = layoutEngine.runLayout(sequence, nets);
|
|
95
|
+
|
|
96
|
+
layoutEngine.printWarnings();
|
|
97
|
+
|
|
98
|
+
showStats && console.log('Layout took:', layoutTimer.lap());
|
|
99
|
+
|
|
100
|
+
dumpData && writeFileSync('dump/raw-layout.txt', layoutEngine.logger.dump());
|
|
101
|
+
|
|
102
|
+
const generateSvgTimer = new SimpleStopwatch();
|
|
103
|
+
svgOutput = generateSVG2(graph);
|
|
104
|
+
showStats && console.log('Render took:', generateSvgTimer.lap());
|
|
105
|
+
|
|
106
|
+
if (outputPath) {
|
|
107
|
+
writeFileSync(outputPath, svgOutput);
|
|
108
|
+
}
|
|
109
|
+
} catch (err) {
|
|
110
|
+
console.log('Error during render: ', err);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return svgOutput;
|
|
114
|
+
}
|