circuitscript 0.0.26 → 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.
@@ -50,6 +50,7 @@ export class CircuitScriptVisitor extends AbstractParseTreeVisitor {
50
50
  visitFunction_return_expr;
51
51
  visitCreate_component_expr;
52
52
  visitCreate_graphic_expr;
53
+ visitNested_properties_inner;
53
54
  visitGraphic_expr;
54
55
  visitProperty_expr;
55
56
  visitProperty_key_expr;
@@ -1,5 +1,5 @@
1
1
  import { ReferenceTypes, SymbolPinSide, defaultFont } from "./globals.mjs";
2
- import { Geometry, GeometryProp, HorizontalAlign, Label, VerticalAlign } from "./geometry.mjs";
2
+ import { Geometry, GeometryProp, HorizontalAlign, Label, Textbox, VerticalAlign } from "./geometry.mjs";
3
3
  import { PinTypes } from "./objects/PinTypes.mjs";
4
4
  const defaultSymbolLineWidth = 2;
5
5
  export class SymbolGraphic {
@@ -252,7 +252,7 @@ export class SymbolText extends SymbolGraphic {
252
252
  }
253
253
  generateDrawing() {
254
254
  const drawing = new SymbolDrawing();
255
- drawing.addLabel(0, 0, this.text, {
255
+ drawing.addTextbox(0, 0, this.text, {
256
256
  fontSize: this.fontSize,
257
257
  anchor: HorizontalAlign.Middle,
258
258
  fontWeight: this.fontWeight,
@@ -317,13 +317,7 @@ export class SymbolPlaceholder extends SymbolGraphic {
317
317
  break;
318
318
  }
319
319
  case PlaceHolderCommands.label: {
320
- const keywords = ['fontSize', 'anchor', 'vanchor', 'angle'];
321
- const style = {};
322
- keywords.forEach(item => {
323
- if (keywordParams.has(item)) {
324
- style[item] = keywordParams.get(item);
325
- }
326
- });
320
+ const style = this.parseLabelStyle(keywordParams);
327
321
  positionParams = [...positionParams];
328
322
  positionParams.push(style);
329
323
  const labelId = positionParams[0];
@@ -336,10 +330,33 @@ export class SymbolPlaceholder extends SymbolGraphic {
336
330
  drawing.addLabelId(...tmpPositionParams);
337
331
  break;
338
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
+ }
339
346
  }
340
347
  });
341
348
  drawing.log("=== end generate drawing ===");
342
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
+ }
343
360
  drawPinParams(drawing, commandName, keywordParams, positionParams) {
344
361
  drawing.log('add pin', ...positionParams);
345
362
  const keywordDisplayPinId = 'display_pin_id';
@@ -449,6 +466,7 @@ export var PlaceHolderCommands;
449
466
  PlaceHolderCommands["lineWidth"] = "lineWidth";
450
467
  PlaceHolderCommands["fill"] = "fill";
451
468
  PlaceHolderCommands["lineColor"] = "lineColor";
469
+ PlaceHolderCommands["text"] = "text";
452
470
  })(PlaceHolderCommands || (PlaceHolderCommands = {}));
453
471
  export class SymbolCustom extends SymbolGraphic {
454
472
  pinDefinition = [];
@@ -636,6 +654,10 @@ export class SymbolDrawing {
636
654
  this.items.push(Geometry.label(id, x, y, textValue, style));
637
655
  return this;
638
656
  }
657
+ addTextbox(x, y, textValue, style) {
658
+ this.items.push(Geometry.textbox(null, x, y, textValue, style));
659
+ return this;
660
+ }
639
661
  addPath(...pathParts) {
640
662
  const parts = pathParts.reduce((accum, tmp) => {
641
663
  if (typeof tmp === "string") {
@@ -710,7 +732,7 @@ export class SymbolDrawing {
710
732
  let currentLineColor = '#333';
711
733
  const pathItems = [];
712
734
  this.items.forEach(item => {
713
- if (!(item instanceof Label)) {
735
+ if (!(item instanceof Textbox)) {
714
736
  if (item instanceof GeometryProp) {
715
737
  if (item.name === 'lineWidth') {
716
738
  currentLineWidth = item.value;
@@ -725,7 +747,7 @@ export class SymbolDrawing {
725
747
  else {
726
748
  let tmpResult = Geometry.groupFlip([item], this.flipX, this.flipY);
727
749
  tmpResult = Geometry.groupRotate(tmpResult, this.angle, this.mainOrigin);
728
- const { path, isClosedPolygon } = this.featuresToPath(tmpResult);
750
+ const { path, isClosedPolygon } = this.featuresToPath(tmpResult, this.flipX, this.flipY);
729
751
  pathItems.push({
730
752
  path: path,
731
753
  lineWidth: currentLineWidth,
@@ -741,14 +763,14 @@ export class SymbolDrawing {
741
763
  let features = this.pins.map(item => item[1]);
742
764
  features = Geometry.groupFlip(features, this.flipX, this.flipY);
743
765
  features = Geometry.groupRotate(features, this.angle, this.mainOrigin);
744
- const { path } = this.featuresToPath(features);
766
+ const { path } = this.featuresToPath(features, this.flipX, this.flipY);
745
767
  return path;
746
768
  }
747
769
  getLabels() {
748
- return this.items.filter(item => item instanceof Label);
770
+ return this.items.filter(item => item instanceof Textbox);
749
771
  }
750
- featuresToPath(items) {
751
- return Geometry.featuresToPath(items);
772
+ featuresToPath(items, flipX, flipY) {
773
+ return Geometry.featuresToPath(items, flipX, flipY);
752
774
  }
753
775
  getBoundingBox(excludeLabels = false) {
754
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;
@@ -149,6 +152,15 @@ export class ExecutionContext {
149
152
  component.widthProp = props.width ?? null;
150
153
  component.typeProp = props.type ?? null;
151
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;
152
164
  const paramsMap = new Map();
153
165
  params.forEach((param) => {
154
166
  component.parameters.set(param.paramName, param.paramValue);
@@ -205,6 +217,7 @@ export class ExecutionContext {
205
217
  addComponentExisting(component, pin) {
206
218
  const startPin = pin;
207
219
  const nextPin = component.getNextPinAfter(startPin);
220
+ this.applyComponentAngleFromWire(component, pin);
208
221
  this.toComponent(component, startPin, { addSequence: true });
209
222
  this.log('move to next pin: ' + nextPin);
210
223
  this.atComponent(component, nextPin, {
@@ -240,6 +253,7 @@ export class ExecutionContext {
240
253
  .toString());
241
254
  }
242
255
  const linkedNet = this.linkComponentPinNet(this.scope.currentComponent, this.scope.currentPin, component, pinId);
256
+ this.applyComponentAngleFromWire(component, pinId);
243
257
  this.scope.currentComponent = component;
244
258
  this.scope.currentPin = pinId;
245
259
  this.scope.clearActive();
@@ -688,6 +702,59 @@ export class ExecutionContext {
688
702
  this.scope.currentComponent.styles[key] = styles[key];
689
703
  }
690
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
+ }
691
758
  enterFrame() {
692
759
  const frameId = this.scope.frames.length + 1;
693
760
  const frameObject = new Frame(frameId);
@@ -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);
@@ -215,6 +215,7 @@ export function renderScript(scriptData, outputPath, options) {
215
215
  showStats && console.log('Render took:', generateSvgTimer.lap());
216
216
  if (outputPath) {
217
217
  writeFileSync(outputPath, svgOutput);
218
+ console.log('Generated file', outputPath);
218
219
  }
219
220
  }
220
221
  catch (err) {
@@ -7,6 +7,7 @@ import { Geometry } from './geometry.mjs';
7
7
  import { Logger } from './logger.mjs';
8
8
  import { Frame, FrameParamKeys, FramePlotDirection } from './objects/Frame.mjs';
9
9
  import { getBoundsSize, printBounds, resizeBounds, resizeToNearestGrid, toNearestGrid } from './utils.mjs';
10
+ import { Direction } from './objects/types.mjs';
10
11
  export class LayoutEngine {
11
12
  logger;
12
13
  placeSubgraphVersion = 2;
@@ -935,16 +936,16 @@ function applyComponentParamsToSymbol(typeProp, component, symbol) {
935
936
  function calculateSymbolAngle(symbol, pin, direction) {
936
937
  let directionVector = 0;
937
938
  switch (direction) {
938
- case 'right':
939
+ case Direction.Right:
939
940
  directionVector = 0;
940
941
  break;
941
- case 'down':
942
+ case Direction.Down:
942
943
  directionVector = 90;
943
944
  break;
944
- case 'left':
945
+ case Direction.Left:
945
946
  directionVector = 180;
946
947
  break;
947
- case 'up':
948
+ case Direction.Up:
948
949
  directionVector = 270;
949
950
  break;
950
951
  }
@@ -1012,16 +1013,16 @@ export class RenderWire extends RenderObject {
1012
1013
  this.segments.forEach(segment => {
1013
1014
  const { direction, value } = segment;
1014
1015
  let didAddPoint = false;
1015
- if (direction === 'down') {
1016
+ if (direction === Direction.Down) {
1016
1017
  tmpY += value;
1017
1018
  }
1018
- else if (direction === 'up') {
1019
+ else if (direction === Direction.Up) {
1019
1020
  tmpY -= value;
1020
1021
  }
1021
- else if (direction === 'left') {
1022
+ else if (direction === Direction.Left) {
1022
1023
  tmpX -= value;
1023
1024
  }
1024
- else if (direction === 'right') {
1025
+ else if (direction === Direction.Right) {
1025
1026
  tmpX += value;
1026
1027
  }
1027
1028
  else if (direction === 'auto' || direction === "auto_") {
@@ -1090,16 +1091,16 @@ export class RenderWire extends RenderObject {
1090
1091
  let tmpY = this.y;
1091
1092
  excludeLastSegment.forEach(segment => {
1092
1093
  const { direction, value } = segment;
1093
- if (direction === 'down') {
1094
+ if (direction === Direction.Down) {
1094
1095
  tmpY += value;
1095
1096
  }
1096
- else if (direction === 'up') {
1097
+ else if (direction === Direction.Up) {
1097
1098
  tmpY -= value;
1098
1099
  }
1099
- else if (direction === 'left') {
1100
+ else if (direction === Direction.Left) {
1100
1101
  tmpX -= value;
1101
1102
  }
1102
- else if (direction === 'right') {
1103
+ else if (direction === Direction.Right) {
1103
1104
  tmpX += value;
1104
1105
  }
1105
1106
  });
@@ -1107,16 +1108,16 @@ export class RenderWire extends RenderObject {
1107
1108
  let valueXY = null;
1108
1109
  const lastSegment = this.segments[this.segments.length - 1];
1109
1110
  switch (lastSegment.direction) {
1110
- case 'left':
1111
+ case Direction.Left:
1111
1112
  useValue = tmpX - untilX;
1112
1113
  break;
1113
- case 'right':
1114
+ case Direction.Right:
1114
1115
  useValue = untilX - tmpX;
1115
1116
  break;
1116
- case 'up':
1117
+ case Direction.Up:
1117
1118
  useValue = untilY - tmpY;
1118
1119
  break;
1119
- case 'down':
1120
+ case Direction.Down:
1120
1121
  useValue = tmpY - untilY;
1121
1122
  break;
1122
1123
  case 'auto':
@@ -1224,6 +1225,24 @@ export class RenderJunction {
1224
1225
  this.y = y;
1225
1226
  }
1226
1227
  }
1228
+ export function CalculatePinPositions(component) {
1229
+ const pinPositionMapping = new Map();
1230
+ let tmpSymbol;
1231
+ if (component.displayProp !== null
1232
+ && component.displayProp instanceof SymbolDrawing) {
1233
+ tmpSymbol = new SymbolPlaceholder(component.displayProp);
1234
+ }
1235
+ else {
1236
+ const symbolPinDefinitions = generateLayoutPinDefinition(component);
1237
+ tmpSymbol = new SymbolCustom(symbolPinDefinitions);
1238
+ }
1239
+ tmpSymbol.refreshDrawing();
1240
+ const pins = component.pins;
1241
+ pins.forEach((value, key) => {
1242
+ pinPositionMapping.set(key, tmpSymbol.pinPosition(key));
1243
+ });
1244
+ return pinPositionMapping;
1245
+ }
1227
1246
  function isPointOverlap(x, y, other) {
1228
1247
  return (x >= other.x && y >= other.y && x <= (other.x + other.width) && y <= (other.y + other.height));
1229
1248
  }
@@ -18,6 +18,10 @@ export class ClassComponent {
18
18
  widthProp = null;
19
19
  typeProp = null;
20
20
  copyProp = false;
21
+ angleProp = 0;
22
+ followWireOrientationProp = true;
23
+ wireOrientationAngle = 0;
24
+ useWireOrientationAngle = true;
21
25
  styles = {};
22
26
  assignedRefDes = null;
23
27
  constructor(instanceName, numPins, className) {
@@ -120,6 +124,9 @@ export class ClassComponent {
120
124
  component.arrangeProps = this.arrangeProps;
121
125
  component.widthProp = this.widthProp;
122
126
  component.typeProp = this.typeProp;
127
+ component.angleProp = this.angleProp;
128
+ component.followWireOrientationProp = this.followWireOrientationProp;
129
+ component.useWireOrientationAngle = this.useWireOrientationAngle;
123
130
  if (this.displayProp) {
124
131
  if (typeof this.displayProp === "string") {
125
132
  component.displayProp = this.displayProp;
@@ -10,3 +10,10 @@ export var ParseSymbolType;
10
10
  ParseSymbolType["Function"] = "function";
11
11
  ParseSymbolType["Undefined"] = "undefined";
12
12
  })(ParseSymbolType || (ParseSymbolType = {}));
13
+ export var Direction;
14
+ (function (Direction) {
15
+ Direction["Left"] = "left";
16
+ Direction["Right"] = "right";
17
+ Direction["Down"] = "down";
18
+ Direction["Up"] = "up";
19
+ })(Direction || (Direction = {}));
@@ -4,7 +4,7 @@ import { PinDefinition, PinIdType } from './objects/PinDefinition.mjs';
4
4
  import { PinTypes } from './objects/PinTypes.mjs';
5
5
  import { UndeclaredReference } from './objects/types.mjs';
6
6
  import { BlockTypes, ComponentTypes, NoNetText, ReferenceTypes } from './globals.mjs';
7
- import { SymbolDrawingCommands } from './draw_symbols.mjs';
7
+ import { PlaceHolderCommands, SymbolDrawingCommands } from './draw_symbols.mjs';
8
8
  import { BaseVisitor } from './BaseVisitor.mjs';
9
9
  export class ParserVisitor extends BaseVisitor {
10
10
  visitKeyword_assignment_expr = (ctx) => {
@@ -30,7 +30,7 @@ export class ParserVisitor extends BaseVisitor {
30
30
  const ctxDataWithAssignmentExpr = ctx.data_expr_with_assignment();
31
31
  this.visit(ctxDataWithAssignmentExpr);
32
32
  const [component, pinValue] = this.getResult(ctxDataWithAssignmentExpr);
33
- return this.getExecutor().addComponentExisting(component, pinValue);
33
+ this.getExecutor().addComponentExisting(component, pinValue);
34
34
  };
35
35
  visitAt_component_expr = (ctx) => {
36
36
  if (ctx.Point()) {
@@ -137,12 +137,13 @@ export class ParserVisitor extends BaseVisitor {
137
137
  properties.get('copy') : false;
138
138
  const width = properties.has('width') ?
139
139
  properties.get('width') : null;
140
+ const angle = properties.has('angle') ?
141
+ properties.get('angle') : null;
142
+ const followWireOrientation = properties.has('followWireOrientation') ?
143
+ properties.get('followWireOrientation') : true;
140
144
  const props = {
141
- arrange,
142
- display,
143
- type,
144
- width,
145
- copy
145
+ arrange, display, type, width, copy,
146
+ angle, followWireOrientation
146
147
  };
147
148
  this.setResult(ctx, this.getExecutor().createComponent(instanceName, pins, params, props));
148
149
  };
@@ -176,9 +177,20 @@ export class ParserVisitor extends BaseVisitor {
176
177
  else {
177
178
  throw "Invalid command!";
178
179
  }
179
- const ctxParameters = ctx.parameters();
180
- this.visit(ctxParameters);
181
- const parameters = this.getResult(ctxParameters);
180
+ let parameters = [];
181
+ const ctxNestedProperties = ctx.nested_properties_inner();
182
+ if (ctxNestedProperties) {
183
+ this.visit(ctxNestedProperties);
184
+ const nestedKeyValues = this.getResult(ctxNestedProperties);
185
+ nestedKeyValues.forEach((value, key) => {
186
+ parameters.push(['keyword', key, value]);
187
+ });
188
+ }
189
+ else {
190
+ const ctxParameters = ctx.parameters();
191
+ this.visit(ctxParameters);
192
+ parameters = this.getResult(ctxParameters);
193
+ }
182
194
  this.setResult(ctx, [commandName, parameters]);
183
195
  };
184
196
  visitProperty_expr = (ctx) => {
@@ -207,7 +219,7 @@ export class ParserVisitor extends BaseVisitor {
207
219
  }
208
220
  this.setResult(ctx, value);
209
221
  };
210
- visitNested_properties = (ctx) => {
222
+ visitNested_properties_inner = (ctx) => {
211
223
  const result = new Map();
212
224
  ctx.property_expr().forEach((item) => {
213
225
  this.visit(item);
@@ -218,6 +230,11 @@ export class ParserVisitor extends BaseVisitor {
218
230
  });
219
231
  this.setResult(ctx, result);
220
232
  };
233
+ visitNested_properties = (ctx) => {
234
+ const ctxNested = ctx.nested_properties_inner();
235
+ this.visit(ctxNested);
236
+ this.setResult(ctx, this.getResult(ctxNested));
237
+ };
221
238
  visitProperty_key_expr = (ctx) => {
222
239
  const ctxID = ctx.ID();
223
240
  const ctxIntegerValue = ctx.INTEGER_VALUE();
@@ -267,18 +284,40 @@ export class ParserVisitor extends BaseVisitor {
267
284
  else if (ctxID2) {
268
285
  result = ctxID2.getText();
269
286
  }
287
+ let shouldIgnoreWireOrientation = false;
270
288
  if (modifierText === 'flip') {
271
289
  const flipValue = result;
272
290
  if (flipValue.indexOf('x') !== -1) {
273
291
  component.setParam('flipX', 1);
292
+ shouldIgnoreWireOrientation = true;
274
293
  }
275
294
  if (flipValue.indexOf('y') !== -1) {
276
295
  component.setParam('flipY', 1);
296
+ shouldIgnoreWireOrientation = true;
277
297
  }
278
298
  }
279
299
  else if (modifierText === 'angle') {
280
300
  const angleValue = Number(result);
281
301
  component.setParam('angle', angleValue);
302
+ shouldIgnoreWireOrientation = true;
303
+ }
304
+ else if (modifierText === 'anchor') {
305
+ if (component.displayProp
306
+ && component.displayProp instanceof SymbolDrawingCommands) {
307
+ const commands = (component.displayProp)
308
+ .getCommands();
309
+ commands.forEach(command => {
310
+ const positionParams = command[1];
311
+ const keywordParams = command[2];
312
+ if (command[0] === PlaceHolderCommands.label
313
+ && positionParams[0] === 'value') {
314
+ keywordParams.set('anchor', result);
315
+ }
316
+ });
317
+ }
318
+ }
319
+ if (shouldIgnoreWireOrientation) {
320
+ component.useWireOrientationAngle = false;
282
321
  }
283
322
  });
284
323
  }