kisch 1.0.3 → 1.0.5

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.5",
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,36 @@
1
+ export default class CompoundSymbol {
2
+ constructor(...symbols) {
3
+ this.symbols=symbols;
4
+ this.pins=[];
5
+
6
+ for (let symbol of symbols) {
7
+ let pinNums=symbol.getPinNums();
8
+ pinNums.sort();
9
+
10
+ for (let pinNum of pinNums) {
11
+ this.pins.push(symbol.pin(pinNum));
12
+ }
13
+ }
14
+ }
15
+
16
+ pin(num) {
17
+ if (!num)
18
+ throw new Error("Pins start at 1");
19
+
20
+ return this.pins[num-1];
21
+ }
22
+
23
+ namePins(names) {
24
+ if (names.length!=this.pins.length)
25
+ throw new Error("pin count mismatch");
26
+
27
+ for (let i=0; i<this.pins.length; i++)
28
+ this[names[i]]=this.pins[i];
29
+
30
+ return this;
31
+ }
32
+ }
33
+
34
+ export function compoundSymbol(...args) {
35
+ return new CompoundSymbol(...args);
36
+ }
@@ -1,31 +1,60 @@
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
8
 
10
- //console.log(this.sexpr);
9
+ //console.log("ctor pin num: "+this.sexpr[1]);
11
10
  }
12
11
 
13
12
  getNum() {
13
+ //console.log("pin num: "+this.sexpr[1]);
14
+
14
15
  return this.sexpr[1];
15
16
  }
16
17
 
17
- getPoint() {
18
+ initPoint() {
18
19
  let librarySymbol=this.entity.getLibrarySymbol();
19
- let librarySymbolPin=librarySymbol.getPin(Number(this.getNum()));
20
+ let librarySymbolPin;
21
+ if (isNaN(this.getNum()))
22
+ librarySymbolPin=librarySymbol.getPin(this.getNum());
23
+
24
+ else
25
+ librarySymbolPin=librarySymbol.getPin(Number(this.getNum()));
26
+
20
27
  let pinAt=Point.from(librarySymbolPin.at);
28
+ let symbolAt=this.entity.getAt();
29
+ let symbolRot=this.entity.getRotation();
21
30
  pinAt[1]=-pinAt[1];
22
31
 
23
- return Point.from(this.entity.getAt()).add(pinAt);
32
+ //this.point=Point.from(symbolAt).add(pinAt.rotateDegrees(-symbolAt[2]));
33
+ //this.point=Point.from(symbolAt).add(pinAt.rotateDegrees(-this.entity.getRotation()));
34
+ this.point=Point.from(symbolAt).add(pinAt.rotateDegrees(-symbolRot));
35
+
36
+ let leg=new Point(librarySymbolPin.length,0);
37
+ leg=leg.rotateDegrees(-(librarySymbolPin.rotation+symbolRot));
38
+ this.legPoint=this.point.add(leg);
39
+ }
40
+
41
+ getLegPoint() {
42
+ if (!this.legPoint)
43
+ this.initPoint();
44
+
45
+ return this.legPoint;
46
+ }
47
+
48
+ getPoint() {
49
+ if (!this.point)
50
+ this.initPoint();
51
+
52
+ return this.point;
24
53
  }
25
54
 
26
55
  isConnected(p) {
27
56
  if (typeof p=="string") {
28
- for (let e of this.entity.schematic.getLabelEntities(p)) {
57
+ for (let e of this.entity.schematic.getEntities({label: p})) {
29
58
  let p=e.getConnectionPoints()[0];
30
59
  if (this.entity.schematic.arePointsConnected(this.getPoint(),p))
31
60
  return true;
@@ -40,9 +69,12 @@ class EntityPin {
40
69
  }
41
70
 
42
71
  connect(p) {
72
+ if (!p)
73
+ return;
74
+
43
75
  if (this.isConnected(p)) {
44
76
  if (typeof p=="string") {
45
- for (let e of this.entity.schematic.getLabelEntities(p)) {
77
+ for (let e of this.entity.schematic.getEntities({label: p})) {
46
78
  let p=e.getConnectionPoints()[0];
47
79
  if (this.entity.schematic.arePointsConnected(this.getPoint(),p)) {
48
80
  e.declared=true;
@@ -64,11 +96,16 @@ class EntityPin {
64
96
  }
65
97
 
66
98
  else {
99
+ //console.log("connecting wire",this.toString(),"->",p.toString());
67
100
  this.entity.schematic.addConnectionWire(this.getPoint(),p.getPoint());
68
101
  this.entity.schematic.markConnectionDeclared(this.getPoint(),p.getPoint());
69
102
  }
70
103
  }
71
104
 
105
+ toString() {
106
+ return this.entity.getReference()+":"+this.getNum();
107
+ }
108
+
72
109
  getConnections() {
73
110
  let connections=[];
74
111
 
@@ -77,7 +114,7 @@ class EntityPin {
77
114
  connections.push(net);
78
115
  }
79
116
 
80
- for (let e of this.entity.schematic.getSymbolEntities()) {
117
+ for (let e of this.entity.schematic.getEntities({type: "symbol"})) {
81
118
  if (e==this.entity)
82
119
  continue;
83
120
 
@@ -97,7 +134,7 @@ export default class Entity {
97
134
  this.pins=[];
98
135
 
99
136
  this.type=symName(this.sexpr[0]);
100
- if (!["symbol","wire","label"].includes(this.type))
137
+ if (!["symbol","wire","label","junction"].includes(this.type))
101
138
  throw new Error("Unknown entity: "+this.type);
102
139
 
103
140
  for (let a of this.sexpr)
@@ -105,6 +142,14 @@ export default class Entity {
105
142
  this.pins.push(new EntityPin(a,this));
106
143
  }
107
144
 
145
+ getRotation() {
146
+ return this.getAt()[2];
147
+ }
148
+
149
+ getPins() {
150
+ return this.pins;
151
+ }
152
+
108
153
  getSexp() {
109
154
  return this.sexpr;
110
155
  }
@@ -178,6 +223,36 @@ export default class Entity {
178
223
  el[2]=footprint;
179
224
  }
180
225
 
226
+ removeProp(name) {
227
+ if (this.getType()!="symbol")
228
+ throw new Error("Only symbols have props");
229
+
230
+ let index=this.sexpr.findIndex(a=>sexpCallName(a)=="property" && a[1]==name);
231
+ if (index<0)
232
+ return;
233
+
234
+ this.sexpr.splice(index,1);
235
+ }
236
+
237
+ setProp(name, value) {
238
+ if (this.getType()!="symbol")
239
+ throw new Error("Only symbols have props");
240
+
241
+ let el=this.sexpr.find(a=>sexpCallName(a)=="property" && a[1]==name);
242
+ if (!el) {
243
+ let exp=[sym("property"),name,"",
244
+ [sym("effects"),
245
+ [sym("hide"),sym("yes")]
246
+ ]
247
+ ];
248
+
249
+ this.sexpr.push(exp);
250
+ el=exp;
251
+ }
252
+
253
+ el[2]=value;
254
+ }
255
+
181
256
  getLibId() {
182
257
  return this.sexpr.find(x=>sexpCallName(x)=="lib_id")[1];
183
258
  }
@@ -196,12 +271,18 @@ export default class Entity {
196
271
  }
197
272
 
198
273
  getBoundingRect() {
274
+ if (!this.librarySymbol)
275
+ throw new Error("Can't get bounding rect, no library symbol");
276
+
199
277
  //console.log(this.librarySymbol);
200
278
  let r=this.librarySymbol.getBoundingRect();
279
+ let corner=r.corner.rotateDegrees(-this.getRotation());
280
+ let size=r.size.rotateDegrees(-this.getRotation());
281
+
201
282
  //console.log(r);
202
283
  let p=Point.from(this.getAt());
203
284
 
204
- return new Rect(p.add(r.corner),r.size);
285
+ return new Rect(p.add(corner),size);
205
286
  }
206
287
 
207
288
  getLibrarySymbol() {
@@ -212,19 +293,26 @@ export default class Entity {
212
293
  if (!num)
213
294
  throw new Error("Pin numbers start at 1");
214
295
 
215
- for (let p of this.pins)
296
+ for (let p of this.pins) {
297
+ //console.log("pin num: "+p.getNum());
298
+
216
299
  if (p.getNum()==num)
217
300
  return p;
301
+ }
302
+
303
+ throw new Error("Can't find pin: "+num);
218
304
  }
219
305
 
220
306
  getType() {
221
307
  return this.type;
222
- /*let t=symName(this.sexpr[0]);
308
+ }
223
309
 
224
- if (!["symbol","wire","label"].includes(t))
225
- throw new Error("Unknown entity: "+t);
310
+ getPinNums() {
311
+ let nums=[];
312
+ for (let p of this.pins)
313
+ nums.push(p.getNum())
226
314
 
227
- return t;*/
315
+ return nums;
228
316
  }
229
317
 
230
318
  getConnectionPoints() {
@@ -239,8 +327,11 @@ export default class Entity {
239
327
 
240
328
  case "symbol":
241
329
  let p=[];
242
- for (let i=1; i<=this.pins.length; i++)
243
- p.push(this.pin(i).getPoint());
330
+ for (let num of this.getPinNums())
331
+ p.push(this.pin(num).getPoint());
332
+
333
+ /*for (let i=1; i<=this.pins.length; i++)
334
+ p.push(this.pin(i).getPoint());*/
244
335
 
245
336
  return p;
246
337
  break;
@@ -249,4 +340,43 @@ export default class Entity {
249
340
  throw new Error("Unknown entity type: "+this.getType());
250
341
  }
251
342
  }
343
+
344
+ connect(...pins) {
345
+ if (this.getType()!="symbol")
346
+ throw new Error("can only connect symbols");
347
+
348
+ if (pins.length!=this.pins.length)
349
+ throw new Error("pin count mismatch");
350
+
351
+ for (let i=0; i<this.pins.length; i++)
352
+ this.pins[i].connect(pins[i]);
353
+ }
354
+
355
+ containsPoint(p) {
356
+ function isNumberInRangeInclusive(num, a, b) {
357
+ return ((num >= Math.min(a, b)) && (num <= Math.max(a, b)));
358
+ }
359
+
360
+ if (this.getType()!="wire")
361
+ throw new Error("Only a wire can contain points");
362
+
363
+ p=Point.from(p);
364
+ let cp=this.getConnectionPoints();
365
+ if (cp[0][0]==cp[1][0]) { // vertical
366
+ if (p[0]!=cp[0][0])
367
+ return false;
368
+
369
+ return isNumberInRangeInclusive(p[1],cp[0][1],cp[1][1]);
370
+ }
371
+
372
+ else if (cp[0][1]==cp[1][1]) { // horizontal
373
+ if (p[1]!=cp[0][1])
374
+ return false;
375
+
376
+ return isNumberInRangeInclusive(p[0],cp[0][0],cp[1][0]);
377
+ }
378
+
379
+ else
380
+ throw new Error("wire is not horizontal or vertical");
381
+ }
252
382
  }
@@ -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]);