diff options
Diffstat (limited to 'tools/parse-layout.js')
-rw-r--r-- | tools/parse-layout.js | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/tools/parse-layout.js b/tools/parse-layout.js new file mode 100644 index 0000000..5219705 --- /dev/null +++ b/tools/parse-layout.js @@ -0,0 +1,224 @@ +const fs = require("fs") + +let points = {} +let rects = {} +let edges = [] +let mode, name, x, y, w, h, cx, cy, rx, ry, x2, y2 + +function add_point(x, y) { + if (name in points) + points[name].push([x,y]) + else + points[name] = [ [x,y] ] +} + +function add_rect(x, y, w, h) { + if (name in rects) + rects[name].push([x,y,x+w,y+h]) + else + rects[name] = [ [x,y,x+w,y+h] ] +} + +function flush() { + if (mode === 'path') { + edges.push([ x, y, x2, y2 ]) + } + if (mode === 'rect') { + if (name.startsWith("$")) + add_rect(x, y, w, h) + else + add_point( x + w/2, y + h/2 ) + } + if (mode === 'circle') { + add_point( cx, cy ) + } + x = y = x2 = y2 = w = h = cx = cy = rx = ry = 0 +} + +function parse_path_data(path) { + let cx = 0 + let cy = 0 + let abs = 0 + for (let i = 0; i < path.length;) { + switch (path[i]) { + case 'M': + x = cx = Number(path[i+1]) + y = cy = Number(path[i+2]) + i += 3 + abs = true + break + case 'm': + x = cx = cx + Number(path[i+1]) + y = cy = cy + Number(path[i+2]) + i += 3 + abs = false + break + case 'C': + x2 = cx = Number(path[i+5]) + y2 = cy = Number(path[i+6]) + i += 7 + abs = true + break + case 'L': + i += 1 + abs = true + break + case 'H': + x2 = cx = Number(path[i+1]) + i += 2 + abs = true + break + case 'V': + y2 = cy = Number(path[i+1]) + i += 2 + abs = true + break + case 'c': + x2 = cx = cx + Number(path[i+5]) + y2 = cy = cy + Number(path[i+6]) + i += 7 + break + case 'l': + i += 1 + abs = false + break + case 'h': + x2 = cx = cx + Number(path[i+1]) + i += 2 + abs = false + break + case 'v': + y2 = cy = cy + Number(path[i+1]) + i += 2 + abs = false + break + default: + if (abs) { + x2 = cx = Number(path[i+0]) + y2 = cy = Number(path[i+1]) + } else { + x2 = cx = cx + Number(path[i+0]) + y2 = cy = cy + Number(path[i+1]) + } + i += 2 + break + } + } +} + +for (let line of fs.readFileSync("tools/layout.svg", "utf-8").split("\n")) { + line = line.trim() + if (line.startsWith("<rect")) { + flush() + mode = "rect" + x = y = w = h = 0 + } + else if (line.startsWith("<ellipse") || line.startsWith("<circle")) { + flush() + mode = "circle" + cx = cy = rx = ry = 0 + } + else if (line.startsWith("<path")) { + flush() + mode = "path" + } + else if (line.startsWith("<g")) { + flush() + mode = "g" + } + else if (line.startsWith('x="')) + x = (Number(line.split('"')[1])) + else if (line.startsWith('y="')) + y = (Number(line.split('"')[1])) + else if (line.startsWith('width="')) + w = (Number(line.split('"')[1])) + else if (line.startsWith('height="')) + h = (Number(line.split('"')[1])) + else if (line.startsWith('cx="')) + cx = (Number(line.split('"')[1])) + else if (line.startsWith('cy="')) + cy = (Number(line.split('"')[1])) + else if (line.startsWith('r="')) + rx = ry = (Number(line.split('"')[1])) + else if (line.startsWith('rx="')) + rx = (Number(line.split('"')[1])) + else if (line.startsWith('ry="')) + ry = (Number(line.split('"')[1])) + else if (line.startsWith('inkscape:label="') && mode === "g") + name = line.split('"')[1] + else if (line.startsWith('d="')) + parse_path_data(line.split('"')[1].split(/[ ,]/)) +} + +flush() + +let labels = [] + +// names.txt is layout.svg cleaned up by svgo and filtered to only include text nodes +for (let line of fs.readFileSync("tools/names.txt", "utf-8").split("\n")) { + let m = line.match(/<tspan x="([\d.]*)" y="([\d.]*)">([^<]*)</) + if (m) + labels.push({x: Number(m[1]), y: Number(m[2]), name: m[3]}) +} + +function find_closest_node(list, x, y) { + let nd = Infinity, nn = null + + for (let n of list) { + let d = Math.hypot(n.x - x, n.y - y) + if (d < nd) { + nd = d + nn = n + } + } + + if (!nn) { + console.log("NOT FOUND", x, y) + return [ null, 0 ] + } + + return [ nn, nd ] +} + +function find_enclosing_rect(list, x, y) { + for (let [x1, y1, x2, y2] of list) { + if (x >= x1 && x <= x2) + if (y >= y1 && y <= y2) + return true + } + return false +} + +// FIND and label all points! +let all_labels = labels.slice() +let out = {} +for (let key in points) { + out = {} + for (let [x, y] of points[key]) { + let [ node, dist ] = find_closest_node(labels, x, y) + if (dist > 10) { + console.log(key,x,y, dist) + } + if (node) { + if (node.name in out) + console.log("DUPLICATE", node.name, x, y, out[node.name]) + labels = labels.filter(x => x !== node) + let suit = "UNKNOWN" + if (find_enclosing_rect(rects.$CLUBS, x, y)) + suit = "clubs" + else if (find_enclosing_rect(rects.$HEARTS, x, y)) + suit = "hearts" + else if (find_enclosing_rect(rects.$DIAMONDS, x, y)) + suit = "diamonds" + else if (find_enclosing_rect(rects.$SPADES, x, y)) + suit = "spades" + else + console.log("NOT ASSIGNED SUIT", x, y) + out[node.name] = [x, y, dist, suit] + } else { + let [ dupname, dupdist ] = find_closest_node(all_labels, x, y) + console.log("ALREADY USED", dupname, dupdist, x, y, "OLD", out[dupname]) + } + } +} + |