@rulecad/cad 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +273 -2
- package/bin/cad.mjs +505 -0
- package/lib/assembly.mjs +308 -0
- package/lib/design.mjs +198 -0
- package/lib/layout.mjs +14 -0
- package/lib/preview/page.html +72 -0
- package/lib/preview.mjs +49 -0
- package/lib/rules.mjs +25 -0
- package/lib/targets/gist.mjs +7 -0
- package/lib/targets.mjs +21 -0
- package/lib/threemf.mjs +7 -0
- package/lib/versions.mjs +103 -0
- package/lib/workspace.mjs +45 -0
- package/package.json +63 -4
- package/templates/object.js +9 -0
- package/templates/object.json +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 JoniBach and contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,274 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @rulecad/cad
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The `cad` command: design physical things in [JSCAD](https://github.com/jscad/OpenJSCAD.org) from rules you can trace,
|
|
4
|
+
check them for printing and for fit, version them by what fits, share them as links, and export them for the printer
|
|
5
|
+
(STL, or 3MF in Bambu Studio's layout with each part on its own filament). Every measurement in a design is a variable,
|
|
6
|
+
and every variable says which rule it comes from: the printer, a standard (Cherry MX), or something you measured.
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
mkdir my-designs && cd my-designs
|
|
10
|
+
npm init -y && npm pkg set type=module
|
|
11
|
+
npm install @rulecad/cad @jscad/modeling
|
|
12
|
+
npx cad new bathroom/soap-tray # a design to start from: soap-tray.js (the shape) and .json (its variables)
|
|
13
|
+
npx cad check bathroom/soap-tray # raw numbers in the design, then printability
|
|
14
|
+
npx cad preview bathroom/soap-tray # six views as a PNG in .preview/ (needs Chrome or Chromium)
|
|
15
|
+
npx cad export bathroom/soap-tray # a 3MF for your slicer, in exports/
|
|
16
|
+
npx cad link bathroom/soap-tray # a link that opens the model in a browser (nothing is uploaded)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
rules/ shared facts, one file per set: x1c.json (printer), mx.json (Cherry MX). A set from
|
|
21
|
+
@rulecad/rules is copied here the first time a design uses it: yours to keep and correct
|
|
22
|
+
projects/<project>/
|
|
23
|
+
project.json name, description, default material, printer, objects, filaments (AMS slots)
|
|
24
|
+
objects/<object>.json variables (value, unit, rule, source, label, show, interface, tweak), variants, parts
|
|
25
|
+
objects/<object>.js main(v): the shape, from variables only
|
|
26
|
+
layouts/<name>.json where the keys go, with each key's own properties (@rulecad/keyboard-layout)
|
|
27
|
+
assemblies/<name>.json objects placed together: to check a fit, or to print together
|
|
28
|
+
CHANGELOG.md, prints.jsonl release notes, and what was printed
|
|
29
|
+
cad.config.json the workspace: printer, model page, targets
|
|
30
|
+
exports/ .preview/ .versions/ .cache/ generated (ignore them in git); .cache/builds holds built parts
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npx cad rules bathroom/soap-tray # every variable, its rule and source
|
|
35
|
+
npx cad check bathroom/soap-tray --set width=60 # change variables that have a tweak range
|
|
36
|
+
npx cad variants keyboard/keycap # named sets of inputs: keyboard/keycap:legend-f
|
|
37
|
+
npx cad compare keyboard/keycap dsa legend-f # two variants (or releases) side by side
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The keyboard examples below come from the project rulecad grew out of: a keycap, a switch plate, a case and their
|
|
41
|
+
circuit boards.
|
|
42
|
+
|
|
43
|
+
## The workspace
|
|
44
|
+
|
|
45
|
+
The workspace is `$CAD_WORKSPACE`, else the nearest folder at or above the current one with a `cad.config.json`,
|
|
46
|
+
else the current folder:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{ "printer": "x1c", "viewUrl": "https://…/", "targets": { "show": "targets/show.mjs", "shorten": "gist" } }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- `printer`: the rule set prints are checked against (bed, overhang, layer, bridge, support_gap); a project.json's
|
|
53
|
+
`printer` wins.
|
|
54
|
+
- `targets`: optional. `show` puts a model on display: a module of yours, exporting `show({ title, ttl, mesh, card })`
|
|
55
|
+
(lib/targets.mjs describes it): a smart mirror, a second screen, a slicer. `shorten` stores a link's data for
|
|
56
|
+
short links (`gist`: a secret Gist, through the `gh` command). Leave one out and `cad show` says so, and links stay
|
|
57
|
+
long.
|
|
58
|
+
- Designs are ES modules that import JSCAD themselves, so the workspace needs `"type": "module"` and
|
|
59
|
+
`@jscad/modeling`. Circuit boards need `@rulecad/pcb` as well.
|
|
60
|
+
- Previews need Chrome or Chromium: `$CAD_CHROME`, else the usual install paths.
|
|
61
|
+
- Designs are code: run only designs you trust. Links and rule files are data and never run.
|
|
62
|
+
|
|
63
|
+
## Links
|
|
64
|
+
|
|
65
|
+
`cad link <project>/<object>` (and `cad show`) prints three links to the model page (`viewUrl`; a public one by default):
|
|
66
|
+
view the model in a browser, or download its STL or 3MF as soon as the link opens. The model travels in the link's
|
|
67
|
+
fragment, which never reaches the web host, so the public page can open private designs. A link records exactly
|
|
68
|
+
which version it holds (`keyboard/keycap@0.1.0`, or `+changes` if it isn't a release). The page is to become `@rulecad/viewer`;
|
|
69
|
+
`scripts/publish-view.sh "message"` publishes it (only that folder) to the public repo.
|
|
70
|
+
|
|
71
|
+
- **Short links** (`--short`, and the default for `--md`): the link's data goes in a secret GitHub Gist (unlisted,
|
|
72
|
+
but anyone with the link can open it) and the link carries only its ID: `…/#s=<id>`. The page fetches it from
|
|
73
|
+
GitHub's API. The same model reuses its Gist (`.cache/links.json`).
|
|
74
|
+
- **Parts panel:** the page lists parts by kind (keycap ×9), opening to each copy (key-1-1). Each row shows or hides
|
|
75
|
+
its parts and sets their style (solid, wireframe, x-ray, faint); the view has a mode, an explode slider, and
|
|
76
|
+
switches for supports and references. Click a part in the model to select its copy and see its size.
|
|
77
|
+
- **Downloads of any parts:** each row has a tick for whether its parts go in a download (all printed parts at first;
|
|
78
|
+
references never). All, None, Shown and Selected set the ticks in one go. Download STL makes one mesh of the
|
|
79
|
+
ticked parts; Download 3MF makes the same Bambu Studio file as `cad export` (an object per printed thing, its parts on
|
|
80
|
+
their AMS slots, which the link carries). The file is named for what was ticked (`staggered-cap-2-4.3mf`). So one
|
|
81
|
+
link covers a whole assembly: `--md` prints a single "View and download" link.
|
|
82
|
+
- **Placed shapes:** assemblies send each distinct shape once with where each copy goes, so a 9-key macropad costs
|
|
83
|
+
one keycap, one switch and a plate. `cad show` and the model page both work this way.
|
|
84
|
+
|
|
85
|
+
## Variables
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
"slot_arm": { "value": "mx.slot_arm + x1c.hole_comp", "unit": "mm", "rule": "Cross slot arm: spec plus hole compensation",
|
|
89
|
+
"label": "Slot arm", "show": true, "interface": true }
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- `value`: a number, or arithmetic over numbers and names (`+ - * /`, brackets, `min`, `max`, `round`). Rule sets are
|
|
93
|
+
`<namespace>.<rule>`; the object's own variables are bare names. Parsed, never evaluated as code.
|
|
94
|
+
- `show`: on the rules card `cad show` hands its target (six at most). `interface`: other parts depend on it (versioning will compare these).
|
|
95
|
+
- `tweak: { min, max }`: may be changed with `--set`. Variables without one are fixed by their rule.
|
|
96
|
+
- Inputs that hold names or text, not numbers (the design reads them; expressions can't), all settable with `--set`:
|
|
97
|
+
- `choice: ["dsa", "oem"]`: one of a list.
|
|
98
|
+
- `"type": "text"` (with `max`, characters): free text, e.g. a keycap's `legend`.
|
|
99
|
+
- `"type": "anchor"`: one of 9 places on a face, `tl tc tr / ml mc mr / bl bc br`, for placing a legend.
|
|
100
|
+
- `"type": "layout"` with `choice` and `fields`: a layout file the design builds from (or a built-in like `grid`).
|
|
101
|
+
The design gets `v.layouts[name]`: the file, plus `keys` (each key's centre and properties), `width` and `depth`.
|
|
102
|
+
`fields` names the key properties it reads besides place and width, so a release only counts changes to those:
|
|
103
|
+
the plate reads `["u"]`, so changing a legend in the layout isn't a change to the plate.
|
|
104
|
+
- `display`: inputs for the on-screen copy only (`cad show`; `mirror`, its first name, still works), e.g. `{ "smooth": 24, "legend": "" }`: fewer segments, no legends, so a big
|
|
105
|
+
assembly fits a display's face limit. They win over everything; checks, exports and links use the real values.
|
|
106
|
+
- `variants`: named sets of inputs, `{ "legend-f": { "profile": "oem", "row": "R3", "legend": "F" } }`, built as
|
|
107
|
+
`keyboard/keycap:legend-f` by any command and in assemblies (`"object": "keycap:legend-f"`). Versions are the design
|
|
108
|
+
over time; variants are alternatives side by side. Adding one is minor; changing or removing one is major.
|
|
109
|
+
- `v.shapes`: helpers every design gets: `text({ text, height, stroke })` (JSCAD's single-line font, one scale for all
|
|
110
|
+
text so punctuation stays small, each straight stroke thickened to whole print lines, dots drawn round, and ↑ ↓ ← →
|
|
111
|
+
as arrows), `anchor(face, size, at)` (where something goes on a face, by anchor), and `cut(a, b)` (a subtract that
|
|
112
|
+
only touches a's faces near b; JSCAD's own splits every face along b's planes, which can leave slivers far away).
|
|
113
|
+
- Rules carry `kind`: spec (manufacturer), default (slicer), community (rule of thumb) or measured (your own test
|
|
114
|
+
print, which beats the rest), plus `source` and `checked`.
|
|
115
|
+
|
|
116
|
+
## Versions
|
|
117
|
+
|
|
118
|
+
Every object has its own semver version, kept as a git tag (`keyboard/keycap@1.2.0`), so any release can be rebuilt
|
|
119
|
+
(`cad export keyboard/keycap@1.0.0`). `cad release` compares the design with its last release and suggests the bump:
|
|
120
|
+
|
|
121
|
+
- **major**: an earlier print may no longer fit. An `interface` variable changed or went away, including through a
|
|
122
|
+
rule it's built from (a new `x1c.hole_comp` widens the slot, so the keycap's slot changes: major).
|
|
123
|
+
- **minor**: something added that doesn't break fits: a new interface variable, a new tweakable option.
|
|
124
|
+
- **patch**: everything else: other variables, the shape's code, rules that don't reach the interface.
|
|
125
|
+
|
|
126
|
+
A lower `--bump` than the changes need is refused (`--force` overrides). Releases are logged in the project's
|
|
127
|
+
`CHANGELOG.md`. A project release (`cad release keyboard`) records the version of each object, and its bump is the
|
|
128
|
+
biggest of theirs. `cad printed` logs what you printed and how it came out in `prints.jsonl`; `cad history` shows
|
|
129
|
+
releases and prints together.
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
npx cad status # versions, and what has changed since
|
|
133
|
+
npx cad release keyboard/keycap -m "Looser slot" --dry-run # see the suggested bump
|
|
134
|
+
npx cad release keyboard/keycap -m "Looser slot" --push
|
|
135
|
+
npx cad release keyboard --all -m "Legends" --push # every changed object, then the project
|
|
136
|
+
npx cad printed keyboard/keycap --result failed --note "slot too tight" --material PETG
|
|
137
|
+
npx cad history keyboard/keycap
|
|
138
|
+
npx cad diff keyboard/keycap 1.0.0 1.1.0
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Parts
|
|
142
|
+
|
|
143
|
+
`main()` can return named parts instead of one shape, described in the .json:
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
"parts": { "cap": { "label": "Keycap" }, "pillar": { "role": "support", "label": "Breakaway pillar" } }
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
A `support` part is printed and then removed (a breakaway pillar, a brim tab). Checks, exports and links use every
|
|
150
|
+
part; previews and the model page draw supports faint, so what they hold up shows through; `cad show` leaves them
|
|
151
|
+
out. `--part cap` works on one part; `cad preview --explode` moves the parts apart.
|
|
152
|
+
|
|
153
|
+
- `"filament": "legend"`: the part prints in another filament, from `project.json`'s `filaments` (`{ "base": { "slot":
|
|
154
|
+
1 }, "legend": { "slot": 2 } }`, AMS slots). A keycap's inlaid legend is the piece cut from the cap's top, so the two
|
|
155
|
+
fill each other exactly.
|
|
156
|
+
- `"display": false`: fine detail the on-screen copy leaves out (or `"mirror": false`). `"optional": true`: main() may leave the part out (a blank key).
|
|
157
|
+
- Print files (`cad export`) are written in Bambu Studio's own project layout (copied from a project it saved, and
|
|
158
|
+
checked by loading ours back with its command line): one object per printed thing (a keycap), its parts' meshes in
|
|
159
|
+
`3D/Objects/`, each part's name and AMS slot in `Metadata/model_settings.config`, all on plate 1. So the file opens
|
|
160
|
+
grouped, with legends already on their filament, and nothing to split. It carries no project settings, so your
|
|
161
|
+
printer, process and filament settings are left alone.
|
|
162
|
+
- The print check treats parts that print together as one print: an underside resting on another part (a legend on its
|
|
163
|
+
cap) isn't an overhang, and each part must be a closed solid on its own.
|
|
164
|
+
|
|
165
|
+
## Assemblies
|
|
166
|
+
|
|
167
|
+
`projects/<project>/assemblies/<name>.json` places objects together. Positions are expressions over rules and the
|
|
168
|
+
other items' variables, so a keycap can sit exactly where its slot meets the switch stem:
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
{ "title": "Keycap on an MX switch", "kind": "fit", "uses": ["mx", "x1c"],
|
|
172
|
+
"requires": { "keycap": "^0.2.0" },
|
|
173
|
+
"items": [ { "id": "switch", "object": "mx-switch" },
|
|
174
|
+
{ "id": "cap", "object": "keycap", "at": [0, 0, "switch.housing_top - cap.lift"] } ],
|
|
175
|
+
"fits": [ { "a": "cap.cap", "b": "switch.stem", "min": "-x1c.hole_comp", "max": "x1c.fit", "note": "Slot over the stem" } ] }
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
- **fit** assemblies check how parts go together: no overlaps (unless a fit allows a press fit with a negative
|
|
179
|
+
`min`), and the closest gap for each `fit` within its range. Supports are left out: they're snipped off first.
|
|
180
|
+
Objects with `"role": "reference"` (the MX switch) model things you don't print: drawn faint, never exported.
|
|
181
|
+
- **plate** assemblies lay objects out on the bed: they're checked as one print and `export` writes one 3MF with an
|
|
182
|
+
object per part. Supports are kept.
|
|
183
|
+
- **Components:** an item can be a whole assembly (`"assembly": "keycap-on-switch"`). Its parts, fits and `requires`
|
|
184
|
+
come along, named per copy (`key-1-1.cap.cap`), and its overlaps are still checked inside each copy.
|
|
185
|
+
- **Repeats:** `"repeat": { "x": { "count": "plate.cols", "step": "plate.pitch" }, "y": { … } }` places a grid of
|
|
186
|
+
copies, `key-0-0` to `key-2-2`, starting from `at`. Counts and steps are expressions, so the plate decides the grid.
|
|
187
|
+
- **Layouts:** `"repeat": { "layout": "staggered-55-arrows", "step": "plate.pitch", "per": { "cap.units": "u",
|
|
188
|
+
"cap.row": "row", "cap.legend": "legend" }, "each": { "cap.profile": "oem" } }` places a copy at each key of a layout
|
|
189
|
+
file (`key-0-0` is the back row's first key). `per` sets a component's inputs from each key's own properties (a key
|
|
190
|
+
without one keeps the default), and `each` sets the same value on every copy, so the layout says what each key is:
|
|
191
|
+
its width, its sculpted row, its legend. (Older forms, `width`, `rows` and `plate/<layout>`, still build.) In a layout,
|
|
192
|
+
`"defaults": { "legend": "@label" }` gives every key its label as its legend; a key's own properties override it.
|
|
193
|
+
A keycap takes a second legend too (`legend2`, at `legend2_anchor`), such as the shifted character above the plain one;
|
|
194
|
+
the two must stay a stroke apart.
|
|
195
|
+
- A fit with `*` applies to every copy: `{ "a": "plate", "b": "key-*.switch.bottom", … }`. With `*` on both sides,
|
|
196
|
+
each copy is checked against every other copy near it: `{ "a": "key-*.cap", "b": "key-*.cap", "min": 0.5 }`. Repeated fits are reported
|
|
197
|
+
together: the worst case, and how many of the copies are in range.
|
|
198
|
+
- `requires` pins versions (`^` same major, at least this version, 0.x included, since a minor release never breaks a
|
|
199
|
+
fit here; `~` same minor); an item can use a release: `"object": "keycap@0.2.1"`, or a variant: `"keycap:oem-r3"`.
|
|
200
|
+
- The check names any `estimate` rule an assembly relies on, through its positions or its objects, so guesses get
|
|
201
|
+
measured.
|
|
202
|
+
- `check`, `preview` (`--explode` lifts each kind of object above the last: plate, switches, keycaps), `show`, `link` and, for plates, `export` all take
|
|
203
|
+
`<project>/<assembly>`.
|
|
204
|
+
|
|
205
|
+
## Boards
|
|
206
|
+
|
|
207
|
+
A circuit board is an object whose .json says `"engine": "pcb"`. Its `main(v)` returns a
|
|
208
|
+
[tscircuit](https://github.com/tscircuit/tscircuit) board (circuits as code, from npm) built with `v.pcb`
|
|
209
|
+
(`lib/pcb.mjs`); the build runs it into Circuit JSON, and the bare board, drilled, becomes a part, so
|
|
210
|
+
previews, links, `cad show` and fit assemblies work as for printed parts. The copper (tracks, pads, vias) is drawn in the
|
|
211
|
+
2D views, not in 3D.
|
|
212
|
+
|
|
213
|
+
```sh
|
|
214
|
+
npx cad check keyboard/macropad-pcb # tscircuit's design checks, JLCPCB's limits, the matrix
|
|
215
|
+
npx cad preview keyboard/macropad-pcb # the board from above (copper, silkscreen) and in 3D
|
|
216
|
+
npx cad export keyboard/macropad-pcb # <name>-fab.zip for JLCPCB, KiCad files, the matrix
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
- **One layout, three parts:** `v.pcb.fromLayout(v.layouts[v.layout].keys, pitch)` reads the same layout file as the
|
|
220
|
+
plate and keycaps (each row lined up with the widest for its matrix columns), so the board's switches sit exactly
|
|
221
|
+
where the plate cuts its holes. `keyboard/main-pcb` is the staggered 55-key board.
|
|
222
|
+
- **Keyboards:** `v.pcb.keyboard(v, keys)` makes a key matrix with a Pico beside it (`controller: beside`, every track
|
|
223
|
+
drawn) or under it (`controller: under`: on header pins, components down, turned so its two pin rows sit in the
|
|
224
|
+
gaps between key rows, clear of every switch housing, its USB out of the `mcu_side` edge; the matrix drawn, the
|
|
225
|
+
Pico's 17 connections autorouted by tscircuit). Footprints come from rules (MX
|
|
226
|
+
switch: `mx.pcb_*`, from KiCad's library; Pico: `pico.pin_*` and its GPIO map; diodes: `sod123`). Nothing is
|
|
227
|
+
autorouted: the keyboard router draws columns on the front copper, rows and SOD-123 diodes on the back, then lanes
|
|
228
|
+
beside the Pico, each a track and a gap (`track`, `clear`) clear of its neighbours.
|
|
229
|
+
- **The fab zip:** Gerbers under KiCad's Protel names (`.GTL`, `.GBL`, `.GTS`, `.GBS`, `.GTO`, `.GBO`, `.GTP`, `.GBP`,
|
|
230
|
+
`.GKO`), plated and unplated drill files, a BOM in JLCPCB's columns and a placement file: upload it as it is.
|
|
231
|
+
`.kicad_pcb` and `.kicad_sch` sit beside it, for `kicad-cli pcb drc` or finishing by hand (one way: changes made in
|
|
232
|
+
KiCad don't come back).
|
|
233
|
+
- **2D views:** links to a board carry its drawings, made by tscircuit's circuit-to-svg: the board (both sides, front,
|
|
234
|
+
back) and the schematic, laid out like a keyboard's (a switch and diode per key on the matrix grid, the Pico beside
|
|
235
|
+
it with every pin named). The model page shows them in tabs over the 3D view, as images (nothing in them runs), with
|
|
236
|
+
drag to pan, wheel or pinch to zoom, double-click to fit. `cad preview` writes the schematic as a PNG too. An
|
|
237
|
+
assembly's link carries its board's views as well, so one link holds the whole build; picking the board in the 3D
|
|
238
|
+
model (or the list) offers "Board view ›".
|
|
239
|
+
- **What a board tells its neighbours:** a built board adds `board_x`, `board_y`, `board_w`, `board_d` and (Pico
|
|
240
|
+
under) `pico_x`, `pico_y` to its values, for assemblies to use.
|
|
241
|
+
- **In the case:** `case.controller: pcb` drops the cradle and puts the USB opening where the board's socket is
|
|
242
|
+
(`usb_wall`, `usb_y` along the plate, `usb_drop` below it). `keyboard/staggered-pcb` puts it together: the board
|
|
243
|
+
(with its Pico, `pcb-with-pico`) under a plate thinned by `top_clearance` for the header joints, in a case built
|
|
244
|
+
from the board's numbers, every fit checked (switches on the board, plate over it, board and Pico in the case).
|
|
245
|
+
- **The matrix:** `<name>-matrix.json` gives each key's row and column and each line's GPIO, for firmware.
|
|
246
|
+
- **Versions:** the npm packages are pinned to exact versions (tscircuit moves fast); `@tscircuit/math-utils` is
|
|
247
|
+
overridden in package.json so every part of it agrees.
|
|
248
|
+
|
|
249
|
+
### Speed
|
|
250
|
+
Fit checks find closest gaps through a grid of triangles (nearest cubes first), and remember overlap volumes between
|
|
251
|
+
placed parts in `.cache/overlaps.json`, keyed by each part's build and placement: the staggered keyboards' checks take
|
|
252
|
+
about 10 s, and a re-check after an edit measures only what the edit moved. The build cache keeps its 800 most
|
|
253
|
+
recently used builds.
|
|
254
|
+
|
|
255
|
+
### Assemblies: values from other parts
|
|
256
|
+
An item's `from` sets inputs from other items' values, in the assembly's names: `"from": { "usb_y":
|
|
257
|
+
"board.pcb.pico_y" }`. Every item is built before any is placed, so positions can refer to items listed after them
|
|
258
|
+
(the board placed under the case's top while the case is built from the board's Pico position). `"place": false`
|
|
259
|
+
builds an item only for its values: the PCB build's print plates (`staggered-pcb-case`, `staggered-pcb-plate`) take
|
|
260
|
+
the board's numbers without the board being on the bed. In assemblies a board counts as a reference: checked, drawn
|
|
261
|
+
faint, never in a print file.
|
|
262
|
+
|
|
263
|
+
## Checks
|
|
264
|
+
|
|
265
|
+
- **Closed solids:** every edge must be matched by faces on the other side. T-junctions (an edge covered by several
|
|
266
|
+
shorter ones) count as closed, and so do hairline seams: a seam within 0.01 mm of another face, which booleans leave
|
|
267
|
+
and no printer can resolve. Anything wider is reported as a hole.
|
|
268
|
+
|
|
269
|
+
- **Every measurement is a variable**: numbers other than 0, 1 and 2 in a design fail.
|
|
270
|
+
- **Fits the bed** (256 mm), **sits on it** (lowest point z = 0, faces flat on it), **overhangs** past 45° (warning:
|
|
271
|
+
supports or another orientation), **closed solid** (warning: JSCAD's booleans can leave small gaps where solids
|
|
272
|
+
touch; slicers usually repair them).
|
|
273
|
+
|
|
274
|
+
Z is up and the bed is z = 0: a design is built the way it prints.
|