"use strict" /* COMMON PARSING */ const fs = require("fs") let points = {} let circles = {} let rects = {} let edges = {} let labels = [] let mode, name, x, y, w, h, cx, cy, rx, ry, x2, y2 function array_insert(array, index, item) { for (let i = array.length; i > index; --i) array[i] = array[i - 1] array[index] = item } function set_add(set, item) { let a = 0 let b = set.length - 1 while (a <= b) { let m = (a + b) >> 1 let x = set[m] if (item < x) b = m - 1 else if (item > x) a = m + 1 else return } array_insert(set, a, item) } function add_point(x, y) { if (!(name in points)) points[name] = [] points[name].push({x,y}) } function add_circle(cx, cy, rx, ry) { if (!(name in circles)) circles[name] = [] circles[name].push({x:cx-rx,y:cy-ry,w:rx*2,h:ry*2}) } function add_rect(x, y, w, h) { if (!(name in rects)) rects[name] = [] rects[name].push({x,y,w,h}) } function add_edge(x1, y1, x2, y2) { if (!(name in edges)) edges[name] = [] edges[name].push({x1,y1,x2,y2}) } function flush() { if (mode === 'path') { add_edge(x, y, x2, y2) } if (mode === 'rect') { add_rect(x, y, w, h) add_point(x + w/2, y + h/2) } if (mode === 'circle') { add_circle(cx, cy, rx, ry) 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': x2 = x = cx = Number(path[i+1]) y2 = y = cy = Number(path[i+2]) i += 3 abs = true break case 'm': x2 = x = cx = cx + Number(path[i+1]) y2 = 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 abs = false 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("")) { let name = line.replace(/^[^>]*>/, "").replace(/<\/tspan.*/, "") labels.push({x, y, name}) } } flush() function find_closest_label(x, y) { let nd = Infinity, nn = null for (let n of labels) { 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 } if (nd > 50) { console.log("NO LABEL", x, y, nd) return null } return nn.name } function find_closest_node(nodes, x, y) { let nd = Infinity, nn = -1 for (let i = 0; i < nodes.length; ++i) { let n = nodes[i] let d = Math.hypot(n.x - x, n.y - y) if (d < nd) { nd = d nn = i } } if (nd > 100) return -1 return nn } function label_boxes(top) { for (let key in top) { console.log("BOX", key) for (let item of top[key]) item.name = find_closest_label(item.x + item.w/2, item.y + item.h/2) } } function label_points(top) { for (let key in top) { console.log("POINT", key) for (let item of top[key]) item.name = find_closest_label(item.x, item.y) } } label_boxes(rects) label_boxes(circles) label_points(points) function connect_edge_2way(node_list, edge_name) { console.log("EDGE", edge_name) for (let e of edges[edge_name]) { let a = find_closest_node(node_list, e.x1, e.y1) let b = find_closest_node(node_list, e.x2, e.y2) if (a < 0 || b < 0) console.log("CANNOT FIND EDGE", e, node_list[a], node_list[b]) if (!node_list[a][edge_name]) node_list[a][edge_name] = [] if (!node_list[b][edge_name]) node_list[b][edge_name] = [] set_add(node_list[a][edge_name], b) set_add(node_list[b][edge_name], a) } } function connect_edge_1way(a_list, b_list, edge_name, prop_name) { console.log("EDGE", edge_name) for (let e of edges[edge_name]) { let a1 = find_closest_node(a_list, e.x1, e.y1) let a2 = find_closest_node(a_list, e.x2, e.y2) let b1 = find_closest_node(b_list, e.x1, e.y1) let b2 = find_closest_node(b_list, e.x2, e.y2) let a = a1 >= 0 ? a1 : a2 let b = b1 >= 0 ? b1 : b2 if (a < 0 || b < 0) console.log("CANNOT FIND EDGE", e, a_list[a], b_list[b]) if (!a_list[a][prop_name]) a_list[a][prop_name] = [] set_add(a_list[a][prop_name], b) } } /* WASHINGTON'S WAR */ function sort_alpha(list) { list.sort((a,b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0) } sort_alpha(points.fortress) sort_alpha(points.winter_quarters) sort_alpha(points.space) points.colony.reverse() const data = {} data.spaces = [ ...points.fortress, ...points.winter_quarters, ...points.space ] data.colonies = [ ...points.colony ] data.seas = [ ...points.sea ] connect_edge_2way(data.spaces, "path") connect_edge_2way(data.spaces, "wilderness") connect_edge_1way(data.colonies, data.spaces, "colony", "spaces") connect_edge_1way(data.seas, data.spaces, "sea", "spaces") console.log(JSON.stringify(data,0,4)) /* 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 } function make_spaces(points) { let spaces = [] for (let [x,y] of points) { let [ name, dist ] = find_closest_node(labels, x, y) if (dist > 50) console.log("DISTANCE TOO FAR", x, y, dist) spaces.push({ name: name.name, x, y }) } return spaces } let data = {} data.spaces = make_spaces([ ...points.fortress, ...points.winter_quarters, ...points.space ]) console.log(data) function find_closest_point(x, y) { let nd = Infinity, nn = -1 for (let i = 0; i < points.length; ++i) { let n = points[i] let d = Math.hypot(n.x - x, n.y - y) if (d < nd) { nd = d nn = i } } return nn } // FIND and label all points! let all_labels = labels.slice() let cities = [] for (let key in points) { for (let [x, y] of points[key]) { let [ node, dist ] = find_closest_node(labels, x, y) if (dist > 15) { console.log("DISTANCE TOO FAR", key,x,y, "dist=" + dist, "name=" + node.name) } if (node) { 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) let country = "UNKNOWN" if (find_enclosing_rect(rects.$Empire, x, y)) { country = EMPIRE } else if (find_enclosing_rect(rects.$Austria, x, y)) { country = AUSTRIA } else if (find_enclosing_rect(rects.$Hanover, x, y)) { country = HANOVER } else if (find_enclosing_rect(rects.$Saxony, x, y)) { country = SAXONY } else if (find_enclosing_rect(rects.$Sweden, x, y)) { country = SWEDEN } else if (find_enclosing_rect(rects.$Poland, x, y)) { country = POLAND } else if (find_enclosing_rect(rects.$Prussia, x, y)) { country = PRUSSIA } if (country === "UNKNOWN") console.log("no country:", node) cities.push({ name: node.name, country, suit, type: key, x: Math.round(x), y: Math.round(y), adjacent: [], major_roads: [], roads: [], }) } else { let [ dupname, dupdist ] = find_closest_node(all_labels, x, y) console.log("ALREADY USED", dupname, dupdist, x, y) } } } for (let e of edges.major_road) { let a = find_closest_point(e.x1, e.y1) let b = find_closest_point(e.x2, e.y2) set_add(cities[a].major_roads, b) set_add(cities[b].major_roads, a) set_add(cities[a].adjacent, b) set_add(cities[b].adjacent, a) } for (let e of edges.road) { let a = find_closest_point(e.x1, e.y1) let b = find_closest_point(e.x2, e.y2) set_add(cities[a].roads, b) set_add(cities[b].roads, a) set_add(cities[a].adjacent, b) set_add(cities[b].adjacent, a) } console.log("if (typeof module === 'object') module.exports = data") */