circuitscript 0.0.25 → 0.0.27

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.
Files changed (51) hide show
  1. package/LICENSE +1 -1
  2. package/dist/cjs/BaseVisitor.js +16 -12
  3. package/dist/cjs/SemanticTokenVisitor.js +3 -3
  4. package/dist/cjs/antlr/CircuitScriptLexer.js +189 -166
  5. package/dist/cjs/antlr/CircuitScriptParser.js +1295 -719
  6. package/dist/cjs/draw_symbols.js +42 -14
  7. package/dist/cjs/execute.js +91 -30
  8. package/dist/cjs/export.js +91 -5
  9. package/dist/cjs/geometry.js +45 -26
  10. package/dist/cjs/globals.js +1 -2
  11. package/dist/cjs/helpers.js +6 -2
  12. package/dist/cjs/layout.js +37 -17
  13. package/dist/cjs/main.js +21 -9
  14. package/dist/cjs/objects/ClassComponent.js +8 -0
  15. package/dist/cjs/objects/types.js +8 -1
  16. package/dist/cjs/render.js +1 -1
  17. package/dist/cjs/visitor.js +131 -23
  18. package/dist/esm/BaseVisitor.mjs +17 -13
  19. package/dist/esm/SemanticTokenVisitor.mjs +3 -3
  20. package/dist/esm/antlr/CircuitScriptLexer.mjs +189 -166
  21. package/dist/esm/antlr/CircuitScriptParser.mjs +1287 -716
  22. package/dist/esm/antlr/CircuitScriptVisitor.mjs +6 -1
  23. package/dist/esm/draw_symbols.mjs +44 -16
  24. package/dist/esm/execute.mjs +90 -26
  25. package/dist/esm/export.mjs +89 -6
  26. package/dist/esm/geometry.mjs +44 -25
  27. package/dist/esm/globals.mjs +1 -2
  28. package/dist/esm/helpers.mjs +7 -3
  29. package/dist/esm/layout.mjs +35 -16
  30. package/dist/esm/main.mjs +21 -9
  31. package/dist/esm/objects/ClassComponent.mjs +8 -0
  32. package/dist/esm/objects/types.mjs +7 -0
  33. package/dist/esm/render.mjs +2 -2
  34. package/dist/esm/visitor.mjs +133 -25
  35. package/dist/types/BaseVisitor.d.ts +2 -5
  36. package/dist/types/SemanticTokenVisitor.d.ts +2 -2
  37. package/dist/types/antlr/CircuitScriptLexer.d.ts +29 -22
  38. package/dist/types/antlr/CircuitScriptParser.d.ts +121 -44
  39. package/dist/types/antlr/CircuitScriptVisitor.d.ts +12 -2
  40. package/dist/types/draw_symbols.d.ts +11 -6
  41. package/dist/types/execute.d.ts +6 -4
  42. package/dist/types/export.d.ts +27 -1
  43. package/dist/types/geometry.d.ts +12 -9
  44. package/dist/types/globals.d.ts +2 -3
  45. package/dist/types/layout.d.ts +5 -0
  46. package/dist/types/objects/ClassComponent.d.ts +5 -0
  47. package/dist/types/objects/Wire.d.ts +2 -1
  48. package/dist/types/objects/types.d.ts +6 -0
  49. package/dist/types/visitor.d.ts +7 -3
  50. package/libs/lib.cst +28 -10
  51. package/package.json +1 -1
@@ -31,6 +31,7 @@ export class CircuitScriptVisitor extends AbstractParseTreeVisitor {
31
31
  visitFunctionCallExpr;
32
32
  visitAdditionExpr;
33
33
  visitMultiplyExpr;
34
+ visitLogicalOperatorExpr;
34
35
  visitDataExpr;
35
36
  visitUnaryOperatorExpr;
36
37
  visitValueAtomExpr;
@@ -49,7 +50,8 @@ export class CircuitScriptVisitor extends AbstractParseTreeVisitor {
49
50
  visitFunction_return_expr;
50
51
  visitCreate_component_expr;
51
52
  visitCreate_graphic_expr;
52
- visitSub_expr;
53
+ visitNested_properties_inner;
54
+ visitGraphic_expr;
53
55
  visitProperty_expr;
54
56
  visitProperty_key_expr;
55
57
  visitNested_properties;
@@ -61,4 +63,7 @@ export class CircuitScriptVisitor extends AbstractParseTreeVisitor {
61
63
  visitPoint_expr;
62
64
  visitImport_expr;
63
65
  visitFrame_expr;
66
+ visitIf_expr;
67
+ visitIf_inner_expr;
68
+ visitElse_expr;
64
69
  }
@@ -1,5 +1,6 @@
1
- import { SymbolPinSide, defaultFont } from "./globals.mjs";
2
- import { Geometry, GeometryProp, HorizontalAlign, Label, VerticalAlign } from "./geometry.mjs";
1
+ import { ReferenceTypes, SymbolPinSide, defaultFont } from "./globals.mjs";
2
+ import { Geometry, GeometryProp, HorizontalAlign, Label, Textbox, VerticalAlign } from "./geometry.mjs";
3
+ import { PinTypes } from "./objects/PinTypes.mjs";
3
4
  const defaultSymbolLineWidth = 2;
4
5
  export class SymbolGraphic {
5
6
  drawPortsName = true;
@@ -251,7 +252,7 @@ export class SymbolText extends SymbolGraphic {
251
252
  }
252
253
  generateDrawing() {
253
254
  const drawing = new SymbolDrawing();
254
- drawing.addLabel(0, 0, this.text, {
255
+ drawing.addTextbox(0, 0, this.text, {
255
256
  fontSize: this.fontSize,
256
257
  anchor: HorizontalAlign.Middle,
257
258
  fontWeight: this.fontWeight,
@@ -316,13 +317,7 @@ export class SymbolPlaceholder extends SymbolGraphic {
316
317
  break;
317
318
  }
318
319
  case PlaceHolderCommands.label: {
319
- const keywords = ['fontSize', 'anchor', 'vanchor', 'angle'];
320
- const style = {};
321
- keywords.forEach(item => {
322
- if (keywordParams.has(item)) {
323
- style[item] = keywordParams.get(item);
324
- }
325
- });
320
+ const style = this.parseLabelStyle(keywordParams);
326
321
  positionParams = [...positionParams];
327
322
  positionParams.push(style);
328
323
  const labelId = positionParams[0];
@@ -335,10 +330,33 @@ export class SymbolPlaceholder extends SymbolGraphic {
335
330
  drawing.addLabelId(...tmpPositionParams);
336
331
  break;
337
332
  }
333
+ case PlaceHolderCommands.text: {
334
+ const style = this.parseLabelStyle(keywordParams);
335
+ const content = keywordParams.get('content');
336
+ let offsetX = 0;
337
+ let offsetY = 0;
338
+ if (keywordParams.has('offset')) {
339
+ const offset = keywordParams.get('offset');
340
+ offsetX = offset[0];
341
+ offsetY = offset[1];
342
+ }
343
+ drawing.addTextbox(offsetX, offsetY, content, style);
344
+ break;
345
+ }
338
346
  }
339
347
  });
340
348
  drawing.log("=== end generate drawing ===");
341
349
  }
350
+ parseLabelStyle(keywordParams) {
351
+ const keywords = ['fontSize', 'anchor', 'vanchor', 'angle'];
352
+ const style = {};
353
+ keywords.forEach(item => {
354
+ if (keywordParams.has(item)) {
355
+ style[item] = keywordParams.get(item);
356
+ }
357
+ });
358
+ return style;
359
+ }
342
360
  drawPinParams(drawing, commandName, keywordParams, positionParams) {
343
361
  drawing.log('add pin', ...positionParams);
344
362
  const keywordDisplayPinId = 'display_pin_id';
@@ -348,6 +366,11 @@ export class SymbolPlaceholder extends SymbolGraphic {
348
366
  displayPinId = false;
349
367
  }
350
368
  let pinNameParam = null;
369
+ let pinType = PinTypes.Any;
370
+ if (positionParams[1].type && positionParams[1].type === ReferenceTypes.pinType) {
371
+ pinType = positionParams[1].value;
372
+ positionParams = [positionParams[0], ...positionParams.slice(2)];
373
+ }
351
374
  if (typeof positionParams[1] === 'string') {
352
375
  pinNameParam = positionParams[1];
353
376
  positionParams = [positionParams[0], ...positionParams.slice(2)];
@@ -443,6 +466,7 @@ export var PlaceHolderCommands;
443
466
  PlaceHolderCommands["lineWidth"] = "lineWidth";
444
467
  PlaceHolderCommands["fill"] = "fill";
445
468
  PlaceHolderCommands["lineColor"] = "lineColor";
469
+ PlaceHolderCommands["text"] = "text";
446
470
  })(PlaceHolderCommands || (PlaceHolderCommands = {}));
447
471
  export class SymbolCustom extends SymbolGraphic {
448
472
  pinDefinition = [];
@@ -630,6 +654,10 @@ export class SymbolDrawing {
630
654
  this.items.push(Geometry.label(id, x, y, textValue, style));
631
655
  return this;
632
656
  }
657
+ addTextbox(x, y, textValue, style) {
658
+ this.items.push(Geometry.textbox(null, x, y, textValue, style));
659
+ return this;
660
+ }
633
661
  addPath(...pathParts) {
634
662
  const parts = pathParts.reduce((accum, tmp) => {
635
663
  if (typeof tmp === "string") {
@@ -704,7 +732,7 @@ export class SymbolDrawing {
704
732
  let currentLineColor = '#333';
705
733
  const pathItems = [];
706
734
  this.items.forEach(item => {
707
- if (!(item instanceof Label)) {
735
+ if (!(item instanceof Textbox)) {
708
736
  if (item instanceof GeometryProp) {
709
737
  if (item.name === 'lineWidth') {
710
738
  currentLineWidth = item.value;
@@ -719,7 +747,7 @@ export class SymbolDrawing {
719
747
  else {
720
748
  let tmpResult = Geometry.groupFlip([item], this.flipX, this.flipY);
721
749
  tmpResult = Geometry.groupRotate(tmpResult, this.angle, this.mainOrigin);
722
- const { path, isClosedPolygon } = this.featuresToPath(tmpResult);
750
+ const { path, isClosedPolygon } = this.featuresToPath(tmpResult, this.flipX, this.flipY);
723
751
  pathItems.push({
724
752
  path: path,
725
753
  lineWidth: currentLineWidth,
@@ -735,14 +763,14 @@ export class SymbolDrawing {
735
763
  let features = this.pins.map(item => item[1]);
736
764
  features = Geometry.groupFlip(features, this.flipX, this.flipY);
737
765
  features = Geometry.groupRotate(features, this.angle, this.mainOrigin);
738
- const { path } = this.featuresToPath(features);
766
+ const { path } = this.featuresToPath(features, this.flipX, this.flipY);
739
767
  return path;
740
768
  }
741
769
  getLabels() {
742
- return this.items.filter(item => item instanceof Label);
770
+ return this.items.filter(item => item instanceof Textbox);
743
771
  }
744
- featuresToPath(items) {
745
- return Geometry.featuresToPath(items);
772
+ featuresToPath(items, flipX, flipY) {
773
+ return Geometry.featuresToPath(items, flipX, flipY);
746
774
  }
747
775
  getBoundingBox(excludeLabels = false) {
748
776
  const pinFeatures = this.pins.map(pin => {
@@ -3,8 +3,10 @@ import { ClassComponent } from './objects/ClassComponent.mjs';
3
3
  import { ActiveObject, ExecutionScope, FrameAction, SequenceAction } from './objects/ExecutionScope.mjs';
4
4
  import { Net } from './objects/Net.mjs';
5
5
  import { PortSide } from './objects/PinDefinition.mjs';
6
+ import { Direction } from './objects/types.mjs';
6
7
  import { Wire } from './objects/Wire.mjs';
7
8
  import { Frame } from './objects/Frame.mjs';
9
+ import { CalculatePinPositions } from './layout.mjs';
8
10
  export class ExecutionContext {
9
11
  name;
10
12
  namespace;
@@ -19,6 +21,7 @@ export class ExecutionContext {
19
21
  logger;
20
22
  __functionCache = {};
21
23
  parentContext;
24
+ componentAngleFollowsWire = true;
22
25
  constructor(name, namespace, netNamespace, executionLevel = 0, indentLevel = 0, silent = false, logger, parent) {
23
26
  this.name = name;
24
27
  this.namespace = namespace;
@@ -144,12 +147,27 @@ export class ExecutionContext {
144
147
  pins.forEach((pin) => {
145
148
  component.pins.set(pin.id, pin);
146
149
  });
150
+ component.arrangeProps = props.arrange ?? null;
151
+ component.displayProp = props.display ?? null;
152
+ component.widthProp = props.width ?? null;
153
+ component.typeProp = props.type ?? null;
154
+ component.copyProp = props.copy ?? false;
155
+ let useAngle = null;
156
+ if (props.angle) {
157
+ useAngle = props.angle % 360;
158
+ if (useAngle < 0) {
159
+ useAngle += 360;
160
+ }
161
+ }
162
+ component.angleProp = useAngle ?? 0;
163
+ component.followWireOrientationProp = props.followWireOrientation;
147
164
  const paramsMap = new Map();
148
165
  params.forEach((param) => {
149
166
  component.parameters.set(param.paramName, param.paramValue);
150
167
  paramsMap.set(param.paramName, param.paramValue);
151
168
  });
152
- if (paramsMap.has(ParamKeys.__is_net)) {
169
+ if (component.typeProp === ComponentTypes.net
170
+ || component.typeProp === ComponentTypes.label) {
153
171
  const netName = paramsMap.get(ParamKeys.net_name);
154
172
  const priority = paramsMap.get(ParamKeys.priority);
155
173
  const result = this.resolveNet(netName, this.netNamespace);
@@ -165,12 +183,7 @@ export class ExecutionContext {
165
183
  this.scope.setNet(component, 1, tmpNet);
166
184
  this.log('set net', netName, 'component', component);
167
185
  }
168
- const { arrange = null } = props;
169
- component.arrangeProps = arrange;
170
- component.displayProp = props.display ?? null;
171
- component.widthProp = props.width ?? null;
172
- component.typeProp = props.type ?? null;
173
- const portSides = getPortSide(component.pins, arrange);
186
+ const portSides = getPortSide(component.pins, component.arrangeProps);
174
187
  portSides.forEach(({ pinId, side, position }) => {
175
188
  if (component.pins.has(pinId)) {
176
189
  const tmpPin = component.pins.get(pinId);
@@ -187,6 +200,10 @@ export class ExecutionContext {
187
200
  }
188
201
  printPoint(extra = '') {
189
202
  let netName = NoNetText;
203
+ if (this.scope.currentComponent === null || this.scope.currentPin === null) {
204
+ this.log((extra !== '' ? (extra + ' ') : '') + 'point is null');
205
+ return;
206
+ }
190
207
  if (this.scope.hasNet(this.scope.currentComponent, this.scope.currentPin)) {
191
208
  netName = this.scope
192
209
  .getNet(this.scope.currentComponent, this.scope.currentPin)
@@ -200,6 +217,7 @@ export class ExecutionContext {
200
217
  addComponentExisting(component, pin) {
201
218
  const startPin = pin;
202
219
  const nextPin = component.getNextPinAfter(startPin);
220
+ this.applyComponentAngleFromWire(component, pin);
203
221
  this.toComponent(component, startPin, { addSequence: true });
204
222
  this.log('move to next pin: ' + nextPin);
205
223
  this.atComponent(component, nextPin, {
@@ -235,6 +253,7 @@ export class ExecutionContext {
235
253
  .toString());
236
254
  }
237
255
  const linkedNet = this.linkComponentPinNet(this.scope.currentComponent, this.scope.currentPin, component, pinId);
256
+ this.applyComponentAngleFromWire(component, pinId);
238
257
  this.scope.currentComponent = component;
239
258
  this.scope.currentPin = pinId;
240
259
  this.scope.clearActive();
@@ -283,22 +302,22 @@ export class ExecutionContext {
283
302
  this.printPoint();
284
303
  return this.getCurrentPoint();
285
304
  }
286
- cloneComponent(component) {
287
- let clonedComponent = null;
305
+ copyComponent(component) {
306
+ let componentCopy = null;
288
307
  if (!this.scope.copyIDs.has(component.instanceName)) {
289
308
  this.scope.copyIDs.set(component.instanceName, 0);
290
309
  }
291
310
  const idNum = this.scope.copyIDs.get(component.instanceName);
292
- clonedComponent = component.clone();
293
- clonedComponent._copyID = idNum;
294
- clonedComponent._copyFrom = component;
311
+ componentCopy = component.clone();
312
+ componentCopy._copyID = idNum;
313
+ componentCopy._copyFrom = component;
295
314
  this.scope.copyIDs.set(component.instanceName, idNum + 1);
296
315
  const cloneInstanceName = component.instanceName + ':' + idNum;
297
- this.scope.instances.set(cloneInstanceName, clonedComponent);
298
- clonedComponent.instanceName = cloneInstanceName;
299
- this.linkComponentPinNet(component, 1, clonedComponent, 1);
316
+ this.scope.instances.set(cloneInstanceName, componentCopy);
317
+ componentCopy.instanceName = cloneInstanceName;
318
+ this.linkComponentPinNet(component, 1, componentCopy, 1);
300
319
  this.log('created clone of net component:', cloneInstanceName);
301
- return clonedComponent;
320
+ return componentCopy;
302
321
  }
303
322
  enterBlocks(blockType) {
304
323
  if (blockType === BlockTypes.Point) {
@@ -565,7 +584,8 @@ export class ExecutionContext {
565
584
  }
566
585
  else if (action === SequenceAction.At || action === SequenceAction.To) {
567
586
  const tmpComponent = sequenceAction[1];
568
- if (isNetComponent(tmpComponent) && tmpComponent.parameters.get(ParamKeys.net_name) === 'gnd') {
587
+ if (tmpComponent.typeProp === ComponentTypes.net
588
+ && tmpComponent.parameters.get(ParamKeys.net_name) === 'gnd') {
569
589
  tmpComponent._copyID = gndCopyIdOffset + incrementGndLinkId;
570
590
  incrementGndLinkId += 1;
571
591
  }
@@ -682,6 +702,59 @@ export class ExecutionContext {
682
702
  this.scope.currentComponent.styles[key] = styles[key];
683
703
  }
684
704
  }
705
+ applyComponentAngleFromWire(component, pin) {
706
+ if (this.componentAngleFollowsWire
707
+ && component.followWireOrientationProp
708
+ && component.useWireOrientationAngle
709
+ && this.scope.currentWireId !== -1) {
710
+ const currentWire = this.scope.wires[this.scope.currentWireId];
711
+ const lastSegment = currentWire.path[currentWire.path.length - 1];
712
+ const pinPositions = CalculatePinPositions(component);
713
+ if (pinPositions.has(pin)) {
714
+ const connectedPinPos = pinPositions.get(pin);
715
+ let targetAngle = null;
716
+ switch (lastSegment.direction) {
717
+ case Direction.Down:
718
+ targetAngle = 90;
719
+ break;
720
+ case Direction.Up:
721
+ targetAngle = 270;
722
+ break;
723
+ case Direction.Right:
724
+ targetAngle = 0;
725
+ break;
726
+ case Direction.Left:
727
+ targetAngle = 180;
728
+ break;
729
+ default:
730
+ targetAngle = null;
731
+ }
732
+ if (targetAngle === null) {
733
+ return;
734
+ }
735
+ this.log('set component angle from wire, target angle:', targetAngle, ', component angle:', component.angleProp, 'pin angle:', connectedPinPos.angle);
736
+ let useAngle = (targetAngle - connectedPinPos.angle) % 360;
737
+ if (useAngle < 0) {
738
+ useAngle += 360;
739
+ }
740
+ if (useAngle === 90) {
741
+ component.setParam('angle', 90);
742
+ }
743
+ else if (useAngle === 180) {
744
+ if (component.angleProp === 0 || component.angleProp === 180) {
745
+ component.setParam('flipX', 1);
746
+ }
747
+ else if (component.angleProp === 90 || component.angleProp === 270) {
748
+ component.setParam('flipY', 1);
749
+ }
750
+ }
751
+ else if (useAngle === 270) {
752
+ component.setParam('angle', 270);
753
+ }
754
+ component.wireOrientationAngle = useAngle;
755
+ }
756
+ }
757
+ }
685
758
  enterFrame() {
686
759
  const frameId = this.scope.frames.length + 1;
687
760
  const frameObject = new Frame(frameId);
@@ -706,15 +779,6 @@ function isWireSegmentsEndAuto(segments) {
706
779
  }
707
780
  return false;
708
781
  }
709
- export function isNetComponent(component) {
710
- return component.parameters.has(ParamKeys.__is_net);
711
- }
712
- export function isLabelComponent(component) {
713
- return component.parameters.has(ParamKeys.__is_label);
714
- }
715
- export function isNetOnlyComponent(component) {
716
- return isNetComponent(component) && !isLabelComponent(component);
717
- }
718
782
  export function getPortSide(pins, arrangeProps) {
719
783
  const result = [];
720
784
  if (arrangeProps === null) {
@@ -3,6 +3,7 @@ import { NumericValue } from "./objects/ParamDefinition.mjs";
3
3
  export function generateKiCADNetList(netlist) {
4
4
  const componentsList = [];
5
5
  const nets = {};
6
+ const missingFootprints = [];
6
7
  netlist.forEach(entry => {
7
8
  const { instance, pins } = entry;
8
9
  if (instance.assignedRefDes !== null) {
@@ -22,7 +23,10 @@ export function generateKiCADNetList(netlist) {
22
23
  instance.parameters.get('footprint')]);
23
24
  }
24
25
  else {
25
- console.log(instance.assignedRefDes, instance.instanceName, 'does not have footprint');
26
+ missingFootprints.push({
27
+ refdes: instance.assignedRefDes,
28
+ instanceName: instance.instanceName
29
+ });
26
30
  }
27
31
  componentsList.push(instanceDetails);
28
32
  for (const key in pins) {
@@ -63,20 +67,24 @@ export function generateKiCADNetList(netlist) {
63
67
  ]);
64
68
  counter++;
65
69
  }
70
+ const dateString = new Date().toISOString().slice(0, 10);
66
71
  const tree = [
67
72
  Id("export"),
68
73
  [Id("version"), "E"],
69
74
  [Id("design"),
70
- [Id("source"), "/somefile"],
71
- [Id("date"), "2023-11-19"],
75
+ [Id("source"), "/unknown-file"],
76
+ [Id("date"), dateString],
72
77
  [Id("tool"), "circuitscript-to-kicad"]
73
78
  ],
74
79
  [Id('components'), ...componentsList],
75
80
  [Id('nets'), ...netItems]
76
81
  ];
77
- return printTree(tree);
82
+ return {
83
+ tree,
84
+ missingFootprints
85
+ };
78
86
  }
79
- function printTree(tree, level = 0) {
87
+ export function printTree(tree, level = 0) {
80
88
  const output = [];
81
89
  if (!Array.isArray(tree)) {
82
90
  return "\"" + tree + "\"";
@@ -98,9 +106,84 @@ function printTree(tree, level = 0) {
98
106
  function Id(name) {
99
107
  return new IdObject(name);
100
108
  }
101
- class IdObject {
109
+ export class IdObject {
102
110
  keyName;
103
111
  constructor(keyName) {
104
112
  this.keyName = keyName;
105
113
  }
106
114
  }
115
+ export function _id(key) {
116
+ return new IdObject(key);
117
+ }
118
+ export class SExpObject {
119
+ object;
120
+ constructor(object) {
121
+ this.object = object;
122
+ }
123
+ getKey(object = null) {
124
+ object = object ?? this.object;
125
+ return object[0];
126
+ }
127
+ getValue(object = null) {
128
+ object = object ?? this.object;
129
+ return object.slice(1);
130
+ }
131
+ getJSON(object = null) {
132
+ object = object ?? this.object;
133
+ if (!Array.isArray(object)) {
134
+ return object;
135
+ }
136
+ const properties = {};
137
+ const keyName = object[0].keyName;
138
+ if (object.length === 2) {
139
+ properties[keyName] = this.getJSON(object[1]);
140
+ }
141
+ else {
142
+ const innerProps = {};
143
+ this.getValue(object).forEach(item => {
144
+ const tmpValue = this.getJSON(item);
145
+ if (typeof tmpValue === "object") {
146
+ for (const key in tmpValue) {
147
+ if (innerProps[key]) {
148
+ if (!Array.isArray(innerProps[key])) {
149
+ innerProps[key] = [innerProps[key]];
150
+ }
151
+ innerProps[key].push(tmpValue[key]);
152
+ }
153
+ else {
154
+ innerProps[key] = tmpValue[key];
155
+ }
156
+ }
157
+ }
158
+ else {
159
+ innerProps[item[0].keyName] = tmpValue;
160
+ }
161
+ });
162
+ properties[keyName] = innerProps;
163
+ }
164
+ return properties;
165
+ }
166
+ getWithId(id, object = null) {
167
+ object = object ?? this.object;
168
+ let result = null;
169
+ const key = object[0];
170
+ if (key.keyName === id) {
171
+ return object;
172
+ }
173
+ else {
174
+ this.getValue(object).some(item => {
175
+ if (Array.isArray(item)) {
176
+ result = this.getWithId(id, item);
177
+ if (result !== null) {
178
+ return true;
179
+ }
180
+ }
181
+ return false;
182
+ });
183
+ }
184
+ return result;
185
+ }
186
+ print() {
187
+ console.log(printTree(this.object));
188
+ }
189
+ }
@@ -2,7 +2,7 @@ import Flatten from '@flatten-js/core';
2
2
  import { measureTextSize2 } from './sizing.mjs';
3
3
  import { defaultFont } from './globals.mjs';
4
4
  import { NumericValue } from './objects/ParamDefinition.mjs';
5
- export class Label extends Flatten.Polygon {
5
+ export class Textbox extends Flatten.Polygon {
6
6
  id;
7
7
  text;
8
8
  anchorPoint = [0, 0];
@@ -11,10 +11,11 @@ export class Label extends Flatten.Polygon {
11
11
  font = 'default';
12
12
  style;
13
13
  textMeasurementBounds;
14
+ label;
14
15
  get box() {
15
16
  return this.polygon.box;
16
17
  }
17
- constructor(id, text, anchorPoint, polygon, style, bounds) {
18
+ constructor(id, text, anchorPoint, polygon, style, bounds, label) {
18
19
  super(polygon.vertices);
19
20
  this.id = id;
20
21
  this.text = text;
@@ -23,8 +24,9 @@ export class Label extends Flatten.Polygon {
23
24
  this.boundingBox = polygon.box;
24
25
  this.polygon = polygon;
25
26
  this.textMeasurementBounds = bounds;
27
+ this.label = label;
26
28
  }
27
- static fromPoint(id, x, y, text, style) {
29
+ static fromPoint(id, x, y, text, style, label) {
28
30
  let useText;
29
31
  if (typeof text === 'number') {
30
32
  useText = text.toString();
@@ -37,7 +39,7 @@ export class Label extends Flatten.Polygon {
37
39
  useText = text;
38
40
  }
39
41
  else {
40
- throw 'Invalid string passed into label';
42
+ throw 'Invalid string passed into textbox';
41
43
  }
42
44
  const { fontSize = 10, anchor = HorizontalAlign.Left, vanchor = VerticalAlign.Bottom, fontWeight = 'regular', } = style ?? {};
43
45
  const { width, height, box } = measureTextSize2(useText, defaultFont, fontSize, fontWeight, anchor, vanchor);
@@ -49,20 +51,22 @@ export class Label extends Flatten.Polygon {
49
51
  [box.x, box.y],
50
52
  ];
51
53
  const polygon = new Flatten.Polygon(polygonCoords);
52
- return new Label(id, useText, [x, y], polygon, style, box);
54
+ return new Textbox(id, useText, [x, y], polygon, style, box, label);
53
55
  }
54
56
  rotate(angle, origin) {
55
57
  const feature = super.rotate(angle, origin);
56
- return new Label(this.id, this.text, this.anchorPoint, feature, this.style, this.textMeasurementBounds);
58
+ return new Textbox(this.id, this.text, this.anchorPoint, feature, this.style, this.textMeasurementBounds, this.label);
57
59
  }
58
60
  transform(matrix) {
59
61
  const feature = super.transform(matrix);
60
- return new Label(this.id, this.text, this.anchorPoint, feature, this.style, this.textMeasurementBounds);
62
+ return new Textbox(this.id, this.text, this.anchorPoint, feature, this.style, this.textMeasurementBounds, this.label);
61
63
  }
62
64
  getLabelPosition() {
63
65
  return this.anchorPoint;
64
66
  }
65
67
  }
68
+ export class Label extends Textbox {
69
+ }
66
70
  export class GeometryProp {
67
71
  name;
68
72
  value;
@@ -79,7 +83,10 @@ export class Geometry {
79
83
  return new Flatten.Line(Geometry.point(x1, y1), Geometry.point(x2, y2));
80
84
  }
81
85
  static label(id, x, y, text, style) {
82
- return Label.fromPoint(id, x, y, text, style);
86
+ return Textbox.fromPoint(id, x, y, text, style, true);
87
+ }
88
+ static textbox(id, x, y, text, style) {
89
+ return Textbox.fromPoint(id, x, y, text, style, false);
83
90
  }
84
91
  static segment(start, end) {
85
92
  return new Flatten.Segment(Geometry.point(start[0], start[1]), Geometry.point(end[0], end[1]));
@@ -130,12 +137,28 @@ export class Geometry {
130
137
  let maxX = Number.NEGATIVE_INFINITY;
131
138
  let maxY = Number.NEGATIVE_INFINITY;
132
139
  features.forEach(feature => {
133
- const box = feature.box;
134
- if (feature instanceof Label
140
+ const tmpBox = feature.box;
141
+ let box = {
142
+ xmin: tmpBox.xmin,
143
+ ymin: tmpBox.ymin,
144
+ xmax: tmpBox.xmax,
145
+ ymax: tmpBox.ymax
146
+ };
147
+ if (feature instanceof Textbox
148
+ && feature.label
135
149
  && typeof feature.text === 'string'
136
150
  && feature.text.trim().length === 0) {
137
151
  return;
138
152
  }
153
+ if (feature instanceof Textbox && !feature.label) {
154
+ const [x, y] = feature.anchorPoint;
155
+ box = {
156
+ xmin: box.xmin + x,
157
+ ymin: box.ymin + y,
158
+ xmax: box.xmax + x,
159
+ ymax: box.ymax + y
160
+ };
161
+ }
139
162
  if (box.xmin === undefined) {
140
163
  throw "Invalid box!";
141
164
  }
@@ -151,24 +174,12 @@ export class Geometry {
151
174
  height: maxY - minY,
152
175
  };
153
176
  }
154
- static getType(feature) {
155
- if (feature instanceof Label) {
156
- return 'label';
157
- }
158
- else if (feature instanceof Flatten.Polygon) {
159
- return 'polygon';
160
- }
161
- else if (feature instanceof Flatten.Segment) {
162
- return 'segment';
163
- }
164
- console.log('unknown type', feature);
165
- }
166
177
  static FullCircleRadians = 2 * Math.PI;
167
- static featuresToPath(items) {
178
+ static featuresToPath(items, flipX, flipY) {
168
179
  const paths = [];
169
180
  let isClosedPolygon = false;
170
181
  items.forEach(item => {
171
- if (item instanceof Label) {
182
+ if (item instanceof Textbox) {
172
183
  return;
173
184
  }
174
185
  const path = [];
@@ -185,7 +196,15 @@ export class Geometry {
185
196
  }
186
197
  const startPoint = getArcPointRadians(x, y, radius, item.startAngle);
187
198
  const endPoint = getArcPointRadians(x, y, radius, useEndAngle);
188
- paths.push('M', startPoint[0], startPoint[1], 'A', radius, radius, 0, 1, 1, endPoint[0], endPoint[1], extraEnd);
199
+ let largeArcSweepFlag = 0;
200
+ if (useEndAngle - item.startAngle > Math.PI) {
201
+ largeArcSweepFlag = 1;
202
+ }
203
+ let sweepFlag = 1;
204
+ if (flipX === 1 && flipY === 0) {
205
+ sweepFlag = 0;
206
+ }
207
+ paths.push('M', startPoint[0], startPoint[1], 'A', radius, radius, 0, largeArcSweepFlag, sweepFlag, endPoint[0], endPoint[1], extraEnd);
189
208
  }
190
209
  else {
191
210
  const coords = Geometry.getCoords(item);
@@ -10,8 +10,6 @@ export var GlobalNames;
10
10
  export const NoNetText = 'NO_NET';
11
11
  export var ParamKeys;
12
12
  (function (ParamKeys) {
13
- ParamKeys["__is_net"] = "__is_net";
14
- ParamKeys["__is_label"] = "__is_label";
15
13
  ParamKeys["priority"] = "priority";
16
14
  ParamKeys["net_name"] = "net_name";
17
15
  })(ParamKeys || (ParamKeys = {}));
@@ -47,6 +45,7 @@ export var ReferenceTypes;
47
45
  ReferenceTypes["value"] = "value";
48
46
  ReferenceTypes["variable"] = "variable";
49
47
  ReferenceTypes["instance"] = "instance";
48
+ ReferenceTypes["pinType"] = "pinType";
50
49
  })(ReferenceTypes || (ReferenceTypes = {}));
51
50
  export var BlockTypes;
52
51
  (function (BlockTypes) {