1
0

Getting coverage to 100%

This commit is contained in:
Bán Dénes 2023-01-23 23:34:06 +01:00
parent b27e10374e
commit c45523f38a
25 changed files with 2393 additions and 2282 deletions

2993
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -32,7 +32,8 @@
"glob": "^8.1.0", "glob": "^8.1.0",
"mocha": "^10.2.0", "mocha": "^10.2.0",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"rollup": "^3.10.1" "rollup": "^3.10.1",
"sinon": "^15.0.1"
}, },
"nyc": { "nyc": {
"all": true, "all": true,

View File

@ -15,6 +15,7 @@
### Minor ### Minor
- Add full anchor support to individual points (via `adjust`, probably) - Add full anchor support to individual points (via `adjust`, probably)
- Allow footprints to access raw array/object fields from points with templating
- Include raw kicad footprint integrations - Include raw kicad footprint integrations
- pull torik's script to be able to convert raw kicad footprints into positionable ergogen ones - 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 - have a `dummy` footprint which can just be updated from schematic

View File

@ -136,20 +136,15 @@ const whats = {
outline outline
} }
const expand_shorthand = (config, units) => { const expand_shorthand = (config, name, units) => {
if (a.type(config.expand)(units) == 'string') { if (a.type(config.expand)(units) == 'string') {
const prefix = config.expand.slice(0, -1) const prefix = config.expand.slice(0, -1)
const suffix = config.expand.slice(-1) const suffix = config.expand.slice(-1)
let expand = prefix const valid_suffixes = [')', '>', ']']
let joints = 0 a.assert(valid_suffixes.includes(suffix), `If field "${name}" is a string, ` +
`it should end with one of [${valid_suffixes.map(s => `'${s}'`).join(', ')}]!`)
if (suffix == ')') ; // noop config.expand = prefix
else if (suffix == '>') joints = 1 config.joints = config.joints || valid_suffixes.indexOf(suffix)
else if (suffix == ']') joints = 2
else expand = config.expand
config.expand = parseFloat(expand)
config.joints = config.joints || joints
} }
if (a.type(config.joints)(units) == 'string') { if (a.type(config.joints)(units) == 'string') {
@ -159,7 +154,7 @@ const expand_shorthand = (config, units) => {
} }
} }
exports.parse = (config = {}, points = {}, units = {}) => { exports.parse = (config, points, units) => {
// output outlines will be collected here // output outlines will be collected here
const outlines = {} const outlines = {}
@ -201,7 +196,7 @@ exports.parse = (config = {}, points = {}, units = {}) => {
const original_adjust = part.adjust // same as above const original_adjust = part.adjust // same as above
const adjust = start => anchor(original_adjust || {}, `${name}.adjust`, points, start)(units) const adjust = start => anchor(original_adjust || {}, `${name}.adjust`, points, start)(units)
const fillet = a.sane(part.fillet || 0, `${name}.fillet`, 'number')(units) const fillet = a.sane(part.fillet || 0, `${name}.fillet`, 'number')(units)
expand_shorthand(part, units) expand_shorthand(part, `${name}.expand`, units)
const expand = a.sane(part.expand || 0, `${name}.expand`, 'number')(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 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) const scale = a.sane(part.scale || 1, `${name}.scale`, 'number')(units)

View File

@ -115,7 +115,7 @@ const kicad_netclass = `
) )
` `
const makerjs2kicad = exports._makerjs2kicad = (model, layer='Edge.Cuts') => { const makerjs2kicad = exports._makerjs2kicad = (model, layer) => {
const grs = [] const grs = []
const xy = val => `${val[0]} ${-val[1]}` const xy = val => `${val[0]} ${-val[1]}`
m.model.walk(model, { m.model.walk(model, {
@ -185,30 +185,40 @@ const footprint = exports._footprint = (points, net_indexer, component_indexer,
parsed_def = {type: 'boolean', value: param_def} parsed_def = {type: 'boolean', value: param_def}
} else if (def_type == 'array') { } else if (def_type == 'array') {
parsed_def = {type: 'array', value: param_def} parsed_def = {type: 'array', value: param_def}
} else if (def_type == 'undefined') { } 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} parsed_def = {type: 'net', value: undefined}
} }
// combine default value with potential user override // combine default value with potential user override
let value = prep.extend(parsed_def.value, params[param_name]) let value = params[param_name] !== undefined ? params[param_name] : parsed_def.value
let type = parsed_def.type const type = parsed_def.type
a.in(type, `${name}.params.${param_name}.type`, [
'string', 'number', 'boolean', 'array', 'object', 'net', 'anchor'
])
// templating support, with conversion back to raw datatypes // templating support, with conversion back to raw datatypes
const converters = { const converters = {
string: v => v, string: v => v,
number: v => a.sane(v, `${name}.params.${param_name}`, 'number')(units), number: v => a.sane(v, `${name}.params.${param_name}`, 'number')(units),
boolean: v => v === 'true', boolean: v => v === 'true'
net: v => v,
anchor: v => v,
array: v => v
} }
if (a.type(value)() == 'string') { if (a.type(value)() == 'string' && Object.keys(converters).includes(type)) {
value = u.template(value, point.meta) value = u.template(value, point.meta)
value = converters[type](value) value = converters[type](value)
} }
// type-specific processing // type-specific processing
if (['string', 'number', 'boolean', 'array'].includes(type)) { if (['string', 'number', 'boolean', 'array', 'object'].includes(type)) {
parsed_params[param_name] = value parsed_params[param_name] = value
} else if (type == 'net') { } else if (type == 'net') {
const net = a.sane(value, `${name}.params.${param_name}`, 'string')(units) const net = a.sane(value, `${name}.params.${param_name}`, 'string')(units)
@ -218,8 +228,8 @@ const footprint = exports._footprint = (points, net_indexer, component_indexer,
index: index, index: index,
str: `(net ${index} "${net}")` str: `(net ${index} "${net}")`
} }
} else if (type == 'anchor') { } else { // anchor
let parsed_anchor = anchor(value || {}, `${name}.params.${param_name}`, points, point)(units) let parsed_anchor = anchor(value, `${name}.params.${param_name}`, points, point)(units)
parsed_anchor.y = -parsed_anchor.y // kicad mirror, as per usual parsed_anchor.y = -parsed_anchor.y // kicad mirror, as per usual
parsed_params[param_name] = parsed_anchor parsed_params[param_name] = parsed_anchor
} }
@ -236,7 +246,7 @@ const footprint = exports._footprint = (points, net_indexer, component_indexer,
const sign = point.meta.mirrored ? -1 : 1 const sign = point.meta.mirrored ? -1 : 1
return `${sign * x} ${y}` return `${sign * x} ${y}`
} }
const xyfunc = (x, y, resist=true) => { const xyfunc = (x, y, resist) => {
const new_anchor = anchor({ const new_anchor = anchor({
shift: [x, -y], shift: [x, -y],
resist: resist resist: resist

View File

@ -244,7 +244,7 @@ const render_zone = exports._render_zone = (zone_name, zone, anchor, global_key,
const parse_axis = exports._parse_axis = (config, name, points, units) => { const parse_axis = exports._parse_axis = (config, name, points, units) => {
if (!['number', 'undefined'].includes(a.type(config)(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) const distance = a.sane(mirror_obj.distance || 0, `${name}.distance`, 'number')(units)
delete mirror_obj.distance delete mirror_obj.distance
let axis = anchor_lib.parse(mirror_obj, name, points)(units).x let axis = anchor_lib.parse(mirror_obj, name, points)(units).x
@ -254,21 +254,18 @@ const parse_axis = exports._parse_axis = (config, name, points, units) => {
} }
const perform_mirror = exports._perform_mirror = (point, axis) => { const perform_mirror = exports._perform_mirror = (point, axis) => {
if (axis !== undefined) { point.meta.mirrored = false
point.meta.mirrored = false if (point.meta.asym == 'source') return ['', null]
if (point.meta.asym == 'source') return ['', null] const mp = point.clone().mirror(axis)
const mp = point.clone().mirror(axis) const mirrored_name = `mirror_${point.meta.name}`
const mirrored_name = `mirror_${point.meta.name}` mp.meta = prep.extend(mp.meta, mp.meta.mirror || {})
mp.meta = prep.extend(mp.meta, mp.meta.mirror || {}) mp.meta.name = mirrored_name
mp.meta.name = mirrored_name mp.meta.colrow = `mirror_${mp.meta.colrow}`
mp.meta.colrow = `mirror_${mp.meta.colrow}` mp.meta.mirrored = true
mp.meta.mirrored = true if (point.meta.asym == 'clone') {
if (point.meta.asym == 'clone') { point.meta.skip = true
point.meta.skip = true
}
return [mirrored_name, mp]
} }
return ['', null] return [mirrored_name, mp]
} }
exports.parse = (config, units) => { exports.parse = (config, units) => {
@ -300,13 +297,11 @@ exports.parse = (config, units) => {
// simplifying the names in individual point "zones" and single-key columns // simplifying the names in individual point "zones" and single-key columns
while (Object.keys(new_points).some(k => k.endsWith('_default'))) { while (Object.keys(new_points).some(k => k.endsWith('_default'))) {
for (const key of Object.keys(new_points)) { for (const key of Object.keys(new_points).filter(k => k.endsWith('_default'))) {
if (key.endsWith('_default')) { const new_key = key.slice(0, -8)
const new_key = key.slice(0, -8) new_points[new_key] = new_points[key]
new_points[new_key] = new_points[key] new_points[new_key].meta.name = new_key
new_points[new_key].meta.name = new_key delete new_points[key]
delete new_points[key]
}
} }
} }
@ -352,7 +347,7 @@ exports.parse = (config, units) => {
const global_axis = parse_axis(global_mirror, `points.mirror`, points, units) const global_axis = parse_axis(global_mirror, `points.mirror`, points, units)
const global_mirrored_points = {} const global_mirrored_points = {}
for (const point of Object.values(points)) { for (const point of Object.values(points)) {
if (global_axis !== undefined && point.mirrored === undefined) { if (global_axis !== undefined && point.meta.mirrored === undefined) {
const [mname, mp] = perform_mirror(point, global_axis) const [mname, mp] = perform_mirror(point, global_axis)
if (mp) { if (mp) {
global_mirrored_points[mname] = mp global_mirrored_points[mname] = mp

View File

@ -5,8 +5,8 @@
(page A3) (page A3)
(title_block (title_block
(title _export) (title _export)
(rev v1.0.0) (rev v3.14)
(company Unknown) (company MrZealot)
) )
(general (general

View File

@ -5,8 +5,8 @@
(page A3) (page A3)
(title_block (title_block
(title export) (title export)
(rev v1.0.0) (rev v3.14)
(company Unknown) (company MrZealot)
) )
(general (general

View File

@ -1,3 +1,6 @@
meta:
author: MrZealot
version: v3.14
units: units:
a: 28 + u a: 28 + u
points: points:

View File

@ -1,3 +1,6 @@
meta:
author: MrZealot
version: v3.14
units: units:
a: 28 + u a: 28 + u
points.zones.matrix: points.zones.matrix:

View File

@ -1,3 +1,6 @@
meta:
author: MrZealot
version: v3.14
units: units:
a: 28 + u a: 28 + u
points.zones.matrix: points.zones.matrix:

View File

@ -107,6 +107,32 @@ exports.inject = (ergogen) => {
} }
}) })
ergogen.inject('footprint', 'arrobj_test', {
params: {
designator: 'T',
side: 'F',
start: {x: 0, y: 0},
end: [[1, 0], [0, 1]]
},
body: p => {
lines = ''
for (const item of p.end) {
lines += `(fp_line (start ${p.start.x} ${p.start.y}) (end ${item[0]} ${item[1]}) (layer Dwgs.User) (width 0.05))\n`
}
return `
(module arrobj_test (layer ${p.side}.Cu) (tedit 5CF31DEF)
${p.at /* parametric position */}
${lines}
)
`
}
})
ergogen.inject('references_test', { ergogen.inject('references_test', {
params: {}, params: {},
body: p => { body: p => {

View File

@ -1,4 +1,12 @@
global.chai = require('chai') global.chai = require('chai')
global.chai.use(require('chai-as-promised')) global.chai.use(require('chai-as-promised'))
global.expect = global.chai.expect global.expect = global.chai.expect
global.should = global.chai.should() global.should = global.chai.should()
global.sinon = require('sinon')
// Restore the default sandbox after every test
exports.mochaHooks = {
afterEach() {
sinon.restore()
}
}

View File

@ -9,6 +9,7 @@ require('./helpers/mock_footprints').inject(ergogen)
let what = process.env.npm_config_what let what = process.env.npm_config_what
const dump = process.env.npm_config_dump const dump = process.env.npm_config_dump
const lineends = /(?:\r\n|\r|\n)/g
@ -30,6 +31,19 @@ for (const unit of glob.sync(path.join(__dirname, 'unit', '*.js'))) {
// as well as individual tests using slash-notation (like `points/default`) // as well as individual tests using slash-notation (like `points/default`)
// the --dump switch can output the new results, overriding the old reference // the --dump switch can output the new results, overriding the old reference
const dump_structure = (obj, depth=-1, prefix='', breadcrumbs=[]) => {
if (a.type(obj)() != 'object') {
console.log(prefix + breadcrumbs.join('_'))
return
}
if (depth == 0) return
for (const [key, val] of Object.entries(obj)) {
breadcrumbs.push(key)
dump_structure(val, depth-1, prefix, breadcrumbs)
breadcrumbs.pop()
}
}
const cap = s => s.charAt(0).toUpperCase() + s.slice(1) const cap = s => s.charAt(0).toUpperCase() + s.slice(1)
const test = function(input_path) { const test = function(input_path) {
@ -37,33 +51,52 @@ const test = function(input_path) {
this.slow(120000) this.slow(120000)
title = path.basename(input_path, '.yaml').split('_').join(' ') title = path.basename(input_path, '.yaml').split('_').join(' ')
it(title, async function() { it(title, async function() {
const input = yaml.load(fs.readFileSync(input_path).toString()) const input = yaml.load(fs.readFileSync(input_path).toString())
const base = path.join(path.dirname(input_path), path.basename(input_path, '.yaml'))
const references = glob.sync(base + '___*')
// handle deliberately wrong inputs
const exception = base + '___EXCEPTION.txt'
if (fs.existsSync(exception)) {
const exception_snippet = fs.readFileSync(exception).toString()
return await ergogen.process(input, true).should.be.rejectedWith(exception_snippet)
}
const output = await ergogen.process(input, true) const output = await ergogen.process(input, true)
// compare output vs. reference // compare output vs. reference
const base = path.join(path.dirname(input_path), path.basename(input_path, '.yaml')) if (references.length) {
for (const expected_path of glob.sync(base + '___*')) { for (const expected_path of references) {
let expected = fs.readFileSync(expected_path).toString() let expected = fs.readFileSync(expected_path).toString()
if (expected_path.endsWith('.json')) { if (expected_path.endsWith('.json')) {
expected = JSON.parse(expected) expected = JSON.parse(expected)
}
const comp_path = expected_path.split('___')[1].split('.')[0].split('_').join('.')
const output_part = u.deep(output, comp_path)
if (dump) {
if (a.type(output_part)() == 'string') {
fs.writeFileSync(expected_path, output_part)
} else {
fs.writeJSONSync(expected_path, output_part, {spaces: 4})
} }
} else { const comp_path = expected_path.split('___')[1].split('.')[0].split('_').join('.')
if (a.type(output_part)() == 'string') { const output_part = u.deep(output, comp_path)
const parse_out = output_part.replace(/(?:\r\n|\r|\n)/g,"\n") if (dump) {
const parse_exp = expected.replace(/(?:\r\n|\r|\n)/g,"\n") if (a.type(output_part)() == 'string') {
parse_out.should.deep.equal(parse_exp) fs.writeFileSync(expected_path, output_part)
} else {
fs.writeJSONSync(expected_path, output_part, {spaces: 4})
}
} else { } else {
output_part.should.deep.equal(expected) if (a.type(output_part)() == 'string') {
const parse_out = output_part.replace(lineends, '\n')
const parse_exp = expected.replace(lineends, '\n')
parse_out.should.deep.equal(parse_exp)
} else {
// JSON can hide negative zeroes, for example, so we canonical-ize first
const canonical_part = JSON.parse(JSON.stringify(output_part))
canonical_part.should.deep.equal(expected)
}
} }
} }
// explicit dump-ing above only works, if there are already files with the right name
// if there aren't, dump now outputs a list of candidates that could be referenced
} else if (dump) {
dump_structure(output, 3, base + '___')
} }
}) })
} }
@ -140,17 +173,17 @@ for (let w of cli_what) {
} }
const comp_res = dircompare.compareSync(output_path, ref_path, { const comp_res = dircompare.compareSync(output_path, ref_path, {
compareContent: true, compareContent: true,
ignoreLineEnding: true,
compareFileSync: dircompare.fileCompareHandlers.lineBasedFileCompare.compareSync, compareFileSync: dircompare.fileCompareHandlers.lineBasedFileCompare.compareSync,
compareFileAsync: dircompare.fileCompareHandlers.lineBasedFileCompare.compareAsync, compareFileAsync: dircompare.fileCompareHandlers.lineBasedFileCompare.compareAsync
ignoreLineEnding: true
}) })
if (dump) { if (dump) {
fs.moveSync(output_path, ref_path, {overwrite: true}) fs.moveSync(output_path, ref_path, {overwrite: true})
} else { } else {
fs.removeSync(output_path) fs.removeSync(output_path)
} }
const parse_act_log = actual_log.replace(/(?:\r\n|\r|\n)/g,"\n") const parse_act_log = actual_log.replace(lineends, '\n')
const parse_ref_log = ref_log.replace(/(?:\r\n|\r|\n)/g,"\n") const parse_ref_log = ref_log.replace(lineends, '\n')
parse_act_log.should.equal(parse_ref_log) parse_act_log.should.equal(parse_ref_log)
comp_res.same.should.be.true comp_res.same.should.be.true
// deliberately incorrect execution // deliberately incorrect execution

View File

@ -39,3 +39,8 @@ pcbs:
what: anchor_test what: anchor_test
params: params:
end: matrix end: matrix
arrobj:
what: arrobj_test
params:
start: {x: 5, y: 5}
end: [[6, 6], [7, 7]]

View File

@ -242,6 +242,19 @@
) )
(module arrobj_test (layer F.Cu) (tedit 5CF31DEF)
(at 0 0 0)
(fp_line (start 5 5) (end 6 6) (layer Dwgs.User) (width 0.05))
(fp_line (start 5 5) (end 7 7) (layer Dwgs.User) (width 0.05))
)
(gr_line (start -9.5 9.5) (end 9.5 9.5) (angle 90) (layer Edge.Cuts) (width 0.15)) (gr_line (start -9.5 9.5) (end 9.5 9.5) (angle 90) (layer Edge.Cuts) (width 0.15))
(gr_line (start 9.5 9.5) (end 9.5 -9.5) (angle 90) (layer Edge.Cuts) (width 0.15)) (gr_line (start 9.5 9.5) (end 9.5 -9.5) (angle 90) (layer Edge.Cuts) (width 0.15))
(gr_line (start 9.5 -9.5) (end -9.5 -9.5) (angle 90) (layer Edge.Cuts) (width 0.15)) (gr_line (start 9.5 -9.5) (end -9.5 -9.5) (angle 90) (layer Edge.Cuts) (width 0.15))

24
test/points/autobind.yaml Normal file
View File

@ -0,0 +1,24 @@
points.zones:
none:
key:
autobind: 0
columns:
a:
b:
some:
key:
autobind: 1
columns:
a:
b:
outlines:
none:
- what: rectangle
where: /none_.*/
size: 5
bound: true
some:
- what: rectangle
where: /some_.*/
size: 5
bound: true

View File

@ -0,0 +1,146 @@
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
-2.5
20
-2.5
11
2.5
21
-2.5
0
LINE
8
0
10
2.5
20
-2.5
11
2.5
21
2.5
0
LINE
8
0
10
2.5
20
2.5
11
-2.5
21
2.5
0
LINE
8
0
10
-2.5
20
2.5
11
-2.5
21
-2.5
0
LINE
8
0
10
16.5
20
-2.5
11
21.5
21
-2.5
0
LINE
8
0
10
21.5
20
-2.5
11
21.5
21
2.5
0
LINE
8
0
10
21.5
20
2.5
11
16.5
21
2.5
0
LINE
8
0
10
16.5
20
2.5
11
16.5
21
-2.5
0
ENDSEC
0
EOF

View File

@ -0,0 +1,146 @@
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
-2.5
20
-2.5
11
3.5
21
-2.5
0
LINE
8
0
10
-2.5
20
2.5
11
3.5
21
2.5
0
LINE
8
0
10
-2.5
20
2.5
11
-2.5
21
-2.5
0
LINE
8
0
10
3.5
20
-2.5
11
3.5
21
2.5
0
LINE
8
0
10
15.5
20
-2.5
11
21.5
21
-2.5
0
LINE
8
0
10
21.5
20
-2.5
11
21.5
21
2.5
0
LINE
8
0
10
15.5
20
2.5
11
21.5
21
2.5
0
LINE
8
0
10
15.5
20
-2.5
11
15.5
21
2.5
0
ENDSEC
0
EOF

View File

@ -13,3 +13,18 @@ points:
rows: rows:
bottom: bottom:
top: top:
other:
anchor:
ref: matrix_right_top
shift: [100, 100]
# default mirror object, ref = [0, 0], distance = 0
mirror: {}
columns:
left:
rows:
bottom.asym: source
top.asym: clone
right:
rows:
bottom:
top:

View File

@ -193,6 +193,294 @@ LINE
8 8
0 0
10 10
110
20
128
11
128
21
128
0
LINE
8
0
10
128
20
128
11
128
21
110
0
LINE
8
0
10
128
20
110
11
110
21
110
0
LINE
8
0
10
110
20
110
11
110
21
128
0
LINE
8
0
10
129
20
128
11
147
21
128
0
LINE
8
0
10
147
20
128
11
147
21
110
0
LINE
8
0
10
147
20
110
11
129
21
110
0
LINE
8
0
10
129
20
110
11
129
21
128
0
LINE
8
0
10
129
20
147
11
147
21
147
0
LINE
8
0
10
147
20
147
11
147
21
129
0
LINE
8
0
10
147
20
129
11
129
21
129
0
LINE
8
0
10
129
20
129
11
129
21
147
0
LINE
8
0
10
-128
20
147
11
-110
21
147
0
LINE
8
0
10
-110
20
147
11
-110
21
129
0
LINE
8
0
10
-110
20
129
11
-128
21
129
0
LINE
8
0
10
-128
20
129
11
-128
21
147
0
LINE
8
0
10
-147
20
128
11
-129
21
128
0
LINE
8
0
10
-129
20
128
11
-129
21
110
0
LINE
8
0
10
-129
20
110
11
-147
21
110
0
LINE
8
0
10
-147
20
110
11
-147
21
128
0
LINE
8
0
10
-147
20
147
11
-129
21
147
0
LINE
8
0
10
-129
20
147
11
-129
21
129
0
LINE
8
0
10
-129
20
129
11
-147
21
129
0
LINE
8
0
10
-147
20
129
11
-147
21
147
0
LINE
8
0
10
48.05 48.05
20 20
28 28

View File

@ -0,0 +1,786 @@
{
"matrix_left_bottom": {
"x": 0,
"y": 0,
"r": 0,
"meta": {
"stagger": 0,
"spread": 19,
"splay": 0,
"origin": [
0,
0
],
"orient": 0,
"shift": [
0,
0
],
"rotate": 0,
"width": 18,
"height": 18,
"padding": 19,
"autobind": 10,
"skip": false,
"asym": "source",
"colrow": "left_bottom",
"name": "matrix_left_bottom",
"zone": {
"columns": {
"left": {
"rows": {
"bottom": {
"asym": "source"
},
"top": {
"asym": "clone"
}
},
"key": {},
"name": "left"
},
"right": null
},
"rows": {
"bottom": {},
"top": {}
},
"name": "matrix"
},
"col": {
"rows": {
"bottom": {
"asym": "source"
},
"top": {
"asym": "clone"
}
},
"key": {},
"name": "left"
},
"row": "bottom",
"bind": [
10,
10,
0,
0
],
"mirrored": false
}
},
"matrix_right_bottom": {
"x": 19,
"y": 0,
"r": 0,
"meta": {
"stagger": 0,
"spread": 19,
"splay": 0,
"origin": [
0,
0
],
"orient": 0,
"shift": [
0,
0
],
"rotate": 0,
"width": 18,
"height": 18,
"padding": 19,
"autobind": 10,
"skip": false,
"asym": "both",
"colrow": "right_bottom",
"name": "matrix_right_bottom",
"zone": {
"columns": {
"left": {
"rows": {
"bottom": {
"asym": "source"
},
"top": {
"asym": "clone"
}
},
"key": {},
"name": "left"
},
"right": null
},
"rows": {
"bottom": {},
"top": {}
},
"name": "matrix"
},
"col": {
"rows": {},
"key": {},
"name": "right"
},
"row": "bottom",
"bind": [
10,
0,
0,
10
],
"mirrored": false
}
},
"matrix_right_top": {
"x": 19,
"y": 19,
"r": 0,
"meta": {
"stagger": 0,
"spread": 19,
"splay": 0,
"origin": [
0,
0
],
"orient": 0,
"shift": [
0,
0
],
"rotate": 0,
"width": 18,
"height": 18,
"padding": 19,
"autobind": 10,
"skip": false,
"asym": "both",
"colrow": "right_top",
"name": "matrix_right_top",
"zone": {
"columns": {
"left": {
"rows": {
"bottom": {
"asym": "source"
},
"top": {
"asym": "clone"
}
},
"key": {},
"name": "left"
},
"right": null
},
"rows": {
"bottom": {},
"top": {}
},
"name": "matrix"
},
"col": {
"rows": {},
"key": {},
"name": "right"
},
"row": "top",
"bind": [
0,
0,
10,
10
],
"mirrored": false
}
},
"other_left_bottom": {
"x": 119,
"y": 119,
"r": 0,
"meta": {
"stagger": 0,
"spread": 19,
"splay": 0,
"origin": [
0,
0
],
"orient": 0,
"shift": [
0,
0
],
"rotate": 0,
"width": 18,
"height": 18,
"padding": 19,
"autobind": 10,
"skip": false,
"asym": "source",
"colrow": "left_bottom",
"name": "other_left_bottom",
"zone": {
"columns": {
"left": {
"rows": {
"bottom": {
"asym": "source"
},
"top": {
"asym": "clone"
}
},
"key": {},
"name": "left"
},
"right": null
},
"rows": {
"bottom": {},
"top": {}
},
"name": "other"
},
"col": {
"rows": {
"bottom": {
"asym": "source"
},
"top": {
"asym": "clone"
}
},
"key": {},
"name": "left"
},
"row": "bottom",
"bind": [
10,
10,
0,
0
],
"mirrored": false
}
},
"other_right_bottom": {
"x": 138,
"y": 119,
"r": 0,
"meta": {
"stagger": 0,
"spread": 19,
"splay": 0,
"origin": [
0,
0
],
"orient": 0,
"shift": [
0,
0
],
"rotate": 0,
"width": 18,
"height": 18,
"padding": 19,
"autobind": 10,
"skip": false,
"asym": "both",
"colrow": "right_bottom",
"name": "other_right_bottom",
"zone": {
"columns": {
"left": {
"rows": {
"bottom": {
"asym": "source"
},
"top": {
"asym": "clone"
}
},
"key": {},
"name": "left"
},
"right": null
},
"rows": {
"bottom": {},
"top": {}
},
"name": "other"
},
"col": {
"rows": {},
"key": {},
"name": "right"
},
"row": "bottom",
"bind": [
10,
0,
0,
10
],
"mirrored": false
}
},
"other_right_top": {
"x": 138,
"y": 138,
"r": 0,
"meta": {
"stagger": 0,
"spread": 19,
"splay": 0,
"origin": [
0,
0
],
"orient": 0,
"shift": [
0,
0
],
"rotate": 0,
"width": 18,
"height": 18,
"padding": 19,
"autobind": 10,
"skip": false,
"asym": "both",
"colrow": "right_top",
"name": "other_right_top",
"zone": {
"columns": {
"left": {
"rows": {
"bottom": {
"asym": "source"
},
"top": {
"asym": "clone"
}
},
"key": {},
"name": "left"
},
"right": null
},
"rows": {
"bottom": {},
"top": {}
},
"name": "other"
},
"col": {
"rows": {},
"key": {},
"name": "right"
},
"row": "top",
"bind": [
0,
0,
10,
10
],
"mirrored": false
}
},
"mirror_other_left_top": {
"x": -119,
"y": 138,
"r": 0,
"meta": {
"stagger": 0,
"spread": 19,
"splay": 0,
"origin": [
0,
0
],
"orient": 0,
"shift": [
0,
0
],
"rotate": 0,
"width": 18,
"height": 18,
"padding": 19,
"autobind": 10,
"skip": false,
"asym": "clone",
"colrow": "mirror_left_top",
"name": "mirror_other_left_top",
"zone": {
"columns": {
"left": {
"rows": {
"bottom": {
"asym": "source"
},
"top": {
"asym": "clone"
}
},
"key": {},
"name": "left"
},
"right": null
},
"rows": {
"bottom": {},
"top": {}
},
"name": "other"
},
"col": {
"rows": {
"bottom": {
"asym": "source"
},
"top": {
"asym": "clone"
}
},
"key": {},
"name": "left"
},
"row": "top",
"bind": [
0,
10,
10,
0
],
"mirrored": true
}
},
"mirror_other_right_bottom": {
"x": -138,
"y": 119,
"r": 0,
"meta": {
"stagger": 0,
"spread": 19,
"splay": 0,
"origin": [
0,
0
],
"orient": 0,
"shift": [
0,
0
],
"rotate": 0,
"width": 18,
"height": 18,
"padding": 19,
"autobind": 10,
"skip": false,
"asym": "both",
"colrow": "mirror_right_bottom",
"name": "mirror_other_right_bottom",
"zone": {
"columns": {
"left": {
"rows": {
"bottom": {
"asym": "source"
},
"top": {
"asym": "clone"
}
},
"key": {},
"name": "left"
},
"right": null
},
"rows": {
"bottom": {},
"top": {}
},
"name": "other"
},
"col": {
"rows": {},
"key": {},
"name": "right"
},
"row": "bottom",
"bind": [
10,
0,
0,
10
],
"mirrored": true
}
},
"mirror_other_right_top": {
"x": -138,
"y": 138,
"r": 0,
"meta": {
"stagger": 0,
"spread": 19,
"splay": 0,
"origin": [
0,
0
],
"orient": 0,
"shift": [
0,
0
],
"rotate": 0,
"width": 18,
"height": 18,
"padding": 19,
"autobind": 10,
"skip": false,
"asym": "both",
"colrow": "mirror_right_top",
"name": "mirror_other_right_top",
"zone": {
"columns": {
"left": {
"rows": {
"bottom": {
"asym": "source"
},
"top": {
"asym": "clone"
}
},
"key": {},
"name": "left"
},
"right": null
},
"rows": {
"bottom": {},
"top": {}
},
"name": "other"
},
"col": {
"rows": {},
"key": {},
"name": "right"
},
"row": "top",
"bind": [
0,
0,
10,
10
],
"mirrored": true
}
},
"mirror_matrix_left_top": {
"x": 57.05,
"y": 19,
"r": 0,
"meta": {
"stagger": 0,
"spread": 19,
"splay": 0,
"origin": [
0,
0
],
"orient": 0,
"shift": [
0,
0
],
"rotate": 0,
"width": 18,
"height": 18,
"padding": 19,
"autobind": 10,
"skip": false,
"asym": "clone",
"colrow": "mirror_left_top",
"name": "mirror_matrix_left_top",
"zone": {
"columns": {
"left": {
"rows": {
"bottom": {
"asym": "source"
},
"top": {
"asym": "clone"
}
},
"key": {},
"name": "left"
},
"right": null
},
"rows": {
"bottom": {},
"top": {}
},
"name": "matrix"
},
"col": {
"rows": {
"bottom": {
"asym": "source"
},
"top": {
"asym": "clone"
}
},
"key": {},
"name": "left"
},
"row": "top",
"bind": [
0,
10,
10,
0
],
"mirrored": true
}
},
"mirror_matrix_right_bottom": {
"x": 38.05,
"y": 0,
"r": 0,
"meta": {
"stagger": 0,
"spread": 19,
"splay": 0,
"origin": [
0,
0
],
"orient": 0,
"shift": [
0,
0
],
"rotate": 0,
"width": 18,
"height": 18,
"padding": 19,
"autobind": 10,
"skip": false,
"asym": "both",
"colrow": "mirror_right_bottom",
"name": "mirror_matrix_right_bottom",
"zone": {
"columns": {
"left": {
"rows": {
"bottom": {
"asym": "source"
},
"top": {
"asym": "clone"
}
},
"key": {},
"name": "left"
},
"right": null
},
"rows": {
"bottom": {},
"top": {}
},
"name": "matrix"
},
"col": {
"rows": {},
"key": {},
"name": "right"
},
"row": "bottom",
"bind": [
10,
0,
0,
10
],
"mirrored": true
}
},
"mirror_matrix_right_top": {
"x": 38.05,
"y": 19,
"r": 0,
"meta": {
"stagger": 0,
"spread": 19,
"splay": 0,
"origin": [
0,
0
],
"orient": 0,
"shift": [
0,
0
],
"rotate": 0,
"width": 18,
"height": 18,
"padding": 19,
"autobind": 10,
"skip": false,
"asym": "both",
"colrow": "mirror_right_top",
"name": "mirror_matrix_right_top",
"zone": {
"columns": {
"left": {
"rows": {
"bottom": {
"asym": "source"
},
"top": {
"asym": "clone"
}
},
"key": {},
"name": "left"
},
"right": null
},
"rows": {
"bottom": {},
"top": {}
},
"name": "matrix"
},
"col": {
"rows": {},
"key": {},
"name": "right"
},
"row": "top",
"bind": [
0,
0,
10,
10
],
"mirrored": true
}
}
}

View File

@ -0,0 +1,2 @@
points.zones.matrix.key.name: samename
points.zones.other.key.name: samename

View File

@ -0,0 +1 @@
defined more than once

22
test/unit/internals.js Normal file
View File

@ -0,0 +1,22 @@
const m = require('makerjs')
const pcb_lib = require('../../src/pcbs')
const ergogen = require('../../src/ergogen')
describe('Internals', function() {
it('makerjs2kicad', function() {
// warn on unknown path type
sinon.stub(m.model, 'walk').callsFake(function(model, config) {
config.onPath({pathContext: {type: 'nonexistent'}})
})
pcb_lib._makerjs2kicad.bind(this).should.throw("Can't convert path type")
})
it('injection', function() {
// warn on unknown injection type
ergogen.inject.bind(this, 'nonexistent', 'name', 'value').should.throw('Unknown injection type')
})
})