Compare commits
120 commits
Author | SHA1 | Date | |
---|---|---|---|
73775d6999 | |||
5060864b08 | |||
efc7329646 | |||
85bd46a4d8 | |||
c57440a231 | |||
a48fb3c064 | |||
56a81c20b8 | |||
9370da75b7 | |||
fd3fd6b5c0 | |||
![]() |
73483e290b | ||
![]() |
35f2684348 | ||
![]() |
ce65a9ef5d | ||
![]() |
3032e6b3d1 | ||
![]() |
57e8173848 | ||
![]() |
85a13fa4e7 | ||
6afbf0385a | |||
bec14b1705 | |||
99acabbd12 | |||
1190f35c66 | |||
52af7f0a20 | |||
![]() |
96d043126a | ||
![]() |
21e50cb11d | ||
![]() |
353e07654d | ||
![]() |
e1697e367f | ||
![]() |
1f57ec4e19 | ||
![]() |
6e520e745a | ||
![]() |
e924352763 | ||
![]() |
14cd499182 | ||
![]() |
7baa6a3b3a | ||
![]() |
9f644c2e2b | ||
![]() |
9832489d41 | ||
![]() |
89981199d9 | ||
![]() |
63684e33d7 | ||
![]() |
d74f657f24 | ||
![]() |
daaef0af79 | ||
![]() |
4d65eb19a6 | ||
![]() |
3eac7f8e6d | ||
![]() |
5761266260 | ||
![]() |
244fc53eae | ||
![]() |
9ad080d24c | ||
![]() |
f0d22328cd | ||
![]() |
480c362c1e | ||
![]() |
c45523f38a | ||
![]() |
b27e10374e | ||
![]() |
75f907917b | ||
![]() |
86a74945ca | ||
![]() |
c480a33fa3 | ||
![]() |
841addb9ad | ||
![]() |
d2c3999d41 | ||
![]() |
77778249d2 | ||
![]() |
357e01d639 | ||
![]() |
e0eb43566f | ||
![]() |
3746900490 | ||
![]() |
68df6aee0a | ||
![]() |
5006f5b862 | ||
![]() |
da1417ce2f | ||
![]() |
83b7dc1bb8 | ||
![]() |
e076b62190 | ||
![]() |
58bb16ed28 | ||
![]() |
3a0f326a31 | ||
![]() |
1de68843ce | ||
![]() |
3aef729465 | ||
![]() |
2cfdf10327 | ||
![]() |
158dc33212 | ||
![]() |
05a489fa6d | ||
![]() |
561309139a | ||
![]() |
02dac3316b | ||
![]() |
f5f787edce | ||
![]() |
820a1a2888 | ||
![]() |
07d6fcfb34 | ||
![]() |
8a57891b24 | ||
![]() |
71419145ad | ||
![]() |
53b6a98edc | ||
![]() |
17dc5ab169 | ||
![]() |
1da986e609 | ||
![]() |
cf9007aa50 | ||
![]() |
40406fbc03 | ||
![]() |
0d73c59538 | ||
![]() |
24466eb01d | ||
![]() |
4446a60380 | ||
![]() |
5e68bdb630 | ||
![]() |
5a25c1c423 | ||
![]() |
802a988705 | ||
![]() |
a586d9058c | ||
![]() |
5bfa3b5932 | ||
![]() |
bbab283850 | ||
![]() |
c6f4832ee4 | ||
![]() |
8748a37c04 | ||
![]() |
891f55ace1 | ||
![]() |
d505b19a0f | ||
![]() |
fdc2adf30e | ||
![]() |
e13927d050 | ||
![]() |
b928cbd35d | ||
![]() |
d23bd71b7a | ||
![]() |
6dc6b5d8e9 | ||
![]() |
b8c71bef0f | ||
![]() |
cff15dd3b9 | ||
![]() |
d5129832b9 | ||
![]() |
e0f5c910eb | ||
![]() |
06d2ae4a7f | ||
![]() |
2b98b502d6 | ||
![]() |
6225013828 | ||
![]() |
7f5e5e7544 | ||
![]() |
28d076ea38 | ||
![]() |
d6f83232a8 | ||
![]() |
4844a044df | ||
![]() |
df7b76c610 | ||
![]() |
6504b2b952 | ||
![]() |
bd6b5a0ca6 | ||
![]() |
a7f333c9bc | ||
![]() |
534ac4b75d | ||
![]() |
e48631fac8 | ||
![]() |
f2bd0d23a1 | ||
![]() |
2c18902e9f | ||
![]() |
9ee099b16c | ||
![]() |
0ed29e7e1d | ||
![]() |
73045e4754 | ||
![]() |
86c00eb62d | ||
![]() |
3570be2184 | ||
![]() |
f326594743 |
191 changed files with 51494 additions and 6645 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
|||
eval "$(lorri direnv)"
|
13
.github/FUNDING.yml
vendored
Normal file
13
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: [mrzealot] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
249
.gitignore
vendored
249
.gitignore
vendored
|
@ -1,120 +1,129 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
# Project specific
|
||||
output
|
||||
temp*
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
# Project specific
|
||||
output
|
||||
temp*
|
||||
kicad/45treus/main.pro
|
||||
kicad/45treus/main.kicad_pcb
|
||||
*.kicad_pcb-bak
|
||||
kicad/45treus/gerber_v*/*.gbr
|
||||
kicad/45treus/gerber_v*/*.gbrjob
|
||||
kicad/45treus/gerber_v*/*.drl
|
||||
|
||||
kicad/23creus/23creus-backups
|
||||
kicad/23creus/fp-info-cache
|
||||
|
|
271
23creus.yaml
Normal file
271
23creus.yaml
Normal file
|
@ -0,0 +1,271 @@
|
|||
# yaml-language-server: $schema=./meta/schema.json
|
||||
units:
|
||||
kx: cx
|
||||
ky: cy
|
||||
px: 2
|
||||
py: 2
|
||||
step: .5
|
||||
|
||||
points:
|
||||
zones:
|
||||
matrix:
|
||||
columns:
|
||||
pinky:
|
||||
key:
|
||||
column_net: P21
|
||||
mirror: { column_net: P2 }
|
||||
ring:
|
||||
key:
|
||||
stagger: 4
|
||||
spread: kx + step
|
||||
column_net: P20
|
||||
mirror: { column_net: P3 }
|
||||
middle:
|
||||
key:
|
||||
stagger: 5
|
||||
spread: kx + step
|
||||
column_net: P19
|
||||
mirror: { column_net: P4 }
|
||||
index:
|
||||
key:
|
||||
stagger: -5
|
||||
spread: kx + step
|
||||
column_net: P18
|
||||
mirror: { column_net: P5 }
|
||||
inner:
|
||||
key:
|
||||
stagger: -6
|
||||
spread: kx + step
|
||||
column_net: P15
|
||||
mirror: { column_net: P6 }
|
||||
thumb:
|
||||
rows:
|
||||
bottom:
|
||||
skip: false
|
||||
key:
|
||||
skip: true
|
||||
spread: kx + step
|
||||
stagger: 10
|
||||
column_net: P14
|
||||
mirror: { column_net: P7 }
|
||||
rows:
|
||||
bottom:
|
||||
row_net: P16
|
||||
home:
|
||||
spread: ky + step
|
||||
row_net: P10
|
||||
top:
|
||||
spread: ky + step
|
||||
row_net: P9
|
||||
num:
|
||||
spread: ky + step
|
||||
row_net: P8
|
||||
|
||||
rotate: -23
|
||||
mirror:
|
||||
ref: matrix_thumb_bottom
|
||||
distance: 42
|
||||
|
||||
outlines:
|
||||
# outer border
|
||||
farouter:
|
||||
- what: polygon
|
||||
points:
|
||||
- ref: matrix_inner_num
|
||||
shift: [13, 13]
|
||||
- ref: matrix_middle_num
|
||||
shift: [13, 13]
|
||||
- ref: matrix_middle_num
|
||||
shift: [-13, 13]
|
||||
- ref: matrix_pinky_num
|
||||
shift: [-13, 13]
|
||||
- ref: matrix_pinky_bottom
|
||||
shift: [-13, -13]
|
||||
- ref: matrix_pinky_bottom
|
||||
shift: [13, -13]
|
||||
- ref: matrix_inner_bottom
|
||||
shift: [-13, -13]
|
||||
- ref: matrix_inner_bottom
|
||||
shift: [13, -13]
|
||||
- ref: mirror_matrix_inner_bottom
|
||||
shift: [13, -13]
|
||||
- ref: mirror_matrix_inner_bottom
|
||||
shift: [-13, -13]
|
||||
- ref: mirror_matrix_pinky_bottom
|
||||
shift: [13, -13]
|
||||
- ref: mirror_matrix_pinky_bottom
|
||||
shift: [-13, -13]
|
||||
- ref: mirror_matrix_pinky_num
|
||||
shift: [-13, 13]
|
||||
- ref: mirror_matrix_middle_num
|
||||
shift: [-13, 13]
|
||||
- ref: mirror_matrix_middle_num
|
||||
shift: [13, 13]
|
||||
- ref: mirror_matrix_inner_num
|
||||
shift: [13, 13]
|
||||
|
||||
compatouter:
|
||||
- what: polygon
|
||||
points:
|
||||
- ref: matrix_inner_num
|
||||
shift: [11, 11]
|
||||
- ref: matrix_middle_num
|
||||
shift: [11, 11]
|
||||
- ref: matrix_middle_num
|
||||
shift: [-11, 11]
|
||||
- ref: matrix_pinky_num
|
||||
shift: [-11, 11]
|
||||
- ref: matrix_pinky_bottom
|
||||
shift: [-11, -11]
|
||||
- ref: matrix_pinky_bottom
|
||||
shift: [11, -11]
|
||||
- ref: matrix_inner_bottom
|
||||
shift: [-11, -11]
|
||||
- ref: matrix_inner_bottom
|
||||
shift: [11, -11]
|
||||
- ref: mirror_matrix_inner_bottom
|
||||
shift: [11, -11]
|
||||
- ref: mirror_matrix_inner_bottom
|
||||
shift: [-11, -11]
|
||||
- ref: mirror_matrix_pinky_bottom
|
||||
shift: [11, -11]
|
||||
- ref: mirror_matrix_pinky_bottom
|
||||
shift: [-11, -11]
|
||||
- ref: mirror_matrix_pinky_num
|
||||
shift: [-11, 11]
|
||||
- ref: mirror_matrix_middle_num
|
||||
shift: [-11, 11]
|
||||
- ref: mirror_matrix_middle_num
|
||||
shift: [11, 11]
|
||||
- ref: mirror_matrix_inner_num
|
||||
shift: [11, 11]
|
||||
|
||||
outer:
|
||||
- what: polygon
|
||||
points:
|
||||
- ref: matrix_inner_num
|
||||
shift: [10, 10]
|
||||
- ref: matrix_middle_num
|
||||
shift: [10, 10]
|
||||
- ref: matrix_middle_num
|
||||
shift: [-10, 10]
|
||||
- ref: matrix_pinky_num
|
||||
shift: [-10, 10]
|
||||
- ref: matrix_pinky_bottom
|
||||
shift: [-10, -10]
|
||||
- ref: matrix_pinky_bottom
|
||||
shift: [10, -10]
|
||||
- ref: matrix_inner_bottom
|
||||
shift: [-10, -10]
|
||||
- ref: matrix_inner_bottom
|
||||
shift: [10, -10]
|
||||
- ref: mirror_matrix_inner_bottom
|
||||
shift: [10, -10]
|
||||
- ref: mirror_matrix_inner_bottom
|
||||
shift: [-10, -10]
|
||||
- ref: mirror_matrix_pinky_bottom
|
||||
shift: [10, -10]
|
||||
- ref: mirror_matrix_pinky_bottom
|
||||
shift: [-10, -10]
|
||||
- ref: mirror_matrix_pinky_num
|
||||
shift: [-10, 10]
|
||||
- ref: mirror_matrix_middle_num
|
||||
shift: [-10, 10]
|
||||
- ref: mirror_matrix_middle_num
|
||||
shift: [10, 10]
|
||||
- ref: mirror_matrix_inner_num
|
||||
shift: [10, 10]
|
||||
|
||||
# chip
|
||||
chip:
|
||||
- what: rectangle
|
||||
size: [18, 50]
|
||||
where:
|
||||
ref:
|
||||
aggregate.parts:
|
||||
- matrix_inner_bottom
|
||||
- mirror_matrix_inner_bottom
|
||||
shift: [0, 40]
|
||||
|
||||
# 14mm holes for cherry switches
|
||||
keyholes:
|
||||
- what: rectangle
|
||||
where: true
|
||||
asym: source
|
||||
size: 13.8
|
||||
|
||||
# keycaps
|
||||
keycaps:
|
||||
- what: rectangle
|
||||
where: true
|
||||
asym: source
|
||||
size: [17.95, 17.5]
|
||||
|
||||
# switchplate
|
||||
switchplate:
|
||||
- outer
|
||||
- -keyholes
|
||||
- -chip
|
||||
|
||||
# far outer switchplate
|
||||
farouterswitchplate:
|
||||
- farouter
|
||||
- -keyholes
|
||||
- -chip
|
||||
|
||||
cases:
|
||||
laser:
|
||||
- name: farouterswitchplate
|
||||
extrude: 6.5
|
||||
- name: compatouter
|
||||
extrude: 5.2
|
||||
shift: [0, 0, 1.3]
|
||||
operation: subtract
|
||||
|
||||
pcbs:
|
||||
main:
|
||||
outlines:
|
||||
outer:
|
||||
outline: outer
|
||||
|
||||
footprints:
|
||||
keys:
|
||||
what: choc
|
||||
where: true
|
||||
params:
|
||||
from: "{{colrow}}"
|
||||
to: "{{column_net}}"
|
||||
keycaps: true
|
||||
hotswap: true
|
||||
|
||||
diode:
|
||||
what: diode
|
||||
where: true
|
||||
params:
|
||||
from: "{{colrow}}"
|
||||
to: "{{row_net}}"
|
||||
adjust:
|
||||
shift: [0, -5]
|
||||
rotate: 180
|
||||
|
||||
reset:
|
||||
what: button
|
||||
params:
|
||||
from: RST
|
||||
to: GND
|
||||
where:
|
||||
ref:
|
||||
aggregate.parts:
|
||||
- matrix_inner_bottom
|
||||
- mirror_matrix_inner_bottom
|
||||
rotate: 90
|
||||
|
||||
mcu:
|
||||
what: promicro
|
||||
where:
|
||||
rotate: 270
|
||||
ref:
|
||||
aggregate.parts:
|
||||
- matrix_inner_bottom
|
||||
- mirror_matrix_inner_bottom
|
||||
shift: [0, -4]
|
169
23treus.yaml
Normal file
169
23treus.yaml
Normal file
|
@ -0,0 +1,169 @@
|
|||
points:
|
||||
key:
|
||||
footprints:
|
||||
mx_hotswap:
|
||||
type: mx
|
||||
params:
|
||||
hotswap: true
|
||||
reverse: false
|
||||
keycaps: true
|
||||
nets:
|
||||
from: =colrow
|
||||
to: =column_net
|
||||
diode:
|
||||
type: diode
|
||||
nets:
|
||||
from: =colrow
|
||||
to: =row_net
|
||||
anchor:
|
||||
shift: [0, -5]
|
||||
rotate: 180
|
||||
|
||||
zones:
|
||||
matrix:
|
||||
columns:
|
||||
pinky:
|
||||
key:
|
||||
column_net: P21
|
||||
mirror: { column_net: P2 }
|
||||
ring:
|
||||
key:
|
||||
stagger: 4
|
||||
column_net: P20
|
||||
mirror: { column_net: P3 }
|
||||
middle:
|
||||
key:
|
||||
stagger: 5
|
||||
column_net: P19
|
||||
mirror: { column_net: P4 }
|
||||
index:
|
||||
key:
|
||||
stagger: -5
|
||||
column_net: P18
|
||||
mirror: { column_net: P5 }
|
||||
inner:
|
||||
key:
|
||||
stagger: -6
|
||||
column_net: P15
|
||||
mirror: { column_net: P6 }
|
||||
thumb:
|
||||
row_overrides:
|
||||
bottom:
|
||||
key:
|
||||
stagger: 10
|
||||
column_net: P14
|
||||
mirror: { column_net: P7}
|
||||
rows:
|
||||
bottom:
|
||||
row_net: P16
|
||||
home:
|
||||
row_net: P10
|
||||
top:
|
||||
row_net: P9
|
||||
num:
|
||||
row_net: P8
|
||||
|
||||
rotate: -23
|
||||
mirror:
|
||||
ref: matrix_thumb_bottom
|
||||
distance: 42
|
||||
|
||||
outlines:
|
||||
exports:
|
||||
# outer border
|
||||
outer:
|
||||
- type: polygon
|
||||
points:
|
||||
- ref: matrix_inner_num
|
||||
shift: [10, 10]
|
||||
- ref: matrix_middle_num
|
||||
shift: [10, 10]
|
||||
- ref: matrix_middle_num
|
||||
shift: [-10, 10]
|
||||
- ref: matrix_pinky_num
|
||||
shift: [-10, 10]
|
||||
- ref: matrix_pinky_bottom
|
||||
shift: [-10, -10]
|
||||
- ref: matrix_pinky_bottom
|
||||
shift: [10, -10]
|
||||
- ref: matrix_inner_bottom
|
||||
shift: [-10, -10]
|
||||
- ref: matrix_inner_bottom
|
||||
shift: [10, -10]
|
||||
- ref: mirror_matrix_inner_bottom
|
||||
shift: [10, -10]
|
||||
- ref: mirror_matrix_inner_bottom
|
||||
shift: [-10, -10]
|
||||
- ref: mirror_matrix_pinky_bottom
|
||||
shift: [10, -10]
|
||||
- ref: mirror_matrix_pinky_bottom
|
||||
shift: [-10, -10]
|
||||
- ref: mirror_matrix_pinky_num
|
||||
shift: [-10, 10]
|
||||
- ref: mirror_matrix_middle_num
|
||||
shift: [-10, 10]
|
||||
- ref: mirror_matrix_middle_num
|
||||
shift: [10, 10]
|
||||
- ref: mirror_matrix_inner_num
|
||||
shift: [10, 10]
|
||||
#- type: keys
|
||||
# side: both
|
||||
# size: 20
|
||||
# corner: 1
|
||||
#- type: polygon
|
||||
# points:
|
||||
# - ref: matrix_inner_num
|
||||
# shift: [0, -5]
|
||||
# - ref: matrix_thumb_bottom
|
||||
# shift: [-10, -10]
|
||||
# - ref: mirror_matrix_thumb_bottom
|
||||
# shift: [-10, -10]
|
||||
# - ref: mirror_matrix_inner_num
|
||||
# shift: [0, -5]
|
||||
chip:
|
||||
# cutout for the chip itself
|
||||
- type: rectangle
|
||||
size: [18, 33]
|
||||
anchor:
|
||||
ref: [matrix_inner_top, mirror_matrix_inner_top]
|
||||
shift: [-9, -24.5]
|
||||
# cutout for the cable (looks)
|
||||
- type: rectangle
|
||||
size: [8, 50]
|
||||
anchor:
|
||||
ref: [matrix_inner_top, mirror_matrix_inner_top]
|
||||
shift: [-4, -24.5]
|
||||
# 14mm holes for cherry switches
|
||||
keyholes:
|
||||
- type: keys
|
||||
side: both
|
||||
size: 14
|
||||
bound: false
|
||||
# switchplate
|
||||
switchplate:
|
||||
- outer
|
||||
- -keyholes
|
||||
- -chip
|
||||
|
||||
cases:
|
||||
|
||||
pcbs:
|
||||
main:
|
||||
outlines:
|
||||
outer:
|
||||
outline: outer
|
||||
footprints:
|
||||
reset:
|
||||
type: button
|
||||
nets:
|
||||
from: RST
|
||||
to: GND
|
||||
anchor:
|
||||
ref: [matrix_inner_bottom, mirror_matrix_inner_bottom]
|
||||
rotate: 90
|
||||
mcu:
|
||||
type: promicro
|
||||
anchor:
|
||||
rotate: 270
|
||||
ref: [matrix_inner_top, mirror_matrix_inner_top]
|
||||
shift: [0, -4]
|
6
Makefile
Normal file
6
Makefile
Normal file
|
@ -0,0 +1,6 @@
|
|||
all: node_modules
|
||||
node src/cli.js 23creus.yaml -d -o output
|
||||
cp output/pcbs/main.kicad_pcb kicad/23creus/
|
||||
|
||||
node_modules:
|
||||
npm ci
|
111
README.md
Normal file
111
README.md
Normal file
|
@ -0,0 +1,111 @@
|
|||
|
||||
# Ergogen
|
||||
|
||||
***Ergonomic Keyboard Generator***
|
||||
|
||||
<br>
|
||||
|
||||
<img
|
||||
src = 'showcase.png'
|
||||
width = 400
|
||||
align = right
|
||||
/>
|
||||
|
||||
The project aims to provide a common configuration format to describe ***ergonomic*** 2D layouts and generate automatic plates, cases, as well as un-routed PCBs for them. The project grew out of (and is an integral part of) the [Absolem keyboard], and shares its [Discord] server as well.
|
||||
|
||||
|
||||
<div align = center>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
---
|
||||
|
||||
[![Button WebUI]][WebUI]
|
||||
[![Button Documentation]][Documentation]
|
||||
[![Button Discord]][Discord]
|
||||
[![Button Donate]][Donate]
|
||||
|
||||
---
|
||||
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
## Getting Started
|
||||
|
||||
Until there's a proper "Getting started" guide, try getting acquainted with **Ergogen** by following these steps in order:
|
||||
|
||||
<br>
|
||||
|
||||
1. Read the **[Documentation]**.
|
||||
|
||||
D'uuh.
|
||||
They're not complete by any measure, but should give you a fairly good idea what you're dealing with here.
|
||||
|
||||
<br>
|
||||
|
||||
2. Try one of the web-based deployments.
|
||||
|
||||
[![Button Official]][WebUI]
|
||||
[![Button Unofficial]][Unofficial]
|
||||
|
||||
The unofficial deployment is probably better, tbh, and will soon be replacing the official one.
|
||||
Choose either one, then click things, look at outputs and see if things start to make sense.
|
||||
|
||||
There is no need for you to download the **CLI** unless you want to do one of the following:
|
||||
|
||||
- Preview in-development features
|
||||
- Use custom modifications
|
||||
- Contribute code
|
||||
|
||||
<br>
|
||||
|
||||
3. Search the [`ergogen`][Topic] topic on GitHub.
|
||||
|
||||
There, you can look at (and reverse engineer) a variety of real life configs using **Ergogen**.
|
||||
Pop them into the web UI to see what they do, tinker with them and things should start to make more sense.
|
||||
|
||||
<br>
|
||||
|
||||
4. If a question persists after all of the above, feel free to ask it over on **[Discord]** and we'll do our best to help you out.
|
||||
|
||||
<br>
|
||||
|
||||
## Contributions
|
||||
|
||||
Feature ideas, documentation improvements, examples, tests, or pull requests welcome!
|
||||
Get in touch on our **[Discord]**, and we can definitely find something you can help with, if you'd like to.
|
||||
|
||||
<br>
|
||||
|
||||
## Sponsors
|
||||
|
||||
Huge thanks go to everyone who chooses to support my work!
|
||||
But even huger thanks are due to the following, *distinguished* sponsors:
|
||||
|
||||
- [perce](https://madebyperce.com/)
|
||||
- [Cache](https://github.com/MvEerd)
|
||||
- [Neil Gilmour](https://github.com/neilgilmour)
|
||||
- [ochief](https://github.com/ochief)
|
||||
- [Alyx Brett](https://github.com/alyx-brett)
|
||||
|
||||
<!----------------------------------------------------------------------------->
|
||||
|
||||
[Absolem keyboard]: https://zealot.hu/absolem
|
||||
[Documentation]: https://docs.ergogen.xyz
|
||||
[Discord]: http://discord.ergogen.xyz
|
||||
[WebUI]: https://ergogen.xyz
|
||||
[Unofficial]: https://ergogen.cache.works/
|
||||
[Topic]: https://github.com/topics/ergogen
|
||||
[Donate]: https://github.com/sponsors/mrzealot
|
||||
|
||||
<!--------------------------------{ Buttons }---------------------------------->
|
||||
|
||||
[Button WebUI]: https://img.shields.io/badge/Deployment-37a779?style=for-the-badge&logoColor=white&logo=AppleArcade
|
||||
[Button Unofficial]: https://img.shields.io/badge/Unofficial-yellow?style=for-the-badge
|
||||
[Button Official]: https://img.shields.io/badge/Official-37a779?style=for-the-badge
|
||||
[Button Documentation]: https://img.shields.io/badge/Documentation-1793D1?style=for-the-badge&logoColor=white&logo=GitBook
|
||||
[Button Discord]: https://img.shields.io/badge/Discord-5865F2?style=for-the-badge&logoColor=white&logo=Discord
|
||||
[Button Donate]: https://img.shields.io/badge/Donate-EA4AAA?style=for-the-badge&logoColor=white&logo=githubsponsors
|
195
changelog.md
Normal file
195
changelog.md
Normal file
|
@ -0,0 +1,195 @@
|
|||
# 4.0.0 (2022-??-??)
|
||||
|
||||
## :boom: Breaking Changes
|
||||
## :seedling: Enhancements
|
||||
## :skull: Bugfixes
|
||||
## :wrench: Other
|
||||
|
||||
* fdc2adf - Dependency and roadmap housecleaning (2022-02-27)
|
||||
* e13927d - Make reference dumping easier during testing (2022-02-27)
|
||||
* b928cbd - Implement a simple autobind (2022-02-27)
|
||||
* d23bd71 - Support orient/rotate towards other points (2022-02-27)
|
||||
* 6dc6b5d - Anchor recursivization (2022-02-27)
|
||||
* b8c71be - Improve readme (2022-01-24)
|
||||
* cff15dd - Add missing bbox test (2022-01-23)
|
||||
* d512983 - More flexible semver handling (2022-01-23)
|
||||
* e0f5c91 - Fix rollup build warning (2022-01-23)
|
||||
* 06d2ae4 - Switch to handrolled semver implementation (2022-01-23)
|
||||
* 2b98b50 - Support string templating for key-level attributes (2022-01-22)
|
||||
* 6225013 - Indicate dev version in package.json (2022-01-20)
|
||||
* 7f5e5e7 - Roadmap update (2022-01-18)
|
||||
* 28d076e - Remove accidentally added folder (2022-01-16)
|
||||
* d6f8323 - Outlines rewrite actually done (2022-01-16)
|
||||
* 4844a04 - Outlines rewrite theoretically done (2022-01-11)
|
||||
* df7b76c - Outlines rewrite, part 2 (2022-01-10)
|
||||
* 6504b2b - Outlines rewrite in progress (2022-01-09)
|
||||
* bd6b5a0 - Add filter tests (2022-01-08)
|
||||
* a7f333c - Filter implementation progress (2022-01-03)
|
||||
* 534ac4b - Filter implementation started (2021-12-26)
|
||||
* e48631f - Add key-level orient (2021-12-25)
|
||||
* f2bd0d2 - Roadmap update (2021-12-18)
|
||||
* 2c18902 - Simplify the names in individual point "zones" (2021-12-18)
|
||||
* 9ee099b - Minor test adjustments (2021-12-18)
|
||||
* 0ed29e7 - Place rectangles by their centers (2021-12-18)
|
||||
* 73045e4 - Move column-level attributes to key-level (2021-12-18)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 3.1.2 (2022-01-20)
|
||||
|
||||
* 86c00eb - (tag: v3.1.2, origin/master, backup/master, master) Bump patch version (2022-01-20)
|
||||
* 3570be2 - Delete old docs (2022-01-20)
|
||||
* f326594 - De-dupe readme, point usage to docs (2022-01-20)
|
||||
|
||||
|
||||
|
||||
# 3.1.1 (2022-01-14)
|
||||
|
||||
* 47d15c0 - (tag: v3.1.1) Hotfix parameterization (2022-01-14)
|
||||
|
||||
|
||||
# 3.1.0 (2021-12-15)
|
||||
|
||||
* 488fba0 - (tag: v3.1.0) 3.1.0 (2021-12-15)
|
||||
* f16ccd4 - Refresh roadmap (2021-12-15)
|
||||
* 21cfc1f - Allow glue opt-out, even when bound (2021-12-15)
|
||||
* c46a0bc - Add U for 19.05 spacing, closes #48 (2021-12-15)
|
||||
* 3745cf3 - Parameterize PCB component reference hiding (2021-12-15)
|
||||
* 13052cd - PCB metadata propagation (2021-12-15)
|
||||
* 90db02f - Roadmap update (2021-12-15)
|
||||
* 3e0dc84 - Support semver checks (2021-12-15)
|
||||
|
||||
|
||||
# 3.0.3 (2021-12-13)
|
||||
|
||||
* d5ebd70 - (tag: v3.0.3) 3.0.3 (2021-12-13)
|
||||
* e45e563 - Further dependency restructure (2021-12-13)
|
||||
|
||||
|
||||
|
||||
# 3.0.2 (2021-12-13)
|
||||
|
||||
* 96c7caa - (tag: v3.0.2) 3.0.2 (2021-12-13)
|
||||
* 16c9243 - Move custom dependencies to ergogen org + fix metadata (2021-12-13)
|
||||
* 5046667 - Suppress npm warnings (2021-12-13)
|
||||
* e2535da - Update dependencies (2021-12-13)
|
||||
* d193af6 - Add fresh TODO items, change to gitflow (2021-12-12)
|
||||
|
||||
# 3.0.1 (2021-12-12)
|
||||
|
||||
* 9a1f9ec - (tag: v3.0.1) 3.0.1 (2021-12-12)
|
||||
* 16f6b81 - Fillet all outline chains, closes #34 (2021-12-12)
|
||||
* 8490f14 - Add const in scrollwheel footprint (2021-08-16) <Michael van Eerd>
|
||||
* 21e173e - Make spread: 0 possible (2021-08-13) <Michael van Eerd>
|
||||
* 38a8fc5 - Anchor affect bugfix, partially fixes #33 (2021-12-12)
|
||||
* 483f214 - Array unnest bugfix, fixes #50 (2021-12-12)
|
||||
* e58b80c - Redirect readme links to new domain (2021-11-27)
|
||||
* e29613d - Dump accumulated TODOs (2021-11-27)
|
||||
* 0ec16db - Minor fix for mirrored anchor rotations (2021-11-27)
|
||||
* a593f61 - Fixed rotary encoder footprint (2021-11-07) <Albert Engelbrecht>
|
||||
* 68102e8 - Fix ReferenceError: *_unit is not defined (2021-09-03) <Michael van Eerd>
|
||||
* bf2dee6 - docs(readme): fix typo in web ui url link (2021-08-08) <Michael van Eerd>
|
||||
* ad5ac9c - Visualization fix (2021-07-25)
|
||||
|
||||
# 3.0.0 (2021-07-21)
|
||||
|
||||
* 0658057 - (tag: v3.0.0) 3.0.0 (2021-07-21)
|
||||
* fa850a6 - YAML-ification and minor output restructure (2021-07-21)
|
||||
* 282e117 - Web release prep (2021-07-21)
|
||||
* 67e01ed - Base key-level defaults around `u` (2021-07-18)
|
||||
* 9bdcd36 - Some outline testing (2021-07-18)
|
||||
* 26128f8 - Support full anchors in outline shapes (2021-07-18)
|
||||
* 677fae0 - Add roadmap (2021-07-18)
|
||||
* b159cfa - Further testing (2021-07-18)
|
||||
* 4105718 - Complete CLI testing (2021-07-17)
|
||||
* 4d88dac - End-to-end CLI tests (2021-07-17)
|
||||
* 1f3ecb5 - Improved interface testing (2021-07-16)
|
||||
* 7d841b2 - Dependency updates (2021-07-16)
|
||||
* 98d7f66 - Make old jscad stuff audit friendly (2021-07-16)
|
||||
* 58cadde - Minor case fix + test (2021-07-16)
|
||||
* f955aac - Support KLE to PCB metadata and nets (2021-07-16)
|
||||
* cd0ae6d - Smarter dump test switch (2021-07-16)
|
||||
* b43b719 - Auto-debug when output would be empty (2021-07-16)
|
||||
* bc75781 - Adjust tests for async interface (2021-07-15)
|
||||
* 1cb9fdc - Basic KLE support (2021-07-15)
|
||||
* d09b3fd - Smarter dump switch for the test runner, reference adjustments (2021-07-14)
|
||||
* 42a3e2d - CLI and output restructure, SVG/DXF/STL integration (2021-07-14)
|
||||
* c49881c - Remove leftover debug statements (2021-07-11)
|
||||
* c12e8d2 - Update footprints to match new structure (2021-07-11)
|
||||
* fe30b91 - PCB net and parameter overhaul (2021-07-11)
|
||||
* 452d7c1 - Units separated to their own block, and properly tested (2021-07-11)
|
||||
* 71efdbe - Test switch overhaul (2021-07-11)
|
||||
* ea12df2 - Fix typo (2021-07-11)
|
||||
* 2425b06 - Package audit (2021-07-06)
|
||||
* 2f6b9ab - Added new footprints for OLEDs, jumpers, and Omron B3F-4055 switches (2021-07-06) <tapioki>
|
||||
* a431bc4 - Added documentation for switch footprints (2021-07-06) <tapioki>
|
||||
* ee43a93 - Added keycaps option to display mx or choc keycaps on Dwgs.User, and fixed mx footprint (2021-06-12) <tapioki>
|
||||
* 1ff7e07 - Added new switch footprints, updated reversibility functionality, integrated hotswap footprints, added orientation to promicro, alphabetized index, and added net and paramter documentation (2021-05-23) <tapioki>
|
||||
* 3e78e6e - Add files via upload (2021-04-26) <tapioki>
|
||||
* 5a8a66b - Add files via upload (2021-04-26) <tapioki>
|
||||
* 9baae15 - Add unit test for assertions (2021-05-22)
|
||||
* c7b86c7 - Add unit test for anchors (2021-05-22)
|
||||
* 76bb071 - Add footprints to coverage, streamline coverage script call (2021-05-22)
|
||||
* 7f8b1c4 - Add basic interface tests (2021-05-16)
|
||||
* 5cd8985 - Add unit test for operation parsing (2021-05-16)
|
||||
* 2cae521 - Add unit test for utils (2021-05-16)
|
||||
* 2d6ad02 - Add unit test for point class (2021-05-16)
|
||||
* 806a7ec - Package audit (2021-05-16)
|
||||
* de0f61b - Filleting, bevel fix, poly mirroring (2021-04-18)
|
||||
* 18a76e5 - Added p.rot param to pads (#20) (2021-04-18) <adt>
|
||||
* 5754824 - fixed net ref to pad (2021-03-22) <adt>
|
||||
* c0eea2a - Fix makerjs dep and audit (2021-04-01)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 2.0.0 (2021-03-15)
|
||||
|
||||
## :boom: Breaking Changes
|
||||
|
||||
- Change indirection and preprocessing symbols
|
||||
- Rename `reset` footprint to `button`
|
||||
|
||||
## :seedling: Enhancements
|
||||
|
||||
- Added support for variables and/or units
|
||||
- String typed math formulas can now be used anywhere numbers are expected
|
||||
- For example, `spread: 19` is the same as `spread: u`
|
||||
- Added support for nested (a.k.a. deep) key notations
|
||||
- So `zones: {columns: {key: {flag: true}}}` can now be simplified to `zones.columns.key.flag: true`
|
||||
- Added support for parametric declarations
|
||||
- ...
|
||||
- Added MX and choc hotswap footprints
|
||||
- Added rotary encoder footprint - [**@adt**](https://github.com/adt)
|
||||
|
||||
## :skull: Bugfixes
|
||||
|
||||
- Various footprint fixes - [**@adt**](https://github.com/adt)
|
||||
- Documentation typos, fixes, examples - [**@Albert-IV**](https://github.com/Albert-IV) , [**@FanchGadjo**](https://github.com/FanchGadjo)
|
||||
|
||||
## :wrench: Other
|
||||
|
||||
- Lots and lots of structural refactoring, housecleaning, etc.
|
||||
- Testing infrastructure improvements
|
||||
- Better glue-related error messages
|
||||
- Propagate point visualization to the webui
|
||||
- Output SVG alongside DXF in debug mode - [**@brow**](https://github.com/brow)
|
||||
- Added part no. and updated URL for `reset` footprint - [**@brow**](https://github.com/brow)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 1.0.0 (2020-10-18)
|
||||
|
||||
## :sunglasses: Initial release!
|
||||
|
||||
- Ergogen declares its independence from the [Absolem](https://github.com/mrzealot/absolem) project
|
||||
- Thanks to [**Merlin04**](https://github.com/Merlin04) for an initial JSON schema
|
|
@ -1,644 +0,0 @@
|
|||
## Overview
|
||||
|
||||
The whole Ergogen config is a single YAML file.
|
||||
If you prefer JSON over YAML, feel free to use it, conversion is trivial and the generator will detect the input format.
|
||||
The important thing is that the data should contain the following keys:
|
||||
|
||||
```yaml
|
||||
points: <points config...>
|
||||
outlines: <outline config...>
|
||||
cases: <case config...>
|
||||
pcbs: <pcb config...>
|
||||
```
|
||||
|
||||
The `points` section describes the core of the layout: the positions of the keys.
|
||||
The `outlines` section then uses these points to generate plate, case, and PCB outlines.
|
||||
The `cases` section details how the case outlines are to be 3D-ized to form a 3D-printable object.
|
||||
Finally, the `pcbs` section is used to configure KiCAD PCB templates.
|
||||
|
||||
In the following, we'll have an in-depth discussion about each of these.
|
||||
There's also a completely separate "preprocessing" step to help reduce unnecessary repetition.
|
||||
Of course, if the declarative nature of the config is still not terse enough (despite the preprocessor, the built-in YAML references, and the Ergogen-based inheritance detailed below), there's nothing stopping you from writing code that generates the config.
|
||||
It brings the game to yet another abstraction level higher, so that you can use branching, loops, and parametric functions to compose a "drier" keyboard definition.
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Preprocessing
|
||||
|
||||
Ergogen does a separate preprocessor pass on the config before starting to interpret it.
|
||||
This consists of the following steps:
|
||||
|
||||
- **Unnesting**: any object key containing dots (as in, `.`s) will be unnested. This allows the use of the so called "dot notation". For example, `nested.key.definition: value` will turn into `{nested: {key: {definition: value}}}` in the preprocessed config object.
|
||||
|
||||
- **Inheritance**: the `$extends` key can be used in any declaration to inherit values from another declaration. Extension happens according to the following rules:
|
||||
- if the new value is `undefined`, the old value will be used as a default;
|
||||
- if both values are defined (and have the same type), the new one will override the old;
|
||||
- if both values have different types, the new value will take precedence;
|
||||
- if the new value is `$unset`, the resulting value will be `undefined`, regardless of previous type;
|
||||
- for arrays or objects, extension is called for each child element recursively.
|
||||
|
||||
The actual value of the `$extends` key should be the full absolute path of the declaration we wish to inherit from (using the above mentioned, nested "dot notation" if necessary). For example:
|
||||
|
||||
```yaml
|
||||
top:
|
||||
parent:
|
||||
a: 1
|
||||
b: 2
|
||||
child:
|
||||
$extends: top.parent
|
||||
c: 3
|
||||
```
|
||||
|
||||
This declaration will lead to `child` containing all three letter keys: `{a: 1, b: 2, c: 3}`.
|
||||
|
||||
- **Parameterization**: allows regex replacements within declarations. Take the following declaration as a starting point:
|
||||
|
||||
```yaml
|
||||
top:
|
||||
value: placeholder
|
||||
double_value: placeholder * 2
|
||||
$params: [placeholder]
|
||||
$args: [3]
|
||||
```
|
||||
|
||||
In this case, every occurrence of the value "placeholder" will be replaced with "3", which allows us to define it only once and still use it in multiple places (kind of like a pseudo-variable).
|
||||
|
||||
- **Skipping**: the `$skip` key can be used anywhere to, well, skip (or "comment out" entire declarations). It can also be useful when combining inheritance and parameterization. For example:
|
||||
|
||||
```yaml
|
||||
grandparent:
|
||||
a: placeholder1
|
||||
b: placeholder2
|
||||
$params: [placeholder1, placeholder2]
|
||||
parent:
|
||||
$extends: grandparent
|
||||
$args: [value1]
|
||||
child:
|
||||
$extends: parent
|
||||
$args: [,value2]
|
||||
```
|
||||
|
||||
Here, the grandparent defines two different parameters, but only the child knows both arguments that should be substituted. This would lead to an error at the parent's level, because it has two parameters, and only one argument. But, assuming that this is just an intermediary abstract declaration and we wouldn't want to use it anyway, we can just declare `$skip: true`.
|
||||
|
||||
The result of the preprocessor is *almost* just a plain JSON object.
|
||||
The only semantic difference is how numbers are handled. For example, the value `3 * 2` would lead to a string type in JSON, but since it's a mathematical formula, it can also be interpreted as a number.
|
||||
Ergogen tries this interpretation for every string value, and if it succeeds, it calculates the results and converts them to JSON numbers.
|
||||
This syntax also works with variables, which we can use to define units (see below).
|
||||
|
||||
Otherwise, we can begin with the actual keyboard-related layout...
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Points
|
||||
|
||||
A point in this context refers to a 2D point `[x,y]` with a rotation/orientation `r` added in.
|
||||
These can be thought of as the middle points of the keycaps in a resulting keyboard layout, with an additional handling of the angle of the keycap.
|
||||
|
||||
What makes this generator "ergo" is the implicit focus on the column-stagger.
|
||||
Of course we could simulate the traditional row-stagger by defining everything with a 90 degree rotation, but that's really not the goal here.
|
||||
Since we're focusing on column-stagger, keys are laid out in columns, and a collection of columns is called a "zone".
|
||||
For example, we can define multiple, independent zones to make it easy to differentiate between the keywell and the thumb fan/cluster.
|
||||
|
||||
Points can be described as follows:
|
||||
|
||||
```yaml
|
||||
points:
|
||||
units:
|
||||
name: val
|
||||
...
|
||||
zones:
|
||||
my_zone_name:
|
||||
anchor:
|
||||
ref: <point reference>
|
||||
orient: num # default = 0
|
||||
shift: [x, y] # default = [0, 0]
|
||||
rotate: num # default = 0
|
||||
affects: string containing any of x, y, or r # default = xyr
|
||||
columns:
|
||||
column_name: <column def>
|
||||
...
|
||||
rows:
|
||||
row_name: <row-level key def>
|
||||
...
|
||||
key: <zone-level key def>
|
||||
...
|
||||
```
|
||||
|
||||
We start with a `units` clause, where we can define units to use in relative calculations.
|
||||
The three predefined ones are `u` (=19mm), `cx` (=18mm, named for "Choc X distance"), and `cy` (=17mm, named for "Choc Y distance").
|
||||
But we can add any other (or modify these predefined ones), or even use an existing measure in calculating a new value (for example, `double: 2 u`).
|
||||
Recall how each string that can be interpreted as a math formula will be treated like a number, so this is a great way to add math-level variables to your config.
|
||||
|
||||
Then comes the `zones` key, under which we can define the individual, named zones.
|
||||
`anchors` are used to, well, anchor the zone to something.
|
||||
It's the `[0, 0]` origin with a 0 degree orientation by default, but it can be changed to any other pre-existing point. (Consequently, the first zone can't use a ref, because there isn't any yet.)
|
||||
The `ref` field can also be an array of references, in which case their average is used -- mostly useful for anchoring to the center, by averaging a key and its mirror; see later.
|
||||
This initial position can then be changed with the `orient`, `shift`, and `rotate` options.
|
||||
`shift` adds extra translation, while the difference between `orient` and `rotate` is whether they add their rotation before or after the translation.
|
||||
|
||||
Also note that anywhere an anchor can be specified, a list of anchors is also valid.
|
||||
It would be meaningless, though, if each subsequent anchor would override the previous one, so the `affects` clause helps to affect only certain dimensions of the anchor.
|
||||
It can be declared using a string containing any of `x`, `y`, or `r`, which stand for the x and y coordinates and the rotation of the anchor, respectively.
|
||||
|
||||
Once we know _where_ to start, we can describe the `columns` of our layout.
|
||||
|
||||
```yaml
|
||||
columns:
|
||||
column_name:
|
||||
stagger: num # default = 0
|
||||
spread: num # default = 19
|
||||
rotate: num # default = 0
|
||||
origin: [x, y] # relative to center of column's first key, default = [0, 0]
|
||||
rows:
|
||||
row_name: <key-specific key def>
|
||||
...
|
||||
key: <column-level key def>
|
||||
...
|
||||
```
|
||||
|
||||
`stagger` means an extra vertical shift to the starting point of the whole column compared to the previous one (initially 0, cumulative afterwards).
|
||||
The layout of the column then proceeds according to the appropriate key declarations (more on this in a minute).
|
||||
|
||||
Once the column has been laid out, `spread` (the horizontal space between this column and the next) is applied, and an optional (cumulative) rotation is added around the `origin` if `rotate` is specified.
|
||||
We repeat this until the end of the column definitions, then move on to the next zone.
|
||||
|
||||
<hr />
|
||||
|
||||
Regarding lower level layout, rows appear both in zones and columns, and keys can be defined in five (!) different places. So what gives?
|
||||
Don't worry, all this is there just so that we can keep repetition to a minimum.
|
||||
We could safely remove the `rows` and `key` options from zones, and the `key` option from column definitions, without losing any of the functionality.
|
||||
But we'd have to repeat ourselves a lot more.
|
||||
|
||||
Let's start with rows.
|
||||
`zone.rows` can give an overall picture about how many rows we'll have, and set key-related options on a per-row basis.
|
||||
But what if we want to extend this initial picture with some column-specific details? (More on "extension" in a bit.)
|
||||
For example, we want an outer pinky column where padding is tighter than it is for the others.
|
||||
That's where `column.rows` can help, specifying a row "extension" for just that column.
|
||||
|
||||
And what if we want to **override** the zone-level declarations in a certain column?
|
||||
For example, we don't just want to modify a row's attributes for a given column, but actually override the amount of rows there are.
|
||||
Like an outer pinky column with just two keys instead of the regular three.
|
||||
That's where `column.row_overrides` can help, specifying a row-level override that disregards the zone-level defaults.
|
||||
Easy.
|
||||
|
||||
Now for the trickier part: keys.
|
||||
There are five ways to set key-related options (again, to minimize the need for repetition):
|
||||
|
||||
1. at the global-level (affecting all zones)
|
||||
2. at the zone-level
|
||||
3. at the column-level
|
||||
4. at the row-level
|
||||
5. at the key-level
|
||||
|
||||
These "extend" each other in this order so by the time we reach a specific key, every level had an opportunity to modify something.
|
||||
Note that unlike the overriding for rows, key-related extension is additive.
|
||||
For example, let's suppose that a key-related attribute is already defined at the column-level.
|
||||
When we later encounter a key-level extension for this key that specifies a few things but not this exact key, its value will stay the same instead of disappearing.
|
||||
|
||||
When there is a "collision", simple values (like booleans, numbers, or strings) replace the old ones, while composites (arrays or objects) apply this same extension recursively, element-wise.
|
||||
So when `key = 1` is extended by `key = 2`, the result is `key = 2`.
|
||||
But if `key = {a: 1}` is extended by `key = {b: 2}`, the result is `key = {a: 1, b: 2}`.
|
||||
|
||||
Lastly, while there are a few key-specific attributes that have special meaning in the context of points (listed below), any key with any data can be specified here.
|
||||
This can be useful for storing arbitrary meta-info about the keys, or just configuring later stages with key-level parameters.
|
||||
So, for example, when the outline phase specifies `bind` as a key-level parameter (see below), it means that it can be specified just like any other key-level attribute.
|
||||
|
||||
Now for the "official" key-level attributes:
|
||||
|
||||
```yaml
|
||||
name: name_override # default = a concatenation of zone, column, and row
|
||||
shift: [x, y] # default = [0, 0]
|
||||
rotate: num # default = 0
|
||||
padding: num # default = 19
|
||||
skip: boolean # default = false
|
||||
asym: left | right | both # default = both
|
||||
mirror: <arbitrary key-level data>
|
||||
```
|
||||
|
||||
`name` is the unique identifier of this specific key.
|
||||
It defaults to a `<row>_<column>` format, but can be overridden if necessary.
|
||||
`shift` and `rotate` declare an extra, key-level translation or rotation, respectively.
|
||||
Then we leave `padding` amount of vertical space before moving on to the next key in the column.
|
||||
`skip` signals that the point is just a "helper" and should not be included in the output.
|
||||
This can happen when a _real_ point is more easily calculable through a "stepping stone", but then we don't actually want the stepping stone to be a key itself.
|
||||
Finally, `asym` and `mirror` relate to mirroring, which we'll cover in a second.
|
||||
|
||||
<hr />
|
||||
|
||||
Since `zones` was only a single key within the `points` section, it's reasonable to expect something more.
|
||||
Indeed:
|
||||
|
||||
```yaml
|
||||
points:
|
||||
units: <mentioned at the beginning...>
|
||||
zones: <what we talked about so far...>
|
||||
key: <global key def>
|
||||
rotate: num # default = 0
|
||||
mirror:
|
||||
axis: num # default = 0
|
||||
ref: <point reference> # and other anchor-level settings
|
||||
distance: num # default = 0
|
||||
```
|
||||
|
||||
Here, `rotate` can apply a global angle to all the points, which can simulate the inter-half angle of one-piece boards.
|
||||
Then comes the mirroring step, where the generator automatically copies and mirrors each point.
|
||||
If there's an `axis` set within the `mirror` key, points will be mirrored according to that.
|
||||
If not, the axis will be calculated so that there will be exactly `distance` mms between the `ref`erenced point and its duplicate.
|
||||
|
||||
Now if our design is symmetric, we're done.
|
||||
Otherwise, we need to use the `asym` key-level attribute to indicate which side the key should appear on.
|
||||
If it's set as `left`, mirroring will simply skip this key.
|
||||
If it's `right`, mirroring will "move" the point instead of copying it.
|
||||
The default `both` assumes symmetry.
|
||||
|
||||
Using the _key-level_ `mirror` key (not to be confused with the global `mirror` setting we just discussed above), we can set additional data for the mirrored version of the key.
|
||||
It will use the same extension mechanism as it did for the 5 levels before.
|
||||
|
||||
And this concludes point definitions.
|
||||
This should be generic enough to describe any ergo layout, yet easy enough so that you'll appreciate not having to work in raw CAD.
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Outlines
|
||||
|
||||
Once the raw points are available, we want to turn them into solid, continuous outlines.
|
||||
The points are enough to create properly positioned and rotated rectangles (with parametric side lengths), but they won't combine since there won't be any overlap.
|
||||
So the first part of the outline generation is "binding", where we make the individual holes _bind_ to each other.
|
||||
We use a key-level declarations for this:
|
||||
|
||||
```yaml
|
||||
bind: num | [num_x, num_y] | [num_t, num_r, num_b, num_l] # default = 0
|
||||
```
|
||||
|
||||
Again, key-level declaration means that both of these should be specified in the `points` section, benefiting from the same extension process every key-level setting does.
|
||||
This field declares how much we want to bind in each direction, i.e., the amount of overlap we want to make sure that we can reach the neighbor (`num` applies to all directions, `num_x` horizontally, `num_y` vertically, and the t/r/b/l versions to top/right/bottom/left, respectively).
|
||||
Note that it might make sense to have negative `bind` values, in case we not only don't want to bind in the given direction, but also don't want to "cover up" a potential corner rounding or bevel (see below).
|
||||
|
||||
If it's a one-piece design, we also need to "glue" the halves together (or we might want to leave some extra space for the controller on the inner side for splits).
|
||||
This is where the following section comes into play:
|
||||
|
||||
```yaml
|
||||
glue:
|
||||
glue_name:
|
||||
top:
|
||||
left: <anchor>
|
||||
right: <anchor> | num
|
||||
bottom:
|
||||
left: <anchor>
|
||||
right: <anchor> | num
|
||||
waypoints:
|
||||
- percent: num
|
||||
width: num | [num_left, num_right]
|
||||
- ...
|
||||
extra:
|
||||
- <primitive shape>
|
||||
- ...
|
||||
...
|
||||
```
|
||||
|
||||
...where an `<anchor>` is the same as it was for points.
|
||||
|
||||
The `top` and `bottom` fields in each glue's section are both formatted the same, and describe the center line's top and bottom intersections, respectively.
|
||||
In a one-piece case, this means that we project a line from a left-side anchor, another from the right, and converge them to where they meet.
|
||||
Split designs can specify `right` as a single number to mean the x coordinate where the side should be "cut off".
|
||||
|
||||
This leads to a gluing middle patch that can be used to meld the left and right sides together, given by the counter-clockwise polygon:
|
||||
|
||||
- Top intersection
|
||||
- Left top point
|
||||
- Left bottom point
|
||||
- Bottom intersection
|
||||
- Right bottom point
|
||||
- Right top point
|
||||
|
||||
If this is insufficient (maybe because it would leave holes), the `waypoints` can be used to supplement the glue.
|
||||
Here, `percent` means the y coordinate along the centerline (going from the top intersection to the bottom intersection), and `width` means the offset on the x axis.
|
||||
|
||||
If this is somehow _still_ insufficient (or there were problems with the binding phase), we can specify additional primitive shapes under the `extra` key (similarly to how we would use them in the exports; see below).
|
||||
These are then added to what we have so far to finish out the glue.
|
||||
(TODO: while the `extra` key is reserved for this purpose, it hasn't been needed, and therefore is unimplemented for now.)
|
||||
|
||||
<hr />
|
||||
|
||||
Once we're satisfied with the glue, the outline is generated by the union of the bound left/right halves and the glue polygon.
|
||||
Note that this outline is still parametric, so that we can specify different width/height values for the rectangles.
|
||||
|
||||
Now we can configure what we want to "export" as outlines from this phase, given by the combination/subtraction of the following primitives:
|
||||
|
||||
- `keys` : the combined outline that we've just created. Its parameters include:
|
||||
- `side: left | right | middle | both | glue` : the part we want to use
|
||||
- `left` and `right` are just the appropriate side of the laid out keys, without the glue.
|
||||
- `middle` means an "ideal" version of the glue (meaning that instead of the `outline.glue` we defined above, we get `both` - `left` - `right`, so the _exact_ middle piece we would have needed to glue everything together
|
||||
- `both` means both sides, held together by the glue
|
||||
- `glue` is just the raw glue shape we defined above under `outline.glue`
|
||||
- `tag: <array of tags>` : optional tags to filter which points to consider in this step, where tags can be specified as key-level attributes.
|
||||
- `glue: <glue_name>` : the name of the glue to use, if applicable
|
||||
- `size: num | [num_x, num_y]` : the width/height of the rectangles to lay onto the points. Note that these values are added to the evaluation context as the variables `sx` and `sy`. So during a `keys` layout with a size of 18, for example, a relative shift of `[.5 sx, .5 sy]` actually means `[9, 9]` in mms.
|
||||
- `corner: num # default = 0)` : corner radius of the rectangles
|
||||
- `bevel: num # default = 0)` : corner bevel of the rectangles, can be combined with rounding
|
||||
- `bound: boolean # default = true` : whether to use the binding declared previously
|
||||
- `rectangle` : an independent rectangle primitive. Parameters:
|
||||
- `ref`, `rotate`, and `shift`, etc. (the usual anchor settings)
|
||||
- `size`, `corner` and `bevel`, just like for `keys`
|
||||
- `circle` : an independent circle primitive. Parameters:
|
||||
- `ref`, `rotate`, and `shift`, etc. (the usual anchor settings)
|
||||
- `radius: num` : the radius of the circle
|
||||
- `polygon` : an independent polygon primitive. Parameters:
|
||||
- `points: [<anchor>, ...]` : the points of the polygon. Each `<anchor>` can have its own `ref`, `shift`, etc. (all of which are still the same as above). The only difference here is that if a `ref` is unspecified, the previous point will be assumed (as in a continuous chain). For the first, it's `[0, 0]` by default.
|
||||
- `outline` : a previously defined outline, see below.
|
||||
- `name: outline_name` : the name of the referenced outline
|
||||
|
||||
Using these, we define exports as follows:
|
||||
|
||||
```yaml
|
||||
exports:
|
||||
my_name:
|
||||
- operation: add | subtract | intersect | stack # default = add
|
||||
type: <one of the types> # default = outline
|
||||
<type-specific params>
|
||||
- ...
|
||||
```
|
||||
|
||||
Individual parts can also be specified as an object instead of an array (which could be useful when YAML or built-in inheritance is used), like so:
|
||||
|
||||
```yaml
|
||||
exports:
|
||||
my_name:
|
||||
first_phase:
|
||||
operation: add | subtract | intersect | stack # default = add
|
||||
type: <one of the types> # default = outline
|
||||
<type-specific params>
|
||||
second:
|
||||
...
|
||||
```
|
||||
|
||||
Operations are performed in order, and the resulting shape is exported as an output.
|
||||
Additionally, it is going to be available for further export declarations to use (through the `outline` type) under the name specified (`my_name`, in this case).
|
||||
If we only want to use it as a building block for further exports, we can start the name with an underscore (e.g., `_my_name`) to prevent it from being actually exported.
|
||||
(By convention, a starting underscore is kind of like a "private" marker.)
|
||||
|
||||
A shorthand version of a part can be given when the elements of the above arrays/objects are simple strings instead of further objects.
|
||||
The syntax is a symbol from `[+, -, ~, ^]`, followed by a name, and is equivalent to adding/subtracting/intersecting/stacking an outline of that name, respectively.
|
||||
More specifically, `~something` is equivalent to:
|
||||
|
||||
```yaml
|
||||
type: outline
|
||||
name: something
|
||||
operation: intersect
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Cases
|
||||
|
||||
Cases add a pretty basic and minimal 3D aspect to the generation process.
|
||||
In this phase, we take different outlines (exported from the above section, even the "private" ones), extrude and position them in space, and combine them into one 3D-printable object.
|
||||
That's it.
|
||||
Declarations might look like this:
|
||||
|
||||
```yaml
|
||||
cases:
|
||||
case_name:
|
||||
- type: outline # default option
|
||||
name: <outline ref>
|
||||
extrude: num # default = 1
|
||||
shift: [x, y, z] # default = [0, 0, 0]
|
||||
rotate: [ax, ay, az] # default = [0, 0, 0]
|
||||
operation: add | subtract | intersect # default = add
|
||||
- type: case
|
||||
name: <case_ref>
|
||||
# extrude makes no sense here...
|
||||
shift: # same as above
|
||||
rotate: # same as above
|
||||
operation: # same as above
|
||||
- ...
|
||||
...
|
||||
```
|
||||
|
||||
When the `type` is `outline`, `name` specifies which outline to import onto the xy plane, while `extrude` specifies how much it should be extruded along the z axis.
|
||||
When the `type` is `case`, `name` specifies which case to use.
|
||||
After having established our base 3D object, it is (relatively!) `rotate`d, `shift`ed, and combined with what we have so far according to `operation`.
|
||||
If we only want to use an object as a building block for further objects, we can employ the same "start with an underscore" trick we learned at the outlines section to make it "private".
|
||||
|
||||
Individual case parts can again be listed as an object instead of an array, if that's more comfortable for inheritance/reuse (just like for outlines).
|
||||
And speaking of outline similarities, the `[+, -, ~]` plus name shorthand is available again.
|
||||
First it will try to look up cases, and then outlines by the name given.
|
||||
Stacking is omitted as it makes no sense here.
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## PCBs
|
||||
|
||||
Everything should be ready for a handwire, but if you'd like the design to be more accessible and easily replicable, you probably want a PCB as well.
|
||||
To help you get started, the necessary footprints and an edge cut can be automatically positioned so that all you need to do manually is the routing.
|
||||
|
||||
Footprints can be specified at the key-level (under the `points` section, like we discussed above), or here with manually given anchors.
|
||||
The differences between the two footprint types are:
|
||||
|
||||
- an omitted `ref` in the anchor means the current key for key-level declarations, while here it defaults to `[0, 0]`
|
||||
- a parameter starting with an equal sign `=` is an indirect reference to an eponymous key-level attribute -- so, for example, `{from: =column_net}` would mean that the key's `column_net` attribute is read there.
|
||||
|
||||
Additionally, the edge cut of the PCB (or other decorative outlines for the silkscreen maybe) can be specified using a previously defined outline name under the `outlines` key.
|
||||
|
||||
```yaml
|
||||
pcbs:
|
||||
pcb_name:
|
||||
outlines:
|
||||
pcb_outline_name:
|
||||
outline: <outline reference>
|
||||
layer: <kicad layer to export to> # default = Edge.Cuts
|
||||
...
|
||||
footprints:
|
||||
- type: <footprint type>
|
||||
anchor: <anchor declaration>
|
||||
nets: <type-specific net params>
|
||||
params: <type-specific (non-net) footprint params>
|
||||
- ...
|
||||
...
|
||||
```
|
||||
|
||||
Defining both the `outlines` and the `footprints` can be done either as arrays or objects, whichever is more convenient.
|
||||
The currently supported footprint types can be viewed in [this folder](https://github.com/mrzealot/ergogen/tree/master/src/footprints), where:
|
||||
|
||||
- `nets` represents the available PCB nets the footprint should connect to, and
|
||||
- `params` represents other, non-net parameters to customize the footprint.
|
||||
|
||||
These can be specified in the eponymous keys within `pcbs.pcb_name.footprints`.
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Phew, that's it.
|
||||
|
||||
*Theoretically*, you should know everything to start making your own dream keyboard.
|
||||
*Realistically* though, this might have been a bit dense, to say the least. But hey, this is the full reference, what did you expect?
|
||||
|
||||
If you want to look at an example that leverages the full power of Ergogen, you can find [the Absolem project](https://github.com/mrzealot/absolem/) and its [configuration file here.](https://github.com/mrzealot/absolem/blob/master/absolem.yaml)
|
||||
|
||||
If you'd like to get your feet wet with easier examples, and get gradually more hard-core, let me suggest the other tutorials in the docs folder (as they become available).
|
||||
Alternatively, if you'd like to talk to a certified Ergogen representative, come join us [on Discord](https://discord.gg/nbKcAZB)!
|
||||
It's also a great place to get in touch if you are already somewhat familiar with this whole shebang, and would like to contribute examples, tests, features, whatever.
|
||||
See you there!
|
7254
kicad/23creus/23creus.kicad_pcb
Normal file
7254
kicad/23creus/23creus.kicad_pcb
Normal file
File diff suppressed because it is too large
Load diff
77
kicad/23creus/23creus.kicad_prl
Normal file
77
kicad/23creus/23creus.kicad_prl
Normal file
|
@ -0,0 +1,77 @@
|
|||
{
|
||||
"board": {
|
||||
"active_layer": 37,
|
||||
"active_layer_preset": "All Layers",
|
||||
"auto_track_width": true,
|
||||
"hidden_netclasses": [],
|
||||
"hidden_nets": [],
|
||||
"high_contrast_mode": 0,
|
||||
"net_color_mode": 1,
|
||||
"opacity": {
|
||||
"images": 0.6,
|
||||
"pads": 1.0,
|
||||
"tracks": 1.0,
|
||||
"vias": 1.0,
|
||||
"zones": 0.6
|
||||
},
|
||||
"ratsnest_display_mode": 0,
|
||||
"selection_filter": {
|
||||
"dimensions": true,
|
||||
"footprints": true,
|
||||
"graphics": true,
|
||||
"keepouts": true,
|
||||
"lockedItems": true,
|
||||
"otherItems": true,
|
||||
"pads": true,
|
||||
"text": true,
|
||||
"tracks": true,
|
||||
"vias": true,
|
||||
"zones": true
|
||||
},
|
||||
"visible_items": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
16,
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
22,
|
||||
23,
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
27,
|
||||
28,
|
||||
29,
|
||||
30,
|
||||
32,
|
||||
33,
|
||||
34,
|
||||
35,
|
||||
36
|
||||
],
|
||||
"visible_layers": "fffffff_ffffffff",
|
||||
"zone_display_mode": 0
|
||||
},
|
||||
"meta": {
|
||||
"filename": "23creus.kicad_prl",
|
||||
"version": 3
|
||||
},
|
||||
"project": {
|
||||
"files": []
|
||||
}
|
||||
}
|
731
kicad/23creus/23creus.kicad_pro
Normal file
731
kicad/23creus/23creus.kicad_pro
Normal file
|
@ -0,0 +1,731 @@
|
|||
{
|
||||
"board": {
|
||||
"3dviewports": [],
|
||||
"design_settings": {
|
||||
"defaults": {
|
||||
"board_outline_line_width": 0.049999999999999996,
|
||||
"copper_line_width": 0.19999999999999998,
|
||||
"copper_text_italic": false,
|
||||
"copper_text_size_h": 1.5,
|
||||
"copper_text_size_v": 1.5,
|
||||
"copper_text_thickness": 0.3,
|
||||
"copper_text_upright": false,
|
||||
"courtyard_line_width": 0.049999999999999996,
|
||||
"dimension_precision": 4,
|
||||
"dimension_units": 3,
|
||||
"dimensions": {
|
||||
"arrow_length": 1270000,
|
||||
"extension_offset": 500000,
|
||||
"keep_text_aligned": true,
|
||||
"suppress_zeroes": false,
|
||||
"text_position": 0,
|
||||
"units_format": 1
|
||||
},
|
||||
"fab_line_width": 0.09999999999999999,
|
||||
"fab_text_italic": false,
|
||||
"fab_text_size_h": 1.0,
|
||||
"fab_text_size_v": 1.0,
|
||||
"fab_text_thickness": 0.15,
|
||||
"fab_text_upright": false,
|
||||
"other_line_width": 0.09999999999999999,
|
||||
"other_text_italic": false,
|
||||
"other_text_size_h": 1.0,
|
||||
"other_text_size_v": 1.0,
|
||||
"other_text_thickness": 0.15,
|
||||
"other_text_upright": false,
|
||||
"pads": {
|
||||
"drill": 0.762,
|
||||
"height": 1.524,
|
||||
"width": 1.524
|
||||
},
|
||||
"silk_line_width": 0.12,
|
||||
"silk_text_italic": false,
|
||||
"silk_text_size_h": 1.0,
|
||||
"silk_text_size_v": 1.0,
|
||||
"silk_text_thickness": 0.15,
|
||||
"silk_text_upright": false,
|
||||
"zones": {
|
||||
"min_clearance": 0.508
|
||||
}
|
||||
},
|
||||
"diff_pair_dimensions": [],
|
||||
"drc_exclusions": [],
|
||||
"meta": {
|
||||
"filename": "board_design_settings.json",
|
||||
"version": 2
|
||||
},
|
||||
"rule_severities": {
|
||||
"annular_width": "error",
|
||||
"clearance": "error",
|
||||
"connection_width": "warning",
|
||||
"copper_edge_clearance": "error",
|
||||
"copper_sliver": "warning",
|
||||
"courtyards_overlap": "error",
|
||||
"diff_pair_gap_out_of_range": "error",
|
||||
"diff_pair_uncoupled_length_too_long": "error",
|
||||
"drill_out_of_range": "error",
|
||||
"duplicate_footprints": "warning",
|
||||
"extra_footprint": "warning",
|
||||
"footprint": "error",
|
||||
"footprint_type_mismatch": "ignore",
|
||||
"hole_clearance": "error",
|
||||
"hole_near_hole": "error",
|
||||
"invalid_outline": "error",
|
||||
"isolated_copper": "warning",
|
||||
"item_on_disabled_layer": "error",
|
||||
"items_not_allowed": "error",
|
||||
"length_out_of_range": "error",
|
||||
"lib_footprint_issues": "warning",
|
||||
"lib_footprint_mismatch": "warning",
|
||||
"malformed_courtyard": "error",
|
||||
"microvia_drill_out_of_range": "error",
|
||||
"missing_courtyard": "ignore",
|
||||
"missing_footprint": "warning",
|
||||
"net_conflict": "warning",
|
||||
"npth_inside_courtyard": "ignore",
|
||||
"padstack": "warning",
|
||||
"pth_inside_courtyard": "ignore",
|
||||
"shorting_items": "error",
|
||||
"silk_edge_clearance": "warning",
|
||||
"silk_over_copper": "warning",
|
||||
"silk_overlap": "warning",
|
||||
"skew_out_of_range": "error",
|
||||
"solder_mask_bridge": "error",
|
||||
"starved_thermal": "error",
|
||||
"text_height": "warning",
|
||||
"text_thickness": "warning",
|
||||
"through_hole_pad_without_hole": "error",
|
||||
"too_many_vias": "error",
|
||||
"track_dangling": "warning",
|
||||
"track_width": "error",
|
||||
"tracks_crossing": "error",
|
||||
"unconnected_items": "error",
|
||||
"unresolved_variable": "error",
|
||||
"via_dangling": "warning",
|
||||
"zones_intersect": "error"
|
||||
},
|
||||
"rules": {
|
||||
"max_error": 0.005,
|
||||
"min_clearance": 0.0,
|
||||
"min_connection": 0.0,
|
||||
"min_copper_edge_clearance": 0.075,
|
||||
"min_hole_clearance": 0.25,
|
||||
"min_hole_to_hole": 0.25,
|
||||
"min_microvia_diameter": 0.19999999999999998,
|
||||
"min_microvia_drill": 0.09999999999999999,
|
||||
"min_resolved_spokes": 2,
|
||||
"min_silk_clearance": 0.0,
|
||||
"min_text_height": 0.7999999999999999,
|
||||
"min_text_thickness": 0.08,
|
||||
"min_through_hole_diameter": 0.3,
|
||||
"min_track_width": 0.19999999999999998,
|
||||
"min_via_annular_width": 0.09999999999999999,
|
||||
"min_via_diameter": 0.39999999999999997,
|
||||
"solder_mask_to_copper_clearance": 0.0,
|
||||
"use_height_for_length_calcs": true
|
||||
},
|
||||
"teardrop_options": [
|
||||
{
|
||||
"td_allow_use_two_tracks": true,
|
||||
"td_curve_segcount": 5,
|
||||
"td_on_pad_in_zone": false,
|
||||
"td_onpadsmd": true,
|
||||
"td_onroundshapesonly": false,
|
||||
"td_ontrackend": false,
|
||||
"td_onviapad": true
|
||||
}
|
||||
],
|
||||
"teardrop_parameters": [
|
||||
{
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_target_name": "td_round_shape",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
},
|
||||
{
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_target_name": "td_rect_shape",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
},
|
||||
{
|
||||
"td_curve_segcount": 0,
|
||||
"td_height_ratio": 1.0,
|
||||
"td_length_ratio": 0.5,
|
||||
"td_maxheight": 2.0,
|
||||
"td_maxlen": 1.0,
|
||||
"td_target_name": "td_track_end",
|
||||
"td_width_to_size_filter_ratio": 0.9
|
||||
}
|
||||
],
|
||||
"track_widths": [],
|
||||
"via_dimensions": [],
|
||||
"zones_allow_external_fillets": false,
|
||||
"zones_use_no_outline": true
|
||||
},
|
||||
"layer_presets": [],
|
||||
"viewports": []
|
||||
},
|
||||
"boards": [],
|
||||
"cvpcb": {
|
||||
"equivalence_files": []
|
||||
},
|
||||
"erc": {
|
||||
"erc_exclusions": [],
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"pin_map": [
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
]
|
||||
],
|
||||
"rule_severities": {
|
||||
"bus_definition_conflict": "error",
|
||||
"bus_entry_needed": "error",
|
||||
"bus_label_syntax": "error",
|
||||
"bus_to_bus_conflict": "error",
|
||||
"bus_to_net_conflict": "error",
|
||||
"different_unit_footprint": "error",
|
||||
"different_unit_net": "error",
|
||||
"duplicate_reference": "error",
|
||||
"duplicate_sheet_names": "error",
|
||||
"extra_units": "error",
|
||||
"global_label_dangling": "warning",
|
||||
"hier_label_mismatch": "error",
|
||||
"label_dangling": "error",
|
||||
"lib_symbol_issues": "warning",
|
||||
"multiple_net_names": "warning",
|
||||
"net_not_bus_member": "warning",
|
||||
"no_connect_connected": "warning",
|
||||
"no_connect_dangling": "warning",
|
||||
"pin_not_connected": "error",
|
||||
"pin_not_driven": "error",
|
||||
"pin_to_pin": "warning",
|
||||
"power_pin_not_driven": "error",
|
||||
"similar_labels": "warning",
|
||||
"unannotated": "error",
|
||||
"unit_value_mismatch": "error",
|
||||
"unresolved_variable": "error",
|
||||
"wire_dangling": "error"
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"pinned_footprint_libs": [],
|
||||
"pinned_symbol_libs": []
|
||||
},
|
||||
"meta": {
|
||||
"filename": "23creus.kicad_pro",
|
||||
"version": 1
|
||||
},
|
||||
"net_settings": {
|
||||
"classes": [
|
||||
{
|
||||
"bus_width": 12,
|
||||
"clearance": 0.2,
|
||||
"diff_pair_gap": 0.25,
|
||||
"diff_pair_via_gap": 0.25,
|
||||
"diff_pair_width": 0.2,
|
||||
"line_style": 0,
|
||||
"microvia_diameter": 0.3,
|
||||
"microvia_drill": 0.1,
|
||||
"name": "Default",
|
||||
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 0.25,
|
||||
"via_diameter": 0.8,
|
||||
"via_drill": 0.4,
|
||||
"wire_width": 6
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"version": 3
|
||||
},
|
||||
"net_colors": null,
|
||||
"netclass_assignments": null,
|
||||
"netclass_patterns": [
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": ""
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "pinky_bottom"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "P21"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "pinky_home"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "pinky_top"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "pinky_num"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "ring_bottom"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "P20"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "ring_home"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "ring_top"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "ring_num"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "middle_bottom"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "P19"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "middle_home"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "middle_top"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "middle_num"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "index_bottom"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "P18"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "index_home"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "index_top"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "index_num"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "inner_bottom"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "P15"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "inner_home"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "inner_top"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "inner_num"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "thumb_bottom"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "P14"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_pinky_bottom"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "P2"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_pinky_home"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_pinky_top"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_pinky_num"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_ring_bottom"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "P3"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_ring_home"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_ring_top"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_ring_num"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_middle_bottom"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "P4"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_middle_home"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_middle_top"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_middle_num"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_index_bottom"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "P5"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_index_home"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_index_top"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_index_num"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_inner_bottom"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "P6"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_inner_home"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_inner_top"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_inner_num"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "mirror_thumb_bottom"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "P7"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "P16"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "P10"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "P9"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "P8"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "RST"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "GND"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "RAW"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "VCC"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "P1"
|
||||
},
|
||||
{
|
||||
"netclass": "Default",
|
||||
"pattern": "P0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"pcbnew": {
|
||||
"last_paths": {
|
||||
"gencad": "",
|
||||
"idf": "",
|
||||
"netlist": "",
|
||||
"specctra_dsn": "",
|
||||
"step": "",
|
||||
"vrml": ""
|
||||
},
|
||||
"page_layout_descr_file": ""
|
||||
},
|
||||
"schematic": {
|
||||
"annotate_start_num": 0,
|
||||
"drawing": {
|
||||
"default_line_thickness": 6.0,
|
||||
"default_text_size": 50.0,
|
||||
"field_names": [],
|
||||
"intersheets_ref_own_page": false,
|
||||
"intersheets_ref_prefix": "",
|
||||
"intersheets_ref_short": false,
|
||||
"intersheets_ref_show": false,
|
||||
"intersheets_ref_suffix": "",
|
||||
"junction_size_choice": 3,
|
||||
"label_size_ratio": 0.375,
|
||||
"pin_symbol_size": 25.0,
|
||||
"text_offset_ratio": 0.15
|
||||
},
|
||||
"legacy_lib_dir": "",
|
||||
"legacy_lib_list": [],
|
||||
"meta": {
|
||||
"version": 1
|
||||
},
|
||||
"net_format_name": "",
|
||||
"ngspice": {
|
||||
"fix_include_paths": true,
|
||||
"fix_passive_vals": false,
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"model_mode": 0,
|
||||
"workbook_filename": ""
|
||||
},
|
||||
"page_layout_descr_file": "",
|
||||
"plot_directory": "",
|
||||
"spice_adjust_passive_values": false,
|
||||
"spice_external_command": "spice \"%I\"",
|
||||
"subpart_first_id": 65,
|
||||
"subpart_id_separator": 0
|
||||
},
|
||||
"sheets": [],
|
||||
"text_variables": {}
|
||||
}
|
5
kicad/23creus/23creus.kicad_sch
Normal file
5
kicad/23creus/23creus.kicad_sch
Normal file
|
@ -0,0 +1,5 @@
|
|||
(kicad_sch (version 20211123) (generator eeschema)
|
||||
(paper "A4")
|
||||
(lib_symbols)
|
||||
(symbol_instances)
|
||||
)
|
7254
kicad/23creus/main.kicad_pcb
Normal file
7254
kicad/23creus/main.kicad_pcb
Normal file
File diff suppressed because it is too large
Load diff
1
kicad/45treus/45treus.kicad_pcb
Normal file
1
kicad/45treus/45treus.kicad_pcb
Normal file
|
@ -0,0 +1 @@
|
|||
(kicad_pcb (version 4) (host kicad "dummy file") )
|
33
kicad/45treus/45treus.pro
Normal file
33
kicad/45treus/45treus.pro
Normal file
|
@ -0,0 +1,33 @@
|
|||
update=22/05/2015 07:44:53
|
||||
version=1
|
||||
last_client=kicad
|
||||
[general]
|
||||
version=1
|
||||
RootSch=
|
||||
BoardNm=
|
||||
[pcbnew]
|
||||
version=1
|
||||
LastNetListRead=
|
||||
UseCmpFile=1
|
||||
PadDrill=0.600000000000
|
||||
PadDrillOvalY=0.600000000000
|
||||
PadSizeH=1.500000000000
|
||||
PadSizeV=1.500000000000
|
||||
PcbTextSizeV=1.500000000000
|
||||
PcbTextSizeH=1.500000000000
|
||||
PcbTextThickness=0.300000000000
|
||||
ModuleTextSizeV=1.000000000000
|
||||
ModuleTextSizeH=1.000000000000
|
||||
ModuleTextSizeThickness=0.150000000000
|
||||
SolderMaskClearance=0.000000000000
|
||||
SolderMaskMinWidth=0.000000000000
|
||||
DrawSegmentWidth=0.200000000000
|
||||
BoardOutlineThickness=0.100000000000
|
||||
ModuleOutlineThickness=0.150000000000
|
||||
[cvpcb]
|
||||
version=1
|
||||
NetIExt=net
|
||||
[eeschema]
|
||||
version=1
|
||||
LibDir=
|
||||
[eeschema/libraries]
|
4
kicad/45treus/45treus.sch
Normal file
4
kicad/45treus/45treus.sch
Normal file
|
@ -0,0 +1,4 @@
|
|||
EESchema Schematic File Version 2
|
||||
EELAYER 25 0
|
||||
EELAYER END
|
||||
$EndSCHEMATC
|
1
kicad/45treus/fp-info-cache
Normal file
1
kicad/45treus/fp-info-cache
Normal file
|
@ -0,0 +1 @@
|
|||
0
|
BIN
kicad/45treus/gerber_v002/23treus_v002.zip
Normal file
BIN
kicad/45treus/gerber_v002/23treus_v002.zip
Normal file
Binary file not shown.
BIN
kicad/45treus/gerber_v003/23treus_v003.zip
Normal file
BIN
kicad/45treus/gerber_v003/23treus_v003.zip
Normal file
Binary file not shown.
6660
kicad/45treus/main_version_001.kicad_pcb
Normal file
6660
kicad/45treus/main_version_001.kicad_pcb
Normal file
File diff suppressed because it is too large
Load diff
33
kicad/45treus/main_version_001.pro
Normal file
33
kicad/45treus/main_version_001.pro
Normal file
|
@ -0,0 +1,33 @@
|
|||
update=22/05/2015 07:44:53
|
||||
version=1
|
||||
last_client=kicad
|
||||
[general]
|
||||
version=1
|
||||
RootSch=
|
||||
BoardNm=
|
||||
[pcbnew]
|
||||
version=1
|
||||
LastNetListRead=
|
||||
UseCmpFile=1
|
||||
PadDrill=0.600000000000
|
||||
PadDrillOvalY=0.600000000000
|
||||
PadSizeH=1.500000000000
|
||||
PadSizeV=1.500000000000
|
||||
PcbTextSizeV=1.500000000000
|
||||
PcbTextSizeH=1.500000000000
|
||||
PcbTextThickness=0.300000000000
|
||||
ModuleTextSizeV=1.000000000000
|
||||
ModuleTextSizeH=1.000000000000
|
||||
ModuleTextSizeThickness=0.150000000000
|
||||
SolderMaskClearance=0.000000000000
|
||||
SolderMaskMinWidth=0.000000000000
|
||||
DrawSegmentWidth=0.200000000000
|
||||
BoardOutlineThickness=0.100000000000
|
||||
ModuleOutlineThickness=0.150000000000
|
||||
[cvpcb]
|
||||
version=1
|
||||
NetIExt=net
|
||||
[eeschema]
|
||||
version=1
|
||||
LibDir=
|
||||
[eeschema/libraries]
|
6746
kicad/45treus/main_version_002_with_switch_and_reset.kicad_pcb
Normal file
6746
kicad/45treus/main_version_002_with_switch_and_reset.kicad_pcb
Normal file
File diff suppressed because it is too large
Load diff
33
kicad/45treus/main_version_002_with_switch_and_reset.pro
Normal file
33
kicad/45treus/main_version_002_with_switch_and_reset.pro
Normal file
|
@ -0,0 +1,33 @@
|
|||
update=22/05/2015 07:44:53
|
||||
version=1
|
||||
last_client=kicad
|
||||
[general]
|
||||
version=1
|
||||
RootSch=
|
||||
BoardNm=
|
||||
[pcbnew]
|
||||
version=1
|
||||
LastNetListRead=
|
||||
UseCmpFile=1
|
||||
PadDrill=0.600000000000
|
||||
PadDrillOvalY=0.600000000000
|
||||
PadSizeH=1.500000000000
|
||||
PadSizeV=1.500000000000
|
||||
PcbTextSizeV=1.500000000000
|
||||
PcbTextSizeH=1.500000000000
|
||||
PcbTextThickness=0.300000000000
|
||||
ModuleTextSizeV=1.000000000000
|
||||
ModuleTextSizeH=1.000000000000
|
||||
ModuleTextSizeThickness=0.150000000000
|
||||
SolderMaskClearance=0.000000000000
|
||||
SolderMaskMinWidth=0.000000000000
|
||||
DrawSegmentWidth=0.200000000000
|
||||
BoardOutlineThickness=0.100000000000
|
||||
ModuleOutlineThickness=0.150000000000
|
||||
[cvpcb]
|
||||
version=1
|
||||
NetIExt=net
|
||||
[eeschema]
|
||||
version=1
|
||||
LibDir=
|
||||
[eeschema/libraries]
|
7161
kicad/45treus/main_version_003_working.kicad_pcb
Normal file
7161
kicad/45treus/main_version_003_working.kicad_pcb
Normal file
File diff suppressed because it is too large
Load diff
4164
package-lock.json
generated
4164
package-lock.json
generated
File diff suppressed because it is too large
Load diff
28
package.json
28
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ergogen",
|
||||
"version": "3.1.1",
|
||||
"version": "4.0.5",
|
||||
"description": "Ergonomic keyboard layout generator",
|
||||
"author": "Bán Dénes <mr@zealot.hu>",
|
||||
"license": "MIT",
|
||||
|
@ -15,25 +15,25 @@
|
|||
"coverage": "nyc --reporter=html --reporter=text npm test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jscad/openjscad": "github:ergogen/oldjscad",
|
||||
"fs-extra": "^10.0.0",
|
||||
"js-yaml": "^3.14.0",
|
||||
"fs-extra": "^11.1.0",
|
||||
"js-yaml": "^3.14.1",
|
||||
"jszip": "^3.10.1",
|
||||
"kle-serial": "github:ergogen/kle-serial#ergogen",
|
||||
"makerjs": "github:ergogen/maker.js#ergogen",
|
||||
"mathjs": "^10.0.0",
|
||||
"semver": "^7.3.5",
|
||||
"yargs": "^17.3.0"
|
||||
"mathjs": "^11.5.0",
|
||||
"yargs": "^17.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^21.0.1",
|
||||
"@rollup/plugin-json": "^4.1.0",
|
||||
"chai": "^4.3.4",
|
||||
"@rollup/plugin-commonjs": "^24.0.1",
|
||||
"@rollup/plugin-json": "^6.0.0",
|
||||
"chai": "^4.3.7",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"dir-compare": "^3.3.0",
|
||||
"glob": "^7.2.0",
|
||||
"mocha": "^9.1.3",
|
||||
"dir-compare": "^4.0.0",
|
||||
"glob": "^8.1.0",
|
||||
"mocha": "^10.2.0",
|
||||
"nyc": "^15.1.0",
|
||||
"rollup": "^2.61.1"
|
||||
"rollup": "^3.10.1",
|
||||
"sinon": "^15.0.1"
|
||||
},
|
||||
"nyc": {
|
||||
"all": true,
|
||||
|
|
30
readme.md
30
readme.md
|
@ -1,30 +0,0 @@
|
|||
# Ergogen
|
||||
|
||||
Ergogen is a keyboard generator that aims to provide a common configuration format to describe **ergonomic** 2D layouts, and generate automatic plates, cases, and (un-routed) PCBs for them.
|
||||
The project grew out of (and is an integral part of) the [Absolem keyboard](https://zealot.hu/absolem), and shares its [Discord server](https://discord.gg/nbKcAZB) as well.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
Supposing you have a config ready, you can use ergogen either on the command line, or through the [web UI](https://ergogen.xyz).
|
||||
|
||||
Command line usage requires `node v14.4.0+` with `npm v6.14.5+` to be installed, the repo to be checked out, `npm install` to be issued, and then simply calling the CLI interface through `node src/cli.js`.
|
||||
The `--help` switch lists the available command line options.
|
||||
|
||||
The [web UI](https://ergogen.xyz) is a more accessible version of the same codebase, where everything happens in your browser.
|
||||
It's been patched together on a fresh Chrome-derivative, and I didn't take any care to make it compatible with older stuff, so please use something modern!
|
||||
|
||||
As for how to prepare a valid config, please read the [reference](https://docs.ergogen.xyz).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Contributions
|
||||
|
||||
Feature ideas, documentation improvements, examples, tests, or pull requests welcome!
|
||||
Get in touch [on Discord](https://discord.gg/nbKcAZB), and we can definitely find something you can help with, if you'd like to.
|
74
roadmap.md
74
roadmap.md
|
@ -6,54 +6,62 @@
|
|||
|
||||
### Major
|
||||
|
||||
- Move column-level attributes like spread to key-level to unify the structure
|
||||
- Generalize what shapes to be repeated when outlining `keys`
|
||||
- Place rectangles by their centers
|
||||
- Full per-point anchors
|
||||
- Collapse any raw shift or rotation under the anchor infrastructure
|
||||
- Merge, generalize, and uniform-ize footprints
|
||||
- Merge, generalize, uniform-ize and externalize footprints!
|
||||
- onnx-like incremental opset versioning
|
||||
- Template for creating them, built-in variables they can use, documentation, external links, etc.
|
||||
- Also considering how (or, on which layer) they define their silks, universal mirroring behaviour, etc.
|
||||
- Add access to whole set of points + filtering logic, so they can implement their own connection logic as well maybe (see daisy chaining)
|
||||
- Also considering how (or, on which layer) they define their silks, universal mirroring behaviour (see ixy/xy/sxy note), etc.
|
||||
|
||||
### Minor
|
||||
|
||||
- Allow shift/rotate for outlines (via `anchor_def`, probably)
|
||||
- More generic anchors or distances?
|
||||
- Intersect support for anchor affects clauses, which (combined with the math formulas and possible trigonometric functions) should allow for every use case we've discussed so far
|
||||
- Allow both object (as well as arrays) in multiple anchor refs
|
||||
- Support "direct" anchors, as in, recognize num arrays and parse them as x/y/r
|
||||
- Add `origin` to zone-wide and global rotation in points
|
||||
- Handle unnecessary (but seemingly consistent, so easy to confuse) `key` subfield of row-level overrides
|
||||
- Allow footprints to access raw array/object fields from points with templating
|
||||
- Include raw kicad footprint integrations
|
||||
- pull torik's script to be able to convert raw kicad footprints into positionable ergogen ones
|
||||
- have a `dummy` footprint which can just be updated from schematic
|
||||
- Allow footprints to publish outlines
|
||||
- Make these usable in the `outlines` section through a new `what`
|
||||
- Add footprint sanitization to check compatibility for externally loaded ones
|
||||
- Or to double check internal ones for compliance
|
||||
- 3D orient for cases
|
||||
- Even more extreme anchor stuff
|
||||
- Checkpoints, intersects, distances, weighted combinations?
|
||||
- Extend the "turning towards" capabilities of `orient` and `rotate` to `shift` as well (to move as much as it would take the current anchor location to get there)
|
||||
- SVG input (for individual outlines, or even combinations parsed by line color, etc.)
|
||||
- And once that's done, possibly even STL or other input for cases or pcb renders
|
||||
- Support text silk output to PCBs (in configurable fonts, through SVG?)
|
||||
- Maybe a partial markdown preprocess to support bold and italic?
|
||||
- Look into gr_curve to possibly add beziers to the kicad conversion
|
||||
- Support curves (arcs as well as Béziers) in polygons
|
||||
- Support specifying keys/labels for the pcb section (not just blindly assuming all)
|
||||
- Also, three point arcs, tangents, earier "circle tools" in general
|
||||
- Add snappable line footprint
|
||||
- Layer-aware export from Maker.JS, so we can debug in the webui more easily
|
||||
- Add filleting syntax with `@`?
|
||||
- Figure out a manual, but still reasonably comfortable routing method directly from the config
|
||||
- Eeschema support for pcbs
|
||||
- Outline expand and shrink access from makerjs
|
||||
- Resurrect and/or add wider tagging support
|
||||
- Also add subtractive tagging filters (exclude)
|
||||
- Also expand this to footprints (so, which footprints get applied to which pcb)
|
||||
- Or, at least, allow skipping per-key footprints
|
||||
- Generate ZMK shield from config
|
||||
- Export **to** KLE?
|
||||
- Per-footprint mirror support
|
||||
- A flag for footprints to be able to "resist" the mirroring-related special treatment of negative X shift, rotation, etc.
|
||||
- Include 3D models for kicad output for visualization
|
||||
- Include 3D models paths in kicad output for visualization
|
||||
- Also, provide 3D models for built-in footprints
|
||||
- Look into kicad 5 vs. 6 output format
|
||||
- Update json schema and add syntax highlight to editors
|
||||
- Support different netclasses
|
||||
- Allow a potential filter for filleting (only on angles =90°, <45°, left turn vs. right turn when going clockwise, etc.)
|
||||
- Add `operation: skip` to allow easily "commenting out" whole outline parts
|
||||
- Better error message for negative rectangle (it may not only be because of corner/bevel)
|
||||
|
||||
|
||||
### Patch
|
||||
|
||||
- YAML lib v4 update - breaking changes in how undefined is handled!
|
||||
- Prevent double mirroring (see discord "mirror_mirror_")
|
||||
- Check unexpected keys at top level, too
|
||||
- Better error handling for the fillet option?
|
||||
- Implement `glue.extra`
|
||||
- Integration and end2end tests to get coverage to 100%
|
||||
- Fix the intersection of parallel lines when gluing
|
||||
- Add custom fillet implementation that considers line-line connections only
|
||||
|
||||
- Add custom fillet implementation that considers line-line connections only?
|
||||
- Add nicer filleting error messages when makerjs dies for some reason
|
||||
- Empty nets should be allowed (to mean unconnected)
|
||||
- Debug point (orient+shift) differences in circles vs. polygons (see Discord)
|
||||
|
||||
|
||||
## WEBUI
|
||||
|
@ -63,6 +71,9 @@
|
|||
- Change over to Cache's live preview implementation
|
||||
- Add missing KLE functionality
|
||||
- Create browserified version of semver lib
|
||||
- Or at least a shim with a console warning
|
||||
- Visualizing multiple outlines at once, with different colors
|
||||
- Add snapping/measurement capabilities for quicker iteration
|
||||
|
||||
### Minor
|
||||
|
||||
|
@ -70,12 +81,13 @@
|
|||
- Attempt to auto-compile (if inactive for n secs, or whatever)
|
||||
- Support saving to gists
|
||||
- Add kicad_pcb visualization as well
|
||||
- Get dropdown examples from a separate repo
|
||||
- Expand the config dropdown with opensource stuff: corne, lily, ergodox, atreus...
|
||||
|
||||
### Patch
|
||||
|
||||
- Streamlining (and documenting) an update pipeline
|
||||
- Puppeteer tests
|
||||
- Streamline (and document) an update pipeline
|
||||
- Add puppeteer tests
|
||||
|
||||
|
||||
|
||||
|
@ -85,18 +97,18 @@
|
|||
- With a progression of increasingly complex steps
|
||||
- And lots of illustrations!
|
||||
- Complete reference
|
||||
- some known deficiencies:
|
||||
- Some known deficiencies:
|
||||
- Units separated to their own block at the front
|
||||
- Key-level `width` and `height` are supported during visualization
|
||||
- This key-level example should probably be added from discord: https://discord.com/channels/714176584269168732/759825860617437204/773104093546676244
|
||||
- Change outline fields to have their full anchor support documented
|
||||
- Mention the ability to opt out of gluing!
|
||||
- Key-level defaults are based around u's, not 19!
|
||||
- change over to built, per-chapter docs, like how Cache has them
|
||||
- Contribution guidelines
|
||||
- including test commands (npm test, npm run coverage, --what switch, --dump switch)
|
||||
- Include test commands (npm test, npm run coverage, --what switch, --dump switch)
|
||||
- Changelog, Roadmap
|
||||
- A public catalog of real-life ergogen configs
|
||||
- Probably could be the same as the separate examples repo for the dropdown
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,22 +1,21 @@
|
|||
import pkg from './package.json'
|
||||
import pkg from './package.json' assert { type: 'json' }
|
||||
import json from '@rollup/plugin-json'
|
||||
import commonjs from '@rollup/plugin-commonjs'
|
||||
|
||||
export default {
|
||||
input: 'src/ergogen.js',
|
||||
external: ['makerjs', 'js-yaml', 'mathjs', 'kle-serial', '@jscad/openjscad', 'semver'],
|
||||
external: ['makerjs', 'js-yaml', 'mathjs', 'kle-serial', 'jszip'],
|
||||
output: {
|
||||
name: 'ergogen',
|
||||
file: 'dist/ergogen.js',
|
||||
format: 'umd',
|
||||
banner: `/*!\n * Ergogen v${pkg.version}\n * https://zealot.hu/ergogen\n */\n`,
|
||||
banner: `/*!\n * Ergogen v${pkg.version}\n * https://ergogen.xyz\n */\n`,
|
||||
globals: {
|
||||
'makerjs': 'makerjs',
|
||||
'js-yaml': 'jsyaml',
|
||||
'mathjs': 'math',
|
||||
'kle-serial': 'kle',
|
||||
'@jscad/openjscad': 'myjscad',
|
||||
'semver': 'semver'
|
||||
'jszip': 'jszip'
|
||||
}
|
||||
},
|
||||
plugins: [
|
8
shell.nix
Normal file
8
shell.nix
Normal file
|
@ -0,0 +1,8 @@
|
|||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
bashInteractive
|
||||
nodejs-16_x
|
||||
];
|
||||
}
|
BIN
showcase.png
Normal file
BIN
showcase.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
167
src/anchor.js
167
src/anchor.js
|
@ -1,73 +1,159 @@
|
|||
const u = require('./utils')
|
||||
const a = require('./assert')
|
||||
const Point = require('./point')
|
||||
const m = require('makerjs')
|
||||
|
||||
const mirror_ref = exports.mirror = (ref, mirror) => {
|
||||
const mirror_ref = exports.mirror = (ref, mirror=true) => {
|
||||
if (mirror) {
|
||||
if (ref.startsWith('mirror_')) {
|
||||
return ref.substring(7)
|
||||
} else {
|
||||
return 'mirror_' + ref
|
||||
}
|
||||
return 'mirror_' + ref
|
||||
}
|
||||
return ref
|
||||
}
|
||||
|
||||
const anchor = exports.parse = (raw, name, points={}, check_unexpected=true, default_point=new Point(), mirror=false) => units => {
|
||||
if (a.type(raw)() == 'array') {
|
||||
// recursive call with incremental default_point mods, according to `affect`s
|
||||
let current = default_point.clone()
|
||||
const aggregator_common = ['parts', 'method']
|
||||
|
||||
const aggregators = {
|
||||
average: (config, name, parts) => {
|
||||
a.unexpected(config, name, aggregator_common)
|
||||
const len = parts.length
|
||||
if (len == 0) {
|
||||
return new Point()
|
||||
}
|
||||
let x = 0, y = 0, r = 0
|
||||
for (const part of parts) {
|
||||
x += part.x
|
||||
y += part.y
|
||||
r += part.r
|
||||
}
|
||||
return new Point(x / len, y / len, r / len)
|
||||
},
|
||||
intersect: (config, name, parts) => {
|
||||
// a line is generated from a point by taking their
|
||||
// (rotated) Y axis. The line is not extended to
|
||||
// +/- Infinity as that doesn't work with makerjs.
|
||||
// An arbitrary offset of 1 meter is considered
|
||||
// sufficient for practical purposes, and the point
|
||||
// coordinates are used as pivot point for the rotation.
|
||||
const get_line_from_point = (point, offset=1000) => {
|
||||
const origin = [point.x, point.y]
|
||||
const p1 = [point.x, point.y - offset]
|
||||
const p2 = [point.x, point.y + offset]
|
||||
|
||||
let line = new m.paths.Line(p1, p2)
|
||||
line = m.path.rotate(line, point.r, origin)
|
||||
|
||||
return line
|
||||
}
|
||||
|
||||
a.unexpected(config, name, aggregator_common)
|
||||
a.assert(parts.length==2, `Intersect expects exactly two parts, but it got ${parts.length}!`)
|
||||
|
||||
const line1 = get_line_from_point(parts[0])
|
||||
const line2 = get_line_from_point(parts[1])
|
||||
const intersection = m.path.intersection(line1, line2)
|
||||
|
||||
a.assert(intersection, `The points under "${name}.parts" do not intersect!`)
|
||||
|
||||
const intersection_point_arr = intersection.intersectionPoints[0]
|
||||
const intersection_point = new Point(
|
||||
intersection_point_arr[0], intersection_point_arr[1], 0
|
||||
)
|
||||
|
||||
return intersection_point
|
||||
},
|
||||
}
|
||||
|
||||
const anchor = exports.parse = (raw, name, points={}, start=new Point(), mirror=false) => units => {
|
||||
|
||||
//
|
||||
// Anchor type handling
|
||||
//
|
||||
|
||||
if (a.type(raw)() == 'string') {
|
||||
raw = {ref: raw}
|
||||
}
|
||||
|
||||
else if (a.type(raw)() == 'array') {
|
||||
// recursive call with incremental start mods, according to `affect`s
|
||||
let current = start.clone()
|
||||
let index = 1
|
||||
for (const step of raw) {
|
||||
current = anchor(step, name, points, check_unexpected, current, mirror)(units)
|
||||
current = anchor(step, `${name}[${index++}]`, points, current, mirror)(units)
|
||||
}
|
||||
return current
|
||||
}
|
||||
if (check_unexpected) a.unexpected(raw, name, ['ref', 'orient', 'shift', 'rotate', 'affect'])
|
||||
let point = default_point.clone()
|
||||
|
||||
a.unexpected(raw, name, ['ref', 'aggregate', 'orient', 'shift', 'rotate', 'affect', 'resist'])
|
||||
|
||||
//
|
||||
// Reference or aggregate handling
|
||||
//
|
||||
|
||||
let point = start.clone()
|
||||
if (raw.ref !== undefined && raw.aggregate !== undefined) {
|
||||
throw new Error(`Fields "ref" and "aggregate" cannot appear together in anchor "${name}"!`)
|
||||
}
|
||||
|
||||
if (raw.ref !== undefined) {
|
||||
if (a.type(raw.ref)() == 'array') {
|
||||
// averaging multiple anchors
|
||||
let x = 0, y = 0, r = 0
|
||||
const len = raw.ref.length
|
||||
for (const ref of raw.ref) {
|
||||
const parsed_ref = mirror_ref(ref, mirror)
|
||||
a.assert(points[parsed_ref], `Unknown point reference "${parsed_ref}" in anchor "${name}"!`)
|
||||
const resolved = points[parsed_ref]
|
||||
x += resolved.x
|
||||
y += resolved.y
|
||||
r += resolved.r
|
||||
}
|
||||
point = new Point(x / len, y / len, r / len)
|
||||
} else {
|
||||
// base case, resolve directly
|
||||
if (a.type(raw.ref)() == 'string') {
|
||||
const parsed_ref = mirror_ref(raw.ref, mirror)
|
||||
a.assert(points[parsed_ref], `Unknown point reference "${parsed_ref}" in anchor "${name}"!`)
|
||||
point = points[parsed_ref].clone()
|
||||
// recursive case
|
||||
} else {
|
||||
point = anchor(raw.ref, `${name}.ref`, points, start, mirror)(units)
|
||||
}
|
||||
}
|
||||
|
||||
if (raw.aggregate !== undefined) {
|
||||
raw.aggregate = a.sane(raw.aggregate, `${name}.aggregate`, 'object')()
|
||||
raw.aggregate.method = a.sane(raw.aggregate.method || 'average', `${name}.aggregate.method`, 'string')()
|
||||
a.assert(aggregators[raw.aggregate.method], `Unknown aggregator method "${raw.aggregate.method}" in anchor "${name}"!`)
|
||||
raw.aggregate.parts = a.sane(raw.aggregate.parts || [], `${name}.aggregate.parts`, 'array')()
|
||||
|
||||
const parts = []
|
||||
let index = 1
|
||||
for (const part of raw.aggregate.parts) {
|
||||
parts.push(anchor(part, `${name}.aggregate.parts[${index++}]`, points, start, mirror)(units))
|
||||
}
|
||||
|
||||
point = aggregators[raw.aggregate.method](raw.aggregate, `${name}.aggregate`, parts)
|
||||
}
|
||||
|
||||
//
|
||||
// Actual orient/shift/rotate/affect handling
|
||||
//
|
||||
|
||||
const resist = a.sane(raw.resist || false, `${name}.resist`, 'boolean')()
|
||||
const rotator = (config, name, point) => {
|
||||
// simple case: number gets added to point rotation
|
||||
if (a.type(config)(units) == 'number') {
|
||||
let angle = a.sane(config, name, 'number')(units)
|
||||
point.rotate(angle, false, resist)
|
||||
// recursive case: points turns "towards" target anchor
|
||||
} else {
|
||||
const target = anchor(config, name, points, start, mirror)(units)
|
||||
point.r = point.angle(target)
|
||||
}
|
||||
}
|
||||
|
||||
if (raw.orient !== undefined) {
|
||||
let angle = a.sane(raw.orient, `${name}.orient`, 'number')(units)
|
||||
if (point.meta.mirrored) {
|
||||
angle = -angle
|
||||
}
|
||||
point.r += angle
|
||||
rotator(raw.orient, `${name}.orient`, point)
|
||||
}
|
||||
if (raw.shift !== undefined) {
|
||||
let xyval = a.wh(raw.shift, `${name}.shift`)(units)
|
||||
if (point.meta.mirrored) {
|
||||
xyval[0] = -xyval[0]
|
||||
}
|
||||
point.shift(xyval, true)
|
||||
const xyval = a.wh(raw.shift, `${name}.shift`)(units)
|
||||
point.shift(xyval, true, resist)
|
||||
}
|
||||
if (raw.rotate !== undefined) {
|
||||
let angle = a.sane(raw.rotate, `${name}.rotate`, 'number')(units)
|
||||
if (point.meta.mirrored) {
|
||||
angle = -angle
|
||||
}
|
||||
point.r += angle
|
||||
rotator(raw.rotate, `${name}.rotate`, point)
|
||||
}
|
||||
if (raw.affect !== undefined) {
|
||||
const candidate = point.clone()
|
||||
point = default_point.clone()
|
||||
point = start.clone()
|
||||
point.meta = candidate.meta
|
||||
let affect = raw.affect
|
||||
if (a.type(affect)() == 'string') affect = affect.split('')
|
||||
|
@ -78,5 +164,6 @@ const anchor = exports.parse = (raw, name, points={}, check_unexpected=true, def
|
|||
point[aff] = candidate[aff]
|
||||
}
|
||||
}
|
||||
|
||||
return point
|
||||
}
|
|
@ -44,7 +44,7 @@ const _in = exports.in = (raw, name, arr) => {
|
|||
const arr = exports.arr = (raw, name, length, _type, _default) => units => {
|
||||
assert(type(raw)(units) == 'array', `Field "${name}" should be an array!`)
|
||||
assert(length == 0 || raw.length == length, `Field "${name}" should be an array of length ${length}!`)
|
||||
raw = raw.map(val => val || _default)
|
||||
raw = raw.map(val => val === undefined ? _default : val)
|
||||
raw.map(val => assert(type(val)(units) == _type, `Field "${name}" should contain ${_type}s!`))
|
||||
if (_type == 'number') {
|
||||
raw = raw.map(val => mathnum(val)(units))
|
||||
|
@ -62,8 +62,19 @@ const wh = exports.wh = (raw, name) => units => {
|
|||
return xy(raw, name)(units)
|
||||
}
|
||||
|
||||
exports.trbl = (raw, name) => units => {
|
||||
exports.trbl = (raw, name, _default=0) => units => {
|
||||
if (!Array.isArray(raw)) raw = [raw, raw, raw, raw]
|
||||
if (raw.length == 2) raw = [raw[1], raw[0], raw[1], raw[0]]
|
||||
return numarr(raw, name, 4, 'number', 0)(units)
|
||||
return arr(raw, name, 4, 'number', _default)(units)
|
||||
}
|
||||
|
||||
exports.asym = (raw, name) => {
|
||||
// allow different aliases
|
||||
const source_aliases = ['source', 'origin', 'base', 'primary', 'left']
|
||||
const clone_aliases = ['clone', 'image', 'derived', 'secondary', 'right']
|
||||
_in(raw, name, ['both'].concat(source_aliases, clone_aliases))
|
||||
// return aliases to canonical names
|
||||
if (source_aliases.includes(raw)) return 'source'
|
||||
if (clone_aliases.includes(raw)) return 'clone'
|
||||
return raw
|
||||
}
|
||||
|
|
22
src/cases.js
22
src/cases.js
|
@ -57,29 +57,33 @@ exports.parse = (config, outlines, units) => {
|
|||
}
|
||||
const part_qname = `cases.${case_name}.${part_name}`
|
||||
const part_var = `${case_name}__part_${part_name}`
|
||||
a.unexpected(part, part_qname, ['type', 'name', 'extrude', 'shift', 'rotate', 'operation'])
|
||||
const type = a.in(part.type || 'outline', `${part_qname}.type`, ['outline', 'case'])
|
||||
a.unexpected(part, part_qname, ['what', 'name', 'extrude', 'shift', 'rotate', 'operation'])
|
||||
const what = a.in(part.what || 'outline', `${part_qname}.what`, ['outline', 'case'])
|
||||
const name = a.sane(part.name, `${part_qname}.name`, 'string')()
|
||||
const shift = a.numarr(part.shift || [0, 0, 0], `${part_qname}.shift`, 3)(units)
|
||||
const rotate = a.numarr(part.rotate || [0, 0, 0], `${part_qname}.rotate`, 3)(units)
|
||||
const operation = a.in(part.operation || 'add', `${part_qname}.operation`, ['add', 'subtract', 'intersect'])
|
||||
|
||||
let base
|
||||
if (type == 'outline') {
|
||||
if (what == 'outline') {
|
||||
const extrude = a.sane(part.extrude || 1, `${part_qname}.extrude`, 'number')(units)
|
||||
const outline = outlines[name]
|
||||
a.assert(outline, `Field "${part_qname}.name" does not name a valid outline!`)
|
||||
if (!scripts[name]) {
|
||||
scripts[name] = m.exporter.toJscadScript(outline, {
|
||||
functionName: `${name}_outline_fn`,
|
||||
// This is a hack to separate multiple calls to the same outline with different extrude values
|
||||
// I know it needlessly duplicates a lot of code, but it's the quickest fix in the short term
|
||||
// And on the long run, we'll probably be moving to CADQuery anyway...
|
||||
const extruded_name = `${name}_extrude_` + ('' + extrude).replace(/\D/g, '_')
|
||||
if (!scripts[extruded_name]) {
|
||||
scripts[extruded_name] = m.exporter.toJscadScript(outline, {
|
||||
functionName: `${extruded_name}_outline_fn`,
|
||||
extrude: extrude,
|
||||
indent: 4
|
||||
})
|
||||
}
|
||||
outline_dependencies.push(name)
|
||||
base = `${name}_outline_fn()`
|
||||
outline_dependencies.push(extruded_name)
|
||||
base = `${extruded_name}_outline_fn()`
|
||||
} else {
|
||||
a.assert(part.extrude === undefined, `Field "${part_qname}.extrude" should not be used when type=case!`)
|
||||
a.assert(part.extrude === undefined, `Field "${part_qname}.extrude" should not be used when what=case!`)
|
||||
a.in(name, `${part_qname}.name`, Object.keys(cases))
|
||||
case_dependencies.push(name)
|
||||
base = `${name}_case_fn()`
|
||||
|
|
103
src/cli.js
103
src/cli.js
|
@ -1,11 +1,17 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs-extra')
|
||||
const fsp = require('fs/promises')
|
||||
const path = require('path')
|
||||
const yaml = require('js-yaml')
|
||||
const yargs = require('yargs')
|
||||
const ergogen = require('./ergogen')
|
||||
const jszip = require('jszip')
|
||||
|
||||
const io = require('./io')
|
||||
const pkg = require('../package.json')
|
||||
const ergogen = require('./ergogen')
|
||||
|
||||
;(async () => {
|
||||
|
||||
// command line args
|
||||
|
||||
|
@ -29,7 +35,52 @@ const args = yargs
|
|||
})
|
||||
.argv
|
||||
|
||||
// config reading
|
||||
// greetings
|
||||
|
||||
const title_suffix = args.debug ? ' (Debug Mode)' : ''
|
||||
console.log(`Ergogen v${pkg.version} CLI${title_suffix}`)
|
||||
console.log()
|
||||
|
||||
// input helpers
|
||||
|
||||
// zip handling is baked in at the io level, so that both the cli and the webui can use it
|
||||
// if, for local development, we want to use a folder as input, we temporarily zip it in
|
||||
// memory so that it can be handled the exact same way
|
||||
// functions shamelessly repurposed from https://github.com/Stuk/jszip/issues/386
|
||||
|
||||
// return a flat array of absolute paths of all files recursively contained in the dir
|
||||
const list_files_in_dir = async (dir) => {
|
||||
const list = await fsp.readdir(dir)
|
||||
const statPromises = list.map(async (file) => {
|
||||
const fullPath = path.resolve(dir, file)
|
||||
const stat = await fsp.stat(fullPath)
|
||||
if (stat && stat.isDirectory()) {
|
||||
return list_files_in_dir(fullPath)
|
||||
}
|
||||
return fullPath
|
||||
})
|
||||
|
||||
return (await Promise.all(statPromises)).flat(Infinity)
|
||||
}
|
||||
|
||||
// create an in-memory zip stream from a folder in the file system
|
||||
const zip_from_dir = async (dir) => {
|
||||
const absRoot = path.resolve(dir)
|
||||
const filePaths = await list_files_in_dir(dir)
|
||||
return filePaths.reduce((z, filePath) => {
|
||||
const relative = filePath.replace(absRoot, '')
|
||||
// create folder trees manually :(
|
||||
const zipFolder = path
|
||||
.dirname(relative)
|
||||
.split(path.sep)
|
||||
.reduce((zf, dirName) => zf.folder(dirName), z)
|
||||
|
||||
zipFolder.file(path.basename(filePath), fs.createReadStream(filePath))
|
||||
return z
|
||||
}, new jszip())
|
||||
}
|
||||
|
||||
// input reading
|
||||
|
||||
const config_file = args._[0]
|
||||
if (!config_file) {
|
||||
|
@ -37,19 +88,37 @@ if (!config_file) {
|
|||
process.exit(1)
|
||||
}
|
||||
|
||||
let config_text
|
||||
try {
|
||||
config_text = fs.readFileSync(config_file).toString()
|
||||
} catch (err) {
|
||||
console.error(`Could not read config file "${config_file}": ${err}`)
|
||||
if (!fs.existsSync(config_file)) {
|
||||
console.error(`Could not read config file "${config_file}": File does not exist!`)
|
||||
process.exit(2)
|
||||
}
|
||||
|
||||
const title_suffix = args.debug ? ' (Debug Mode)' : ''
|
||||
console.log(`Ergogen v${pkg.version} CLI${title_suffix}`)
|
||||
console.log()
|
||||
let config_text = ''
|
||||
let injections = []
|
||||
|
||||
;(async () => {
|
||||
try {
|
||||
if (config_file.endsWith('.zip') || config_file.endsWith('.ekb')) {
|
||||
console.log('Analyzing bundle...');
|
||||
[config_text, injections] = await io.unpack(
|
||||
await (new jszip()).loadAsync(fs.readFileSync(config_file))
|
||||
)
|
||||
} else if (fs.statSync(config_file).isDirectory()) {
|
||||
console.log('Analyzing folder...');
|
||||
[config_text, injections] = await io.unpack(
|
||||
await zip_from_dir(config_file)
|
||||
)
|
||||
} else {
|
||||
config_text = fs.readFileSync(config_file).toString()
|
||||
// no injections...
|
||||
}
|
||||
for (const [type, name, value] of injections) {
|
||||
ergogen.inject(type, name, value)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Could not read config file "${config_file}"!`)
|
||||
console.error(err)
|
||||
process.exit(2)
|
||||
}
|
||||
|
||||
// processing
|
||||
|
||||
|
@ -61,14 +130,16 @@ try {
|
|||
process.exit(3)
|
||||
}
|
||||
|
||||
// helpers
|
||||
// output helpers
|
||||
|
||||
const yamldump = data => yaml.dump(data, {indent: 4, noRefs: true})
|
||||
|
||||
const single = (data, rel) => {
|
||||
if (!data) return
|
||||
const abs = path.join(args.o, rel)
|
||||
fs.mkdirpSync(path.dirname(abs))
|
||||
if (abs.endsWith('.yaml')) {
|
||||
fs.writeFileSync(abs, yaml.dump(data, {indent: 4}))
|
||||
fs.writeFileSync(abs, yamldump(data))
|
||||
} else {
|
||||
fs.writeFileSync(abs, data)
|
||||
}
|
||||
|
@ -79,9 +150,9 @@ const composite = (data, rel) => {
|
|||
const abs = path.join(args.o, rel)
|
||||
if (data.yaml) {
|
||||
fs.mkdirpSync(path.dirname(abs))
|
||||
fs.writeFileSync(abs + '.yaml', yaml.dump(data.yaml, {indent: 4}))
|
||||
fs.writeFileSync(abs + '.yaml', yamldump(data.yaml))
|
||||
}
|
||||
for (const format of ['svg', 'dxf', 'jscad', 'stl']) {
|
||||
for (const format of ['svg', 'dxf', 'jscad']) {
|
||||
if (data[format]) {
|
||||
fs.mkdirpSync(path.dirname(abs))
|
||||
fs.writeFileSync(abs + '.' + format, data[format])
|
||||
|
@ -89,7 +160,7 @@ const composite = (data, rel) => {
|
|||
}
|
||||
}
|
||||
|
||||
// output
|
||||
// output generation
|
||||
|
||||
if (args.clean) {
|
||||
console.log('Cleaning output folder...')
|
||||
|
|
|
@ -7,7 +7,6 @@ const outlines_lib = require('./outlines')
|
|||
const cases_lib = require('./cases')
|
||||
const pcbs_lib = require('./pcbs')
|
||||
|
||||
const semver = require('semver')
|
||||
const version = require('../package.json').version
|
||||
|
||||
const process = async (raw, debug=false, logger=()=>{}) => {
|
||||
|
@ -16,6 +15,8 @@ const process = async (raw, debug=false, logger=()=>{}) => {
|
|||
let empty = true
|
||||
let [config, format] = io.interpret(raw, logger)
|
||||
let suffix = format
|
||||
// KLE conversion warrants automaticly engaging debug mode
|
||||
// as, usually, we're only interested in the points anyway
|
||||
if (format == 'KLE') {
|
||||
suffix = `${format} (Auto-debug)`
|
||||
debug = true
|
||||
|
@ -34,12 +35,9 @@ const process = async (raw, debug=false, logger=()=>{}) => {
|
|||
|
||||
if (config.meta && config.meta.engine) {
|
||||
logger('Checking compatibility...')
|
||||
const engine = semver.validRange(config.meta.engine)
|
||||
if (!engine) {
|
||||
throw new Error('Invalid config engine declaration!')
|
||||
}
|
||||
if (!semver.satisfies(version, engine)) {
|
||||
throw new Error(`Current ergogen version (${version}) doesn\'t satisfy config's engine requirement (${engine})!`)
|
||||
const engine = u.semver(config.meta.engine, 'config.meta.engine')
|
||||
if (!u.satisfies(version, engine)) {
|
||||
throw new Error(`Current ergogen version (${version}) doesn\'t satisfy config's engine requirement (${config.meta.engine})!`)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,12 +69,12 @@ const process = async (raw, debug=false, logger=()=>{}) => {
|
|||
empty = false
|
||||
}
|
||||
|
||||
logger('Extruding cases...')
|
||||
logger('Modeling cases...')
|
||||
const cases = cases_lib.parse(config.cases || {}, outlines, units)
|
||||
results.cases = {}
|
||||
for (const [case_name, case_script] of Object.entries(cases)) {
|
||||
if (!debug && case_name.startsWith('_')) continue
|
||||
results.cases[case_name] = await io.threedee(case_script, debug)
|
||||
results.cases[case_name] = {jscad: case_script}
|
||||
empty = false
|
||||
}
|
||||
|
||||
|
@ -96,8 +94,22 @@ const process = async (raw, debug=false, logger=()=>{}) => {
|
|||
return results
|
||||
}
|
||||
|
||||
const inject = (type, name, value) => {
|
||||
if (value === undefined) {
|
||||
value = name
|
||||
name = type
|
||||
type = 'footprint'
|
||||
}
|
||||
switch (type) {
|
||||
case 'footprint':
|
||||
return pcbs_lib.inject_footprint(name, value)
|
||||
default:
|
||||
throw new Error(`Unknown injection type "${type}" with name "${name}" and value "${value}"!`)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
version,
|
||||
process,
|
||||
inject_footprint: pcbs_lib.inject_footprint
|
||||
inject
|
||||
}
|
156
src/filter.js
Normal file
156
src/filter.js
Normal file
|
@ -0,0 +1,156 @@
|
|||
const u = require('./utils')
|
||||
const a = require('./assert')
|
||||
const anchor_lib = require('./anchor')
|
||||
const Point = require('./point')
|
||||
const anchor = anchor_lib.parse
|
||||
|
||||
const _true = () => true
|
||||
const _false = () => false
|
||||
const _and = arr => p => arr.map(e => e(p)).reduce((a, b) => a && b)
|
||||
const _or = arr => p => arr.map(e => e(p)).reduce((a, b) => a || b)
|
||||
|
||||
const similar = (keys, reference, name, units) => {
|
||||
let neg = false
|
||||
if (reference.startsWith('-')) {
|
||||
neg = true
|
||||
reference = reference.slice(1)
|
||||
}
|
||||
|
||||
// support both string or regex as reference
|
||||
let internal_tester = val => (''+val) == reference
|
||||
if (reference.startsWith('/')) {
|
||||
try {
|
||||
const regex_parts = reference.split('/')
|
||||
regex_parts.shift() // remove starting slash
|
||||
const flags = regex_parts.pop()
|
||||
const regex = new RegExp(regex_parts.join('/'), flags)
|
||||
internal_tester = val => regex.test(''+val)
|
||||
} catch (ex) {
|
||||
throw new Error(`Invalid regex "${reference}" found at filter "${name}"!`)
|
||||
}
|
||||
}
|
||||
|
||||
// support strings, arrays, or objects as key
|
||||
const external_tester = (point, key) => {
|
||||
const value = u.deep(point, key)
|
||||
if (a.type(value)() == 'array') {
|
||||
return value.some(subkey => internal_tester(subkey))
|
||||
} else if (a.type(value)() == 'object') {
|
||||
return Object.keys(value).some(subkey => internal_tester(subkey))
|
||||
} else {
|
||||
return internal_tester(value)
|
||||
}
|
||||
}
|
||||
|
||||
// consider negation
|
||||
if (neg) {
|
||||
return point => keys.every(key => !external_tester(point, key))
|
||||
} else {
|
||||
return point => keys.some(key => external_tester(point, key))
|
||||
}
|
||||
}
|
||||
|
||||
const comparators = {
|
||||
'~': similar
|
||||
// TODO: extension point for other operators...
|
||||
}
|
||||
const symbols = Object.keys(comparators)
|
||||
|
||||
const simple = (exp, name, units) => {
|
||||
|
||||
let keys = ['meta.name', 'meta.tags']
|
||||
let op = '~'
|
||||
let value
|
||||
const parts = exp.split(/\s+/g)
|
||||
|
||||
// full case
|
||||
if (symbols.includes(parts[1])) {
|
||||
keys = parts[0].split(',')
|
||||
op = parts[1]
|
||||
value = parts.slice(2).join(' ')
|
||||
|
||||
// middle case, just an operator spec, default "keys"
|
||||
} else if (symbols.includes(parts[0])) {
|
||||
op = parts[0]
|
||||
value = parts.slice(1).join(' ')
|
||||
|
||||
// basic case, only "value"
|
||||
} else {
|
||||
value = exp
|
||||
}
|
||||
|
||||
return point => comparators[op](keys, value, name, units)(point)
|
||||
}
|
||||
|
||||
const complex = (config, name, units, aggregator=_or) => {
|
||||
|
||||
// we branch by type
|
||||
const type = a.type(config)(units)
|
||||
switch(type) {
|
||||
|
||||
// boolean --> either all or nothing
|
||||
case 'boolean':
|
||||
return config ? _true : _false
|
||||
|
||||
// string --> base case, meaning a simple/single filter
|
||||
case 'string':
|
||||
return simple(config, name, units)
|
||||
|
||||
// array --> aggregated simple filters with alternating and/or conditions
|
||||
case 'array':
|
||||
const alternate = aggregator == _and ? _or : _and
|
||||
return aggregator(config.map(elem => complex(elem, name, units, alternate)))
|
||||
|
||||
default:
|
||||
throw new Error(`Unexpected type "${type}" found at filter "${name}"!`)
|
||||
}
|
||||
}
|
||||
|
||||
const contains_object = (val) => {
|
||||
if (a.type(val)() == 'object') return true
|
||||
if (a.type(val)() == 'array') return val.some(el => contains_object(el))
|
||||
return false
|
||||
}
|
||||
|
||||
exports.parse = (config, name, points={}, units={}, asym='source') => {
|
||||
|
||||
let result = []
|
||||
|
||||
// if a filter decl is undefined, it's just the default point at [0, 0]
|
||||
if (config === undefined) {
|
||||
result.push(new Point())
|
||||
|
||||
// if a filter decl is an object, or an array that contains an object at any depth, it is an anchor
|
||||
} else if (contains_object(config)) {
|
||||
if (['source', 'both'].includes(asym)) {
|
||||
result.push(anchor(config, name, points)(units))
|
||||
}
|
||||
if (['clone', 'both'].includes(asym)) {
|
||||
// this is strict: if the ref of the anchor doesn't have a mirror pair, it will error out
|
||||
// also, we check for duplicates as ref-less anchors mirror to themselves
|
||||
const clone = anchor(config, name, points, undefined, true)(units)
|
||||
if (result.every(p => !p.equals(clone))) {
|
||||
result.push(clone)
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise, it is treated as a condition to filter all available points
|
||||
} else {
|
||||
const source = Object.values(points).filter(complex(config, name, units))
|
||||
if (['source', 'both'].includes(asym)) {
|
||||
result = result.concat(source)
|
||||
}
|
||||
if (['clone', 'both'].includes(asym)) {
|
||||
// this is permissive: we only include mirrored versions if they exist, and don't fuss if they don't
|
||||
// also, we check for duplicates as clones can potentially refer back to their sources, too
|
||||
const pool = result.map(p => p.meta.name)
|
||||
result = result.concat(
|
||||
source.map(p => points[anchor_lib.mirror(p.meta.name)])
|
||||
.filter(p => !!p)
|
||||
.filter(p => !pool.includes(p.meta.name))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
|
@ -1,11 +1,9 @@
|
|||
module.exports = {
|
||||
nets: {
|
||||
params: {
|
||||
designator: 'S',
|
||||
from: undefined,
|
||||
to: undefined
|
||||
},
|
||||
params: {
|
||||
class: 'S'
|
||||
},
|
||||
body: p => `
|
||||
|
||||
(module ALPS (layer F.Cu) (tedit 5CF31DEF)
|
||||
|
@ -27,8 +25,8 @@ module.exports = {
|
|||
(fp_line (start 7 -7) (end 7 -6) (layer Dwgs.User) (width 0.15))
|
||||
|
||||
${''/* pins */}
|
||||
(pad 1 thru_hole circle (at 2.5 -4.5) (size 2.25 2.25) (drill 1.47) (layers *.Cu *.Mask) ${p.net.from.str})
|
||||
(pad 2 thru_hole circle (at -2.5 -4) (size 2.25 2.25) (drill 1.47) (layers *.Cu *.Mask) ${p.net.to.str})
|
||||
(pad 1 thru_hole circle (at 2.5 -4.5) (size 2.25 2.25) (drill 1.47) (layers *.Cu *.Mask) ${p.from})
|
||||
(pad 2 thru_hole circle (at -2.5 -4) (size 2.25 2.25) (drill 1.47) (layers *.Cu *.Mask) ${p.to})
|
||||
)
|
||||
|
||||
`
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
module.exports = {
|
||||
nets: {
|
||||
params: {
|
||||
designator: 'B', // for Button
|
||||
side: 'F',
|
||||
from: undefined,
|
||||
to: undefined
|
||||
},
|
||||
params: {
|
||||
class: 'B', // for Button
|
||||
side: 'F'
|
||||
},
|
||||
body: p => `
|
||||
|
||||
(module E73:SW_TACT_ALPS_SKQGABE010 (layer F.Cu) (tstamp 5BF2CC94)
|
||||
|
@ -20,20 +18,20 @@ module.exports = {
|
|||
(fp_text value "" (at 0 0) (layer F.SilkS) hide (effects (font (size 1.27 1.27) (thickness 0.15))))
|
||||
|
||||
${'' /* outline */}
|
||||
(fp_line (start 2.75 1.25) (end 1.25 2.75) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 2.75 -1.25) (end 1.25 -2.75) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 2.75 -1.25) (end 2.75 1.25) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -1.25 2.75) (end 1.25 2.75) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -1.25 -2.75) (end 1.25 -2.75) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -2.75 1.25) (end -1.25 2.75) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -2.75 -1.25) (end -1.25 -2.75) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -2.75 -1.25) (end -2.75 1.25) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 2.75 1.25) (end 1.25 2.75) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 2.75 -1.25) (end 1.25 -2.75) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 2.75 -1.25) (end 2.75 1.25) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -1.25 2.75) (end 1.25 2.75) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -1.25 -2.75) (end 1.25 -2.75) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -2.75 1.25) (end -1.25 2.75) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -2.75 -1.25) (end -1.25 -2.75) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -2.75 -1.25) (end -2.75 1.25) (layer ${p.side}.SilkS) (width 0.15))
|
||||
|
||||
${'' /* pins */}
|
||||
(pad 1 smd rect (at -3.1 -1.85 ${p.rot}) (size 1.8 1.1) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask) ${p.net.from.str})
|
||||
(pad 1 smd rect (at 3.1 -1.85 ${p.rot}) (size 1.8 1.1) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask) ${p.net.from.str})
|
||||
(pad 2 smd rect (at -3.1 1.85 ${p.rot}) (size 1.8 1.1) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask) ${p.net.to.str})
|
||||
(pad 2 smd rect (at 3.1 1.85 ${p.rot}) (size 1.8 1.1) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask) ${p.net.to.str})
|
||||
(pad 1 smd rect (at -3.1 -1.85 ${p.r}) (size 1.8 1.1) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask) ${p.from})
|
||||
(pad 1 smd rect (at 3.1 -1.85 ${p.r}) (size 1.8 1.1) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask) ${p.from})
|
||||
(pad 2 smd rect (at -3.1 1.85 ${p.r}) (size 1.8 1.1) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask) ${p.to})
|
||||
(pad 2 smd rect (at 3.1 1.85 ${p.r}) (size 1.8 1.1) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask) ${p.to})
|
||||
)
|
||||
|
||||
`
|
||||
|
|
|
@ -13,15 +13,13 @@
|
|||
// note: hotswap and reverse can be used simultaneously
|
||||
|
||||
module.exports = {
|
||||
nets: {
|
||||
from: undefined,
|
||||
to: undefined
|
||||
},
|
||||
params: {
|
||||
class: 'S',
|
||||
designator: 'S',
|
||||
hotswap: false,
|
||||
reverse: false,
|
||||
keycaps: false
|
||||
keycaps: false,
|
||||
from: undefined,
|
||||
to: undefined
|
||||
},
|
||||
body: p => {
|
||||
const standard = `
|
||||
|
@ -57,35 +55,35 @@ module.exports = {
|
|||
(fp_line (start -9 8.5) (end -9 -8.5) (layer Dwgs.User) (width 0.15))
|
||||
`
|
||||
function pins(def_neg, def_pos, def_side) {
|
||||
if(p.param.hotswap) {
|
||||
if(p.hotswap) {
|
||||
return `
|
||||
${'' /* holes */}
|
||||
(pad "" np_thru_hole circle (at ${def_pos}5 -3.75) (size 3 3) (drill 3) (layers *.Cu *.Mask))
|
||||
(pad "" np_thru_hole circle (at 0 -5.95) (size 3 3) (drill 3) (layers *.Cu *.Mask))
|
||||
|
||||
${'' /* net pads */}
|
||||
(pad 1 smd rect (at ${def_neg}3.275 -5.95 ${p.rot}) (size 2.6 2.6) (layers ${def_side}.Cu ${def_side}.Paste ${def_side}.Mask) ${p.net.from.str})
|
||||
(pad 2 smd rect (at ${def_pos}8.275 -3.75 ${p.rot}) (size 2.6 2.6) (layers ${def_side}.Cu ${def_side}.Paste ${def_side}.Mask) ${p.net.to.str})
|
||||
(pad 1 smd rect (at ${def_neg}3.275 -5.95 ${p.r}) (size 2.6 2.6) (layers ${def_side}.Cu ${def_side}.Paste ${def_side}.Mask) ${p.from})
|
||||
(pad 2 smd rect (at ${def_pos}8.275 -3.75 ${p.r}) (size 2.6 2.6) (layers ${def_side}.Cu ${def_side}.Paste ${def_side}.Mask) ${p.to})
|
||||
`
|
||||
} else {
|
||||
return `
|
||||
${''/* pins */}
|
||||
(pad 1 thru_hole circle (at ${def_pos}5 -3.8) (size 2.032 2.032) (drill 1.27) (layers *.Cu *.Mask) ${p.net.from.str})
|
||||
(pad 2 thru_hole circle (at ${def_pos}0 -5.9) (size 2.032 2.032) (drill 1.27) (layers *.Cu *.Mask) ${p.net.to.str})
|
||||
(pad 1 thru_hole circle (at ${def_pos}5 -3.8) (size 2.032 2.032) (drill 1.27) (layers *.Cu *.Mask) ${p.from})
|
||||
(pad 2 thru_hole circle (at ${def_pos}0 -5.9) (size 2.032 2.032) (drill 1.27) (layers *.Cu *.Mask) ${p.to})
|
||||
`
|
||||
}
|
||||
}
|
||||
if(p.param.reverse) {
|
||||
if(p.reverse) {
|
||||
return `
|
||||
${standard}
|
||||
${p.param.keycaps ? keycap : ''}
|
||||
${p.keycaps ? keycap : ''}
|
||||
${pins('-', '', 'B')}
|
||||
${pins('', '-', 'F')})
|
||||
`
|
||||
} else {
|
||||
return `
|
||||
${standard}
|
||||
${p.param.keycaps ? keycap : ''}
|
||||
${p.keycaps ? keycap : ''}
|
||||
${pins('-', '', 'B')})
|
||||
`
|
||||
}
|
||||
|
|
|
@ -9,15 +9,13 @@
|
|||
// if true, will add choc sized keycap box around the footprint
|
||||
|
||||
module.exports = {
|
||||
nets: {
|
||||
from: undefined,
|
||||
to: undefined
|
||||
},
|
||||
params: {
|
||||
class: 'S',
|
||||
designator: 'S',
|
||||
side: 'F',
|
||||
reverse: false,
|
||||
keycaps: false
|
||||
keycaps: false,
|
||||
from: undefined,
|
||||
to: undefined
|
||||
},
|
||||
body: p => {
|
||||
const standard = `
|
||||
|
@ -71,14 +69,14 @@ module.exports = {
|
|||
function pins(def_neg, def_pos) {
|
||||
return `
|
||||
${''/* pins */}
|
||||
(pad 1 thru_hole circle (at ${def_neg}4.58 5.1) (size 1.6 1.6) (drill 1.1) (layers *.Cu *.Mask) ${p.net.from.str} (clearance 0.2))
|
||||
(pad 2 thru_hole circle (at ${def_pos}2 5.4) (size 1.6 1.6) (drill 1.1) (layers *.Cu *.Mask) ${p.net.to.str} (clearance 0.2))
|
||||
(pad 1 thru_hole circle (at ${def_neg}4.58 5.1) (size 1.6 1.6) (drill 1.1) (layers *.Cu *.Mask) ${p.from} (clearance 0.2))
|
||||
(pad 2 thru_hole circle (at ${def_pos}2 5.4) (size 1.6 1.6) (drill 1.1) (layers *.Cu *.Mask) ${p.to} (clearance 0.2))
|
||||
`
|
||||
}
|
||||
if(p.param.reverse){
|
||||
if(p.reverse){
|
||||
return `
|
||||
${standard}
|
||||
${p.param.keycaps ? keycap : ''}
|
||||
${p.keycaps ? keycap : ''}
|
||||
${pins('-', '')}
|
||||
${pins('', '-')})
|
||||
|
||||
|
@ -86,7 +84,7 @@ module.exports = {
|
|||
} else {
|
||||
return `
|
||||
${standard}
|
||||
${p.param.keycaps ? keycap : ''}
|
||||
${p.keycaps ? keycap : ''}
|
||||
${pins('-', '')})
|
||||
`
|
||||
}
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
module.exports = {
|
||||
nets: {
|
||||
params: {
|
||||
designator: 'D',
|
||||
from: undefined,
|
||||
to: undefined
|
||||
},
|
||||
params: {
|
||||
class: 'D'
|
||||
},
|
||||
body: p => `
|
||||
|
||||
(module ComboDiode (layer F.Cu) (tedit 5B24D78E)
|
||||
|
@ -34,14 +32,14 @@ module.exports = {
|
|||
(fp_line (start -0.75 0) (end -0.35 0) (layer B.SilkS) (width 0.1))
|
||||
|
||||
${''/* SMD pads on both sides */}
|
||||
(pad 1 smd rect (at -1.65 0 ${p.rot}) (size 0.9 1.2) (layers F.Cu F.Paste F.Mask) ${p.net.to.str})
|
||||
(pad 2 smd rect (at 1.65 0 ${p.rot}) (size 0.9 1.2) (layers B.Cu B.Paste B.Mask) ${p.net.from.str})
|
||||
(pad 1 smd rect (at -1.65 0 ${p.rot}) (size 0.9 1.2) (layers B.Cu B.Paste B.Mask) ${p.net.to.str})
|
||||
(pad 2 smd rect (at 1.65 0 ${p.rot}) (size 0.9 1.2) (layers F.Cu F.Paste F.Mask) ${p.net.from.str})
|
||||
(pad 1 smd rect (at -1.65 0 ${p.r}) (size 0.9 1.2) (layers F.Cu F.Paste F.Mask) ${p.to})
|
||||
(pad 2 smd rect (at 1.65 0 ${p.r}) (size 0.9 1.2) (layers B.Cu B.Paste B.Mask) ${p.from})
|
||||
(pad 1 smd rect (at -1.65 0 ${p.r}) (size 0.9 1.2) (layers B.Cu B.Paste B.Mask) ${p.to})
|
||||
(pad 2 smd rect (at 1.65 0 ${p.r}) (size 0.9 1.2) (layers F.Cu F.Paste F.Mask) ${p.from})
|
||||
|
||||
${''/* THT terminals */}
|
||||
(pad 1 thru_hole circle (at 3.81 0 ${p.rot}) (size 1.905 1.905) (drill 0.9906) (layers *.Cu *.Mask) ${p.net.from.str})
|
||||
(pad 2 thru_hole rect (at -3.81 0 ${p.rot}) (size 1.778 1.778) (drill 0.9906) (layers *.Cu *.Mask) ${p.net.to.str})
|
||||
(pad 1 thru_hole rect (at -3.81 0 ${p.r}) (size 1.778 1.778) (drill 0.9906) (layers *.Cu *.Mask) ${p.to})
|
||||
(pad 2 thru_hole circle (at 3.81 0 ${p.r}) (size 1.905 1.905) (drill 0.9906) (layers *.Cu *.Mask) ${p.from})
|
||||
)
|
||||
|
||||
`
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
module.exports = {
|
||||
nets: {
|
||||
params: {
|
||||
designator: 'JST',
|
||||
side: 'F',
|
||||
pos: undefined,
|
||||
neg: undefined
|
||||
},
|
||||
params: {
|
||||
class: 'JST',
|
||||
side: 'F'
|
||||
},
|
||||
body: p => `
|
||||
|
||||
(module JST_PH_S2B-PH-K_02x2.00mm_Angled (layer F.Cu) (tedit 58D3FE32)
|
||||
|
@ -20,20 +18,20 @@ module.exports = {
|
|||
(fp_text reference "${p.ref}" (at 0 0) (layer F.SilkS) ${p.ref_hide} (effects (font (size 1.27 1.27) (thickness 0.15))))
|
||||
(fp_text value "" (at 0 0) (layer F.SilkS) hide (effects (font (size 1.27 1.27) (thickness 0.15))))
|
||||
|
||||
(fp_line (start -2.25 0.25) (end -2.25 -1.35) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -2.25 -1.35) (end -2.95 -1.35) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -2.95 -1.35) (end -2.95 6.25) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -2.95 6.25) (end 2.95 6.25) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 2.95 6.25) (end 2.95 -1.35) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 2.95 -1.35) (end 2.25 -1.35) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 2.25 -1.35) (end 2.25 0.25) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 2.25 0.25) (end -2.25 0.25) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -2.25 0.25) (end -2.25 -1.35) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -2.25 -1.35) (end -2.95 -1.35) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -2.95 -1.35) (end -2.95 6.25) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -2.95 6.25) (end 2.95 6.25) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 2.95 6.25) (end 2.95 -1.35) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 2.95 -1.35) (end 2.25 -1.35) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 2.25 -1.35) (end 2.25 0.25) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 2.25 0.25) (end -2.25 0.25) (layer ${p.side}.SilkS) (width 0.15))
|
||||
|
||||
(fp_line (start -1 1.5) (end -1 2.0) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -1.25 1.75) (end -0.75 1.75) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -1 1.5) (end -1 2.0) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -1.25 1.75) (end -0.75 1.75) (layer ${p.side}.SilkS) (width 0.15))
|
||||
|
||||
(pad 1 thru_hole rect (at -1 0 ${p.rot}) (size 1.2 1.7) (drill 0.75) (layers *.Cu *.Mask) ${p.net.pos.str})
|
||||
(pad 2 thru_hole oval (at 1 0 ${p.rot}) (size 1.2 1.7) (drill 0.75) (layers *.Cu *.Mask) ${p.net.neg.str})
|
||||
(pad 1 thru_hole rect (at -1 0 ${p.r}) (size 1.2 1.7) (drill 0.75) (layers *.Cu *.Mask) ${p.pos})
|
||||
(pad 2 thru_hole oval (at 1 0 ${p.r}) (size 1.2 1.7) (drill 0.75) (layers *.Cu *.Mask) ${p.neg})
|
||||
|
||||
)
|
||||
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
module.exports = {
|
||||
nets: {
|
||||
params: {
|
||||
designator: 'J',
|
||||
side: 'F',
|
||||
from: undefined,
|
||||
to: undefined
|
||||
},
|
||||
params: {
|
||||
class: 'J',
|
||||
side: 'F'
|
||||
},
|
||||
body: p => `
|
||||
(module lib:Jumper (layer F.Cu) (tedit 5E1ADAC2)
|
||||
${p.at /* parametric position */}
|
||||
|
@ -16,9 +14,9 @@ module.exports = {
|
|||
(fp_text value Jumper (at 0 -7.3) (layer F.Fab) (effects (font (size 1 1) (thickness 0.15))))
|
||||
|
||||
${'' /* pins */}
|
||||
(pad 1 smd rect (at -0.50038 0 ${p.rot}) (size 0.635 1.143) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask)
|
||||
(clearance 0.1905) ${p.net.from.str})
|
||||
(pad 2 smd rect (at 0.50038 0 ${p.rot}) (size 0.635 1.143) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask)
|
||||
(clearance 0.1905) ${p.net.to.str}))
|
||||
(pad 1 smd rect (at -0.50038 0 ${p.r}) (size 0.635 1.143) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask)
|
||||
(clearance 0.1905) ${p.from})
|
||||
(pad 2 smd rect (at 0.50038 0 ${p.r}) (size 0.635 1.143) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask)
|
||||
(clearance 0.1905) ${p.to}))
|
||||
`
|
||||
}
|
|
@ -13,16 +13,14 @@
|
|||
// note: hotswap and reverse can be used simultaneously
|
||||
|
||||
module.exports = {
|
||||
nets: {
|
||||
params: {
|
||||
designator: 'S',
|
||||
hotswap: false,
|
||||
reverse: false,
|
||||
keycaps: false,
|
||||
from: undefined,
|
||||
to: undefined
|
||||
},
|
||||
params: {
|
||||
class: 'S',
|
||||
hotswap: false,
|
||||
reverse: false,
|
||||
keycaps: false
|
||||
},
|
||||
body: p => {
|
||||
const standard = `
|
||||
(module MX (layer F.Cu) (tedit 5DD4F656)
|
||||
|
@ -57,35 +55,35 @@ module.exports = {
|
|||
(fp_line (start -9.5 9.5) (end -9.5 -9.5) (layer Dwgs.User) (width 0.15))
|
||||
`
|
||||
function pins(def_neg, def_pos, def_side) {
|
||||
if(p.param.hotswap) {
|
||||
if(p.hotswap) {
|
||||
return `
|
||||
${'' /* holes */}
|
||||
(pad "" np_thru_hole circle (at ${def_pos}2.54 -5.08) (size 3 3) (drill 3) (layers *.Cu *.Mask))
|
||||
(pad "" np_thru_hole circle (at ${def_neg}3.81 -2.54) (size 3 3) (drill 3) (layers *.Cu *.Mask))
|
||||
|
||||
${'' /* net pads */}
|
||||
(pad 1 smd rect (at ${def_neg}7.085 -2.54 180) (size 2.55 2.5) (layers ${def_side}.Cu ${def_side}.Paste ${def_side}.Mask) ${p.net.from.str})
|
||||
(pad 2 smd rect (at ${def_pos}5.842 -5.08 180) (size 2.55 2.5) (layers ${def_side}.Cu ${def_side}.Paste ${def_side}.Mask) ${p.net.to.str})
|
||||
(pad 1 smd rect (at ${def_neg}7.085 -2.54 ${p.r}) (size 2.55 2.5) (layers ${def_side}.Cu ${def_side}.Paste ${def_side}.Mask) ${p.from})
|
||||
(pad 2 smd rect (at ${def_pos}5.842 -5.08 ${p.r}) (size 2.55 2.5) (layers ${def_side}.Cu ${def_side}.Paste ${def_side}.Mask) ${p.to})
|
||||
`
|
||||
} else {
|
||||
return `
|
||||
${''/* pins */}
|
||||
(pad 1 thru_hole circle (at ${def_pos}2.54 -5.08) (size 2.286 2.286) (drill 1.4986) (layers *.Cu *.Mask) ${p.net.from.str})
|
||||
(pad 2 thru_hole circle (at ${def_neg}3.81 -2.54) (size 2.286 2.286) (drill 1.4986) (layers *.Cu *.Mask) ${p.net.to.str})
|
||||
(pad 1 thru_hole circle (at ${def_pos}2.54 -5.08) (size 2.286 2.286) (drill 1.4986) (layers *.Cu *.Mask) ${p.from})
|
||||
(pad 2 thru_hole circle (at ${def_neg}3.81 -2.54) (size 2.286 2.286) (drill 1.4986) (layers *.Cu *.Mask) ${p.to})
|
||||
`
|
||||
}
|
||||
}
|
||||
if(p.param.reverse){
|
||||
if(p.reverse){
|
||||
return `
|
||||
${standard}
|
||||
${p.param.keycaps ? keycap : ''}
|
||||
${p.keycaps ? keycap : ''}
|
||||
${pins('-', '', 'B')}
|
||||
${pins('', '-', 'F')})
|
||||
`
|
||||
} else {
|
||||
return `
|
||||
${standard}
|
||||
${p.param.keycaps ? keycap : ''}
|
||||
${p.keycaps ? keycap : ''}
|
||||
${pins('-', '', 'B')})
|
||||
`
|
||||
}
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
module.exports = {
|
||||
nets: {
|
||||
SDA: undefined,
|
||||
SCL: undefined,
|
||||
VCC: 'VCC',
|
||||
GND: 'GND'
|
||||
},
|
||||
params: {
|
||||
class: 'OLED',
|
||||
side: 'F'
|
||||
designator: 'OLED',
|
||||
side: 'F',
|
||||
VCC: {type: 'net', value: 'VCC'},
|
||||
GND: {type: 'net', value: 'GND'},
|
||||
SDA: undefined,
|
||||
SCL: undefined
|
||||
},
|
||||
body: p => `
|
||||
(module lib:OLED_headers (layer F.Cu) (tedit 5E1ADAC2)
|
||||
|
@ -18,14 +16,14 @@ module.exports = {
|
|||
(fp_text value OLED (at 0 -7.3) (layer F.Fab) (effects (font (size 1 1) (thickness 0.15))))
|
||||
|
||||
${'' /* pins */}
|
||||
(pad 4 thru_hole oval (at 1.6 2.18 ${p.rot+270}) (size 1.7 1.7) (drill 1) (layers *.Cu *.Mask)
|
||||
${p.net.SDA.str})
|
||||
(pad 3 thru_hole oval (at 1.6 4.72 ${p.rot+270}) (size 1.7 1.7) (drill 1) (layers *.Cu *.Mask)
|
||||
${p.net.SCL.str})
|
||||
(pad 2 thru_hole oval (at 1.6 7.26 ${p.rot+270}) (size 1.7 1.7) (drill 1) (layers *.Cu *.Mask)
|
||||
${p.net.VCC.str})
|
||||
(pad 1 thru_hole rect (at 1.6 9.8 ${p.rot+270}) (size 1.7 1.7) (drill 1) (layers *.Cu *.Mask)
|
||||
${p.net.GND.str})
|
||||
(pad 4 thru_hole oval (at 1.6 2.18 ${p.r+270}) (size 1.7 1.7) (drill 1) (layers *.Cu *.Mask)
|
||||
${p.SDA})
|
||||
(pad 3 thru_hole oval (at 1.6 4.72 ${p.r+270}) (size 1.7 1.7) (drill 1) (layers *.Cu *.Mask)
|
||||
${p.SCL})
|
||||
(pad 2 thru_hole oval (at 1.6 7.26 ${p.r+270}) (size 1.7 1.7) (drill 1) (layers *.Cu *.Mask)
|
||||
${p.VCC})
|
||||
(pad 1 thru_hole rect (at 1.6 9.8 ${p.r+270}) (size 1.7 1.7) (drill 1) (layers *.Cu *.Mask)
|
||||
${p.GND})
|
||||
)
|
||||
`
|
||||
}
|
|
@ -1,11 +1,9 @@
|
|||
module.exports = {
|
||||
nets: {
|
||||
params: {
|
||||
designator: 'S',
|
||||
from: undefined,
|
||||
to: undefined
|
||||
},
|
||||
params: {
|
||||
class: 'S'
|
||||
},
|
||||
body: p => `
|
||||
|
||||
(module OMRON_B3F-4055 (layer F.Cu) (tstamp 5BF2CC94)
|
||||
|
@ -26,10 +24,10 @@ module.exports = {
|
|||
(fp_line (start -6 6) (end -6 -6) (layer Dwgs.User) (width 0.15))
|
||||
|
||||
${'' /* pins */}
|
||||
(pad 1 np_thru_hole circle (at 6.25 -2.5) (size 1.2 1.2) (drill 1.2) (layers *.Cu *.Mask) ${p.net.from.str})
|
||||
(pad 2 np_thru_hole circle (at -6.25 -2.5) (size 1.2 1.2) (drill 1.2) (layers *.Cu *.Mask) ${p.net.from.str})
|
||||
(pad 3 np_thru_hole circle (at 6.25 2.5) (size 1.2 1.2) (drill 1.2) (layers *.Cu *.Mask) ${p.net.to.str})
|
||||
(pad 4 np_thru_hole circle (at -6.25 2.5 ) (size 1.2 1.2) (drill 1.2) (layers *.Cu *.Mask) ${p.net.to.str})
|
||||
(pad 1 np_thru_hole circle (at 6.25 -2.5) (size 1.2 1.2) (drill 1.2) (layers *.Cu *.Mask) ${p.from})
|
||||
(pad 2 np_thru_hole circle (at -6.25 -2.5) (size 1.2 1.2) (drill 1.2) (layers *.Cu *.Mask) ${p.from})
|
||||
(pad 3 np_thru_hole circle (at 6.25 2.5) (size 1.2 1.2) (drill 1.2) (layers *.Cu *.Mask) ${p.to})
|
||||
(pad 4 np_thru_hole circle (at -6.25 2.5 ) (size 1.2 1.2) (drill 1.2) (layers *.Cu *.Mask) ${p.to})
|
||||
)
|
||||
|
||||
`
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
module.exports = {
|
||||
nets: {
|
||||
net: undefined
|
||||
},
|
||||
params: {
|
||||
class: 'PAD',
|
||||
designator: 'PAD',
|
||||
width: 1,
|
||||
height: 1,
|
||||
front: true,
|
||||
back: true,
|
||||
text: '',
|
||||
align: 'left',
|
||||
mirrored: '=mirrored'
|
||||
mirrored: {type: 'boolean', value: '{{mirrored}}'},
|
||||
net: undefined
|
||||
},
|
||||
body: p => {
|
||||
|
||||
|
@ -18,18 +16,21 @@ module.exports = {
|
|||
if (!toggle) return ''
|
||||
let x = 0, y = 0
|
||||
const mirror = side == 'B' ? '(justify mirror)' : ''
|
||||
const plus = (p.param.text.length + 1) * 0.5
|
||||
let align = p.param.align
|
||||
if (p.param.mirrored === true) {
|
||||
const plus = (p.text.length + 1) * 0.5
|
||||
let align = p.align
|
||||
if (p.mirrored === true) {
|
||||
if (align == 'left') align = 'right'
|
||||
else if (align == 'right') align = 'left'
|
||||
}
|
||||
if (align == 'left') x -= p.param.width / 2 + plus
|
||||
if (align == 'right') x += p.param.width / 2 + plus
|
||||
if (align == 'up') y += p.param.height / 2 + plus
|
||||
if (align == 'down') y -= p.param.height / 2 + plus
|
||||
const text = `(fp_text user ${p.param.text} (at ${x} ${y} ${p.rot}) (layer ${side}.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)) ${mirror}))`
|
||||
return `(pad 1 smd rect (at 0 0 ${p.rot}) (size ${p.param.width} ${p.param.height}) (layers ${side}.Cu ${side}.Paste ${side}.Mask) ${p.net.net.str})\n${text}`
|
||||
if (align == 'left') x -= p.width / 2 + plus
|
||||
if (align == 'right') x += p.width / 2 + plus
|
||||
if (align == 'up') y += p.height / 2 + plus
|
||||
if (align == 'down') y -= p.height / 2 + plus
|
||||
let text = ''
|
||||
if (p.text.length) {
|
||||
text = `(fp_text user ${p.text} (at ${x} ${y} ${p.r}) (layer ${side}.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)) ${mirror}))`
|
||||
}
|
||||
return `(pad 1 smd rect (at 0 0 ${p.r}) (size ${p.width} ${p.height}) (layers ${side}.Cu ${side}.Paste ${side}.Mask) ${p.net})\n${text}`
|
||||
}
|
||||
|
||||
return `
|
||||
|
@ -43,8 +44,8 @@ module.exports = {
|
|||
(fp_text value "" (at 0 0) (layer F.SilkS) hide (effects (font (size 1.27 1.27) (thickness 0.15))))
|
||||
|
||||
${''/* SMD pads */}
|
||||
${layout(p.param.front, 'F')}
|
||||
${layout(p.param.back, 'B')}
|
||||
${layout(p.front, 'F')}
|
||||
${layout(p.back, 'B')}
|
||||
|
||||
)
|
||||
|
||||
|
|
|
@ -5,33 +5,31 @@
|
|||
// if up, power led will face away from pcb
|
||||
|
||||
module.exports = {
|
||||
nets: {
|
||||
RAW: 'RAW',
|
||||
GND: 'GND',
|
||||
RST: 'RST',
|
||||
VCC: 'VCC',
|
||||
P21: 'P21',
|
||||
P20: 'P20',
|
||||
P19: 'P19',
|
||||
P18: 'P18',
|
||||
P15: 'P15',
|
||||
P14: 'P14',
|
||||
P16: 'P16',
|
||||
P10: 'P10',
|
||||
P1: 'P1',
|
||||
P0: 'P0',
|
||||
P2: 'P2',
|
||||
P3: 'P3',
|
||||
P4: 'P4',
|
||||
P5: 'P5',
|
||||
P6: 'P6',
|
||||
P7: 'P7',
|
||||
P8: 'P8',
|
||||
P9: 'P9',
|
||||
},
|
||||
params: {
|
||||
class: 'MCU',
|
||||
orientation: 'down'
|
||||
designator: 'MCU',
|
||||
orientation: 'down',
|
||||
RAW: {type: 'net', value: 'RAW'},
|
||||
GND: {type: 'net', value: 'GND'},
|
||||
RST: {type: 'net', value: 'RST'},
|
||||
VCC: {type: 'net', value: 'VCC'},
|
||||
P21: {type: 'net', value: 'P21'},
|
||||
P20: {type: 'net', value: 'P20'},
|
||||
P19: {type: 'net', value: 'P19'},
|
||||
P18: {type: 'net', value: 'P18'},
|
||||
P15: {type: 'net', value: 'P15'},
|
||||
P14: {type: 'net', value: 'P14'},
|
||||
P16: {type: 'net', value: 'P16'},
|
||||
P10: {type: 'net', value: 'P10'},
|
||||
P1: {type: 'net', value: 'P1'},
|
||||
P0: {type: 'net', value: 'P0'},
|
||||
P2: {type: 'net', value: 'P2'},
|
||||
P3: {type: 'net', value: 'P3'},
|
||||
P4: {type: 'net', value: 'P4'},
|
||||
P5: {type: 'net', value: 'P5'},
|
||||
P6: {type: 'net', value: 'P6'},
|
||||
P7: {type: 'net', value: 'P7'},
|
||||
P8: {type: 'net', value: 'P8'},
|
||||
P9: {type: 'net', value: 'P9'}
|
||||
},
|
||||
body: p => {
|
||||
const standard = `
|
||||
|
@ -62,61 +60,61 @@ module.exports = {
|
|||
(fp_line (start -12.7 ${def_pos}6.35) (end -12.7 ${def_pos}8.89) (layer F.SilkS) (width 0.15))
|
||||
|
||||
${''/* pin names */}
|
||||
(fp_text user RAW (at -13.97 ${def_pos}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user GND (at -11.43 ${def_pos}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user RST (at -8.89 ${def_pos}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user VCC (at -6.35 ${def_pos}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P21 (at -3.81 ${def_pos}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P20 (at -1.27 ${def_pos}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P19 (at 1.27 ${def_pos}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P18 (at 3.81 ${def_pos}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P15 (at 6.35 ${def_pos}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P14 (at 8.89 ${def_pos}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P16 (at 11.43 ${def_pos}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P10 (at 13.97 ${def_pos}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user RAW (at -13.97 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user GND (at -11.43 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user RST (at -8.89 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user VCC (at -6.35 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P21 (at -3.81 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P20 (at -1.27 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P19 (at 1.27 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P18 (at 3.81 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P15 (at 6.35 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P14 (at 8.89 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P16 (at 11.43 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P10 (at 13.97 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
|
||||
(fp_text user P01 (at -13.97 ${def_neg}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P00 (at -11.43 ${def_neg}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user GND (at -8.89 ${def_neg}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user GND (at -6.35 ${def_neg}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P02 (at -3.81 ${def_neg}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P03 (at -1.27 ${def_neg}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P04 (at 1.27 ${def_neg}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P05 (at 3.81 ${def_neg}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P06 (at 6.35 ${def_neg}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P07 (at 8.89 ${def_neg}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P08 (at 11.43 ${def_neg}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P09 (at 13.97 ${def_neg}4.8 ${p.rot + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P01 (at -13.97 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P00 (at -11.43 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user GND (at -8.89 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user GND (at -6.35 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P02 (at -3.81 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P03 (at -1.27 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P04 (at 1.27 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P05 (at 3.81 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P06 (at 6.35 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P07 (at 8.89 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P08 (at 11.43 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
(fp_text user P09 (at 13.97 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
|
||||
|
||||
${''/* and now the actual pins */}
|
||||
(pad 1 thru_hole rect (at -13.97 ${def_pos}7.62 ${p.rot}) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.RAW.str})
|
||||
(pad 2 thru_hole circle (at -11.43 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.GND.str})
|
||||
(pad 3 thru_hole circle (at -8.89 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.RST.str})
|
||||
(pad 4 thru_hole circle (at -6.35 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.VCC.str})
|
||||
(pad 5 thru_hole circle (at -3.81 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.P21.str})
|
||||
(pad 6 thru_hole circle (at -1.27 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.P20.str})
|
||||
(pad 7 thru_hole circle (at 1.27 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.P19.str})
|
||||
(pad 8 thru_hole circle (at 3.81 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.P18.str})
|
||||
(pad 9 thru_hole circle (at 6.35 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.P15.str})
|
||||
(pad 10 thru_hole circle (at 8.89 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.P14.str})
|
||||
(pad 11 thru_hole circle (at 11.43 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.P16.str})
|
||||
(pad 12 thru_hole circle (at 13.97 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.P10.str})
|
||||
(pad 1 thru_hole rect (at -13.97 ${def_pos}7.62 ${p.r}) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.RAW})
|
||||
(pad 2 thru_hole circle (at -11.43 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.GND})
|
||||
(pad 3 thru_hole circle (at -8.89 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.RST})
|
||||
(pad 4 thru_hole circle (at -6.35 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.VCC})
|
||||
(pad 5 thru_hole circle (at -3.81 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P21})
|
||||
(pad 6 thru_hole circle (at -1.27 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P20})
|
||||
(pad 7 thru_hole circle (at 1.27 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P19})
|
||||
(pad 8 thru_hole circle (at 3.81 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P18})
|
||||
(pad 9 thru_hole circle (at 6.35 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P15})
|
||||
(pad 10 thru_hole circle (at 8.89 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P14})
|
||||
(pad 11 thru_hole circle (at 11.43 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P16})
|
||||
(pad 12 thru_hole circle (at 13.97 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P10})
|
||||
|
||||
(pad 13 thru_hole circle (at -13.97 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.P1.str})
|
||||
(pad 14 thru_hole circle (at -11.43 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.P0.str})
|
||||
(pad 15 thru_hole circle (at -8.89 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.GND.str})
|
||||
(pad 16 thru_hole circle (at -6.35 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.GND.str})
|
||||
(pad 17 thru_hole circle (at -3.81 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.P2.str})
|
||||
(pad 18 thru_hole circle (at -1.27 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.P3.str})
|
||||
(pad 19 thru_hole circle (at 1.27 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.P4.str})
|
||||
(pad 20 thru_hole circle (at 3.81 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.P5.str})
|
||||
(pad 21 thru_hole circle (at 6.35 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.P6.str})
|
||||
(pad 22 thru_hole circle (at 8.89 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.P7.str})
|
||||
(pad 23 thru_hole circle (at 11.43 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.P8.str})
|
||||
(pad 24 thru_hole circle (at 13.97 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.net.P9.str})
|
||||
(pad 13 thru_hole circle (at -13.97 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P1})
|
||||
(pad 14 thru_hole circle (at -11.43 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P0})
|
||||
(pad 15 thru_hole circle (at -8.89 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.GND})
|
||||
(pad 16 thru_hole circle (at -6.35 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.GND})
|
||||
(pad 17 thru_hole circle (at -3.81 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P2})
|
||||
(pad 18 thru_hole circle (at -1.27 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P3})
|
||||
(pad 19 thru_hole circle (at 1.27 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P4})
|
||||
(pad 20 thru_hole circle (at 3.81 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P5})
|
||||
(pad 21 thru_hole circle (at 6.35 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P6})
|
||||
(pad 22 thru_hole circle (at 8.89 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P7})
|
||||
(pad 23 thru_hole circle (at 11.43 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P8})
|
||||
(pad 24 thru_hole circle (at 13.97 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P9})
|
||||
`
|
||||
}
|
||||
if(p.param.orientation == 'down') {
|
||||
if(p.orientation == 'down') {
|
||||
return `
|
||||
${standard}
|
||||
${pins('-', '')})
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
module.exports = {
|
||||
nets: {
|
||||
params: {
|
||||
designator: 'LED',
|
||||
side: 'F',
|
||||
din: undefined,
|
||||
dout: undefined,
|
||||
VCC: 'VCC',
|
||||
GND: 'GND'
|
||||
},
|
||||
params: {
|
||||
class: 'LED',
|
||||
side: 'F'
|
||||
VCC: {type: 'net', value: 'VCC'},
|
||||
GND: {type: 'net', value: 'GND'}
|
||||
},
|
||||
body: p => `
|
||||
|
||||
|
@ -19,27 +17,27 @@ module.exports = {
|
|||
(fp_text reference "${p.ref}" (at 0 0) (layer F.SilkS) ${p.ref_hide} (effects (font (size 1.27 1.27) (thickness 0.15))))
|
||||
(fp_text value "" (at 0 0) (layer F.SilkS) hide (effects (font (size 1.27 1.27) (thickness 0.15))))
|
||||
|
||||
(fp_line (start -1.75 -1.75) (end -1.75 1.75) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -1.75 1.75) (end 1.75 1.75) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 1.75 1.75) (end 1.75 -1.75) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 1.75 -1.75) (end -1.75 -1.75) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -1.75 -1.75) (end -1.75 1.75) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -1.75 1.75) (end 1.75 1.75) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 1.75 1.75) (end 1.75 -1.75) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 1.75 -1.75) (end -1.75 -1.75) (layer ${p.side}.SilkS) (width 0.15))
|
||||
|
||||
(fp_line (start -2.5 -2.5) (end -2.5 2.5) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -2.5 2.5) (end 2.5 2.5) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 2.5 2.5) (end 2.5 -2.5) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 2.5 -2.5) (end -2.5 -2.5) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -2.5 -2.5) (end -2.5 2.5) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -2.5 2.5) (end 2.5 2.5) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 2.5 2.5) (end 2.5 -2.5) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 2.5 -2.5) (end -2.5 -2.5) (layer ${p.side}.SilkS) (width 0.15))
|
||||
|
||||
(fp_poly (pts (xy 4 2.2) (xy 4 0.375) (xy 5 1.2875)) (layer ${p.param.side}.SilkS) (width 0.1))
|
||||
(fp_poly (pts (xy 4 2.2) (xy 4 0.375) (xy 5 1.2875)) (layer ${p.side}.SilkS) (width 0.1))
|
||||
|
||||
(pad 1 smd rect (at -2.2 -0.875 ${p.rot}) (size 2.6 1) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask) ${p.net.VCC.str})
|
||||
(pad 2 smd rect (at -2.2 0.875 ${p.rot}) (size 2.6 1) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask) ${p.net.dout.str})
|
||||
(pad 3 smd rect (at 2.2 0.875 ${p.rot}) (size 2.6 1) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask) ${p.net.GND.str})
|
||||
(pad 4 smd rect (at 2.2 -0.875 ${p.rot}) (size 2.6 1) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask) ${p.net.din.str})
|
||||
(pad 1 smd rect (at -2.2 -0.875 ${p.r}) (size 2.6 1) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask) ${p.VCC})
|
||||
(pad 2 smd rect (at -2.2 0.875 ${p.r}) (size 2.6 1) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask) ${p.dout})
|
||||
(pad 3 smd rect (at 2.2 0.875 ${p.r}) (size 2.6 1) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask) ${p.GND})
|
||||
(pad 4 smd rect (at 2.2 -0.875 ${p.r}) (size 2.6 1) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask) ${p.din})
|
||||
|
||||
(pad 11 smd rect (at -2.5 -1.6 ${p.rot}) (size 2 1.2) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask) ${p.net.VCC.str})
|
||||
(pad 22 smd rect (at -2.5 1.6 ${p.rot}) (size 2 1.2) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask) ${p.net.dout.str})
|
||||
(pad 33 smd rect (at 2.5 1.6 ${p.rot}) (size 2 1.2) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask) ${p.net.GND.str})
|
||||
(pad 44 smd rect (at 2.5 -1.6 ${p.rot}) (size 2 1.2) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask) ${p.net.din.str})
|
||||
(pad 11 smd rect (at -2.5 -1.6 ${p.r}) (size 2 1.2) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask) ${p.VCC})
|
||||
(pad 22 smd rect (at -2.5 1.6 ${p.r}) (size 2 1.2) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask) ${p.dout})
|
||||
(pad 33 smd rect (at 2.5 1.6 ${p.r}) (size 2 1.2) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask) ${p.GND})
|
||||
(pad 44 smd rect (at 2.5 -1.6 ${p.r}) (size 2 1.2) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask) ${p.din})
|
||||
|
||||
)
|
||||
|
||||
|
|
|
@ -8,16 +8,14 @@
|
|||
// C: corresponds to pin 3 (for rotary)
|
||||
|
||||
module.exports = {
|
||||
nets: {
|
||||
params: {
|
||||
designator: 'ROT',
|
||||
from: undefined,
|
||||
to: undefined,
|
||||
A: undefined,
|
||||
B: undefined,
|
||||
C: undefined
|
||||
},
|
||||
params: {
|
||||
class: 'ROT'
|
||||
},
|
||||
body: p => `
|
||||
(module rotary_encoder (layer F.Cu) (tedit 603326DE)
|
||||
|
||||
|
@ -58,15 +56,15 @@ module.exports = {
|
|||
(fp_circle (center -0.12 -0.04) (end 2.88 -0.04) (layer F.Fab) (width 0.12))
|
||||
|
||||
${''/* pin names */}
|
||||
(pad A thru_hole rect (at -7.62 -2.54 ${p.rot}) (size 2 2) (drill 1) (layers *.Cu *.Mask) ${p.net.A.str})
|
||||
(pad C thru_hole circle (at -7.62 -0.04) (size 2 2) (drill 1) (layers *.Cu *.Mask) ${p.net.C.str})
|
||||
(pad B thru_hole circle (at -7.62 2.46) (size 2 2) (drill 1) (layers *.Cu *.Mask) ${p.net.B.str})
|
||||
(pad 1 thru_hole circle (at 6.88 -2.54) (size 1.5 1.5) (drill 1) (layers *.Cu *.Mask) ${p.net.from.str})
|
||||
(pad 2 thru_hole circle (at 6.88 2.46) (size 1.5 1.5) (drill 1) (layers *.Cu *.Mask) ${p.net.to.str})
|
||||
(pad A thru_hole rect (at -7.62 -2.54 ${p.r}) (size 2 2) (drill 1) (layers *.Cu *.Mask) ${p.A})
|
||||
(pad C thru_hole circle (at -7.62 -0.04) (size 2 2) (drill 1) (layers *.Cu *.Mask) ${p.C})
|
||||
(pad B thru_hole circle (at -7.62 2.46) (size 2 2) (drill 1) (layers *.Cu *.Mask) ${p.B})
|
||||
(pad 1 thru_hole circle (at 6.88 -2.54) (size 1.5 1.5) (drill 1) (layers *.Cu *.Mask) ${p.from})
|
||||
(pad 2 thru_hole circle (at 6.88 2.46) (size 1.5 1.5) (drill 1) (layers *.Cu *.Mask) ${p.to})
|
||||
|
||||
${''/* Legs */}
|
||||
(pad "" thru_hole rect (at -0.12 -5.64 ${p.rot}) (size 3.2 2) (drill oval 2.8 1.5) (layers *.Cu *.Mask))
|
||||
(pad "" thru_hole rect (at -0.12 5.56 ${p.rot}) (size 3.2 2) (drill oval 2.8 1.5) (layers *.Cu *.Mask))
|
||||
(pad "" thru_hole rect (at -0.12 -5.64 ${p.r}) (size 3.2 2) (drill oval 2.8 1.5) (layers *.Cu *.Mask))
|
||||
(pad "" thru_hole rect (at -0.12 5.56 ${p.r}) (size 3.2 2) (drill oval 2.8 1.5) (layers *.Cu *.Mask))
|
||||
)
|
||||
`
|
||||
}
|
||||
|
|
|
@ -21,7 +21,9 @@
|
|||
|
||||
|
||||
module.exports = {
|
||||
nets: {
|
||||
params: {
|
||||
designator: 'S',
|
||||
reverse: false,
|
||||
from: undefined,
|
||||
to: undefined,
|
||||
A: undefined,
|
||||
|
@ -29,16 +31,12 @@ module.exports = {
|
|||
C: undefined,
|
||||
D: undefined
|
||||
},
|
||||
params: {
|
||||
class: 'S',
|
||||
reverse: false
|
||||
},
|
||||
body: p => {
|
||||
const standard = `
|
||||
(module RollerEncoder_Panasonic_EVQWGD001 (layer F.Cu) (tedit 6040A10C)
|
||||
${p.at /* parametric position */}
|
||||
(fp_text reference REF** (at 0 0 ${p.rot}) (layer F.Fab) (effects (font (size 1 1) (thickness 0.15))))
|
||||
(fp_text value RollerEncoder_Panasonic_EVQWGD001 (at -0.1 9 ${p.rot}) (layer F.Fab) (effects (font (size 1 1) (thickness 0.15))))
|
||||
(fp_text reference REF** (at 0 0 ${p.r}) (layer F.Fab) (effects (font (size 1 1) (thickness 0.15))))
|
||||
(fp_text value RollerEncoder_Panasonic_EVQWGD001 (at -0.1 9 ${p.r}) (layer F.Fab) (effects (font (size 1 1) (thickness 0.15))))
|
||||
|
||||
${'' /* corner marks */}
|
||||
(fp_line (start -8.4 -6.4) (end 8.4 -6.4) (layer Dwgs.User) (width 0.12))
|
||||
|
@ -59,18 +57,18 @@ module.exports = {
|
|||
(fp_arc (start ${def_pos}9.5 -6.3) (end ${def_pos}9.8 -6.3) (angle ${def_neg}90) (layer Edge.Cuts) (width 0.15))
|
||||
|
||||
${'' /* pins */}
|
||||
(pad S1 thru_hole circle (at ${def_neg}6.85 -6.2 ${p.rot}) (size 1.6 1.6) (drill 0.9) (layers *.Cu *.Mask) ${p.net.from.str})
|
||||
(pad S2 thru_hole circle (at ${def_neg}5 -6.2 ${p.rot}) (size 1.6 1.6) (drill 0.9) (layers *.Cu *.Mask) ${p.net.to.str})
|
||||
(pad A thru_hole circle (at ${def_neg}5.625 -3.81 ${p.rot}) (size 1.6 1.6) (drill 0.9) (layers *.Cu *.Mask) ${p.net.A.str})
|
||||
(pad B thru_hole circle (at ${def_neg}5.625 -1.27 ${p.rot}) (size 1.6 1.6) (drill 0.9) (layers *.Cu *.Mask) ${p.net.B.str})
|
||||
(pad C thru_hole circle (at ${def_neg}5.625 1.27 ${p.rot}) (size 1.6 1.6) (drill 0.9) (layers *.Cu *.Mask) ${p.net.C.str})
|
||||
(pad D thru_hole circle (at ${def_neg}5.625 3.81 ${p.rot}) (size 1.6 1.6) (drill 0.9) (layers *.Cu *.Mask) ${p.net.D.str})
|
||||
(pad S1 thru_hole circle (at ${def_neg}6.85 -6.2 ${p.r}) (size 1.6 1.6) (drill 0.9) (layers *.Cu *.Mask) ${p.from})
|
||||
(pad S2 thru_hole circle (at ${def_neg}5 -6.2 ${p.r}) (size 1.6 1.6) (drill 0.9) (layers *.Cu *.Mask) ${p.to})
|
||||
(pad A thru_hole circle (at ${def_neg}5.625 -3.81 ${p.r}) (size 1.6 1.6) (drill 0.9) (layers *.Cu *.Mask) ${p.A})
|
||||
(pad B thru_hole circle (at ${def_neg}5.625 -1.27 ${p.r}) (size 1.6 1.6) (drill 0.9) (layers *.Cu *.Mask) ${p.B})
|
||||
(pad C thru_hole circle (at ${def_neg}5.625 1.27 ${p.r}) (size 1.6 1.6) (drill 0.9) (layers *.Cu *.Mask) ${p.C})
|
||||
(pad D thru_hole circle (at ${def_neg}5.625 3.81 ${p.r}) (size 1.6 1.6) (drill 0.9) (layers *.Cu *.Mask) ${p.D})
|
||||
|
||||
${'' /* stabilizer */}
|
||||
(pad "" np_thru_hole circle (at ${def_neg}5.625 6.3 ${p.rot}) (size 1.5 1.5) (drill 1.5) (layers *.Cu *.Mask))
|
||||
(pad "" np_thru_hole circle (at ${def_neg}5.625 6.3 ${p.r}) (size 1.5 1.5) (drill 1.5) (layers *.Cu *.Mask))
|
||||
`
|
||||
}
|
||||
if(p.param.reverse) {
|
||||
if(p.reverse) {
|
||||
return `
|
||||
${standard}
|
||||
${pins('-', '')}
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
module.exports = {
|
||||
nets: {
|
||||
params: {
|
||||
designator: 'T', // for Toggle
|
||||
side: 'F',
|
||||
from: undefined,
|
||||
to: undefined
|
||||
},
|
||||
params: {
|
||||
class: 'T', // for Toggle
|
||||
side: 'F'
|
||||
},
|
||||
body: p => {
|
||||
|
||||
const left = p.param.side == 'F' ? '-' : ''
|
||||
const right = p.param.side == 'F' ? '' : '-'
|
||||
const left = p.side == 'F' ? '-' : ''
|
||||
const right = p.side == 'F' ? '' : '-'
|
||||
|
||||
return `
|
||||
|
||||
|
@ -23,12 +21,12 @@ module.exports = {
|
|||
(fp_text value "" (at 0 0) (layer F.SilkS) hide (effects (font (size 1.27 1.27) (thickness 0.15))))
|
||||
|
||||
${'' /* outline */}
|
||||
(fp_line (start 1.95 -1.35) (end -1.95 -1.35) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 0 -1.35) (end -3.3 -1.35) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -3.3 -1.35) (end -3.3 1.5) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -3.3 1.5) (end 3.3 1.5) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 3.3 1.5) (end 3.3 -1.35) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 0 -1.35) (end 3.3 -1.35) (layer ${p.param.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 1.95 -1.35) (end -1.95 -1.35) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 0 -1.35) (end -3.3 -1.35) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -3.3 -1.35) (end -3.3 1.5) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start -3.3 1.5) (end 3.3 1.5) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 3.3 1.5) (end 3.3 -1.35) (layer ${p.side}.SilkS) (width 0.15))
|
||||
(fp_line (start 0 -1.35) (end 3.3 -1.35) (layer ${p.side}.SilkS) (width 0.15))
|
||||
|
||||
${'' /* extra indicator for the slider */}
|
||||
(fp_line (start -1.95 -3.85) (end 1.95 -3.85) (layer Dwgs.User) (width 0.15))
|
||||
|
@ -40,15 +38,15 @@ module.exports = {
|
|||
(pad "" np_thru_hole circle (at -1.5 0) (size 1 1) (drill 0.9) (layers *.Cu *.Mask))
|
||||
|
||||
${'' /* pins */}
|
||||
(pad 1 smd rect (at ${right}2.25 2.075 ${p.rot}) (size 0.9 1.25) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask) ${p.net.from.str})
|
||||
(pad 2 smd rect (at ${left}0.75 2.075 ${p.rot}) (size 0.9 1.25) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask) ${p.net.to.str})
|
||||
(pad 3 smd rect (at ${left}2.25 2.075 ${p.rot}) (size 0.9 1.25) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask))
|
||||
(pad 1 smd rect (at ${right}2.25 2.075 ${p.r}) (size 0.9 1.25) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask) ${p.from})
|
||||
(pad 2 smd rect (at ${left}0.75 2.075 ${p.r}) (size 0.9 1.25) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask) ${p.to})
|
||||
(pad 3 smd rect (at ${left}2.25 2.075 ${p.r}) (size 0.9 1.25) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask))
|
||||
|
||||
${'' /* side mounts */}
|
||||
(pad "" smd rect (at 3.7 -1.1 ${p.rot}) (size 0.9 0.9) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask))
|
||||
(pad "" smd rect (at 3.7 1.1 ${p.rot}) (size 0.9 0.9) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask))
|
||||
(pad "" smd rect (at -3.7 1.1 ${p.rot}) (size 0.9 0.9) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask))
|
||||
(pad "" smd rect (at -3.7 -1.1 ${p.rot}) (size 0.9 0.9) (layers ${p.param.side}.Cu ${p.param.side}.Paste ${p.param.side}.Mask))
|
||||
(pad "" smd rect (at 3.7 -1.1 ${p.r}) (size 0.9 0.9) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask))
|
||||
(pad "" smd rect (at 3.7 1.1 ${p.r}) (size 0.9 0.9) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask))
|
||||
(pad "" smd rect (at -3.7 1.1 ${p.r}) (size 0.9 0.9) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask))
|
||||
(pad "" smd rect (at -3.7 -1.1 ${p.r}) (size 0.9 0.9) (layers ${p.side}.Cu ${p.side}.Paste ${p.side}.Mask))
|
||||
)
|
||||
|
||||
`
|
||||
|
|
|
@ -1,8 +1,24 @@
|
|||
// TRRS-PJ-320A-dual
|
||||
//
|
||||
// Normal footprint:
|
||||
// _________________
|
||||
// | (1) (3) (4)|
|
||||
// | (2) (3) (4)|
|
||||
// | |
|
||||
// |___(2)__________|
|
||||
// | (1) |
|
||||
// |________________|
|
||||
//
|
||||
// Reverse footprint:
|
||||
// _________________
|
||||
// | (2) (3) (4)|
|
||||
// | (1) |
|
||||
// | (1) |
|
||||
// |___(2)___(3)_(4)|
|
||||
//
|
||||
// Reverse & symmetric footprint:
|
||||
// _________________
|
||||
// | (1|2) (3) (4)|
|
||||
// | |
|
||||
// |_(1|2)___(3)_(4)|
|
||||
//
|
||||
// Nets
|
||||
// A: corresponds to pin 1
|
||||
|
@ -18,17 +34,15 @@
|
|||
// pins 1 and 2 must be identical if symmetric is true, as they will overlap
|
||||
|
||||
module.exports = {
|
||||
nets: {
|
||||
params: {
|
||||
designator: 'TRRS',
|
||||
reverse: false,
|
||||
symmetric: false,
|
||||
A: undefined,
|
||||
B: undefined,
|
||||
C: undefined,
|
||||
D: undefined
|
||||
},
|
||||
params: {
|
||||
class: 'TRRS',
|
||||
reverse: false,
|
||||
symmetric: false
|
||||
},
|
||||
body: p => {
|
||||
const standard = `
|
||||
(module TRRS-PJ-320A-dual (layer F.Cu) (tedit 5970F8E5)
|
||||
|
@ -36,7 +50,7 @@ module.exports = {
|
|||
${p.at /* parametric position */}
|
||||
|
||||
${'' /* footprint reference */}
|
||||
(fp_text reference REF** (at 0 14.2) (layer Dwgs.User) (effects (font (size 1 1) (thickness 0.15))))
|
||||
(fp_text reference "${p.ref}" (at 0 14.2) (layer Dwgs.User) (effects (font (size 1 1) (thickness 0.15))))
|
||||
(fp_text value TRRS-PJ-320A-dual (at 0 -5.6) (layer F.Fab) (effects (font (size 1 1) (thickness 0.15))))
|
||||
|
||||
${''/* corner marks */}
|
||||
|
@ -48,35 +62,42 @@ module.exports = {
|
|||
(fp_line (start 0.75 12.1) (end -5.35 12.1) (layer Dwgs.User) (width 0.15))
|
||||
(fp_line (start 0.75 0) (end -5.35 0) (layer Dwgs.User) (width 0.15))
|
||||
|
||||
${''/* stabilizers */}
|
||||
(pad "" np_thru_hole circle (at -2.3 8.6) (size 1.5 1.5) (drill 1.5) (layers *.Cu *.Mask))
|
||||
(pad "" np_thru_hole circle (at -2.3 1.6) (size 1.5 1.5) (drill 1.5) (layers *.Cu *.Mask))
|
||||
`
|
||||
function pins(def_neg, def_pos) {
|
||||
function stabilizers(def_pos) {
|
||||
return `
|
||||
(pad 1 thru_hole oval (at ${def_neg} 11.3 ${p.rot}) (size 1.6 2.2) (drill oval 0.9 1.5) (layers *.Cu *.Mask) ${p.net.A.str})
|
||||
(pad 2 thru_hole oval (at ${def_pos} 10.2 ${p.rot}) (size 1.6 2.2) (drill oval 0.9 1.5) (layers *.Cu *.Mask) ${p.net.B.str})
|
||||
(pad 3 thru_hole oval (at ${def_pos} 6.2 ${p.rot}) (size 1.6 2.2) (drill oval 0.9 1.5) (layers *.Cu *.Mask) ${p.net.C.str})
|
||||
(pad 4 thru_hole oval (at ${def_pos} 3.2 ${p.rot}) (size 1.6 2.2) (drill oval 0.9 1.5) (layers *.Cu *.Mask) ${p.net.D.str})
|
||||
(pad "" np_thru_hole circle (at ${def_pos} 8.6) (size 1.5 1.5) (drill 1.5) (layers *.Cu *.Mask))
|
||||
(pad "" np_thru_hole circle (at ${def_pos} 1.6) (size 1.5 1.5) (drill 1.5) (layers *.Cu *.Mask))
|
||||
`
|
||||
}
|
||||
if(p.param.reverse & p.param.symmetric) {
|
||||
function pins(def_neg, def_pos) {
|
||||
return `
|
||||
(pad 1 thru_hole oval (at ${def_neg} 11.3 ${p.r}) (size 1.6 2.2) (drill oval 0.9 1.5) (layers *.Cu *.Mask) ${p.A})
|
||||
(pad 2 thru_hole oval (at ${def_pos} 10.2 ${p.r}) (size 1.6 2.2) (drill oval 0.9 1.5) (layers *.Cu *.Mask) ${p.B})
|
||||
(pad 3 thru_hole oval (at ${def_pos} 6.2 ${p.r}) (size 1.6 2.2) (drill oval 0.9 1.5) (layers *.Cu *.Mask) ${p.C})
|
||||
(pad 4 thru_hole oval (at ${def_pos} 3.2 ${p.r}) (size 1.6 2.2) (drill oval 0.9 1.5) (layers *.Cu *.Mask) ${p.D})
|
||||
`
|
||||
}
|
||||
if(p.reverse & p.symmetric) {
|
||||
return `
|
||||
${standard}
|
||||
${stabilizers('-2.3')}
|
||||
${pins('0', '-4.6')}
|
||||
${pins('-4.6', '0')})
|
||||
`
|
||||
} else if(p.param.reverse) {
|
||||
} else if(p.reverse) {
|
||||
return `
|
||||
${standard}
|
||||
${pins('-4.6', '0')}
|
||||
${pins('4.6', '0')})
|
||||
${stabilizers('-2.3')}
|
||||
${stabilizers('0')}
|
||||
${pins('-2.3', '2.3')}
|
||||
${pins('0', '-4.6')})
|
||||
`
|
||||
} else {
|
||||
return `
|
||||
${standard}
|
||||
${pins('-4.6', '0')})
|
||||
`
|
||||
}
|
||||
} else {
|
||||
return `
|
||||
${standard}
|
||||
${stabilizers('-2.3')}
|
||||
${pins('-4.6', '0')})
|
||||
`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// net: the net this via should be connected to
|
||||
|
||||
module.exports = {
|
||||
nets: {
|
||||
params: {
|
||||
net: undefined
|
||||
},
|
||||
body: p => `
|
||||
|
@ -14,7 +14,7 @@ module.exports = {
|
|||
(fp_text value VIA-0.6mm (at 0 -1.4) (layer F.Fab) hide (effects (font (size 1 1) (thickness 0.15))))
|
||||
|
||||
${'' /* via */}
|
||||
(pad 1 thru_hole circle (at 0 0) (size 0.6 0.6) (drill 0.3) (layers *.Cu) (zone_connect 2) ${p.net.net.str})
|
||||
(pad 1 thru_hole circle (at 0 0) (size 0.6 0.6) (drill 0.3) (layers *.Cu) (zone_connect 2) ${p.net})
|
||||
)
|
||||
`
|
||||
}
|
42
src/io.js
42
src/io.js
|
@ -1,11 +1,36 @@
|
|||
const yaml = require('js-yaml')
|
||||
const jszip = require('jszip')
|
||||
const makerjs = require('makerjs')
|
||||
const jscad = require('@jscad/openjscad')
|
||||
|
||||
const u = require('./utils')
|
||||
const a = require('./assert')
|
||||
const kle = require('./kle')
|
||||
|
||||
exports.unpack = async (zip) => {
|
||||
|
||||
// main config text (has to be called "config.ext" where ext is one of yaml/json/js)
|
||||
const candidates = zip.file(/^config\.(yaml|json|js)$/)
|
||||
if (candidates.length != 1) {
|
||||
throw new Error('Ambiguous config in bundle!')
|
||||
}
|
||||
const config_text = await candidates[0].async('string')
|
||||
const injections = []
|
||||
|
||||
// bundled footprints
|
||||
const fps = zip.folder('footprints')
|
||||
const module_prefix = 'const module = {};\n\n'
|
||||
const module_suffix = '\n\nreturn module.exports;'
|
||||
for (const fp of fps.file(/.*\.js$/)) {
|
||||
const name = fp.name.slice('footprints/'.length).split('.')[0]
|
||||
const text = await fp.async('string')
|
||||
const parsed = new Function(module_prefix + text + module_suffix)()
|
||||
// TODO: some sort of footprint validation?
|
||||
injections.push(['footprint', name, parsed])
|
||||
}
|
||||
|
||||
return [config_text, injections]
|
||||
}
|
||||
|
||||
exports.interpret = (raw, logger) => {
|
||||
let config = raw
|
||||
let format = 'OBJ'
|
||||
|
@ -65,18 +90,3 @@ exports.twodee = (model, debug) => {
|
|||
}
|
||||
return result
|
||||
}
|
||||
|
||||
exports.threedee = async (script, debug) => {
|
||||
const compiled = await new Promise((resolve, reject) => {
|
||||
jscad.compile(script, {}).then(compiled => {
|
||||
resolve(compiled)
|
||||
})
|
||||
})
|
||||
const result = {
|
||||
stl: jscad.generateOutput('stla', compiled).asBuffer().toString()
|
||||
}
|
||||
if (debug) {
|
||||
result.jscad = script
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -70,4 +70,4 @@ exports.convert = (config, logger) => {
|
|||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,23 @@
|
|||
const op_prefix = exports.op_prefix = str => {
|
||||
|
||||
const prefix = str[0]
|
||||
const suffix = str.slice(1)
|
||||
if (str.startsWith('+')) return {name: suffix, operation: 'add'}
|
||||
if (str.startsWith('-')) return {name: suffix, operation: 'subtract'}
|
||||
if (str.startsWith('~')) return {name: suffix, operation: 'intersect'}
|
||||
if (str.startsWith('^')) return {name: suffix, operation: 'stack'}
|
||||
return {name: str, operation: 'add'}
|
||||
const result = {name: suffix, operation: 'add'}
|
||||
|
||||
if (prefix == '+') ; // noop
|
||||
else if (prefix == '-') result.operation = 'subtract'
|
||||
else if (prefix == '~') result.operation = 'intersect'
|
||||
else if (prefix == '^') result.operation = 'stack'
|
||||
else result.name = str // no prefix, so the name was the whole string
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
exports.operation = (str, choices={}, order=Object.keys(choices)) => {
|
||||
let res = op_prefix(str)
|
||||
for (const key of order) {
|
||||
if (choices[key].includes(res.name)) {
|
||||
res.type = key
|
||||
res.what = key
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
521
src/outlines.js
521
src/outlines.js
|
@ -4,342 +4,251 @@ const a = require('./assert')
|
|||
const o = require('./operation')
|
||||
const Point = require('./point')
|
||||
const prep = require('./prepare')
|
||||
const anchor_lib = require('./anchor')
|
||||
const anchor = require('./anchor').parse
|
||||
const filter = require('./filter').parse
|
||||
|
||||
const rectangle = (w, h, corner, bevel, name='') => {
|
||||
const error = (dim, val) => `Rectangle for "${name}" isn't ${dim} enough for its corner and bevel (${val} - 2 * ${corner} - 2 * ${bevel} <= 0)!`
|
||||
const mod = 2 * (corner + bevel)
|
||||
const cw = w - mod
|
||||
a.assert(cw >= 0, error('wide', w))
|
||||
const ch = h - mod
|
||||
a.assert(ch >= 0, error('tall', h))
|
||||
const binding = (base, bbox, point, units) => {
|
||||
|
||||
let res = new m.models.Rectangle(cw, ch)
|
||||
if (bevel) {
|
||||
res = u.poly([
|
||||
[-bevel, 0],
|
||||
[-bevel, ch],
|
||||
[0, ch + bevel],
|
||||
[cw, ch + bevel],
|
||||
[cw + bevel, ch],
|
||||
[cw + bevel, 0],
|
||||
[cw, -bevel],
|
||||
[0, -bevel]
|
||||
])
|
||||
let bind = a.trbl(point.meta.bind || 0, `${point.meta.name}.bind`)(units)
|
||||
// if it's a mirrored key, we swap the left and right bind values
|
||||
if (point.meta.mirrored) {
|
||||
bind = [bind[0], bind[3], bind[2], bind[1]]
|
||||
}
|
||||
if (corner > 0) res = m.model.outline(res, corner, 0)
|
||||
return m.model.moveRelative(res, [corner + bevel, corner + bevel])
|
||||
|
||||
const bt = Math.max(bbox.high[1], 0) + Math.max(bind[0], 0)
|
||||
const br = Math.max(bbox.high[0], 0) + Math.max(bind[1], 0)
|
||||
const bd = Math.min(bbox.low[1], 0) - Math.max(bind[2], 0)
|
||||
const bl = Math.min(bbox.low[0], 0) - Math.max(bind[3], 0)
|
||||
|
||||
if (bind[0] || bind[1]) base = u.union(base, u.rect(br, bt))
|
||||
if (bind[1] || bind[2]) base = u.union(base, u.rect(br, -bd, [0, bd]))
|
||||
if (bind[2] || bind[3]) base = u.union(base, u.rect(-bl, -bd, [bl, bd]))
|
||||
if (bind[3] || bind[0]) base = u.union(base, u.rect(-bl, bt, [bl, 0]))
|
||||
|
||||
return base
|
||||
}
|
||||
|
||||
const layout = exports._layout = (config = {}, points = {}, units = {}) => {
|
||||
const rectangle = (config, name, points, outlines, units) => {
|
||||
|
||||
// Glue config sanitization
|
||||
// prepare params
|
||||
a.unexpected(config, `${name}`, ['size', 'corner', 'bevel'])
|
||||
const size = a.wh(config.size, `${name}.size`)(units)
|
||||
const rec_units = prep.extend({
|
||||
sx: size[0],
|
||||
sy: size[1]
|
||||
}, units)
|
||||
const corner = a.sane(config.corner || 0, `${name}.corner`, 'number')(rec_units)
|
||||
const bevel = a.sane(config.bevel || 0, `${name}.bevel`, 'number')(rec_units)
|
||||
|
||||
const parsed_glue = u.deepcopy(a.sane(config, 'outlines.glue', 'object')())
|
||||
for (let [gkey, gval] of Object.entries(parsed_glue)) {
|
||||
a.unexpected(gval, `outlines.glue.${gkey}`, ['top', 'bottom', 'waypoints', 'extra'])
|
||||
|
||||
for (const y of ['top', 'bottom']) {
|
||||
a.unexpected(gval[y], `outlines.glue.${gkey}.${y}`, ['left', 'right'])
|
||||
gval[y].left = anchor_lib.parse(gval[y].left, `outlines.glue.${gkey}.${y}.left`, points)
|
||||
if (a.type(gval[y].right)(units) != 'number') {
|
||||
gval[y].right = anchor_lib.parse(gval[y].right, `outlines.glue.${gkey}.${y}.right`, points)
|
||||
}
|
||||
// return shape function and its units
|
||||
return [() => {
|
||||
|
||||
const error = (dim, val) => `Rectangle for "${name}" isn't ${dim} enough for its corner and bevel (${val} - 2 * ${corner} - 2 * ${bevel} <= 0)!`
|
||||
const [w, h] = size
|
||||
const mod = 2 * (corner + bevel)
|
||||
const cw = w - mod
|
||||
a.assert(cw >= 0, error('wide', w))
|
||||
const ch = h - mod
|
||||
a.assert(ch >= 0, error('tall', h))
|
||||
|
||||
let rect = new m.models.Rectangle(cw, ch)
|
||||
if (bevel) {
|
||||
rect = u.poly([
|
||||
[-bevel, 0],
|
||||
[-bevel, ch],
|
||||
[0, ch + bevel],
|
||||
[cw, ch + bevel],
|
||||
[cw + bevel, ch],
|
||||
[cw + bevel, 0],
|
||||
[cw, -bevel],
|
||||
[0, -bevel]
|
||||
])
|
||||
}
|
||||
|
||||
gval.waypoints = a.sane(gval.waypoints || [], `outlines.glue.${gkey}.waypoints`, 'array')(units)
|
||||
let wi = 0
|
||||
gval.waypoints = gval.waypoints.map(w => {
|
||||
const name = `outlines.glue.${gkey}.waypoints[${++wi}]`
|
||||
a.unexpected(w, name, ['percent', 'width'])
|
||||
w.percent = a.sane(w.percent, name + '.percent', 'number')(units)
|
||||
w.width = a.wh(w.width, name + '.width')(units)
|
||||
return w
|
||||
})
|
||||
if (corner > 0) rect = m.model.outline(rect, corner, 0)
|
||||
rect = m.model.moveRelative(rect, [-cw/2, -ch/2])
|
||||
const bbox = {high: [w/2, h/2], low: [-w/2, -h/2]}
|
||||
|
||||
parsed_glue[gkey] = gval
|
||||
return [rect, bbox]
|
||||
}, rec_units]
|
||||
}
|
||||
|
||||
const circle = (config, name, points, outlines, units) => {
|
||||
|
||||
// prepare params
|
||||
a.unexpected(config, `${name}`, ['radius'])
|
||||
const radius = a.sane(config.radius, `${name}.radius`, 'number')(units)
|
||||
const circ_units = prep.extend({
|
||||
r: radius
|
||||
}, units)
|
||||
|
||||
// return shape function and its units
|
||||
return [() => {
|
||||
let circle = u.circle([0, 0], radius)
|
||||
const bbox = {high: [radius, radius], low: [-radius, -radius]}
|
||||
return [circle, bbox]
|
||||
}, circ_units]
|
||||
}
|
||||
|
||||
const polygon = (config, name, points, outlines, units) => {
|
||||
|
||||
// prepare params
|
||||
a.unexpected(config, `${name}`, ['points'])
|
||||
const poly_points = a.sane(config.points, `${name}.points`, 'array')()
|
||||
|
||||
// return shape function and its units
|
||||
return [point => {
|
||||
const parsed_points = []
|
||||
// the poly starts at [0, 0] as it will be positioned later
|
||||
// but we keep the point metadata for potential mirroring purposes
|
||||
let last_anchor = new Point(0, 0, 0, point.meta)
|
||||
let poly_index = -1
|
||||
for (const poly_point of poly_points) {
|
||||
const poly_name = `${name}.points[${++poly_index}]`
|
||||
last_anchor = anchor(poly_point, poly_name, points, last_anchor)(units)
|
||||
parsed_points.push(last_anchor.p)
|
||||
}
|
||||
let poly = u.poly(parsed_points)
|
||||
const bbox = u.bbox(parsed_points)
|
||||
return [poly, bbox]
|
||||
}, units]
|
||||
}
|
||||
|
||||
const outline = (config, name, points, outlines, units) => {
|
||||
|
||||
// prepare params
|
||||
a.unexpected(config, `${name}`, ['name', 'origin'])
|
||||
a.assert(outlines[config.name], `Field "${name}.name" does not name an existing outline!`)
|
||||
const origin = anchor(config.origin || {}, `${name}.origin`, points)(units)
|
||||
|
||||
// return shape function and its units
|
||||
return [() => {
|
||||
let o = u.deepcopy(outlines[config.name])
|
||||
o = origin.unposition(o)
|
||||
const bbox = m.measure.modelExtents(o)
|
||||
return [o, bbox]
|
||||
}, units]
|
||||
}
|
||||
|
||||
const whats = {
|
||||
rectangle,
|
||||
circle,
|
||||
polygon,
|
||||
outline
|
||||
}
|
||||
|
||||
const expand_shorthand = (config, name, units) => {
|
||||
if (a.type(config.expand)(units) == 'string') {
|
||||
const prefix = config.expand.slice(0, -1)
|
||||
const suffix = config.expand.slice(-1)
|
||||
const valid_suffixes = [')', '>', ']']
|
||||
a.assert(valid_suffixes.includes(suffix), `If field "${name}" is a string, ` +
|
||||
`it should end with one of [${valid_suffixes.map(s => `'${s}'`).join(', ')}]!`)
|
||||
config.expand = prefix
|
||||
config.joints = config.joints || valid_suffixes.indexOf(suffix)
|
||||
}
|
||||
|
||||
|
||||
// TODO: handle glue.extra (or revoke it from the docs)
|
||||
|
||||
return (params, export_name, expected) => {
|
||||
|
||||
// Layout params sanitization
|
||||
|
||||
a.unexpected(params, `${export_name}`, expected.concat(['side', 'tags', 'glue', 'size', 'corner', 'bevel', 'bound']))
|
||||
const size = a.wh(params.size, `${export_name}.size`)(units)
|
||||
const relative_units = prep.extend({
|
||||
sx: size[0],
|
||||
sy: size[1]
|
||||
}, units)
|
||||
|
||||
|
||||
|
||||
const side = a.in(params.side, `${export_name}.side`, ['left', 'right', 'middle', 'both', 'glue'])
|
||||
const tags = a.sane(params.tags || [], `${export_name}.tags`, 'array')()
|
||||
const corner = a.sane(params.corner || 0, `${export_name}.corner`, 'number')(relative_units)
|
||||
const bevel = a.sane(params.bevel || 0, `${export_name}.bevel`, 'number')(relative_units)
|
||||
const bound = a.sane(params.bound === undefined ? true : params.bound, `${export_name}.bound`, 'boolean')()
|
||||
|
||||
// Actual layout
|
||||
|
||||
let left = {models: {}}
|
||||
let right = {models: {}}
|
||||
if (['left', 'right', 'middle', 'both'].includes(side)) {
|
||||
for (const [pname, p] of Object.entries(points)) {
|
||||
|
||||
// filter by tags, if necessary
|
||||
if (tags.length) {
|
||||
const source = p.meta.tags || {}
|
||||
const point_tags = Object.keys(source).filter(t => !!source[t])
|
||||
const relevant = point_tags.some(pt => tags.includes(pt))
|
||||
if (!relevant) continue
|
||||
}
|
||||
|
||||
let from_x = -size[0] / 2, to_x = size[0] / 2
|
||||
let from_y = -size[1] / 2, to_y = size[1] / 2
|
||||
|
||||
// the original position
|
||||
let rect = rectangle(to_x - from_x, to_y - from_y, corner, bevel, `${export_name}.size`)
|
||||
rect = m.model.moveRelative(rect, [from_x, from_y])
|
||||
|
||||
// extra binding "material", if necessary
|
||||
if (bound) {
|
||||
let bind = a.trbl(p.meta.bind || 0, `${pname}.bind`)(relative_units)
|
||||
// if it's a mirrored key, we swap the left and right bind values
|
||||
if (p.meta.mirrored) {
|
||||
bind = [bind[0], bind[3], bind[2], bind[1]]
|
||||
}
|
||||
|
||||
const bt = to_y + Math.max(bind[0], 0)
|
||||
const br = to_x + Math.max(bind[1], 0)
|
||||
const bd = from_y - Math.max(bind[2], 0)
|
||||
const bl = from_x - Math.max(bind[3], 0)
|
||||
|
||||
if (bind[0] || bind[1]) rect = u.union(rect, u.rect(br, bt))
|
||||
if (bind[1] || bind[2]) rect = u.union(rect, u.rect(br, -bd, [0, bd]))
|
||||
if (bind[2] || bind[3]) rect = u.union(rect, u.rect(-bl, -bd, [bl, bd]))
|
||||
if (bind[3] || bind[0]) rect = u.union(rect, u.rect(-bl, bt, [bl, 0]))
|
||||
}
|
||||
|
||||
// positioning and unioning the resulting shape
|
||||
rect = p.position(rect)
|
||||
if (p.meta.mirrored) {
|
||||
right = u.union(right, rect)
|
||||
} else {
|
||||
left = u.union(left, rect)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (side == 'left') return left
|
||||
if (side == 'right') return right
|
||||
|
||||
// allow opting out of gluing, when
|
||||
// A) there are no glue definitions, or
|
||||
// B) glue is explicitly set to false
|
||||
const glue_opt_out = (!Object.keys(parsed_glue).length || params.glue === false)
|
||||
|
||||
let glue = {models: {}}
|
||||
if (bound && ['middle', 'both', 'glue'].includes(side) && !glue_opt_out) {
|
||||
|
||||
const default_glue_name = Object.keys(parsed_glue)[0]
|
||||
const computed_glue_name = a.sane(params.glue || default_glue_name, `${export_name}.glue`, 'string')()
|
||||
const glue_def = parsed_glue[computed_glue_name]
|
||||
a.assert(glue_def, `Field "${export_name}.glue" does not name a valid glue!`)
|
||||
|
||||
const get_line = (anchor) => {
|
||||
if (a.type(anchor)(relative_units) == 'number') {
|
||||
return u.line([anchor, -1000], [anchor, 1000])
|
||||
}
|
||||
|
||||
// if it wasn't a number, then it's a (possibly relative) anchor
|
||||
const from = anchor(relative_units).clone()
|
||||
const to = from.clone().shift([from.meta.mirrored ? -1 : 1, 0])
|
||||
|
||||
return u.line(from.p, to.p)
|
||||
}
|
||||
|
||||
const tll = get_line(glue_def.top.left)
|
||||
const trl = get_line(glue_def.top.right)
|
||||
const tip = m.path.converge(tll, trl)
|
||||
if (!tip) {
|
||||
throw new Error(`Top lines don't intersect in glue "${computed_glue_name}"!`)
|
||||
}
|
||||
const tlp = u.eq(tll.origin, tip) ? tll.end : tll.origin
|
||||
const trp = u.eq(trl.origin, tip) ? trl.end : trl.origin
|
||||
|
||||
const bll = get_line(glue_def.bottom.left)
|
||||
const brl = get_line(glue_def.bottom.right)
|
||||
const bip = m.path.converge(bll, brl)
|
||||
if (!bip) {
|
||||
throw new Error(`Bottom lines don't intersect in glue "${computed_glue_name}"!`)
|
||||
}
|
||||
const blp = u.eq(bll.origin, bip) ? bll.end : bll.origin
|
||||
const brp = u.eq(brl.origin, bip) ? brl.end : brl.origin
|
||||
|
||||
const left_waypoints = []
|
||||
const right_waypoints = []
|
||||
|
||||
for (const w of glue_def.waypoints) {
|
||||
const percent = w.percent / 100
|
||||
const center_x = tip[0] + percent * (bip[0] - tip[0])
|
||||
const center_y = tip[1] + percent * (bip[1] - tip[1])
|
||||
const left_x = center_x - w.width[0]
|
||||
const right_x = center_x + w.width[1]
|
||||
left_waypoints.push([left_x, center_y])
|
||||
right_waypoints.unshift([right_x, center_y])
|
||||
}
|
||||
|
||||
let waypoints
|
||||
const is_split = a.type(glue_def.top.right)(relative_units) == 'number'
|
||||
if (is_split) {
|
||||
waypoints = [tip, tlp]
|
||||
.concat(left_waypoints)
|
||||
.concat([blp, bip])
|
||||
} else {
|
||||
waypoints = [trp, tip, tlp]
|
||||
.concat(left_waypoints)
|
||||
.concat([blp, bip, brp])
|
||||
.concat(right_waypoints)
|
||||
}
|
||||
|
||||
glue = u.poly(waypoints)
|
||||
}
|
||||
if (side == 'glue') return glue
|
||||
|
||||
if (side == 'middle') {
|
||||
let middle = u.subtract(glue, left)
|
||||
middle = u.subtract(middle, right)
|
||||
return middle
|
||||
}
|
||||
|
||||
let both = u.union(u.deepcopy(left), glue)
|
||||
both = u.union(both, u.deepcopy(right))
|
||||
return both
|
||||
if (a.type(config.joints)(units) == 'string') {
|
||||
if (config.joints == 'round') config.joints = 0
|
||||
if (config.joints == 'pointy') config.joints = 1
|
||||
if (config.joints == 'beveled') config.joints = 2
|
||||
}
|
||||
}
|
||||
|
||||
exports.parse = (config = {}, points = {}, units = {}) => {
|
||||
a.unexpected(config, 'outline', ['glue', 'exports'])
|
||||
const layout_fn = layout(config.glue, points, units)
|
||||
exports.parse = (config, points, units) => {
|
||||
|
||||
// output outlines will be collected here
|
||||
const outlines = {}
|
||||
|
||||
const ex = a.sane(config.exports || {}, 'outlines.exports', 'object')()
|
||||
for (let [key, parts] of Object.entries(ex)) {
|
||||
// the config must be an actual object so that the exports have names
|
||||
config = a.sane(config, 'outlines', 'object')()
|
||||
for (let [outline_name, parts] of Object.entries(config)) {
|
||||
|
||||
// placeholder for the current outline
|
||||
outlines[outline_name] = {models: {}}
|
||||
|
||||
// each export can consist of multiple parts
|
||||
// either sub-objects or arrays are fine...
|
||||
if (a.type(parts)() == 'array') {
|
||||
parts = {...parts}
|
||||
}
|
||||
parts = a.sane(parts, `outlines.exports.${key}`, 'object')()
|
||||
let result = {models: {}}
|
||||
parts = a.sane(parts, `outlines.${outline_name}`, 'object')()
|
||||
|
||||
for (let [part_name, part] of Object.entries(parts)) {
|
||||
const name = `outlines.exports.${key}.${part_name}`
|
||||
|
||||
const name = `outlines.${outline_name}.${part_name}`
|
||||
|
||||
// string part-shortcuts are expanded first
|
||||
if (a.type(part)() == 'string') {
|
||||
part = o.operation(part, {outline: Object.keys(outlines)})
|
||||
}
|
||||
const expected = ['type', 'operation']
|
||||
part.type = a.in(part.type || 'outline', `${name}.type`, ['keys', 'rectangle', 'circle', 'polygon', 'outline'])
|
||||
part.operation = a.in(part.operation || 'add', `${name}.operation`, ['add', 'subtract', 'intersect', 'stack'])
|
||||
|
||||
let op = u.union
|
||||
if (part.operation == 'subtract') op = u.subtract
|
||||
else if (part.operation == 'intersect') op = u.intersect
|
||||
else if (part.operation == 'stack') op = u.stack
|
||||
// process keys that are common to all part declarations
|
||||
const operation = u[a.in(part.operation || 'add', `${name}.operation`, ['add', 'subtract', 'intersect', 'stack'])]
|
||||
const what = a.in(part.what || 'outline', `${name}.what`, ['rectangle', 'circle', 'polygon', 'outline'])
|
||||
const bound = !!part.bound
|
||||
const asym = a.asym(part.asym || 'source', `${name}.asym`)
|
||||
|
||||
let arg
|
||||
let anchor
|
||||
const anchor_def = part.anchor || {}
|
||||
switch (part.type) {
|
||||
case 'keys':
|
||||
arg = layout_fn(part, name, expected)
|
||||
break
|
||||
case 'rectangle':
|
||||
a.unexpected(part, name, expected.concat(['anchor', 'size', 'corner', 'bevel', 'mirror']))
|
||||
const size = a.wh(part.size, `${name}.size`)(units)
|
||||
const rec_units = prep.extend({
|
||||
sx: size[0],
|
||||
sy: size[1]
|
||||
}, units)
|
||||
anchor = anchor_lib.parse(anchor_def, `${name}.anchor`, points)(rec_units)
|
||||
const corner = a.sane(part.corner || 0, `${name}.corner`, 'number')(rec_units)
|
||||
const bevel = a.sane(part.bevel || 0, `${name}.bevel`, 'number')(rec_units)
|
||||
const rect_mirror = a.sane(part.mirror || false, `${name}.mirror`, 'boolean')()
|
||||
const rect = rectangle(size[0], size[1], corner, bevel, name)
|
||||
arg = anchor.position(u.deepcopy(rect))
|
||||
if (rect_mirror) {
|
||||
const mirror_anchor = u.deepcopy(anchor_def)
|
||||
a.assert(mirror_anchor.ref, `Field "${name}.anchor.ref" must be speficied if mirroring is required!`)
|
||||
anchor = anchor_lib.parse(mirror_anchor, `${name}.anchor --> mirror`, points, undefined, undefined, true)(rec_units)
|
||||
const mirror_rect = m.model.moveRelative(u.deepcopy(rect), [-size[0], 0])
|
||||
arg = u.union(arg, anchor.position(mirror_rect))
|
||||
}
|
||||
break
|
||||
case 'circle':
|
||||
a.unexpected(part, name, expected.concat(['anchor', 'radius', 'mirror']))
|
||||
const radius = a.sane(part.radius, `${name}.radius`, 'number')(units)
|
||||
const circle_units = prep.extend({
|
||||
r: radius
|
||||
}, units)
|
||||
anchor = anchor_lib.parse(anchor_def, `${name}.anchor`, points)(circle_units)
|
||||
const circle_mirror = a.sane(part.mirror || false, `${name}.mirror`, 'boolean')()
|
||||
arg = u.circle(anchor.p, radius)
|
||||
if (circle_mirror) {
|
||||
const mirror_anchor = u.deepcopy(anchor_def)
|
||||
a.assert(mirror_anchor.ref, `Field "${name}.anchor.ref" must be speficied if mirroring is required!`)
|
||||
anchor = anchor_lib.parse(mirror_anchor, `${name}.anchor --> mirror`, points, undefined, undefined, true)(circle_units)
|
||||
arg = u.union(arg, u.circle(anchor.p, radius))
|
||||
}
|
||||
break
|
||||
case 'polygon':
|
||||
a.unexpected(part, name, expected.concat(['points', 'mirror']))
|
||||
const poly_points = a.sane(part.points, `${name}.points`, 'array')()
|
||||
const poly_mirror = a.sane(part.mirror || false, `${name.mirror}`, 'boolean')()
|
||||
const parsed_points = []
|
||||
const mirror_points = []
|
||||
let poly_mirror_x = 0
|
||||
let last_anchor = new Point()
|
||||
let poly_index = 0
|
||||
for (const poly_point of poly_points) {
|
||||
const poly_name = `${name}.points[${++poly_index}]`
|
||||
if (poly_index == 1 && poly_mirror) {
|
||||
a.assert(poly_point.ref, `Field "${poly_name}.ref" must be speficied if mirroring is required!`)
|
||||
const mirrored_ref = anchor_lib.mirror(poly_point.ref, poly_mirror)
|
||||
a.assert(points[poly_point.ref], `Field "${poly_name}.ref" does not name an existing point!`)
|
||||
a.assert(points[mirrored_ref], `The mirror of field "${poly_name}.ref" ("${mirrored_ref}") does not name an existing point!`)
|
||||
poly_mirror_x = (points[poly_point.ref].x + points[mirrored_ref].x) / 2
|
||||
}
|
||||
last_anchor = anchor_lib.parse(poly_point, poly_name, points, true, last_anchor)(units)
|
||||
parsed_points.push(last_anchor.p)
|
||||
mirror_points.push(last_anchor.clone().mirror(poly_mirror_x).p)
|
||||
}
|
||||
arg = u.poly(parsed_points)
|
||||
if (poly_mirror) {
|
||||
arg = u.union(arg, u.poly(mirror_points))
|
||||
}
|
||||
break
|
||||
case 'outline':
|
||||
a.unexpected(part, name, expected.concat(['name', 'fillet']))
|
||||
a.assert(outlines[part.name], `Field "${name}.name" does not name an existing outline!`)
|
||||
const fillet = a.sane(part.fillet || 0, `${name}.fillet`, 'number')(units)
|
||||
arg = u.deepcopy(outlines[part.name])
|
||||
if (fillet) {
|
||||
for (const [index, chain] of m.model.findChains(arg).entries()) {
|
||||
arg.models[`fillet_${index}`] = m.chain.fillet(chain, fillet)
|
||||
}
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new Error(`Field "${name}.type" (${part.type}) does not name a valid outline part type!`)
|
||||
// `where` is delayed until we have all, potentially what-dependent units
|
||||
// default where is [0, 0], as per filter parsing
|
||||
const original_where = part.where // need to save, so the delete's don't get rid of it below
|
||||
const where = units => filter(original_where, `${name}.where`, points, units, asym)
|
||||
|
||||
const original_adjust = part.adjust // same as above
|
||||
const fillet = a.sane(part.fillet || 0, `${name}.fillet`, 'number')(units)
|
||||
expand_shorthand(part, `${name}.expand`, units)
|
||||
const expand = a.sane(part.expand || 0, `${name}.expand`, 'number')(units)
|
||||
const joints = a.in(a.sane(part.joints || 0, `${name}.joints`, 'number')(units), `${name}.joints`, [0, 1, 2])
|
||||
const scale = a.sane(part.scale || 1, `${name}.scale`, 'number')(units)
|
||||
|
||||
// these keys are then removed, so ops can check their own unexpected keys without interference
|
||||
delete part.operation
|
||||
delete part.what
|
||||
delete part.bound
|
||||
delete part.asym
|
||||
delete part.where
|
||||
delete part.adjust
|
||||
delete part.fillet
|
||||
delete part.expand
|
||||
delete part.joints
|
||||
delete part.scale
|
||||
|
||||
// a prototype "shape" maker (and its units) are computed
|
||||
const [shape_maker, shape_units] = whats[what](part, name, points, outlines, units)
|
||||
const adjust = start => anchor(original_adjust || {}, `${name}.adjust`, points, start)(shape_units)
|
||||
|
||||
// and then the shape is repeated for all where positions
|
||||
for (const w of where(shape_units)) {
|
||||
const point = adjust(w.clone())
|
||||
let [shape, bbox] = shape_maker(point) // point is passed for mirroring metadata only...
|
||||
if (bound) {
|
||||
shape = binding(shape, bbox, point, shape_units)
|
||||
}
|
||||
shape = point.position(shape) // ...actual positioning happens here
|
||||
outlines[outline_name] = operation(outlines[outline_name], shape)
|
||||
}
|
||||
|
||||
result = op(result, arg)
|
||||
if (scale !== 1) {
|
||||
outlines[outline_name] = m.model.scale(outlines[outline_name], scale)
|
||||
}
|
||||
|
||||
if (expand) {
|
||||
outlines[outline_name] = m.model.outline(
|
||||
outlines[outline_name], Math.abs(expand), joints, (expand < 0), {farPoint: u.farPoint}
|
||||
)
|
||||
}
|
||||
|
||||
if (fillet) {
|
||||
for (const [index, chain] of m.model.findChains(outlines[outline_name]).entries()) {
|
||||
outlines[outline_name].models[`fillet_${part_name}_${index}`] = m.chain.fillet(chain, fillet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m.model.originate(result)
|
||||
m.model.simplify(result)
|
||||
outlines[key] = result
|
||||
// final adjustments
|
||||
m.model.originate(outlines[outline_name])
|
||||
m.model.simplify(outlines[outline_name])
|
||||
|
||||
}
|
||||
|
||||
return outlines
|
||||
}
|
||||
}
|
||||
|
|
206
src/pcbs.js
206
src/pcbs.js
|
@ -1,7 +1,11 @@
|
|||
const m = require('makerjs')
|
||||
const yaml = require('js-yaml')
|
||||
|
||||
const u = require('./utils')
|
||||
const a = require('./assert')
|
||||
const prep = require('./prepare')
|
||||
const anchor_lib = require('./anchor')
|
||||
const anchor = require('./anchor').parse
|
||||
const filter = require('./filter').parse
|
||||
|
||||
const kicad_prefix = `
|
||||
(kicad_pcb (version 20171130) (host pcbnew 5.1.6)
|
||||
|
@ -113,7 +117,7 @@ const kicad_netclass = `
|
|||
)
|
||||
`
|
||||
|
||||
const makerjs2kicad = exports._makerjs2kicad = (model, layer='Edge.Cuts') => {
|
||||
const makerjs2kicad = exports._makerjs2kicad = (model, layer) => {
|
||||
const grs = []
|
||||
const xy = val => `${val[0]} ${-val[1]}`
|
||||
m.model.walk(model, {
|
||||
|
@ -149,84 +153,140 @@ exports.inject_footprint = (name, fp) => {
|
|||
footprint_types[name] = fp
|
||||
}
|
||||
|
||||
const footprint = exports._footprint = (config, name, points, point, net_indexer, component_indexer, units, extra) => {
|
||||
const xy_obj = (x, y) => {
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
str: `${x} ${y}`,
|
||||
toString: function() { return this.str }
|
||||
}
|
||||
}
|
||||
|
||||
if (config === false) return ''
|
||||
const net_obj = (name, index) => {
|
||||
return {
|
||||
name,
|
||||
index,
|
||||
str: `(net ${index} "${name}")`,
|
||||
toString: function() { return this.str }
|
||||
}
|
||||
}
|
||||
|
||||
const footprint = exports._footprint = (points, net_indexer, component_indexer, units, extra) => (config, name, point) => {
|
||||
|
||||
// config sanitization
|
||||
a.unexpected(config, name, ['type', 'anchor', 'nets', 'anchors', 'params'])
|
||||
const type = a.in(config.type, `${name}.type`, Object.keys(footprint_types))
|
||||
let anchor = anchor_lib.parse(config.anchor || {}, `${name}.anchor`, points, true, point)(units)
|
||||
const nets = a.sane(config.nets || {}, `${name}.nets`, 'object')()
|
||||
const anchors = a.sane(config.anchors || {}, `${name}.anchors`, 'object')()
|
||||
const params = a.sane(config.params || {}, `${name}.params`, 'object')()
|
||||
a.unexpected(config, name, ['what', 'params'])
|
||||
const what = a.in(config.what, `${name}.what`, Object.keys(footprint_types))
|
||||
const fp = footprint_types[what]
|
||||
const original_params = config.params || {}
|
||||
|
||||
// basic setup
|
||||
const fp = footprint_types[type]
|
||||
// param sanitization
|
||||
// we unset the mirror config, as it would be an unexpected field
|
||||
let params = u.deepcopy(original_params)
|
||||
delete params.mirror
|
||||
// but still override with it when applicable
|
||||
if (point.meta.mirrored && original_params.mirror !== undefined) {
|
||||
const mirror_overrides = a.sane(original_params.mirror, `${name}.params.mirror`, 'object')()
|
||||
params = prep.extend(params, mirror_overrides)
|
||||
}
|
||||
a.unexpected(params, `${name}.params`, Object.keys(fp.params))
|
||||
|
||||
// parsing parameters
|
||||
const parsed_params = {}
|
||||
for (const [param_name, param_def] of Object.entries(fp.params)) {
|
||||
|
||||
// connecting other, non-net, non-anchor parameters
|
||||
parsed_params.param = {}
|
||||
for (const [param_name, param_value] of Object.entries(prep.extend(fp.params || {}, params))) {
|
||||
let value = param_value
|
||||
if (a.type(value)() == 'string' && value.startsWith('=') && point) {
|
||||
const indirect = value.substring(1)
|
||||
value = point.meta[indirect]
|
||||
if (value === undefined) {
|
||||
throw new Error(`Indirection "${name}.params.${param}" --> "${point.meta.name}.${indirect}" to undefined value!`)
|
||||
// expand param definition shorthand
|
||||
let parsed_def = param_def
|
||||
let def_type = a.type(param_def)(units)
|
||||
if (def_type == 'string') {
|
||||
parsed_def = {type: 'string', value: param_def}
|
||||
} else if (def_type == 'number') {
|
||||
parsed_def = {type: 'number', value: a.mathnum(param_def)(units)}
|
||||
} else if (def_type == 'boolean') {
|
||||
parsed_def = {type: 'boolean', value: param_def}
|
||||
} else if (def_type == 'array') {
|
||||
parsed_def = {type: 'array', value: param_def}
|
||||
} else if (def_type == 'object') {
|
||||
// parsed param definitions also expand to an object
|
||||
// so to detect whether this is an arbitrary object,
|
||||
// we first have to make sure it's not an expanded param def
|
||||
// (this has to be a heuristic, but should be pretty reliable)
|
||||
const defarr = Object.keys(param_def)
|
||||
const already_expanded = defarr.length == 2 && defarr.includes('type') && defarr.includes('value')
|
||||
if (!already_expanded) {
|
||||
parsed_def = {type: 'object', value: param_def}
|
||||
}
|
||||
} else {
|
||||
parsed_def = {type: 'net', value: undefined}
|
||||
}
|
||||
|
||||
// combine default value with potential user override
|
||||
let value = params[param_name] !== undefined ? params[param_name] : parsed_def.value
|
||||
const type = parsed_def.type
|
||||
|
||||
// templating support, with conversion back to raw datatypes
|
||||
const converters = {
|
||||
string: v => v,
|
||||
number: v => a.sane(v, `${name}.params.${param_name}`, 'number')(units),
|
||||
boolean: v => v === 'true',
|
||||
array: v => yaml.load(v),
|
||||
object: v => yaml.load(v),
|
||||
net: v => v,
|
||||
anchor: v => yaml.load(v)
|
||||
}
|
||||
a.in(type, `${name}.params.${param_name}.type`, Object.keys(converters))
|
||||
if (a.type(value)() == 'string') {
|
||||
value = u.template(value, point.meta)
|
||||
value = converters[type](value)
|
||||
}
|
||||
|
||||
// type-specific postprocessing
|
||||
if (['string', 'number', 'boolean', 'array', 'object'].includes(type)) {
|
||||
parsed_params[param_name] = value
|
||||
} else if (type == 'net') {
|
||||
const net = a.sane(value, `${name}.params.${param_name}`, 'string')(units)
|
||||
const index = net_indexer(net)
|
||||
parsed_params[param_name] = net_obj(net, index)
|
||||
} else { // anchor
|
||||
let parsed_anchor = anchor(value, `${name}.params.${param_name}`, points, point)(units)
|
||||
parsed_anchor.y = -parsed_anchor.y // kicad mirror, as per usual
|
||||
parsed_params[param_name] = parsed_anchor
|
||||
}
|
||||
parsed_params.param[param_name] = value
|
||||
}
|
||||
|
||||
// reference
|
||||
const component_ref = parsed_params.ref = component_indexer(parsed_params.param.class || '_')
|
||||
const component_ref = parsed_params.ref = component_indexer(parsed_params.designator || '_')
|
||||
parsed_params.ref_hide = extra.references ? '' : 'hide'
|
||||
|
||||
// footprint positioning
|
||||
parsed_params.at = `(at ${anchor.x} ${-anchor.y} ${anchor.r})`
|
||||
parsed_params.rot = anchor.r
|
||||
parsed_params.xy = (x, y) => {
|
||||
const new_anchor = anchor_lib.parse({
|
||||
shift: [x, -y]
|
||||
}, '_internal_footprint_xy', points, true, anchor)(units)
|
||||
return `${new_anchor.x} ${-new_anchor.y}`
|
||||
}
|
||||
parsed_params.x = point.x
|
||||
parsed_params.y = -point.y
|
||||
parsed_params.r = point.r
|
||||
parsed_params.rot = point.r // to be deprecated
|
||||
parsed_params.xy = `${point.x} ${-point.y}`
|
||||
parsed_params.at = `(at ${point.x} ${-point.y} ${point.r})`
|
||||
|
||||
// connecting nets
|
||||
parsed_params.net = {}
|
||||
for (const [net_name, net_value] of Object.entries(prep.extend(fp.nets || {}, nets))) {
|
||||
let net = a.sane(net_value, `${name}.nets.${net_name}`, 'string')()
|
||||
if (net.startsWith('=') && point) {
|
||||
const indirect = net.substring(1)
|
||||
net = point.meta[indirect]
|
||||
net = a.sane(net, `${name}.nets.${net_name} --> ${point.meta.name}.${indirect}`, 'string')()
|
||||
}
|
||||
const index = net_indexer(net)
|
||||
parsed_params.net[net_name] = {
|
||||
name: net,
|
||||
index: index,
|
||||
str: `(net ${index} "${net}")`
|
||||
}
|
||||
const internal_xyfunc = (x, y, resist) => {
|
||||
const sign = resist ? 1 : (point.meta.mirrored ? -1 : 1)
|
||||
return xy_obj(sign * x, y)
|
||||
}
|
||||
parsed_params.isxy = (x, y) => internal_xyfunc(x, y, false)
|
||||
parsed_params.iaxy = (x, y) => internal_xyfunc(x, y, true)
|
||||
|
||||
const external_xyfunc = (x, y, resist) => {
|
||||
const new_anchor = anchor({
|
||||
shift: [x, -y],
|
||||
resist: resist
|
||||
}, '_internal_footprint_xy', points, point)(units)
|
||||
return xy_obj(new_anchor.x, -new_anchor.y)
|
||||
}
|
||||
parsed_params.esxy = (x, y) => external_xyfunc(x, y, false)
|
||||
parsed_params.eaxy = (x, y) => external_xyfunc(x, y, true)
|
||||
|
||||
// allowing footprints to add dynamic nets
|
||||
parsed_params.local_net = suffix => {
|
||||
const net = `${component_ref}_${suffix}`
|
||||
const index = net_indexer(net)
|
||||
return {
|
||||
name: net,
|
||||
index: index,
|
||||
str: `(net ${index} "${net}")`
|
||||
}
|
||||
}
|
||||
|
||||
// parsing anchor-type parameters
|
||||
parsed_params.anchors = {}
|
||||
for (const [anchor_name, anchor_config] of Object.entries(prep.extend(fp.anchors || {}, anchors))) {
|
||||
let parsed_anchor = anchor_lib.parse(anchor_config || {}, `${name}.anchors.${anchor_name}`, points, true, anchor)(units)
|
||||
parsed_anchor.y = -parsed_anchor.y
|
||||
parsed_params.anchors[anchor_name] = parsed_anchor
|
||||
return net_obj(net, index)
|
||||
}
|
||||
|
||||
return fp.body(parsed_params)
|
||||
|
@ -273,21 +333,27 @@ exports.parse = (config, points, outlines, units) => {
|
|||
}
|
||||
|
||||
const footprints = []
|
||||
const footprint_factory = footprint(points, net_indexer, component_indexer, units, {references})
|
||||
|
||||
// key-level footprints
|
||||
for (const [p_name, point] of Object.entries(points)) {
|
||||
for (const [f_name, f] of Object.entries(point.meta.footprints || {})) {
|
||||
footprints.push(footprint(f, `${p_name}.footprints.${f_name}`, points, point, net_indexer, component_indexer, units, {references}))
|
||||
}
|
||||
}
|
||||
|
||||
// global one-off footprints
|
||||
// generate footprints
|
||||
if (a.type(pcb_config.footprints)() == 'array') {
|
||||
pcb_config.footprints = {...pcb_config.footprints}
|
||||
}
|
||||
const global_footprints = a.sane(pcb_config.footprints || {}, `pcbs.${pcb_name}.footprints`, 'object')()
|
||||
for (const [gf_name, gf] of Object.entries(global_footprints)) {
|
||||
footprints.push(footprint(gf, `pcbs.${pcb_name}.footprints.${gf_name}`, points, undefined, net_indexer, component_indexer, units, {references}))
|
||||
const footprints_config = a.sane(pcb_config.footprints || {}, `pcbs.${pcb_name}.footprints`, 'object')()
|
||||
for (const [f_name, f] of Object.entries(footprints_config)) {
|
||||
const name = `pcbs.${pcb_name}.footprints.${f_name}`
|
||||
a.sane(f, name, 'object')()
|
||||
const asym = a.asym(f.asym || 'source', `${name}.asym`)
|
||||
const where = filter(f.where, `${name}.where`, points, units, asym)
|
||||
const original_adjust = f.adjust // need to save, so the delete's don't get rid of it below
|
||||
const adjust = start => anchor(original_adjust || {}, `${name}.adjust`, points, start)(units)
|
||||
delete f.asym
|
||||
delete f.where
|
||||
delete f.adjust
|
||||
for (const w of where) {
|
||||
const aw = adjust(w.clone())
|
||||
footprints.push(footprint_factory(f, name, aw))
|
||||
}
|
||||
}
|
||||
|
||||
// finalizing nets
|
||||
|
|
29
src/point.js
29
src/point.js
|
@ -24,7 +24,8 @@ module.exports = class Point {
|
|||
[this.x, this.y] = val
|
||||
}
|
||||
|
||||
shift(s, relative=true) {
|
||||
shift(s, relative=true, resist=false) {
|
||||
s[0] *= (!resist && this.meta.mirrored) ? -1 : 1
|
||||
if (relative) {
|
||||
s = m.point.rotate(s, this.r)
|
||||
}
|
||||
|
@ -33,8 +34,11 @@ module.exports = class Point {
|
|||
return this
|
||||
}
|
||||
|
||||
rotate(angle, origin=[0, 0]) {
|
||||
this.p = m.point.rotate(this.p, angle, origin)
|
||||
rotate(angle, origin=[0, 0], resist=false) {
|
||||
angle *= (!resist && this.meta.mirrored) ? -1 : 1
|
||||
if (origin) {
|
||||
this.p = m.point.rotate(this.p, angle, origin)
|
||||
}
|
||||
this.r += angle
|
||||
return this
|
||||
}
|
||||
|
@ -58,8 +62,25 @@ module.exports = class Point {
|
|||
return m.model.moveRelative(m.model.rotate(model, this.r), this.p)
|
||||
}
|
||||
|
||||
unposition(model) {
|
||||
return m.model.rotate(m.model.moveRelative(model, [-this.x, -this.y]), -this.r)
|
||||
}
|
||||
|
||||
rect(size=14) {
|
||||
let rect = u.rect(size, size, [-size/2, -size/2], this.meta.mirrored)
|
||||
let rect = u.rect(size, size, [-size/2, -size/2])
|
||||
return this.position(rect)
|
||||
}
|
||||
|
||||
angle(other) {
|
||||
const dx = other.x - this.x
|
||||
const dy = other.y - this.y
|
||||
return -Math.atan2(dx, dy) * (180 / Math.PI)
|
||||
}
|
||||
|
||||
equals(other) {
|
||||
return this.x === other.x
|
||||
&& this.y === other.y
|
||||
&& this.r === other.r
|
||||
&& JSON.stringify(this.meta) === JSON.stringify(other.meta)
|
||||
}
|
||||
}
|
||||
|
|
303
src/points.js
303
src/points.js
|
@ -21,10 +21,10 @@ const render_zone = exports._render_zone = (zone_name, zone, anchor, global_key,
|
|||
|
||||
a.unexpected(zone, `points.zones.${zone_name}`, ['columns', 'rows', 'key'])
|
||||
// the anchor comes from "above", because it needs other zones too (for references)
|
||||
const cols = a.sane(zone.columns || {}, `points.zones.${zone_name}.columns`, 'object')()
|
||||
const cols = zone.columns = a.sane(zone.columns || {}, `points.zones.${zone_name}.columns`, 'object')()
|
||||
const zone_wide_rows = a.sane(zone.rows || {}, `points.zones.${zone_name}.rows`, 'object')()
|
||||
for (const [key, val] of Object.entries(zone_wide_rows)) {
|
||||
zone_wide_rows[key] = a.sane(val || {}, `points.zones.${zone_name}.rows.${key}`, 'object')()
|
||||
zone_wide_rows[key] = val || {} // no check yet, as it will be extended later
|
||||
}
|
||||
const zone_wide_key = a.sane(zone.key || {}, `points.zones.${zone_name}.key`, 'object')()
|
||||
|
||||
|
@ -32,11 +32,14 @@ const render_zone = exports._render_zone = (zone_name, zone, anchor, global_key,
|
|||
|
||||
const points = {}
|
||||
const rotations = []
|
||||
const zone_anchor = anchor.clone()
|
||||
// transferring the anchor rotation to "real" rotations
|
||||
rotations.push({
|
||||
angle: anchor.r,
|
||||
origin: anchor.p
|
||||
angle: zone_anchor.r,
|
||||
origin: zone_anchor.p
|
||||
})
|
||||
// and now clear it from the anchor so that we don't apply it twice
|
||||
zone_anchor.r = 0
|
||||
|
||||
// column layout
|
||||
|
||||
|
@ -53,47 +56,15 @@ const render_zone = exports._render_zone = (zone_name, zone, anchor, global_key,
|
|||
a.unexpected(
|
||||
col,
|
||||
`points.zones.${zone_name}.columns.${col_name}`,
|
||||
['stagger', 'spread', 'rotate', 'origin', 'rows', 'row_overrides', 'key']
|
||||
['rows', 'key']
|
||||
)
|
||||
col.stagger = a.sane(
|
||||
col.stagger || 0,
|
||||
`points.zones.${zone_name}.columns.${col_name}.stagger`,
|
||||
'number'
|
||||
)(units)
|
||||
col.spread = a.sane(
|
||||
col.spread !== undefined ? col.spread : (first_col ? 0 : 'u'),
|
||||
`points.zones.${zone_name}.columns.${col_name}.spread`,
|
||||
'number'
|
||||
)(units)
|
||||
col.rotate = a.sane(
|
||||
col.rotate || 0,
|
||||
`points.zones.${zone_name}.columns.${col_name}.rotate`,
|
||||
'number'
|
||||
)(units)
|
||||
col.origin = a.xy(
|
||||
col.origin || [0, 0],
|
||||
`points.zones.${zone_name}.columns.${col_name}.origin`
|
||||
)(units)
|
||||
let override = false
|
||||
col.rows = a.sane(
|
||||
col.rows || {},
|
||||
`points.zones.${zone_name}.columns.${col_name}.rows`,
|
||||
'object'
|
||||
)()
|
||||
if (col.row_overrides) {
|
||||
override = true
|
||||
col.rows = a.sane(
|
||||
col.row_overrides,
|
||||
`points.zones.${zone_name}.columns.${col_name}.row_overrides`,
|
||||
'object'
|
||||
)()
|
||||
}
|
||||
for (const [key, val] of Object.entries(col.rows)) {
|
||||
col.rows[key] = a.sane(
|
||||
val || {},
|
||||
`points.zones.${zone_name}.columns.${col_name}.rows.${key}`,
|
||||
'object'
|
||||
)()
|
||||
col.rows[key] = val || {} // again, no check yet, as it will be extended later
|
||||
}
|
||||
col.key = a.sane(
|
||||
col.key || {},
|
||||
|
@ -101,49 +72,33 @@ const render_zone = exports._render_zone = (zone_name, zone, anchor, global_key,
|
|||
'object'
|
||||
)()
|
||||
|
||||
// propagating object key to name field
|
||||
|
||||
col.name = col_name
|
||||
|
||||
// combining row data from zone-wide defs and col-specific defs
|
||||
// (while also handling potential overrides)
|
||||
|
||||
const actual_rows = override ? Object.keys(col.rows)
|
||||
: Object.keys(prep.extend(zone_wide_rows, col.rows))
|
||||
const actual_rows = Object.keys(prep.extend(zone_wide_rows, col.rows))
|
||||
if (!actual_rows.length) {
|
||||
actual_rows.push('default')
|
||||
}
|
||||
|
||||
// setting up column-level anchor
|
||||
|
||||
anchor.x += col.spread
|
||||
anchor.y += col.stagger
|
||||
const col_anchor = anchor.clone()
|
||||
// clear potential rotations, as they will get re-applied anyway
|
||||
// and we don't want to apply them twice...
|
||||
col_anchor.r = 0
|
||||
|
||||
// applying col-level rotation (cumulatively, for the next columns as well)
|
||||
|
||||
if (col.rotate) {
|
||||
push_rotation(
|
||||
rotations,
|
||||
col.rotate,
|
||||
col_anchor.clone().shift(col.origin, false).p
|
||||
)
|
||||
}
|
||||
|
||||
// getting key config through the 5-level extension
|
||||
|
||||
const keys = []
|
||||
const default_key = {
|
||||
stagger: units.$default_stagger,
|
||||
spread: units.$default_spread,
|
||||
splay: units.$default_splay,
|
||||
origin: [0, 0],
|
||||
orient: 0,
|
||||
shift: [0, 0],
|
||||
rotate: 0,
|
||||
padding: 'u',
|
||||
width: 1,
|
||||
height: 1,
|
||||
adjust: {},
|
||||
width: units.$default_width,
|
||||
height: units.$default_height,
|
||||
padding: units.$default_padding,
|
||||
autobind: units.$default_autobind,
|
||||
skip: false,
|
||||
asym: 'both'
|
||||
asym: 'both',
|
||||
colrow: '{{col.name}}_{{row}}',
|
||||
name: '{{zone.name}}_{{colrow}}'
|
||||
}
|
||||
for (const row of actual_rows) {
|
||||
const key = prep.extend(
|
||||
|
@ -155,32 +110,80 @@ const render_zone = exports._render_zone = (zone_name, zone, anchor, global_key,
|
|||
col.rows[row] || {}
|
||||
)
|
||||
|
||||
key.name = key.name || `${zone_name}_${col_name}_${row}`
|
||||
key.colrow = `${col_name}_${row}`
|
||||
key.zone = zone
|
||||
key.zone.name = zone_name
|
||||
key.col = col
|
||||
key.col.name = col_name
|
||||
key.row = row
|
||||
|
||||
key.stagger = a.sane(key.stagger, `${key.name}.stagger`, 'number')(units)
|
||||
key.spread = a.sane(key.spread, `${key.name}.spread`, 'number')(units)
|
||||
key.splay = a.sane(key.splay, `${key.name}.splay`, 'number')(units)
|
||||
key.origin = a.xy(key.origin, `${key.name}.origin`)(units)
|
||||
key.orient = a.sane(key.orient, `${key.name}.orient`, 'number')(units)
|
||||
key.shift = a.xy(key.shift, `${key.name}.shift`)(units)
|
||||
key.rotate = a.sane(key.rotate, `${key.name}.rotate`, 'number')(units)
|
||||
key.width = a.sane(key.width, `${key.name}.width`, 'number')(units)
|
||||
key.height = a.sane(key.height, `${key.name}.height`, 'number')(units)
|
||||
key.padding = a.sane(key.padding, `${key.name}.padding`, 'number')(units)
|
||||
key.skip = a.sane(key.skip, `${key.name}.skip`, 'boolean')()
|
||||
key.asym = a.in(key.asym, `${key.name}.asym`, ['left', 'right', 'both'])
|
||||
key.col = col
|
||||
key.row = row
|
||||
key.asym = a.asym(key.asym, `${key.name}.asym`)
|
||||
|
||||
// templating support
|
||||
for (const [k, v] of Object.entries(key)) {
|
||||
if (a.type(v)(units) == 'string') {
|
||||
key[k] = u.template(v, key)
|
||||
}
|
||||
}
|
||||
|
||||
keys.push(key)
|
||||
}
|
||||
|
||||
// setting up column-level anchor
|
||||
if (!first_col) {
|
||||
zone_anchor.x += keys[0].spread
|
||||
}
|
||||
zone_anchor.y += keys[0].stagger
|
||||
const col_anchor = zone_anchor.clone()
|
||||
|
||||
// applying col-level rotation (cumulatively, for the next columns as well)
|
||||
|
||||
if (keys[0].splay) {
|
||||
push_rotation(
|
||||
rotations,
|
||||
keys[0].splay,
|
||||
col_anchor.clone().shift(keys[0].origin, false).p
|
||||
)
|
||||
}
|
||||
|
||||
// actually laying out keys
|
||||
let running_anchor = col_anchor.clone()
|
||||
for (const r of rotations) {
|
||||
running_anchor.rotate(r.angle, r.origin)
|
||||
}
|
||||
|
||||
for (const key of keys) {
|
||||
let point = col_anchor.clone()
|
||||
for (const r of rotations) {
|
||||
point.rotate(r.angle, r.origin)
|
||||
}
|
||||
|
||||
// copy the current column anchor
|
||||
let point = running_anchor.clone()
|
||||
|
||||
// apply cumulative per-key adjustments
|
||||
point.r += key.orient
|
||||
point.shift(key.shift)
|
||||
point.r += key.rotate
|
||||
|
||||
// commit running anchor
|
||||
running_anchor = point.clone()
|
||||
|
||||
// apply independent adjustments
|
||||
point = anchor_lib.parse(key.adjust, `${key.name}.adjust`, {}, point)(units)
|
||||
|
||||
// save new key
|
||||
point.meta = key
|
||||
points[key.name] = point
|
||||
col_anchor.y += key.padding
|
||||
|
||||
// advance the running anchor to the next position
|
||||
running_anchor.shift([0, key.padding])
|
||||
}
|
||||
|
||||
first_col = false
|
||||
|
@ -191,7 +194,7 @@ const render_zone = exports._render_zone = (zone_name, zone, anchor, global_key,
|
|||
|
||||
const parse_axis = exports._parse_axis = (config, name, points, units) => {
|
||||
if (!['number', 'undefined'].includes(a.type(config)(units))) {
|
||||
const mirror_obj = a.sane(config || {}, name, 'object')()
|
||||
const mirror_obj = a.sane(config, name, 'object')()
|
||||
const distance = a.sane(mirror_obj.distance || 0, `${name}.distance`, 'number')(units)
|
||||
delete mirror_obj.distance
|
||||
let axis = anchor_lib.parse(mirror_obj, name, points)(units).x
|
||||
|
@ -201,21 +204,91 @@ const parse_axis = exports._parse_axis = (config, name, points, units) => {
|
|||
}
|
||||
|
||||
const perform_mirror = exports._perform_mirror = (point, axis) => {
|
||||
if (axis !== undefined) {
|
||||
point.meta.mirrored = false
|
||||
if (point.meta.asym == 'left') return ['', null]
|
||||
const mp = point.clone().mirror(axis)
|
||||
const mirrored_name = `mirror_${point.meta.name}`
|
||||
mp.meta = prep.extend(mp.meta, mp.meta.mirror || {})
|
||||
mp.meta.name = mirrored_name
|
||||
mp.meta.colrow = `mirror_${mp.meta.colrow}`
|
||||
mp.meta.mirrored = true
|
||||
if (point.meta.asym == 'right') {
|
||||
point.meta.skip = true
|
||||
}
|
||||
return [mirrored_name, mp]
|
||||
point.meta.mirrored = false
|
||||
if (point.meta.asym == 'source') return ['', null]
|
||||
const mp = point.clone().mirror(axis)
|
||||
const mirrored_name = `mirror_${point.meta.name}`
|
||||
mp.meta = prep.extend(mp.meta, mp.meta.mirror || {})
|
||||
mp.meta.name = mirrored_name
|
||||
mp.meta.colrow = `mirror_${mp.meta.colrow}`
|
||||
mp.meta.mirrored = true
|
||||
if (point.meta.asym == 'clone') {
|
||||
point.meta.skip = true
|
||||
}
|
||||
return [mirrored_name, mp]
|
||||
}
|
||||
|
||||
const perform_autobind = exports._perform_autobind = (points, units) => {
|
||||
|
||||
const bounds = {}
|
||||
const col_lists = {}
|
||||
const mirrorzone = p => (p.meta.mirrored ? 'mirror_' : '') + p.meta.zone.name
|
||||
|
||||
// round one: get column upper/lower bounds and per-zone column lists
|
||||
for (const p of Object.values(points)) {
|
||||
|
||||
const zone = mirrorzone(p)
|
||||
const col = p.meta.col.name
|
||||
|
||||
if (!bounds[zone]) bounds[zone] = {}
|
||||
if (!bounds[zone][col]) bounds[zone][col] = {min: Infinity, max: -Infinity}
|
||||
if (!col_lists[zone]) col_lists[zone] = Object.keys(p.meta.zone.columns)
|
||||
|
||||
bounds[zone][col].min = Math.min(bounds[zone][col].min, p.y)
|
||||
bounds[zone][col].max = Math.max(bounds[zone][col].max, p.y)
|
||||
}
|
||||
|
||||
// round two: apply autobind as appropriate
|
||||
for (const p of Object.values(points)) {
|
||||
|
||||
const autobind = a.sane(p.meta.autobind, `${p.meta.name}.autobind`, 'number')(units)
|
||||
if (!autobind) continue
|
||||
|
||||
const zone = mirrorzone(p)
|
||||
const col = p.meta.col.name
|
||||
const col_list = col_lists[zone]
|
||||
const col_bounds = bounds[zone][col]
|
||||
|
||||
|
||||
// specify default as -1, so we can recognize where it was left undefined even after number-ification
|
||||
const bind = p.meta.bind = a.trbl(p.meta.bind, `${p.meta.name}.bind`, -1)(units)
|
||||
|
||||
// up
|
||||
if (bind[0] == -1) {
|
||||
if (p.y < col_bounds.max) bind[0] = autobind
|
||||
else bind[0] = 0
|
||||
}
|
||||
|
||||
// down
|
||||
if (bind[2] == -1) {
|
||||
if (p.y > col_bounds.min) bind[2] = autobind
|
||||
else bind[2] = 0
|
||||
}
|
||||
|
||||
// left
|
||||
if (bind[3] == -1) {
|
||||
bind[3] = 0
|
||||
const col_index = col_list.indexOf(col)
|
||||
if (col_index > 0) {
|
||||
const left = bounds[zone][col_list[col_index - 1]]
|
||||
if (left && p.y >= left.min && p.y <= left.max) {
|
||||
bind[3] = autobind
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// right
|
||||
if (bind[1] == -1) {
|
||||
bind[1] = 0
|
||||
const col_index = col_list.indexOf(col)
|
||||
if (col_index < col_list.length - 1) {
|
||||
const right = bounds[zone][col_list[col_index + 1]]
|
||||
if (right && p.y >= right.min && p.y <= right.max) {
|
||||
bind[1] = autobind
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ['', null]
|
||||
}
|
||||
|
||||
exports.parse = (config, units) => {
|
||||
|
@ -227,15 +300,15 @@ exports.parse = (config, units) => {
|
|||
const global_rotate = a.sane(config.rotate || 0, 'points.rotate', 'number')(units)
|
||||
const global_mirror = config.mirror
|
||||
let points = {}
|
||||
let mirrored_points = {}
|
||||
let all_points = {}
|
||||
|
||||
|
||||
// rendering zones
|
||||
for (let [zone_name, zone] of Object.entries(zones)) {
|
||||
|
||||
// zone sanitization
|
||||
zone = a.sane(zone || {}, `points.zones.${zone_name}`, 'object')()
|
||||
|
||||
// extracting keys that are handled here, not at the zone render level
|
||||
const anchor = anchor_lib.parse(zone.anchor || {}, `points.zones.${zone_name}.anchor`, all_points)(units)
|
||||
const anchor = anchor_lib.parse(zone.anchor || {}, `points.zones.${zone_name}.anchor`, points)(units)
|
||||
const rotate = a.sane(zone.rotate || 0, `points.zones.${zone_name}.rotate`, 'number')(units)
|
||||
const mirror = zone.mirror
|
||||
delete zone.anchor
|
||||
|
@ -243,7 +316,17 @@ exports.parse = (config, units) => {
|
|||
delete zone.mirror
|
||||
|
||||
// creating new points
|
||||
const new_points = render_zone(zone_name, zone, anchor, global_key, units)
|
||||
let new_points = render_zone(zone_name, zone, anchor, global_key, units)
|
||||
|
||||
// simplifying the names in individual point "zones" and single-key columns
|
||||
while (Object.keys(new_points).some(k => k.endsWith('_default'))) {
|
||||
for (const key of Object.keys(new_points).filter(k => k.endsWith('_default'))) {
|
||||
const new_key = key.slice(0, -8)
|
||||
new_points[new_key] = new_points[key]
|
||||
new_points[new_key].meta.name = new_key
|
||||
delete new_points[key]
|
||||
}
|
||||
}
|
||||
|
||||
// adjusting new points
|
||||
for (const [new_name, new_point] of Object.entries(new_points)) {
|
||||
|
@ -261,24 +344,21 @@ exports.parse = (config, units) => {
|
|||
|
||||
// adding new points so that they can be referenced from now on
|
||||
points = Object.assign(points, new_points)
|
||||
all_points = Object.assign(all_points, points)
|
||||
|
||||
// per-zone mirroring for the new keys
|
||||
const axis = parse_axis(mirror, `points.zones.${zone_name}.mirror`, all_points, units)
|
||||
if (axis) {
|
||||
const axis = parse_axis(mirror, `points.zones.${zone_name}.mirror`, points, units)
|
||||
if (axis !== undefined) {
|
||||
const mirrored_points = {}
|
||||
for (const new_point of Object.values(new_points)) {
|
||||
const [mname, mp] = perform_mirror(new_point, axis)
|
||||
if (mp) {
|
||||
mirrored_points[mname] = mp
|
||||
all_points[mname] = mp
|
||||
}
|
||||
}
|
||||
points = Object.assign(points, mirrored_points)
|
||||
}
|
||||
}
|
||||
|
||||
// merging regular and early-mirrored points
|
||||
points = Object.assign(points, mirrored_points)
|
||||
|
||||
// applying global rotation
|
||||
for (const point of Object.values(points)) {
|
||||
if (global_rotate) {
|
||||
|
@ -290,15 +370,13 @@ exports.parse = (config, units) => {
|
|||
const global_axis = parse_axis(global_mirror, `points.mirror`, points, units)
|
||||
const global_mirrored_points = {}
|
||||
for (const point of Object.values(points)) {
|
||||
if (global_axis && point.mirrored === undefined) {
|
||||
if (global_axis !== undefined && point.meta.mirrored === undefined) {
|
||||
const [mname, mp] = perform_mirror(point, global_axis)
|
||||
if (mp) {
|
||||
global_mirrored_points[mname] = mp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// merging the global-mirrored points as well
|
||||
points = Object.assign(points, global_mirrored_points)
|
||||
|
||||
// removing temporary points
|
||||
|
@ -308,17 +386,18 @@ exports.parse = (config, units) => {
|
|||
filtered[k] = p
|
||||
}
|
||||
|
||||
// apply autobind
|
||||
perform_autobind(filtered, units)
|
||||
|
||||
// done
|
||||
return filtered
|
||||
}
|
||||
|
||||
exports.visualize = (points, units) => {
|
||||
const models = {}
|
||||
const x_unit = units.visual_x || (units.u - 1)
|
||||
const y_unit = units.visual_y || (units.u - 1)
|
||||
for (const [pname, p] of Object.entries(points)) {
|
||||
const w = p.meta.width * x_unit
|
||||
const h = p.meta.height * y_unit
|
||||
const w = p.meta.width
|
||||
const h = p.meta.height
|
||||
const rect = u.rect(w, h, [-w/2, -h/2])
|
||||
models[pname] = p.position(rect)
|
||||
}
|
||||
|
|
|
@ -42,11 +42,14 @@ const traverse = exports.traverse = (config, root, breadcrumbs, op) => {
|
|||
}
|
||||
return result
|
||||
} else if (a.type(config)() == 'array') {
|
||||
// needed so that arrays can set output the same way as objects within ops
|
||||
const dummy = {}
|
||||
const result = []
|
||||
let index = 0
|
||||
for (const val of config) {
|
||||
breadcrumbs.push(`[${index}]`)
|
||||
result[index] = traverse(val, root, breadcrumbs, op)
|
||||
op(dummy, 'dummykey', traverse(val, root, breadcrumbs, op), root, breadcrumbs)
|
||||
result[index] = dummy.dummykey
|
||||
breadcrumbs.pop()
|
||||
index++
|
||||
}
|
||||
|
@ -73,7 +76,7 @@ exports.inherit = config => traverse(config, config, [], (target, key, val, root
|
|||
candidates = candidates.concat(parents)
|
||||
list.unshift(other)
|
||||
}
|
||||
val = extend.apply(this, list)
|
||||
val = extend.apply(null, list)
|
||||
delete val.$extends
|
||||
}
|
||||
target[key] = val
|
||||
|
|
|
@ -5,7 +5,14 @@ const default_units = {
|
|||
U: 19.05,
|
||||
u: 19,
|
||||
cx: 18,
|
||||
cy: 17
|
||||
cy: 17,
|
||||
$default_stagger: 0,
|
||||
$default_spread: 'u',
|
||||
$default_splay: 0,
|
||||
$default_height: 'u-1',
|
||||
$default_width: 'u-1',
|
||||
$default_padding: 'u',
|
||||
$default_autobind: 10
|
||||
}
|
||||
|
||||
exports.parse = (config = {}) => {
|
||||
|
|
57
src/utils.js
57
src/utils.js
|
@ -18,6 +18,20 @@ const deep = exports.deep = (obj, key, val) => {
|
|||
return obj
|
||||
}
|
||||
|
||||
exports.template = (str, vals={}) => {
|
||||
const regex = /\{\{([^}]*)\}\}/g
|
||||
let res = str
|
||||
let shift = 0
|
||||
for (const match of str.matchAll(regex)) {
|
||||
const replacement = (deep(vals, match[1]) || '') + ''
|
||||
res = res.substring(0, match.index + shift)
|
||||
+ replacement
|
||||
+ res.substring(match.index + shift + match[0].length)
|
||||
shift += replacement.length - match[0].length
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
const eq = exports.eq = (a=[], b=[]) => {
|
||||
return a[0] === b[0] && a[1] === b[1]
|
||||
}
|
||||
|
@ -54,9 +68,23 @@ exports.poly = (arr) => {
|
|||
return res
|
||||
}
|
||||
|
||||
const farPoint = [1234.1234, 2143.56789]
|
||||
exports.bbox = (arr) => {
|
||||
let minx = Infinity
|
||||
let miny = Infinity
|
||||
let maxx = -Infinity
|
||||
let maxy = -Infinity
|
||||
for (const p of arr) {
|
||||
minx = Math.min(minx, p[0])
|
||||
miny = Math.min(miny, p[1])
|
||||
maxx = Math.max(maxx, p[0])
|
||||
maxy = Math.max(maxy, p[1])
|
||||
}
|
||||
return {low: [minx, miny], high: [maxx, maxy]}
|
||||
}
|
||||
|
||||
exports.union = (a, b) => {
|
||||
const farPoint = exports.farPoint = [1234.1234, 2143.56789]
|
||||
|
||||
exports.union = exports.add = (a, b) => {
|
||||
return m.model.combine(a, b, false, true, false, true, {
|
||||
farPoint
|
||||
})
|
||||
|
@ -80,4 +108,29 @@ exports.stack = (a, b) => {
|
|||
a, b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const semver = exports.semver = (str, name='') => {
|
||||
let main = str.split('-')[0]
|
||||
if (main.startsWith('v')) {
|
||||
main = main.substring(1)
|
||||
}
|
||||
while (main.split('.').length < 3) {
|
||||
main += '.0'
|
||||
}
|
||||
if (/^\d+\.\d+\.\d+$/.test(main)) {
|
||||
const parts = main.split('.').map(part => parseInt(part, 10))
|
||||
return {major: parts[0], minor: parts[1], patch: parts[2]}
|
||||
} else throw new Error(`Invalid semver "${str}" at ${name}!`)
|
||||
}
|
||||
|
||||
const satisfies = exports.satisfies = (current, expected) => {
|
||||
if (current.major === undefined) current = semver(current)
|
||||
if (expected.major === undefined) expected = semver(expected)
|
||||
return current.major === expected.major && (
|
||||
current.minor > expected.minor || (
|
||||
current.minor === expected.minor &&
|
||||
current.patch >= expected.patch
|
||||
)
|
||||
)
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
points:
|
||||
zones:
|
||||
arst:
|
||||
columns:
|
||||
c1:
|
||||
rows:
|
||||
r1:
|
||||
outlines:
|
||||
exports:
|
||||
square:
|
||||
- type: rectangle
|
||||
size: [5, 5]
|
||||
cases:
|
||||
cube:
|
||||
- name: square
|
||||
extrude: 5
|
|
@ -1,86 +0,0 @@
|
|||
solid csg.js
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 0 5 0
|
||||
vertex 5 5 0
|
||||
vertex 5 0 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 0 5 0
|
||||
vertex 5 0 0
|
||||
vertex 0 0 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 1
|
||||
outer loop
|
||||
vertex 0 5 5
|
||||
vertex 0 0 5
|
||||
vertex 5 0 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 1
|
||||
outer loop
|
||||
vertex 0 5 5
|
||||
vertex 5 0 5
|
||||
vertex 5 5 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 0
|
||||
outer loop
|
||||
vertex 5 0 0
|
||||
vertex 5 0 5
|
||||
vertex 0 0 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 0
|
||||
outer loop
|
||||
vertex 5 0 0
|
||||
vertex 0 0 5
|
||||
vertex 0 0 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 1 0 0
|
||||
outer loop
|
||||
vertex 5 5 0
|
||||
vertex 5 5 5
|
||||
vertex 5 0 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 1 0 0
|
||||
outer loop
|
||||
vertex 5 5 0
|
||||
vertex 5 0 5
|
||||
vertex 5 0 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 5 5 5
|
||||
vertex 5 5 0
|
||||
vertex 0 5 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 5 5 5
|
||||
vertex 0 5 0
|
||||
vertex 0 5 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -1 0 0
|
||||
outer loop
|
||||
vertex 0 5 5
|
||||
vertex 0 5 0
|
||||
vertex 0 0 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -1 0 0
|
||||
outer loop
|
||||
vertex 0 5 5
|
||||
vertex 0 0 0
|
||||
vertex 0 0 5
|
||||
endloop
|
||||
endfacet
|
||||
endsolid csg.js
|
10
test/cases/cube.yaml
Normal file
10
test/cases/cube.yaml
Normal file
|
@ -0,0 +1,10 @@
|
|||
points.zones.matrix: {}
|
||||
outlines:
|
||||
square:
|
||||
- what: rectangle
|
||||
where: true
|
||||
size: [5, 5]
|
||||
cases:
|
||||
cube:
|
||||
- name: square
|
||||
extrude: 5
|
36
test/cases/cube___cases_cube_jscad.jscad
Normal file
36
test/cases/cube___cases_cube_jscad.jscad
Normal file
|
@ -0,0 +1,36 @@
|
|||
function square_extrude_5_outline_fn(){
|
||||
return new CSG.Path2D([[-2.5,-2.5],[2.5,-2.5]]).appendPoint([2.5,2.5]).appendPoint([-2.5,2.5]).appendPoint([-2.5,-2.5]).close().innerToCAG()
|
||||
.extrude({ offset: [0, 0, 5] });
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function cube_case_fn() {
|
||||
|
||||
|
||||
// creating part 0 of case cube
|
||||
let cube__part_0 = square_extrude_5_outline_fn();
|
||||
|
||||
// make sure that rotations are relative
|
||||
let cube__part_0_bounds = cube__part_0.getBounds();
|
||||
let cube__part_0_x = cube__part_0_bounds[0].x + (cube__part_0_bounds[1].x - cube__part_0_bounds[0].x) / 2
|
||||
let cube__part_0_y = cube__part_0_bounds[0].y + (cube__part_0_bounds[1].y - cube__part_0_bounds[0].y) / 2
|
||||
cube__part_0 = translate([-cube__part_0_x, -cube__part_0_y, 0], cube__part_0);
|
||||
cube__part_0 = rotate([0,0,0], cube__part_0);
|
||||
cube__part_0 = translate([cube__part_0_x, cube__part_0_y, 0], cube__part_0);
|
||||
|
||||
cube__part_0 = translate([0,0,0], cube__part_0);
|
||||
let result = cube__part_0;
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function main() {
|
||||
return cube_case_fn();
|
||||
}
|
||||
|
||||
|
36
test/cases/operations.yaml
Normal file
36
test/cases/operations.yaml
Normal file
|
@ -0,0 +1,36 @@
|
|||
points.zones.matrix: {}
|
||||
outlines:
|
||||
_square:
|
||||
- what: rectangle
|
||||
where: true
|
||||
size: [8, 8]
|
||||
_circle:
|
||||
- what: circle
|
||||
where: true
|
||||
radius: 3
|
||||
cases:
|
||||
_cube:
|
||||
- name: _square
|
||||
extrude: 8
|
||||
_cylinder_one:
|
||||
- name: _circle
|
||||
extrude: 8
|
||||
_subtract:
|
||||
target:
|
||||
name: _cube
|
||||
what: case
|
||||
tool:
|
||||
name: _cylinder_one
|
||||
what: case
|
||||
operation: subtract
|
||||
_cylinder_two:
|
||||
- name: _circle
|
||||
extrude: 8
|
||||
shift: [0,4,4]
|
||||
rotate: [90,0,0]
|
||||
_flat_square:
|
||||
- "_square"
|
||||
combination:
|
||||
- "_subtract"
|
||||
- "~_cylinder_two"
|
||||
- "+_flat_square"
|
211
test/cases/operations___cases_combination_jscad.jscad
Normal file
211
test/cases/operations___cases_combination_jscad.jscad
Normal file
|
@ -0,0 +1,211 @@
|
|||
function _square_extrude_8_outline_fn(){
|
||||
return new CSG.Path2D([[-4,-4],[4,-4]]).appendPoint([4,4]).appendPoint([-4,4]).appendPoint([-4,-4]).close().innerToCAG()
|
||||
.extrude({ offset: [0, 0, 8] });
|
||||
}
|
||||
|
||||
|
||||
function _circle_extrude_8_outline_fn(){
|
||||
return CAG.circle({"center":[0,0],"radius":3})
|
||||
.extrude({ offset: [0, 0, 8] });
|
||||
}
|
||||
|
||||
|
||||
function _square_extrude_1_outline_fn(){
|
||||
return new CSG.Path2D([[-4,-4],[4,-4]]).appendPoint([4,4]).appendPoint([-4,4]).appendPoint([-4,-4]).close().innerToCAG()
|
||||
.extrude({ offset: [0, 0, 1] });
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function _subtract_case_fn() {
|
||||
|
||||
|
||||
// creating part target of case _subtract
|
||||
let _subtract__part_target = _cube_case_fn();
|
||||
|
||||
// make sure that rotations are relative
|
||||
let _subtract__part_target_bounds = _subtract__part_target.getBounds();
|
||||
let _subtract__part_target_x = _subtract__part_target_bounds[0].x + (_subtract__part_target_bounds[1].x - _subtract__part_target_bounds[0].x) / 2
|
||||
let _subtract__part_target_y = _subtract__part_target_bounds[0].y + (_subtract__part_target_bounds[1].y - _subtract__part_target_bounds[0].y) / 2
|
||||
_subtract__part_target = translate([-_subtract__part_target_x, -_subtract__part_target_y, 0], _subtract__part_target);
|
||||
_subtract__part_target = rotate([0,0,0], _subtract__part_target);
|
||||
_subtract__part_target = translate([_subtract__part_target_x, _subtract__part_target_y, 0], _subtract__part_target);
|
||||
|
||||
_subtract__part_target = translate([0,0,0], _subtract__part_target);
|
||||
let result = _subtract__part_target;
|
||||
|
||||
|
||||
|
||||
// creating part tool of case _subtract
|
||||
let _subtract__part_tool = _cylinder_one_case_fn();
|
||||
|
||||
// make sure that rotations are relative
|
||||
let _subtract__part_tool_bounds = _subtract__part_tool.getBounds();
|
||||
let _subtract__part_tool_x = _subtract__part_tool_bounds[0].x + (_subtract__part_tool_bounds[1].x - _subtract__part_tool_bounds[0].x) / 2
|
||||
let _subtract__part_tool_y = _subtract__part_tool_bounds[0].y + (_subtract__part_tool_bounds[1].y - _subtract__part_tool_bounds[0].y) / 2
|
||||
_subtract__part_tool = translate([-_subtract__part_tool_x, -_subtract__part_tool_y, 0], _subtract__part_tool);
|
||||
_subtract__part_tool = rotate([0,0,0], _subtract__part_tool);
|
||||
_subtract__part_tool = translate([_subtract__part_tool_x, _subtract__part_tool_y, 0], _subtract__part_tool);
|
||||
|
||||
_subtract__part_tool = translate([0,0,0], _subtract__part_tool);
|
||||
result = result.subtract(_subtract__part_tool);
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function _cube_case_fn() {
|
||||
|
||||
|
||||
// creating part 0 of case _cube
|
||||
let _cube__part_0 = _square_extrude_8_outline_fn();
|
||||
|
||||
// make sure that rotations are relative
|
||||
let _cube__part_0_bounds = _cube__part_0.getBounds();
|
||||
let _cube__part_0_x = _cube__part_0_bounds[0].x + (_cube__part_0_bounds[1].x - _cube__part_0_bounds[0].x) / 2
|
||||
let _cube__part_0_y = _cube__part_0_bounds[0].y + (_cube__part_0_bounds[1].y - _cube__part_0_bounds[0].y) / 2
|
||||
_cube__part_0 = translate([-_cube__part_0_x, -_cube__part_0_y, 0], _cube__part_0);
|
||||
_cube__part_0 = rotate([0,0,0], _cube__part_0);
|
||||
_cube__part_0 = translate([_cube__part_0_x, _cube__part_0_y, 0], _cube__part_0);
|
||||
|
||||
_cube__part_0 = translate([0,0,0], _cube__part_0);
|
||||
let result = _cube__part_0;
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function _cylinder_one_case_fn() {
|
||||
|
||||
|
||||
// creating part 0 of case _cylinder_one
|
||||
let _cylinder_one__part_0 = _circle_extrude_8_outline_fn();
|
||||
|
||||
// make sure that rotations are relative
|
||||
let _cylinder_one__part_0_bounds = _cylinder_one__part_0.getBounds();
|
||||
let _cylinder_one__part_0_x = _cylinder_one__part_0_bounds[0].x + (_cylinder_one__part_0_bounds[1].x - _cylinder_one__part_0_bounds[0].x) / 2
|
||||
let _cylinder_one__part_0_y = _cylinder_one__part_0_bounds[0].y + (_cylinder_one__part_0_bounds[1].y - _cylinder_one__part_0_bounds[0].y) / 2
|
||||
_cylinder_one__part_0 = translate([-_cylinder_one__part_0_x, -_cylinder_one__part_0_y, 0], _cylinder_one__part_0);
|
||||
_cylinder_one__part_0 = rotate([0,0,0], _cylinder_one__part_0);
|
||||
_cylinder_one__part_0 = translate([_cylinder_one__part_0_x, _cylinder_one__part_0_y, 0], _cylinder_one__part_0);
|
||||
|
||||
_cylinder_one__part_0 = translate([0,0,0], _cylinder_one__part_0);
|
||||
let result = _cylinder_one__part_0;
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function _cylinder_two_case_fn() {
|
||||
|
||||
|
||||
// creating part 0 of case _cylinder_two
|
||||
let _cylinder_two__part_0 = _circle_extrude_8_outline_fn();
|
||||
|
||||
// make sure that rotations are relative
|
||||
let _cylinder_two__part_0_bounds = _cylinder_two__part_0.getBounds();
|
||||
let _cylinder_two__part_0_x = _cylinder_two__part_0_bounds[0].x + (_cylinder_two__part_0_bounds[1].x - _cylinder_two__part_0_bounds[0].x) / 2
|
||||
let _cylinder_two__part_0_y = _cylinder_two__part_0_bounds[0].y + (_cylinder_two__part_0_bounds[1].y - _cylinder_two__part_0_bounds[0].y) / 2
|
||||
_cylinder_two__part_0 = translate([-_cylinder_two__part_0_x, -_cylinder_two__part_0_y, 0], _cylinder_two__part_0);
|
||||
_cylinder_two__part_0 = rotate([90,0,0], _cylinder_two__part_0);
|
||||
_cylinder_two__part_0 = translate([_cylinder_two__part_0_x, _cylinder_two__part_0_y, 0], _cylinder_two__part_0);
|
||||
|
||||
_cylinder_two__part_0 = translate([0,4,4], _cylinder_two__part_0);
|
||||
let result = _cylinder_two__part_0;
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function _flat_square_case_fn() {
|
||||
|
||||
|
||||
// creating part 0 of case _flat_square
|
||||
let _flat_square__part_0 = _square_extrude_1_outline_fn();
|
||||
|
||||
// make sure that rotations are relative
|
||||
let _flat_square__part_0_bounds = _flat_square__part_0.getBounds();
|
||||
let _flat_square__part_0_x = _flat_square__part_0_bounds[0].x + (_flat_square__part_0_bounds[1].x - _flat_square__part_0_bounds[0].x) / 2
|
||||
let _flat_square__part_0_y = _flat_square__part_0_bounds[0].y + (_flat_square__part_0_bounds[1].y - _flat_square__part_0_bounds[0].y) / 2
|
||||
_flat_square__part_0 = translate([-_flat_square__part_0_x, -_flat_square__part_0_y, 0], _flat_square__part_0);
|
||||
_flat_square__part_0 = rotate([0,0,0], _flat_square__part_0);
|
||||
_flat_square__part_0 = translate([_flat_square__part_0_x, _flat_square__part_0_y, 0], _flat_square__part_0);
|
||||
|
||||
_flat_square__part_0 = translate([0,0,0], _flat_square__part_0);
|
||||
let result = _flat_square__part_0;
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function combination_case_fn() {
|
||||
|
||||
|
||||
// creating part 0 of case combination
|
||||
let combination__part_0 = _subtract_case_fn();
|
||||
|
||||
// make sure that rotations are relative
|
||||
let combination__part_0_bounds = combination__part_0.getBounds();
|
||||
let combination__part_0_x = combination__part_0_bounds[0].x + (combination__part_0_bounds[1].x - combination__part_0_bounds[0].x) / 2
|
||||
let combination__part_0_y = combination__part_0_bounds[0].y + (combination__part_0_bounds[1].y - combination__part_0_bounds[0].y) / 2
|
||||
combination__part_0 = translate([-combination__part_0_x, -combination__part_0_y, 0], combination__part_0);
|
||||
combination__part_0 = rotate([0,0,0], combination__part_0);
|
||||
combination__part_0 = translate([combination__part_0_x, combination__part_0_y, 0], combination__part_0);
|
||||
|
||||
combination__part_0 = translate([0,0,0], combination__part_0);
|
||||
let result = combination__part_0;
|
||||
|
||||
|
||||
|
||||
// creating part 1 of case combination
|
||||
let combination__part_1 = _cylinder_two_case_fn();
|
||||
|
||||
// make sure that rotations are relative
|
||||
let combination__part_1_bounds = combination__part_1.getBounds();
|
||||
let combination__part_1_x = combination__part_1_bounds[0].x + (combination__part_1_bounds[1].x - combination__part_1_bounds[0].x) / 2
|
||||
let combination__part_1_y = combination__part_1_bounds[0].y + (combination__part_1_bounds[1].y - combination__part_1_bounds[0].y) / 2
|
||||
combination__part_1 = translate([-combination__part_1_x, -combination__part_1_y, 0], combination__part_1);
|
||||
combination__part_1 = rotate([0,0,0], combination__part_1);
|
||||
combination__part_1 = translate([combination__part_1_x, combination__part_1_y, 0], combination__part_1);
|
||||
|
||||
combination__part_1 = translate([0,0,0], combination__part_1);
|
||||
result = result.intersect(combination__part_1);
|
||||
|
||||
|
||||
|
||||
// creating part 2 of case combination
|
||||
let combination__part_2 = _flat_square_case_fn();
|
||||
|
||||
// make sure that rotations are relative
|
||||
let combination__part_2_bounds = combination__part_2.getBounds();
|
||||
let combination__part_2_x = combination__part_2_bounds[0].x + (combination__part_2_bounds[1].x - combination__part_2_bounds[0].x) / 2
|
||||
let combination__part_2_y = combination__part_2_bounds[0].y + (combination__part_2_bounds[1].y - combination__part_2_bounds[0].y) / 2
|
||||
combination__part_2 = translate([-combination__part_2_x, -combination__part_2_y, 0], combination__part_2);
|
||||
combination__part_2 = rotate([0,0,0], combination__part_2);
|
||||
combination__part_2 = translate([combination__part_2_x, combination__part_2_y, 0], combination__part_2);
|
||||
|
||||
combination__part_2 = translate([0,0,0], combination__part_2);
|
||||
result = result.union(combination__part_2);
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function main() {
|
||||
return combination_case_fn();
|
||||
}
|
||||
|
||||
|
1
test/cli/bad_bundle/command
Normal file
1
test/cli/bad_bundle/command
Normal file
|
@ -0,0 +1 @@
|
|||
node src/cli.js test/
|
1
test/cli/bad_bundle/error
Normal file
1
test/cli/bad_bundle/error
Normal file
|
@ -0,0 +1 @@
|
|||
Could not read config file "test/"!
|
|
@ -1 +1 @@
|
|||
Error: Input doesn't resolve into an object!
|
||||
Error: Input doesn't resolve into an object!
|
|
@ -5,7 +5,7 @@ Preprocessing input...
|
|||
Calculating variables...
|
||||
Parsing points...
|
||||
Generating outlines...
|
||||
Extruding cases...
|
||||
Modeling cases...
|
||||
Scaffolding PCBs...
|
||||
Cleaning output folder...
|
||||
Writing output to disk...
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
function export_outline_fn(){
|
||||
function export_extrude_1_outline_fn(){
|
||||
return new CSG.Path2D([[-9,-9],[9,-9]]).appendPoint([9,9]).appendPoint([-9,9]).appendPoint([-9,-9]).close().innerToCAG()
|
||||
.extrude({ offset: [0, 0, 1] });
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ function export_outline_fn(){
|
|||
|
||||
|
||||
// creating part 0 of case _export
|
||||
let _export__part_0 = export_outline_fn();
|
||||
let _export__part_0 = export_extrude_1_outline_fn();
|
||||
|
||||
// make sure that rotations are relative
|
||||
let _export__part_0_bounds = _export__part_0.getBounds();
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
solid csg.js
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex -9 9 0
|
||||
vertex 9 9 0
|
||||
vertex 9 -9 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex -9 9 0
|
||||
vertex 9 -9 0
|
||||
vertex -9 -9 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 1
|
||||
outer loop
|
||||
vertex -9 9 1
|
||||
vertex -9 -9 1
|
||||
vertex 9 -9 1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 1
|
||||
outer loop
|
||||
vertex -9 9 1
|
||||
vertex 9 -9 1
|
||||
vertex 9 9 1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 0
|
||||
outer loop
|
||||
vertex 9 -9 0
|
||||
vertex 9 -9 1
|
||||
vertex -9 -9 1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 0
|
||||
outer loop
|
||||
vertex 9 -9 0
|
||||
vertex -9 -9 1
|
||||
vertex -9 -9 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 1 0 0
|
||||
outer loop
|
||||
vertex 9 9 0
|
||||
vertex 9 9 1
|
||||
vertex 9 -9 1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 1 0 0
|
||||
outer loop
|
||||
vertex 9 9 0
|
||||
vertex 9 -9 1
|
||||
vertex 9 -9 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 9 9 1
|
||||
vertex 9 9 0
|
||||
vertex -9 9 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 9 9 1
|
||||
vertex -9 9 0
|
||||
vertex -9 9 1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -1 0 0
|
||||
outer loop
|
||||
vertex -9 9 1
|
||||
vertex -9 9 0
|
||||
vertex -9 -9 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -1 0 0
|
||||
outer loop
|
||||
vertex -9 9 1
|
||||
vertex -9 -9 0
|
||||
vertex -9 -9 1
|
||||
endloop
|
||||
endfacet
|
||||
endsolid csg.js
|
|
@ -1,4 +1,4 @@
|
|||
function export_outline_fn(){
|
||||
function export_extrude_1_outline_fn(){
|
||||
return new CSG.Path2D([[-9,-9],[9,-9]]).appendPoint([9,9]).appendPoint([-9,9]).appendPoint([-9,-9]).close().innerToCAG()
|
||||
.extrude({ offset: [0, 0, 1] });
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ function export_outline_fn(){
|
|||
|
||||
|
||||
// creating part 0 of case export
|
||||
let export__part_0 = export_outline_fn();
|
||||
let export__part_0 = export_extrude_1_outline_fn();
|
||||
|
||||
// make sure that rotations are relative
|
||||
let export__part_0_bounds = export__part_0.getBounds();
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
solid csg.js
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex -9 9 0
|
||||
vertex 9 9 0
|
||||
vertex 9 -9 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex -9 9 0
|
||||
vertex 9 -9 0
|
||||
vertex -9 -9 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 1
|
||||
outer loop
|
||||
vertex -9 9 1
|
||||
vertex -9 -9 1
|
||||
vertex 9 -9 1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 1
|
||||
outer loop
|
||||
vertex -9 9 1
|
||||
vertex 9 -9 1
|
||||
vertex 9 9 1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 0
|
||||
outer loop
|
||||
vertex 9 -9 0
|
||||
vertex 9 -9 1
|
||||
vertex -9 -9 1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 0
|
||||
outer loop
|
||||
vertex 9 -9 0
|
||||
vertex -9 -9 1
|
||||
vertex -9 -9 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 1 0 0
|
||||
outer loop
|
||||
vertex 9 9 0
|
||||
vertex 9 9 1
|
||||
vertex 9 -9 1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 1 0 0
|
||||
outer loop
|
||||
vertex 9 9 0
|
||||
vertex 9 -9 1
|
||||
vertex 9 -9 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 9 9 1
|
||||
vertex 9 9 0
|
||||
vertex -9 9 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 9 9 1
|
||||
vertex -9 9 0
|
||||
vertex -9 9 1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -1 0 0
|
||||
outer loop
|
||||
vertex -9 9 1
|
||||
vertex -9 9 0
|
||||
vertex -9 -9 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -1 0 0
|
||||
outer loop
|
||||
vertex -9 9 1
|
||||
vertex -9 -9 0
|
||||
vertex -9 -9 1
|
||||
endloop
|
||||
endfacet
|
||||
endsolid csg.js
|
|
@ -7,49 +7,39 @@ models:
|
|||
- 0
|
||||
- 0
|
||||
b:
|
||||
models:
|
||||
a:
|
||||
models: {}
|
||||
paths:
|
||||
ShapeLine1:
|
||||
type: line
|
||||
origin:
|
||||
- 0
|
||||
- 0
|
||||
b:
|
||||
paths:
|
||||
ShapeLine1:
|
||||
type: line
|
||||
origin:
|
||||
- -9
|
||||
- -9
|
||||
end:
|
||||
- 9
|
||||
- -9
|
||||
ShapeLine2:
|
||||
type: line
|
||||
origin:
|
||||
- 9
|
||||
- -9
|
||||
end:
|
||||
- 9
|
||||
- 9
|
||||
ShapeLine3:
|
||||
type: line
|
||||
origin:
|
||||
- 9
|
||||
- 9
|
||||
end:
|
||||
- -9
|
||||
- 9
|
||||
ShapeLine4:
|
||||
type: line
|
||||
origin:
|
||||
- -9
|
||||
- 9
|
||||
end:
|
||||
- -9
|
||||
- -9
|
||||
- -9
|
||||
- -9
|
||||
end:
|
||||
- 9
|
||||
- -9
|
||||
ShapeLine2:
|
||||
type: line
|
||||
origin:
|
||||
- 0
|
||||
- 0
|
||||
- 9
|
||||
- -9
|
||||
end:
|
||||
- 9
|
||||
- 9
|
||||
ShapeLine3:
|
||||
type: line
|
||||
origin:
|
||||
- 9
|
||||
- 9
|
||||
end:
|
||||
- -9
|
||||
- 9
|
||||
ShapeLine4:
|
||||
type: line
|
||||
origin:
|
||||
- -9
|
||||
- 9
|
||||
end:
|
||||
- -9
|
||||
- -9
|
||||
origin:
|
||||
- 0
|
||||
- 0
|
||||
|
|
|
@ -7,49 +7,39 @@ models:
|
|||
- 0
|
||||
- 0
|
||||
b:
|
||||
models:
|
||||
a:
|
||||
models: {}
|
||||
paths:
|
||||
ShapeLine1:
|
||||
type: line
|
||||
origin:
|
||||
- 0
|
||||
- 0
|
||||
b:
|
||||
paths:
|
||||
ShapeLine1:
|
||||
type: line
|
||||
origin:
|
||||
- -9
|
||||
- -9
|
||||
end:
|
||||
- 9
|
||||
- -9
|
||||
ShapeLine2:
|
||||
type: line
|
||||
origin:
|
||||
- 9
|
||||
- -9
|
||||
end:
|
||||
- 9
|
||||
- 9
|
||||
ShapeLine3:
|
||||
type: line
|
||||
origin:
|
||||
- 9
|
||||
- 9
|
||||
end:
|
||||
- -9
|
||||
- 9
|
||||
ShapeLine4:
|
||||
type: line
|
||||
origin:
|
||||
- -9
|
||||
- 9
|
||||
end:
|
||||
- -9
|
||||
- -9
|
||||
- -9
|
||||
- -9
|
||||
end:
|
||||
- 9
|
||||
- -9
|
||||
ShapeLine2:
|
||||
type: line
|
||||
origin:
|
||||
- 0
|
||||
- 0
|
||||
- 9
|
||||
- -9
|
||||
end:
|
||||
- 9
|
||||
- 9
|
||||
ShapeLine3:
|
||||
type: line
|
||||
origin:
|
||||
- 9
|
||||
- 9
|
||||
end:
|
||||
- -9
|
||||
- 9
|
||||
ShapeLine4:
|
||||
type: line
|
||||
origin:
|
||||
- -9
|
||||
- 9
|
||||
end:
|
||||
- -9
|
||||
- -9
|
||||
origin:
|
||||
- 0
|
||||
- 0
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
(page A3)
|
||||
(title_block
|
||||
(title _export)
|
||||
(rev v1.0.0)
|
||||
(company Unknown)
|
||||
(rev v3.14)
|
||||
(company MrZealot)
|
||||
)
|
||||
|
||||
(general
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
(page A3)
|
||||
(title_block
|
||||
(title export)
|
||||
(rev v1.0.0)
|
||||
(company Unknown)
|
||||
(rev v3.14)
|
||||
(company MrZealot)
|
||||
)
|
||||
|
||||
(general
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
models:
|
||||
export:
|
||||
models:
|
||||
matrix_col_row:
|
||||
matrix:
|
||||
paths:
|
||||
top:
|
||||
type: line
|
||||
|
|
|
@ -1,27 +1,42 @@
|
|||
matrix_col_row:
|
||||
matrix:
|
||||
x: 0
|
||||
'y': 0
|
||||
r: 0
|
||||
meta:
|
||||
stagger: 0
|
||||
spread: 19
|
||||
splay: 0
|
||||
origin:
|
||||
- 0
|
||||
- 0
|
||||
orient: 0
|
||||
shift:
|
||||
- 0
|
||||
- 0
|
||||
rotate: 0
|
||||
adjust: {}
|
||||
width: 18
|
||||
height: 18
|
||||
padding: 19
|
||||
width: 1
|
||||
height: 1
|
||||
autobind: 10
|
||||
skip: false
|
||||
asym: both
|
||||
name: matrix_col_row
|
||||
colrow: col_row
|
||||
colrow: default_default
|
||||
name: matrix
|
||||
zone:
|
||||
columns:
|
||||
default:
|
||||
rows: {}
|
||||
key: {}
|
||||
name: default
|
||||
name: matrix
|
||||
col:
|
||||
stagger: 0
|
||||
spread: 0
|
||||
rotate: 0
|
||||
origin:
|
||||
- 0
|
||||
- 0
|
||||
rows: {}
|
||||
key: {}
|
||||
name: col
|
||||
row: row
|
||||
name: default
|
||||
row: default
|
||||
bind:
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
|
|
|
@ -2,4 +2,11 @@ U: 19.05
|
|||
u: 19
|
||||
cx: 18
|
||||
cy: 17
|
||||
$default_stagger: 0
|
||||
$default_spread: 19
|
||||
$default_splay: 0
|
||||
$default_height: 18
|
||||
$default_width: 18
|
||||
$default_padding: 19
|
||||
$default_autobind: 10
|
||||
a: 47
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
meta:
|
||||
author: MrZealot
|
||||
version: v3.14
|
||||
units:
|
||||
a: 28 + u
|
||||
points:
|
||||
zones:
|
||||
matrix:
|
||||
columns:
|
||||
col: {}
|
||||
rows:
|
||||
row: {}
|
||||
matrix: null
|
||||
outlines:
|
||||
exports:
|
||||
export:
|
||||
-
|
||||
type: keys
|
||||
side: left
|
||||
size: 18
|
||||
_export:
|
||||
-
|
||||
type: keys
|
||||
side: left
|
||||
size: 18
|
||||
export:
|
||||
-
|
||||
what: rectangle
|
||||
where: true
|
||||
size: 18
|
||||
_export:
|
||||
-
|
||||
what: rectangle
|
||||
where: true
|
||||
size: 18
|
||||
cases:
|
||||
export:
|
||||
-
|
||||
|
|
|
@ -1,23 +1,24 @@
|
|||
meta:
|
||||
author: MrZealot
|
||||
version: v3.14
|
||||
units:
|
||||
a: 28 + u
|
||||
points.zones.matrix:
|
||||
columns.col: {}
|
||||
rows.row: {}
|
||||
outlines.exports:
|
||||
outlines:
|
||||
export:
|
||||
- type: 'keys'
|
||||
side: 'left'
|
||||
- what: rectangle
|
||||
where: true
|
||||
size: 18
|
||||
_export:
|
||||
- type: 'keys'
|
||||
side: 'left'
|
||||
- what: rectangle
|
||||
where: true
|
||||
size: 18
|
||||
cases:
|
||||
export:
|
||||
- name: 'export'
|
||||
- name: export
|
||||
extrude: 1
|
||||
_export:
|
||||
- name: 'export'
|
||||
- name: export
|
||||
extrude: 1
|
||||
pcbs:
|
||||
export: {}
|
||||
|
|
1
test/cli/bundle/command
Normal file
1
test/cli/bundle/command
Normal file
|
@ -0,0 +1 @@
|
|||
node src/cli.js test/fixtures/bundle --clean
|
14
test/cli/bundle/log
Normal file
14
test/cli/bundle/log
Normal file
|
@ -0,0 +1,14 @@
|
|||
Ergogen <version> CLI
|
||||
|
||||
Analyzing folder...
|
||||
Interpreting format: YAML
|
||||
Preprocessing input...
|
||||
Calculating variables...
|
||||
Parsing points...
|
||||
Generating outlines...
|
||||
Modeling cases...
|
||||
Scaffolding PCBs...
|
||||
Cleaning output folder...
|
||||
Writing output to disk...
|
||||
Done.
|
||||
|
98
test/cli/bundle/reference/outlines/box.dxf
Normal file
98
test/cli/bundle/reference/outlines/box.dxf
Normal file
|
@ -0,0 +1,98 @@
|
|||
0
|
||||
SECTION
|
||||
2
|
||||
HEADER
|
||||
9
|
||||
$INSUNITS
|
||||
70
|
||||
4
|
||||
0
|
||||
ENDSEC
|
||||
0
|
||||
SECTION
|
||||
2
|
||||
TABLES
|
||||
0
|
||||
TABLE
|
||||
2
|
||||
LTYPE
|
||||
0
|
||||
LTYPE
|
||||
72
|
||||
65
|
||||
70
|
||||
64
|
||||
2
|
||||
CONTINUOUS
|
||||
3
|
||||
______
|
||||
73
|
||||
0
|
||||
40
|
||||
0
|
||||
0
|
||||
ENDTAB
|
||||
0
|
||||
TABLE
|
||||
2
|
||||
LAYER
|
||||
0
|
||||
ENDTAB
|
||||
0
|
||||
ENDSEC
|
||||
0
|
||||
SECTION
|
||||
2
|
||||
ENTITIES
|
||||
0
|
||||
LINE
|
||||
8
|
||||
0
|
||||
10
|
||||
-9
|
||||
20
|
||||
-9
|
||||
11
|
||||
9
|
||||
21
|
||||
-9
|
||||
0
|
||||
LINE
|
||||
8
|
||||
0
|
||||
10
|
||||
9
|
||||
20
|
||||
-9
|
||||
11
|
||||
9
|
||||
21
|
||||
9
|
||||
0
|
||||
LINE
|
||||
8
|
||||
0
|
||||
10
|
||||
9
|
||||
20
|
||||
9
|
||||
11
|
||||
-9
|
||||
21
|
||||
9
|
||||
0
|
||||
LINE
|
||||
8
|
||||
0
|
||||
10
|
||||
-9
|
||||
20
|
||||
9
|
||||
11
|
||||
-9
|
||||
21
|
||||
-9
|
||||
0
|
||||
ENDSEC
|
||||
0
|
||||
EOF
|
122
test/cli/bundle/reference/pcbs/pcb.kicad_pcb
Normal file
122
test/cli/bundle/reference/pcbs/pcb.kicad_pcb
Normal file
|
@ -0,0 +1,122 @@
|
|||
|
||||
|
||||
(kicad_pcb (version 20171130) (host pcbnew 5.1.6)
|
||||
|
||||
(page A3)
|
||||
(title_block
|
||||
(title pcb)
|
||||
(rev v1.0.0)
|
||||
(company Unknown)
|
||||
)
|
||||
|
||||
(general
|
||||
(thickness 1.6)
|
||||
)
|
||||
|
||||
(layers
|
||||
(0 F.Cu signal)
|
||||
(31 B.Cu signal)
|
||||
(32 B.Adhes user)
|
||||
(33 F.Adhes user)
|
||||
(34 B.Paste user)
|
||||
(35 F.Paste user)
|
||||
(36 B.SilkS user)
|
||||
(37 F.SilkS user)
|
||||
(38 B.Mask user)
|
||||
(39 F.Mask user)
|
||||
(40 Dwgs.User user)
|
||||
(41 Cmts.User user)
|
||||
(42 Eco1.User user)
|
||||
(43 Eco2.User user)
|
||||
(44 Edge.Cuts user)
|
||||
(45 Margin user)
|
||||
(46 B.CrtYd user)
|
||||
(47 F.CrtYd user)
|
||||
(48 B.Fab user)
|
||||
(49 F.Fab user)
|
||||
)
|
||||
|
||||
(setup
|
||||
(last_trace_width 0.25)
|
||||
(trace_clearance 0.2)
|
||||
(zone_clearance 0.508)
|
||||
(zone_45_only no)
|
||||
(trace_min 0.2)
|
||||
(via_size 0.8)
|
||||
(via_drill 0.4)
|
||||
(via_min_size 0.4)
|
||||
(via_min_drill 0.3)
|
||||
(uvia_size 0.3)
|
||||
(uvia_drill 0.1)
|
||||
(uvias_allowed no)
|
||||
(uvia_min_size 0.2)
|
||||
(uvia_min_drill 0.1)
|
||||
(edge_width 0.05)
|
||||
(segment_width 0.2)
|
||||
(pcb_text_width 0.3)
|
||||
(pcb_text_size 1.5 1.5)
|
||||
(mod_edge_width 0.12)
|
||||
(mod_text_size 1 1)
|
||||
(mod_text_width 0.15)
|
||||
(pad_size 1.524 1.524)
|
||||
(pad_drill 0.762)
|
||||
(pad_to_mask_clearance 0.05)
|
||||
(aux_axis_origin 0 0)
|
||||
(visible_elements FFFFFF7F)
|
||||
(pcbplotparams
|
||||
(layerselection 0x010fc_ffffffff)
|
||||
(usegerberextensions false)
|
||||
(usegerberattributes true)
|
||||
(usegerberadvancedattributes true)
|
||||
(creategerberjobfile true)
|
||||
(excludeedgelayer true)
|
||||
(linewidth 0.100000)
|
||||
(plotframeref false)
|
||||
(viasonmask false)
|
||||
(mode 1)
|
||||
(useauxorigin false)
|
||||
(hpglpennumber 1)
|
||||
(hpglpenspeed 20)
|
||||
(hpglpendiameter 15.000000)
|
||||
(psnegative false)
|
||||
(psa4output false)
|
||||
(plotreference true)
|
||||
(plotvalue true)
|
||||
(plotinvisibletext false)
|
||||
(padsonsilk false)
|
||||
(subtractmaskfromsilk false)
|
||||
(outputformat 1)
|
||||
(mirror false)
|
||||
(drillshape 1)
|
||||
(scaleselection 1)
|
||||
(outputdirectory ""))
|
||||
)
|
||||
|
||||
(net 0 "")
|
||||
|
||||
(net_class Default "This is the default net class."
|
||||
(clearance 0.2)
|
||||
(trace_width 0.25)
|
||||
(via_dia 0.8)
|
||||
(via_drill 0.4)
|
||||
(uvia_dia 0.3)
|
||||
(uvia_drill 0.1)
|
||||
(add_net "")
|
||||
)
|
||||
|
||||
|
||||
(module injected_test_footprint (layer F.Cu) (tedit 5E1ADAC2)
|
||||
(at 0 0 0)
|
||||
|
||||
|
||||
(fp_text reference "I1" (at 0 0) (layer F.SilkS) hide (effects (font (size 1.27 1.27) (thickness 0.15))))
|
||||
)
|
||||
|
||||
(gr_line (start -9 9) (end 9 9) (angle 90) (layer Edge.Cuts) (width 0.15))
|
||||
(gr_line (start 9 9) (end 9 -9) (angle 90) (layer Edge.Cuts) (width 0.15))
|
||||
(gr_line (start 9 -9) (end -9 -9) (angle 90) (layer Edge.Cuts) (width 0.15))
|
||||
(gr_line (start -9 -9) (end -9 9) (angle 90) (layer Edge.Cuts) (width 0.15))
|
||||
|
||||
)
|
||||
|
||||
|
|
@ -5,7 +5,7 @@ Preprocessing input...
|
|||
Calculating variables...
|
||||
Parsing points...
|
||||
Generating outlines...
|
||||
Extruding cases...
|
||||
Modeling cases...
|
||||
Scaffolding PCBs...
|
||||
Writing output to disk...
|
||||
Done.
|
||||
|
|
|
@ -5,7 +5,7 @@ Preprocessing input...
|
|||
Calculating variables...
|
||||
Parsing points...
|
||||
Generating outlines...
|
||||
Extruding cases...
|
||||
Modeling cases...
|
||||
Scaffolding PCBs...
|
||||
Output would be empty, rerunning in debug mode...
|
||||
Writing output to disk...
|
||||
|
|
|
@ -3,25 +3,42 @@ matrix_col_row:
|
|||
'y': 0
|
||||
r: 0
|
||||
meta:
|
||||
stagger: 0
|
||||
spread: 19
|
||||
splay: 0
|
||||
origin:
|
||||
- 0
|
||||
- 0
|
||||
orient: 0
|
||||
shift:
|
||||
- 0
|
||||
- 0
|
||||
rotate: 0
|
||||
adjust: {}
|
||||
width: 18
|
||||
height: 18
|
||||
padding: 19
|
||||
width: 1
|
||||
height: 1
|
||||
autobind: 10
|
||||
skip: false
|
||||
asym: both
|
||||
name: matrix_col_row
|
||||
colrow: col_row
|
||||
name: matrix_col_row
|
||||
zone:
|
||||
columns:
|
||||
col:
|
||||
rows: {}
|
||||
key: {}
|
||||
name: col
|
||||
rows:
|
||||
row: {}
|
||||
name: matrix
|
||||
col:
|
||||
stagger: 0
|
||||
spread: 0
|
||||
rotate: 0
|
||||
origin:
|
||||
- 0
|
||||
- 0
|
||||
rows: {}
|
||||
key: {}
|
||||
name: col
|
||||
row: row
|
||||
bind:
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
- 0
|
||||
|
|
|
@ -2,3 +2,10 @@ U: 19.05
|
|||
u: 19
|
||||
cx: 18
|
||||
cy: 17
|
||||
$default_stagger: 0
|
||||
$default_spread: 19
|
||||
$default_splay: 0
|
||||
$default_height: 18
|
||||
$default_width: 18
|
||||
$default_padding: 19
|
||||
$default_autobind: 10
|
||||
|
|
|
@ -1 +1 @@
|
|||
Usage: ergogen <config_file> [options]
|
||||
Usage: ergogen <config_file> [options]
|
|
@ -1 +1 @@
|
|||
Could not read config file "nonexistent.yaml": Error: ENOENT: no such file or directory, open 'nonexistent.yaml'
|
||||
Could not read config file "nonexistent.yaml": File does not exist!
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue