kisch 1.0.2 → 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/DSL.md +209 -0
- package/README.md +4 -0
- package/package.json +5 -2
- package/src/app/exports.js +1 -0
- package/src/{kisch-cli.js → app/kisch-cli.js} +25 -9
- package/src/schematic/CompoundSymbol.js +35 -0
- package/src/{Entity.js → schematic/Entity.js} +122 -18
- package/src/{LibrarySymbol.js → schematic/LibrarySymbol.js} +37 -2
- package/src/{Schematic.js → schematic/Schematic.js} +194 -78
- package/src/{SymbolLibrary.js → schematic/SymbolLibrary.js} +1 -1
- package/src/utils/RoutingGrid.js +260 -0
- package/src/utils/astar.js +113 -0
- package/src/{cartesian-math.js → utils/cartesian-math.js} +44 -3
- package/src/utils/grid-util.js +39 -0
- package/src/utils/js-util.js +33 -0
- package/src/{node-util.js → utils/node-util.js} +5 -0
- package/src/js-util.js +0 -15
- package/src/manhattan-router.js +0 -176
- /package/src/{place-rect.js → utils/place-rect.js} +0 -0
- /package/src/{sexp.js → utils/sexp.js} +0 -0
package/DSL.md
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# Kisch Schematic Script Reference
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
A Kisch schematic is defined as a JavaScript module exporting a default async function:
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
export default async function (sch, defines) {
|
|
9
|
+
// schematic definition here
|
|
10
|
+
}
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The function receives:
|
|
14
|
+
|
|
15
|
+
- `sch` — the schematic builder API
|
|
16
|
+
- `defines` — an object containing CLI-defined parameters
|
|
17
|
+
|
|
18
|
+
The script declares symbols (components) and connects their pins using wires or net labels.
|
|
19
|
+
|
|
20
|
+
# Core Concepts
|
|
21
|
+
|
|
22
|
+
## 1. Declaring a Component
|
|
23
|
+
|
|
24
|
+
Use:
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
sch.declare(reference, options)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Parameters
|
|
31
|
+
|
|
32
|
+
- `reference` (string)
|
|
33
|
+
The component reference (e.g., `"R1"`, `"J1"`).
|
|
34
|
+
|
|
35
|
+
- `options` (object)
|
|
36
|
+
- `symbol` (string, required) — KiCad symbol library identifier
|
|
37
|
+
- `footprint` (string, optional) — KiCad footprint library identifier
|
|
38
|
+
|
|
39
|
+
### Example
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
let J1 = sch.declare("J1", {
|
|
43
|
+
symbol: "Connector_Generic:Conn_01x02",
|
|
44
|
+
footprint: "Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Horizontal"
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`declare()` returns a symbol object.
|
|
49
|
+
|
|
50
|
+
## 2. Accessing an Existing Symbol
|
|
51
|
+
|
|
52
|
+
If you did not store the return value from `declare`, you can retrieve it later:
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
sch.sym("J1")
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Returns the symbol instance with reference `"J1"`.
|
|
59
|
+
|
|
60
|
+
## 3. Working with Pins
|
|
61
|
+
|
|
62
|
+
Access a pin using:
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
symbol.pin(pinNumber)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Example:
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
J1.pin(2)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Returns a pin object.
|
|
75
|
+
|
|
76
|
+
## 4. Connecting Pins
|
|
77
|
+
|
|
78
|
+
`connect()` must always be called on a pin object.
|
|
79
|
+
|
|
80
|
+
It accepts exactly **two argument types**:
|
|
81
|
+
|
|
82
|
+
- A string → connect to a named net (via net label)
|
|
83
|
+
- A pin object → connect directly to another pin (via wire)
|
|
84
|
+
|
|
85
|
+
No other argument types are supported.
|
|
86
|
+
|
|
87
|
+
### A) Connecting to a Net (String Argument)
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
J1.pin(2).connect("GND");
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Behavior:
|
|
94
|
+
|
|
95
|
+
- A net label with the given name is created next to the pin (if not already present at that location).
|
|
96
|
+
- The pin is connected to that net label.
|
|
97
|
+
- The connection is implemented as a 0-length wire between the pin and the label.
|
|
98
|
+
- Multiple pins using the same string connect electrically through that net name.
|
|
99
|
+
|
|
100
|
+
Important:
|
|
101
|
+
|
|
102
|
+
- This explicitly creates or uses a net label in the schematic.
|
|
103
|
+
- This does not implicitly merge abstract nets.
|
|
104
|
+
- If you want a named signal, use a string.
|
|
105
|
+
|
|
106
|
+
### B) Connecting to Another Pin (Pin Object Argument)
|
|
107
|
+
|
|
108
|
+
```js
|
|
109
|
+
J2.pin(1).connect(J1.pin(2));
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Behavior:
|
|
113
|
+
|
|
114
|
+
- A wire is created directly between the two pins.
|
|
115
|
+
- No net label is created automatically.
|
|
116
|
+
- This represents a physical wire in the schematic.
|
|
117
|
+
|
|
118
|
+
Important:
|
|
119
|
+
|
|
120
|
+
- Connecting pin-to-pin does NOT create a named net.
|
|
121
|
+
- If you want a named net, you must connect using a string.
|
|
122
|
+
|
|
123
|
+
## 5. Conditional Configuration via `defines`
|
|
124
|
+
|
|
125
|
+
Scripts can adapt based on CLI-defined values.
|
|
126
|
+
|
|
127
|
+
### Passing Defines from CLI
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
kisch schema.kicad_sch -s script.js -Dtest=123 --define test2=456
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Both forms are equivalent:
|
|
134
|
+
|
|
135
|
+
- `-Dkey=value`
|
|
136
|
+
- `--define key=value`
|
|
137
|
+
|
|
138
|
+
Inside the script:
|
|
139
|
+
|
|
140
|
+
```js
|
|
141
|
+
defines.test // "123"
|
|
142
|
+
defines.test2 // "456"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
All define values are strings.
|
|
146
|
+
|
|
147
|
+
### Example Usage
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
if (defines.test === "123") {
|
|
151
|
+
sch.declare("J6", {
|
|
152
|
+
symbol: "Connector_Generic:Conn_01x04"
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
This enables variant-based schematic generation.
|
|
158
|
+
|
|
159
|
+
# Minimal Complete Example
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
export default async function (sch, defines) {
|
|
163
|
+
|
|
164
|
+
let J1 = sch.declare("J1", {
|
|
165
|
+
symbol: "Connector_Generic:Conn_01x02",
|
|
166
|
+
footprint: "Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Horizontal"
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
J1.pin(2).connect("GND");
|
|
170
|
+
|
|
171
|
+
let J5 = sch.declare("J5", {
|
|
172
|
+
symbol: "Connector_Generic:Conn_01x04"
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
J5.pin(1).connect(J1.pin(1));
|
|
176
|
+
|
|
177
|
+
if (defines.test === "123") {
|
|
178
|
+
sch.declare("J6", {
|
|
179
|
+
symbol: "Connector_Generic:Conn_01x04"
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
# Design Constraints (Important for AI Generation)
|
|
186
|
+
|
|
187
|
+
When generating Kisch scripts:
|
|
188
|
+
|
|
189
|
+
- Always export a default async function.
|
|
190
|
+
- Always use `sch.declare()` to create components.
|
|
191
|
+
- Always access pins using `.pin(n)`.
|
|
192
|
+
- Only use `.connect()` with:
|
|
193
|
+
- a string (net name), or
|
|
194
|
+
- a pin object.
|
|
195
|
+
- Do not assume additional API methods unless explicitly documented.
|
|
196
|
+
- All `defines` values are strings.
|
|
197
|
+
- Connecting to a string creates a net label.
|
|
198
|
+
- Connecting to a pin creates a direct wire.
|
|
199
|
+
|
|
200
|
+
# Philosophy
|
|
201
|
+
|
|
202
|
+
Kisch treats schematics as deterministic, programmable structures.
|
|
203
|
+
|
|
204
|
+
- Wires are explicit.
|
|
205
|
+
- Net labels are explicit.
|
|
206
|
+
- There is no hidden net merging.
|
|
207
|
+
- Behavior depends strictly on argument type.
|
|
208
|
+
|
|
209
|
+
This makes schematic generation reproducible, scriptable, and suitable for AI-assisted workflows.
|
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
|
```
|
|
@@ -125,6 +127,8 @@ A script typically:
|
|
|
125
127
|
|
|
126
128
|
Scripts do **not** contain geometric layout information; placement is determined by KiCad or kisch heuristics.
|
|
127
129
|
|
|
130
|
+
Full scripting reference: [DSL.md](./DSL.md)
|
|
131
|
+
|
|
128
132
|
### Example Script
|
|
129
133
|
|
|
130
134
|
```js
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kisch",
|
|
3
|
-
"version": "1.0.
|
|
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 "
|
|
4
|
+
import Schematic, {loadSchematic, createSchematic} from "../schematic/Schematic.js";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
import pkg from "
|
|
7
|
-
import {DeclaredError} from "
|
|
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
|
-
|
|
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 "
|
|
2
|
-
import {sym, symName, sexpCallName} from "
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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(
|
|
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 "
|
|
2
|
-
import {Rect} from "
|
|
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]);
|