1
0

PCB net and parameter overhaul

- merge static and parametric nets
- allow dynamic nets on the fly with `p.local_net()`
- support local-to-global position calculations with `p.xy()`
  - this also enables intra-footprint traces and zones
- add anchor type parameters to footprints
This commit is contained in:
Bán Dénes 2021-07-11 20:36:11 +02:00
parent 452d7c155b
commit fe30b91309
7 changed files with 225 additions and 36 deletions

View File

@ -70,8 +70,8 @@ const results = ergogen.process(config, args.debug, s => console.log(s))
console.log('Writing output to disk...')
if (args.debug) {
io.dump_model(results.points.demo, path.join(args.o, 'points/demo'), args.debug)
fs.writeJSONSync(path.join(args.o, 'points/data.json'), results.points.data, {spaces: 4})
io.dump_model(results.demo, path.join(args.o, 'points/demo'), args.debug)
fs.writeJSONSync(path.join(args.o, 'points/data.json'), results.points, {spaces: 4})
}
for (const [name, outline] of Object.entries(results.outlines)) {

View File

@ -27,6 +27,7 @@ module.exports = {
const points = points_lib.parse(config.points, units)
if (debug) {
results.points = points
results.demo = points_lib.visualize(points)
}
logger('Generating outlines...')
@ -55,5 +56,6 @@ module.exports = {
return results
},
visualize: points_lib.visualize
visualize: points_lib.visualize,
inject_footprint: pcbs_lib.inject_footprint
}

View File

@ -1,5 +1,6 @@
const m = require('makerjs')
const a = require('./assert')
const prep = require('./prepare')
const make_anchor = require('./anchor')
const kicad_prefix = `
@ -143,62 +144,91 @@ const makerjs2kicad = exports._makerjs2kicad = (model, layer='Edge.Cuts') => {
}
const footprint_types = require('./footprints')
exports.inject_footprint = (name, fp) => {
footprint_types[name] = fp
}
const footprint = exports._footprint = (config, name, points, point, net_indexer, component_indexer, units) => {
if (config === false) return ''
// config sanitization
a.unexpected(config, name, ['type', 'anchor', 'nets', 'params'])
a.unexpected(config, name, ['type', 'anchor', 'nets', 'anchors', 'params'])
const type = a.in(config.type, `${name}.type`, Object.keys(footprint_types))
let anchor = make_anchor(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')()
// basic setup
const fp = footprint_types[type]
const parsed_params = {}
// footprint positioning
parsed_params.at = `(at ${anchor.x} ${-anchor.y} ${anchor.r})`
parsed_params.rot = anchor.r
// connecting static nets
parsed_params.net = {}
for (const net of (fp.static_nets || [])) {
const index = net_indexer(net)
parsed_params.net[net] = `(net ${index} "${net}")`
}
// connecting parametric nets
for (const net_ref of (fp.nets || [])) {
let net = nets[net_ref]
a.sane(net, `${name}.nets.${net_ref}`, 'string')()
if (net.startsWith('=') && point) {
const indirect = net.substring(1)
net = point.meta[indirect]
a.sane(net, `${name}.nets.${net_ref} --> ${point.meta.name}.${indirect}`, 'string')()
}
const index = net_indexer(net)
parsed_params.net[net_ref] = `(net ${index} "${net}")`
}
// connecting other, non-net parameters
// connecting other, non-net, non-anchor parameters
parsed_params.param = {}
for (const param of (Object.keys(fp.params || {}))) {
let value = params[param] === undefined ? fp.params[param] : params[param]
if (value === undefined) throw new Error(`Field "${name}.params.${param}" is missing!`)
for (const [param_name, param_value] of Object.entries(prep.extend(fp.params || {}, params))) {
let value = a.sane(param_value, `${name}.nets.${param_name}`, 'string')()
if (a.type(value)() == 'string' && value.startsWith('=') && point) {
const indirect = value.substring(1)
value = point.meta[indirect]
if (value === undefined) throw new Error(`Field "${name}.params.${param} --> ${point.meta.name}.${indirect}" is missing!`)
value = a.sane(value, `${name}.params.${param} --> ${point.meta.name}.${indirect}`, 'string')()
}
parsed_params.param[param] = value
parsed_params.param[param_name] = value
}
// reference
parsed_params.ref = component_indexer(parsed_params.param.class || '_')
const component_ref = parsed_params.ref = component_indexer(parsed_params.param.class || '_')
parsed_params.ref_hide = 'hide' // TODO: make this parametric?
// 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 = make_anchor({
shift: [x, -y]
}, '_internal_footprint_xy', points, true, anchor)(units)
return `${new_anchor.x} ${-new_anchor.y}`
}
// 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}")`
}
}
// 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 = {}
console.log(name, config, fp.anchors, anchors)
for (const [anchor_name, anchor_config] of Object.entries(prep.extend(fp.anchors || {}, anchors))) {
let parsed_anchor = make_anchor(anchor_config || {}, `${name}.anchors.${anchor_name}`, points, true, anchor)(units)
console.log(anchor_name, anchor_config, anchor, parsed_anchor)
parsed_anchor.y = -parsed_anchor.y
parsed_params.anchors[anchor_name] = parsed_anchor
}
return fp.body(parsed_params)
}

View File

@ -0,0 +1,116 @@
exports.inject = (ergogen) => {
ergogen.inject_footprint('trace_test', {
nets: {
P1: 'P1'
},
params: {
class: 'T',
side: 'F'
},
body: p => {
return `
(module trace_test (layer F.Cu) (tedit 5CF31DEF)
${p.at /* parametric position */}
(pad 1 smd rect (at 0 0 ${p.rot}) (size 1 1) (layers F.Cu F.Paste F.Mask)
${p.net.P1.str} (solder_mask_margin 0.2))
(pad 2 smd rect (at 5 5 ${p.rot}) (size 1 1) (layers F.Cu F.Paste F.Mask)
${p.net.P1.str} (solder_mask_margin 0.2))
)
(segment (start ${p.xy(0, 0)}) (end ${p.xy(5, 5)}) (width 0.25) (layer F.Cu) (net ${p.net.P1.index}))
`
}
})
ergogen.inject_footprint('zone_test', {
nets: {
P1: 'P1'
},
params: {
class: 'T',
side: 'F'
},
body: p => {
return `
(module zone_test (layer F.Cu) (tedit 5CF31DEF)
${p.at /* parametric position */}
(pad 1 smd rect (at 0 0 ${p.rot}) (size 1 1) (layers F.Cu F.Paste F.Mask)
${p.net.P1.str} (solder_mask_margin 0.2))
(pad 2 smd rect (at 5 5 ${p.rot}) (size 1 1) (layers F.Cu F.Paste F.Mask)
${p.net.P1.str} (solder_mask_margin 0.2))
)
(zone (net ${p.net.P1.index}) (net_name ${p.net.P1.name}) (layer ${p.param.side}.Cu) (tstamp 0) (hatch full 0.508)
(connect_pads (clearance 0.508))
(min_thickness 0.254)
(fill yes (arc_segments 32) (thermal_gap 0.508) (thermal_bridge_width 0.508))
(polygon (pts (xy ${p.xy(5, 5)}) (xy ${p.xy(5, -5)}) (xy ${p.xy(-5, -5)}) (xy ${p.xy(-5, 5)})))
)
`
}
})
ergogen.inject_footprint('dynamic_net_test', {
nets: {},
params: {
class: 'T',
side: 'F'
},
body: p => {
return `
(module dynamic_net_test (layer F.Cu) (tedit 5CF31DEF)
${p.at /* parametric position */}
(pad 1 smd rect (at 0 0 ${p.rot}) (size 1 1) (layers F.Cu F.Paste F.Mask)
${p.local_net('1').str} (solder_mask_margin 0.2))
(pad 1 smd rect (at 0 0 ${p.rot}) (size 1 1) (layers F.Cu F.Paste F.Mask)
${p.local_net('2').str} (solder_mask_margin 0.2))
(pad 1 smd rect (at 0 0 ${p.rot}) (size 1 1) (layers F.Cu F.Paste F.Mask)
${p.local_net('3').str} (solder_mask_margin 0.2))
)
`
}
})
ergogen.inject_footprint('anchor_test', {
nets: {},
params: {
class: 'T',
side: 'F'
},
anchors: {
end: undefined
},
body: p => {
return `
(module anchor_test (layer F.Cu) (tedit 5CF31DEF)
${p.at /* parametric position */}
(fp_line (start 0 0) (end ${p.anchors.end.x} ${p.anchors.end.y}) (layer Dwgs.User) (width 0.05))
)
`
}
})
}

View File

@ -4,6 +4,7 @@ const yaml = require('js-yaml')
const glob = require('glob')
const u = require('../src/utils')
const ergogen = require('../src/ergogen')
require('./helpers/mock_footprints').inject(ergogen)
let what = process.env.npm_config_what
let first = process.env.npm_config_first

View File

@ -0,0 +1,37 @@
points:
zones:
matrix:
columns:
one:
rows:
only:
outlines:
exports:
edge:
- type: keys
side: left
size: [u, u]
pcbs:
main:
outlines:
edge:
outline: edge
footprints:
trace:
type: trace_test
anchor:
shift: [1, 1]
rotate: 30
zone:
type: zone_test
anchor:
shift: [1, 1]
rotate: 30
dyn:
type: dynamic_net_test
anc:
type: anchor_test
anchors:
end:
ref: matrix_one_only
shift: [10, 10]

File diff suppressed because one or more lines are too long