diff --git a/src/cli.js b/src/cli.js index bdaeac0..fe0615b 100755 --- a/src/cli.js +++ b/src/cli.js @@ -132,12 +132,14 @@ try { // 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) } @@ -148,7 +150,7 @@ 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']) { if (data[format]) { diff --git a/src/points.js b/src/points.js index 72eecc8..8f50904 100644 --- a/src/points.js +++ b/src/points.js @@ -21,7 +21,7 @@ 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] = val || {} // no check yet, as it will be extended later @@ -43,7 +43,6 @@ const render_zone = exports._render_zone = (zone_name, zone, anchor, global_key, // column layout - const col_minmax = {} if (!Object.keys(cols).length) { cols.default = {} } @@ -53,7 +52,6 @@ const render_zone = exports._render_zone = (zone_name, zone, anchor, global_key, // column-level sanitization col = col || {} - col_minmax[col_name] = {min: Infinity, max: -Infinity} a.unexpected( col, @@ -184,10 +182,6 @@ const render_zone = exports._render_zone = (zone_name, zone, anchor, global_key, point.meta = key points[key.name] = point - // collect minmax stats for autobind - col_minmax[col_name].min = Math.min(col_minmax[col_name].min, point.y) - col_minmax[col_name].max = Math.max(col_minmax[col_name].max, point.y) - // advance the running anchor to the next position running_anchor.shift([0, key.padding]) } @@ -195,56 +189,6 @@ const render_zone = exports._render_zone = (zone_name, zone, anchor, global_key, first_col = false } - // autobind - - let col_names = Object.keys(col_minmax) - let col_index = 0 - for (const [col_name, bounds] of Object.entries(col_minmax)) { - for (const point of Object.values(points)) { - if (point.meta.col.name != col_name) continue - if (!point.meta.autobind) continue - const autobind = a.sane(point.meta.autobind, `${point.meta.name}.autobind`, 'number')(units) - // specify default as -1, so we can recognize where it was left undefined even after number-ification - const bind = point.meta.bind = a.trbl(point.meta.bind, `${point.meta.name}.bind`, -1)(units) - - // up - if (bind[0] == -1) { - if (point.y < bounds.max) bind[0] = autobind - else bind[0] = 0 - - } - - // right - if (bind[1] == -1) { - bind[1] = 0 - if (col_index < col_names.length - 1) { - const right = col_minmax[col_names[col_index + 1]] - if (point.y >= right.min && point.y <= right.max) { - bind[1] = autobind - } - } - } - - // down - if (bind[2] == -1) { - if (point.y > bounds.min) bind[2] = autobind - else bind[2] = 0 - } - - // left - if (bind[3] == -1) { - bind[3] = 0 - if (col_index > 0) { - const left = col_minmax[col_names[col_index - 1]] - if (point.y >= left.min && point.y <= left.max) { - bind[3] = autobind - } - } - } - } - col_index++ - } - return points } @@ -274,6 +218,79 @@ const perform_mirror = exports._perform_mirror = (point, axis) => { 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 + } + } + } + } +} + exports.parse = (config, units) => { // config sanitization @@ -369,6 +386,9 @@ exports.parse = (config, units) => { filtered[k] = p } + // apply autobind + perform_autobind(filtered, units) + // done return filtered } diff --git a/test/cli/big/reference/points/points.yaml b/test/cli/big/reference/points/points.yaml index 79b590d..fc6b036 100644 --- a/test/cli/big/reference/points/points.yaml +++ b/test/cli/big/reference/points/points.yaml @@ -24,6 +24,11 @@ matrix: colrow: default_default name: matrix zone: + columns: + default: + rows: {} + key: {} + name: default name: matrix col: rows: {} diff --git a/test/cli/minimal/reference/points/points.yaml b/test/cli/minimal/reference/points/points.yaml index 9728c56..a2c7bb5 100644 --- a/test/cli/minimal/reference/points/points.yaml +++ b/test/cli/minimal/reference/points/points.yaml @@ -25,14 +25,17 @@ matrix_col_row: name: matrix_col_row zone: columns: - col: &ref_0 + col: rows: {} key: {} name: col rows: row: {} name: matrix - col: *ref_0 + col: + rows: {} + key: {} + name: col row: row bind: - 0 diff --git a/test/points/default___points.json b/test/points/default___points.json index cc98b68..4536086 100644 --- a/test/points/default___points.json +++ b/test/points/default___points.json @@ -27,6 +27,13 @@ "colrow": "default_default", "name": "matrix", "zone": { + "columns": { + "default": { + "rows": {}, + "key": {}, + "name": "default" + } + }, "name": "matrix" }, "col": { diff --git a/test/points/mirrors___points.json b/test/points/mirrors___points.json index 51aafec..96b7841 100644 --- a/test/points/mirrors___points.json +++ b/test/points/mirrors___points.json @@ -61,13 +61,13 @@ "name": "left" }, "row": "bottom", + "mirrored": false, "bind": [ - 10, + 0, 10, 0, 0 - ], - "mirrored": false + ] } }, "matrix_right_bottom": { @@ -125,13 +125,13 @@ "name": "right" }, "row": "bottom", + "mirrored": false, "bind": [ 10, 0, 0, 10 - ], - "mirrored": false + ] } }, "matrix_right_top": { @@ -189,13 +189,13 @@ "name": "right" }, "row": "top", + "mirrored": false, "bind": [ 0, 0, 10, - 10 - ], - "mirrored": false + 0 + ] } }, "other_left_bottom": { @@ -260,13 +260,13 @@ "name": "left" }, "row": "bottom", + "mirrored": false, "bind": [ - 10, + 0, 10, 0, 0 - ], - "mirrored": false + ] } }, "other_right_bottom": { @@ -324,13 +324,13 @@ "name": "right" }, "row": "bottom", + "mirrored": false, "bind": [ 10, 0, 0, 10 - ], - "mirrored": false + ] } }, "other_right_top": { @@ -388,13 +388,13 @@ "name": "right" }, "row": "top", + "mirrored": false, "bind": [ 0, 0, 10, - 10 - ], - "mirrored": false + 0 + ] } }, "mirror_other_left_top": { @@ -459,13 +459,13 @@ "name": "left" }, "row": "top", + "mirrored": true, "bind": [ 0, 10, - 10, + 0, 0 - ], - "mirrored": true + ] } }, "mirror_other_right_bottom": { @@ -523,13 +523,13 @@ "name": "right" }, "row": "bottom", + "mirrored": true, "bind": [ 10, 0, 0, - 10 - ], - "mirrored": true + 0 + ] } }, "mirror_other_right_top": { @@ -587,13 +587,13 @@ "name": "right" }, "row": "top", + "mirrored": true, "bind": [ 0, 0, 10, 10 - ], - "mirrored": true + ] } }, "mirror_matrix_left_top": { @@ -658,13 +658,13 @@ "name": "left" }, "row": "top", + "mirrored": true, "bind": [ 0, 10, - 10, + 0, 0 - ], - "mirrored": true + ] } }, "mirror_matrix_right_bottom": { @@ -722,13 +722,13 @@ "name": "right" }, "row": "bottom", + "mirrored": true, "bind": [ 10, 0, 0, - 10 - ], - "mirrored": true + 0 + ] } }, "mirror_matrix_right_top": { @@ -786,13 +786,13 @@ "name": "right" }, "row": "top", + "mirrored": true, "bind": [ 0, 0, 10, 10 - ], - "mirrored": true + ] } } }