kisch 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  kisch — schema‑transformation tool for KiCad schematics
6
6
 
7
+ [Watch video tutorial](https://www.youtube.com/watch?v=VylXziX1D8U)
8
+
7
9
  ## SYNOPSIS
8
10
 
9
11
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kisch",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -10,7 +10,7 @@
10
10
  "test": "jasmine"
11
11
  },
12
12
  "bin": {
13
- "kisch": "src/kisch-cli.js"
13
+ "kisch": "src/app/kisch-cli.js"
14
14
  },
15
15
  "keywords": [],
16
16
  "author": "",
@@ -22,5 +22,8 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "commander": "^14.0.3"
25
+ },
26
+ "exports": {
27
+ ".": "./src/exports.js"
25
28
  }
26
29
  }
@@ -0,0 +1 @@
1
+ export {default as SymbolLibrary} from "./SymbolLibrary.js";
@@ -1,16 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import {program} from "commander";
4
- import Schematic, {loadSchematic, createSchematic} from "./Schematic.js";
4
+ import Schematic, {loadSchematic, createSchematic} from "../schematic/Schematic.js";
5
5
  import path from "node:path";
6
- import pkg from "../package.json" with { type: "json" };
7
- import {DeclaredError} from "./js-util.js";
6
+ import pkg from "../../package.json" with { type: "json" };
7
+ import {DeclaredError} from "../utils/js-util.js";
8
8
  import fs, {promises as fsp} from "fs";
9
-
10
- /*todo...
11
-
12
- defines
13
- wires*/
9
+ import {compoundSymbol} from "../schematic/CompoundSymbol.js";
14
10
 
15
11
  let HELP_TEXT=`
16
12
  Examples:
@@ -45,6 +41,8 @@ program
45
41
  .option("-s, --script <script.js>", "Input script to apply to schematic.")
46
42
  .option("-e, --emit <script.js>", "Emit script based on schematic.")
47
43
  .option("-q, --quiet", "No output, except for errors.")
44
+ .option("-a, --append", "Append mode: don't remove undeclared.")
45
+ .option("--bounding-boxes", "Remove wires and draw bounding boxes instead (only for debug!).")
48
46
  //.option("-v, --verbose", "Print detailed execution info")
49
47
  .option(
50
48
  "-D, --define <key=value>",
@@ -111,11 +109,29 @@ try {
111
109
  ([e.slice(0,e.indexOf("=")),e.slice(e.indexOf("=")+1)])
112
110
  ));
113
111
 
112
+ global.compoundSymbol=compoundSymbol;
114
113
  let mod=await import(path.resolve(options.script));
115
114
  if (typeof mod.default=="function")
116
115
  await mod.default(schematic,defines);
117
116
 
118
- schematic.removeUndeclared();
117
+ if (!options.append)
118
+ schematic.removeUndeclared();
119
+ }
120
+
121
+ if (options.boundingBoxes) {
122
+ cons.info("Drawing bounding boxes...");
123
+ for (let e of schematic.getEntities({type: "wire"})) {
124
+ schematic.removeEntity(e);
125
+ }
126
+
127
+ for (let s of schematic.getEntities({type: "symbol"})) {
128
+ schematic.drawWireRect(s.getBoundingRect());
129
+ for (let p of s.getPins()) {
130
+ schematic.drawWirePoint(p.getPoint());
131
+ schematic.drawWirePoint(p.getLegPoint());
132
+ schematic.drawWireLine(p.getPoint(),p.getLegPoint());
133
+ }
134
+ }
119
135
  }
120
136
 
121
137
  if (options.emit) {
@@ -0,0 +1,35 @@
1
+ export default class CompoundSymbol {
2
+ constructor(...symbols) {
3
+ this.symbols=symbols;
4
+ this.pins=[];
5
+
6
+ for (let symbol of symbols) {
7
+ for (let pin of symbol.pins) {
8
+ //console.log(pin.getNum());
9
+
10
+ this.pins.push(pin);
11
+ }
12
+ }
13
+ }
14
+
15
+ pin(num) {
16
+ if (!num)
17
+ throw new Error("Pins start at 1");
18
+
19
+ return this.pins[num-1];
20
+ }
21
+
22
+ namePins(names) {
23
+ if (names.length!=this.pins.length)
24
+ throw new Error("pin count mismatch");
25
+
26
+ for (let i=0; i<this.pins.length; i++)
27
+ this[names[i]]=this.pins[i];
28
+
29
+ return this;
30
+ }
31
+ }
32
+
33
+ export function compoundSymbol(...args) {
34
+ return new CompoundSymbol(...args);
35
+ }
@@ -1,31 +1,50 @@
1
- import {Point} from "./cartesian-math.js";
2
- import {sym, symName, sexpCallName} from "./sexp.js";
3
- import {Rect} from "./cartesian-math.js";
1
+ import {Point, Rect} from "../utils/cartesian-math.js";
2
+ import {sym, symName, sexpCallName} from "../utils/sexp.js";
4
3
 
5
4
  class EntityPin {
6
5
  constructor(sexpr, entity) {
7
6
  this.sexpr=sexpr;
8
7
  this.entity=entity;
9
-
10
- //console.log(this.sexpr);
11
8
  }
12
9
 
13
10
  getNum() {
14
11
  return this.sexpr[1];
15
12
  }
16
13
 
17
- getPoint() {
14
+ initPoint() {
18
15
  let librarySymbol=this.entity.getLibrarySymbol();
19
16
  let librarySymbolPin=librarySymbol.getPin(Number(this.getNum()));
20
17
  let pinAt=Point.from(librarySymbolPin.at);
18
+ let symbolAt=this.entity.getAt();
19
+ let symbolRot=this.entity.getRotation();
21
20
  pinAt[1]=-pinAt[1];
22
21
 
23
- return Point.from(this.entity.getAt()).add(pinAt);
22
+ //this.point=Point.from(symbolAt).add(pinAt.rotateDegrees(-symbolAt[2]));
23
+ //this.point=Point.from(symbolAt).add(pinAt.rotateDegrees(-this.entity.getRotation()));
24
+ this.point=Point.from(symbolAt).add(pinAt.rotateDegrees(-symbolRot));
25
+
26
+ let leg=new Point(librarySymbolPin.length,0);
27
+ leg=leg.rotateDegrees(-(librarySymbolPin.rotation+symbolRot));
28
+ this.legPoint=this.point.add(leg);
29
+ }
30
+
31
+ getLegPoint() {
32
+ if (!this.legPoint)
33
+ this.initPoint();
34
+
35
+ return this.legPoint;
36
+ }
37
+
38
+ getPoint() {
39
+ if (!this.point)
40
+ this.initPoint();
41
+
42
+ return this.point;
24
43
  }
25
44
 
26
45
  isConnected(p) {
27
46
  if (typeof p=="string") {
28
- for (let e of this.entity.schematic.getLabelEntities(p)) {
47
+ for (let e of this.entity.schematic.getEntities({label: p})) {
29
48
  let p=e.getConnectionPoints()[0];
30
49
  if (this.entity.schematic.arePointsConnected(this.getPoint(),p))
31
50
  return true;
@@ -40,9 +59,12 @@ class EntityPin {
40
59
  }
41
60
 
42
61
  connect(p) {
62
+ if (!p)
63
+ return;
64
+
43
65
  if (this.isConnected(p)) {
44
66
  if (typeof p=="string") {
45
- for (let e of this.entity.schematic.getLabelEntities(p)) {
67
+ for (let e of this.entity.schematic.getEntities({label: p})) {
46
68
  let p=e.getConnectionPoints()[0];
47
69
  if (this.entity.schematic.arePointsConnected(this.getPoint(),p)) {
48
70
  e.declared=true;
@@ -64,11 +86,16 @@ class EntityPin {
64
86
  }
65
87
 
66
88
  else {
89
+ //console.log("connecting wire",this.toString(),"->",p.toString());
67
90
  this.entity.schematic.addConnectionWire(this.getPoint(),p.getPoint());
68
91
  this.entity.schematic.markConnectionDeclared(this.getPoint(),p.getPoint());
69
92
  }
70
93
  }
71
94
 
95
+ toString() {
96
+ return this.entity.getReference()+":"+this.getNum();
97
+ }
98
+
72
99
  getConnections() {
73
100
  let connections=[];
74
101
 
@@ -77,7 +104,7 @@ class EntityPin {
77
104
  connections.push(net);
78
105
  }
79
106
 
80
- for (let e of this.entity.schematic.getSymbolEntities()) {
107
+ for (let e of this.entity.schematic.getEntities({type: "symbol"})) {
81
108
  if (e==this.entity)
82
109
  continue;
83
110
 
@@ -97,7 +124,7 @@ export default class Entity {
97
124
  this.pins=[];
98
125
 
99
126
  this.type=symName(this.sexpr[0]);
100
- if (!["symbol","wire","label"].includes(this.type))
127
+ if (!["symbol","wire","label","junction"].includes(this.type))
101
128
  throw new Error("Unknown entity: "+this.type);
102
129
 
103
130
  for (let a of this.sexpr)
@@ -105,6 +132,14 @@ export default class Entity {
105
132
  this.pins.push(new EntityPin(a,this));
106
133
  }
107
134
 
135
+ getRotation() {
136
+ return this.getAt()[2];
137
+ }
138
+
139
+ getPins() {
140
+ return this.pins;
141
+ }
142
+
108
143
  getSexp() {
109
144
  return this.sexpr;
110
145
  }
@@ -178,6 +213,36 @@ export default class Entity {
178
213
  el[2]=footprint;
179
214
  }
180
215
 
216
+ removeProp(name) {
217
+ if (this.getType()!="symbol")
218
+ throw new Error("Only symbols have props");
219
+
220
+ let index=this.sexpr.findIndex(a=>sexpCallName(a)=="property" && a[1]==name);
221
+ if (index<0)
222
+ return;
223
+
224
+ this.sexpr.splice(index,1);
225
+ }
226
+
227
+ setProp(name, value) {
228
+ if (this.getType()!="symbol")
229
+ throw new Error("Only symbols have props");
230
+
231
+ let el=this.sexpr.find(a=>sexpCallName(a)=="property" && a[1]==name);
232
+ if (!el) {
233
+ let exp=[sym("property"),name,"",
234
+ [sym("effects"),
235
+ [sym("hide"),sym("yes")]
236
+ ]
237
+ ];
238
+
239
+ this.sexpr.push(exp);
240
+ el=exp;
241
+ }
242
+
243
+ el[2]=value;
244
+ }
245
+
181
246
  getLibId() {
182
247
  return this.sexpr.find(x=>sexpCallName(x)=="lib_id")[1];
183
248
  }
@@ -196,12 +261,18 @@ export default class Entity {
196
261
  }
197
262
 
198
263
  getBoundingRect() {
264
+ if (!this.librarySymbol)
265
+ throw new Error("Can't get bounding rect, no library symbol");
266
+
199
267
  //console.log(this.librarySymbol);
200
268
  let r=this.librarySymbol.getBoundingRect();
269
+ let corner=r.corner.rotateDegrees(-this.getRotation());
270
+ let size=r.size.rotateDegrees(-this.getRotation());
271
+
201
272
  //console.log(r);
202
273
  let p=Point.from(this.getAt());
203
274
 
204
- return new Rect(p.add(r.corner),r.size);
275
+ return new Rect(p.add(corner),size);
205
276
  }
206
277
 
207
278
  getLibrarySymbol() {
@@ -219,12 +290,6 @@ export default class Entity {
219
290
 
220
291
  getType() {
221
292
  return this.type;
222
- /*let t=symName(this.sexpr[0]);
223
-
224
- if (!["symbol","wire","label"].includes(t))
225
- throw new Error("Unknown entity: "+t);
226
-
227
- return t;*/
228
293
  }
229
294
 
230
295
  getConnectionPoints() {
@@ -249,4 +314,43 @@ export default class Entity {
249
314
  throw new Error("Unknown entity type: "+this.getType());
250
315
  }
251
316
  }
317
+
318
+ connect(...pins) {
319
+ if (this.getType()!="symbol")
320
+ throw new Error("can only connect sybols");
321
+
322
+ if (pins.length!=this.pins.length)
323
+ throw new Error("pin count mismatch");
324
+
325
+ for (let i=0; i<this.pins.length; i++)
326
+ this.pins[i].connect(pins[i]);
327
+ }
328
+
329
+ containsPoint(p) {
330
+ function isNumberInRangeInclusive(num, a, b) {
331
+ return ((num >= Math.min(a, b)) && (num <= Math.max(a, b)));
332
+ }
333
+
334
+ if (this.getType()!="wire")
335
+ throw new Error("Only a wire can contain points");
336
+
337
+ p=Point.from(p);
338
+ let cp=this.getConnectionPoints();
339
+ if (cp[0][0]==cp[1][0]) { // vertical
340
+ if (p[0]!=cp[0][0])
341
+ return false;
342
+
343
+ return isNumberInRangeInclusive(p[1],cp[0][1],cp[1][1]);
344
+ }
345
+
346
+ else if (cp[0][1]==cp[1][1]) { // horizontal
347
+ if (p[1]!=cp[0][1])
348
+ return false;
349
+
350
+ return isNumberInRangeInclusive(p[0],cp[0][0],cp[1][0]);
351
+ }
352
+
353
+ else
354
+ throw new Error("wire is not horizontal or vertical");
355
+ }
252
356
  }
@@ -1,5 +1,5 @@
1
- import {sym, isSym, symEq, symName, sexpStringify, sexpCallName} from "./sexp.js";
2
- import {Rect} from "./cartesian-math.js";
1
+ import {sym, isSym, symEq, symName, sexpStringify, sexpCallName} from "../utils/sexp.js";
2
+ import {Rect} from "../utils/cartesian-math.js";
3
3
 
4
4
  export default class LibrarySymbol {
5
5
  constructor(sexpr, qualifiedName) {
@@ -61,6 +61,13 @@ export default class LibrarySymbol {
61
61
  if (sexpCallName(x)=="rectangle") {
62
62
  let start=x.find(x=>sexpCallName(x)=="start").slice(1);
63
63
  let end=x.find(x=>sexpCallName(x)=="end").slice(1);
64
+
65
+ start[1]=-start[1];
66
+ end[1]=-end[1];
67
+
68
+ /*console.log(start);
69
+ console.log(end);*/
70
+
64
71
  let r=Rect.fromCorners(start,end);
65
72
 
66
73
  if (!rect)
@@ -68,6 +75,32 @@ export default class LibrarySymbol {
68
75
 
69
76
  rect=rect.union(r);
70
77
  }
78
+
79
+ if (sexpCallName(x)=="polyline") {
80
+ //console.log("poly line...");
81
+ for (let sub of x) {
82
+ if (sexpCallName(sub)=="pts") {
83
+ for (let p of sub) {
84
+ if (sexpCallName(p)=="xy") {
85
+ let point=[p[1],p[2]];
86
+
87
+ if (!rect)
88
+ rect=new Rect(point,[0,0]);
89
+
90
+ //console.log("rect",rect);
91
+ //console.log("point",point);
92
+
93
+ rect=rect.includePoint(point);
94
+ }
95
+ }
96
+ }
97
+ }
98
+ }
99
+ }
100
+
101
+ if (!rect) {
102
+ //console.log(symbolExp);
103
+ throw new Error("Symbol doesn't have any rect!");
71
104
  }
72
105
 
73
106
  //console.log(sexpStringify(symbolExp));
@@ -93,7 +126,9 @@ export class LibrarySymbolPin {
93
126
 
94
127
  switch (symName(e[0])) {
95
128
  case "at":
129
+ //console.log(e);
96
130
  this.at = e.slice(1).map(Number); // [x, y, rotation]
131
+ this.rotation=Number(e[3]);
97
132
  break;
98
133
  case "length":
99
134
  this.length = Number(e[1]);