Anchor recursivization
This commit is contained in:
parent
b8c71bef0f
commit
6dc6b5d8e9
8 changed files with 119 additions and 45 deletions
|
@ -12,37 +12,85 @@ const mirror_ref = exports.mirror = (ref, mirror=true) => {
|
|||
return ref
|
||||
}
|
||||
|
||||
const anchor = exports.parse = (raw, name, points={}, check_unexpected=true, default_point=new Point(), mirror=false) => units => {
|
||||
if (a.type(raw)() == 'array') {
|
||||
const aggregator_common = ['parts', 'method']
|
||||
|
||||
const aggregators = {
|
||||
average: (config, name, parts) => {
|
||||
a.unexpected(config, name, aggregator_common)
|
||||
let x = 0, y = 0, r = 0
|
||||
const len = parts.length
|
||||
for (const part of parts) {
|
||||
x += part.x
|
||||
y += part.y
|
||||
r += part.r
|
||||
}
|
||||
return new Point(x / len, y / len, r / len)
|
||||
}
|
||||
}
|
||||
|
||||
const anchor = exports.parse = (raw, name, points={}, default_point=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 default_point mods, according to `affect`s
|
||||
let current = default_point.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'])
|
||||
|
||||
a.unexpected(raw, name, ['ref', 'aggregate', 'orient', 'shift', 'rotate', 'affect'])
|
||||
|
||||
//
|
||||
// Reference or aggregate handling
|
||||
//
|
||||
|
||||
let point = default_point.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, default_point, 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, default_point, mirror)(units))
|
||||
}
|
||||
|
||||
point = aggregators[raw.aggregate.method](raw.aggregate, `${name}.aggregate`, parts)
|
||||
}
|
||||
|
||||
//
|
||||
// Actual orient/shift/rotate/affect handling
|
||||
//
|
||||
|
||||
if (raw.orient !== undefined) {
|
||||
let angle = a.sane(raw.orient, `${name}.orient`, 'number')(units)
|
||||
if (point.meta.mirrored) {
|
||||
|
@ -77,5 +125,6 @@ const anchor = exports.parse = (raw, name, points={}, check_unexpected=true, def
|
|||
point[aff] = candidate[aff]
|
||||
}
|
||||
}
|
||||
|
||||
return point
|
||||
}
|
|
@ -121,7 +121,7 @@ exports.parse = (config, name, points={}, units={}, include_mirrors=false) => {
|
|||
result.push(anchor(config, name, points)(units))
|
||||
if (include_mirrors) {
|
||||
// this is strict: if the ref of the anchor doesn't have a mirror pair, it will error out
|
||||
result.push(anchor(config, name, points, true, undefined, true)(units))
|
||||
result.push(anchor(config, name, points, undefined, true)(units))
|
||||
}
|
||||
|
||||
// otherwise, it is treated as a condition to filter all available points
|
||||
|
|
|
@ -112,7 +112,7 @@ const polygon = (config, name, points, outlines, units) => {
|
|||
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, true, last_anchor)(units)
|
||||
last_anchor = anchor(poly_point, poly_name, points, last_anchor)(units)
|
||||
parsed_points.push(last_anchor.p)
|
||||
}
|
||||
let poly = u.poly(parsed_points)
|
||||
|
|
|
@ -156,7 +156,7 @@ const footprint = exports._footprint = (config, name, points, point, net_indexer
|
|||
// 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)
|
||||
let anchor = anchor_lib.parse(config.anchor || {}, `${name}.anchor`, points, 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')()
|
||||
|
@ -189,7 +189,7 @@ const footprint = exports._footprint = (config, name, points, point, net_indexer
|
|||
parsed_params.xy = (x, y) => {
|
||||
const new_anchor = anchor_lib.parse({
|
||||
shift: [x, -y]
|
||||
}, '_internal_footprint_xy', points, true, anchor)(units)
|
||||
}, '_internal_footprint_xy', points, anchor)(units)
|
||||
return `${new_anchor.x} ${-new_anchor.y}`
|
||||
}
|
||||
|
||||
|
@ -224,7 +224,7 @@ const footprint = exports._footprint = (config, name, points, point, net_indexer
|
|||
// 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)
|
||||
let parsed_anchor = anchor_lib.parse(anchor_config || {}, `${name}.anchors.${anchor_name}`, points, anchor)(units)
|
||||
parsed_anchor.y = -parsed_anchor.y
|
||||
parsed_params.anchors[anchor_name] = parsed_anchor
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue