From af1d369e3171f58958a7cd3414b0a2219ebe89a0 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Sun, 13 Jun 2021 19:57:00 +0300 Subject: [PATCH] addOverseaRoute --- modules/routes-generator.js | 173 +++++++++++++++++++++--------------- 1 file changed, 99 insertions(+), 74 deletions(-) diff --git a/modules/routes-generator.js b/modules/routes-generator.js index d9d1905c..bd43ed36 100644 --- a/modules/routes-generator.js +++ b/modules/routes-generator.js @@ -1,33 +1,36 @@ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - (global.Routes = factory()); -}(this, (function () {'use strict'; + typeof exports === "object" && typeof module !== "undefined" ? (module.exports = factory()) : typeof define === "function" && define.amd ? define(factory) : (global.Routes = factory()); +})(this, function () { + "use strict"; - const getRoads = function() { + const getRoads = function () { TIME && console.time("generateMainRoads"); - const cells = pack.cells, burgs = pack.burgs.filter(b => b.i && !b.removed); + const cells = pack.cells; + const burgs = pack.burgs.filter(b => b.i && !b.removed); const capitals = burgs.filter(b => b.capital); + if (capitals.length < 2) return []; // not enough capitals to build main roads const paths = []; // array to store path segments for (const b of capitals) { const connect = capitals.filter(c => c.i > b.i && c.feature === b.feature); if (!connect.length) continue; - const farthest = d3.scan(connect, (a, c) => ((c.y - b.y) ** 2 + (c.x - b.x) ** 2) - ((a.y - b.y) ** 2 + (a.x - b.x) ** 2)); + const farthest = d3.scan(connect, (a, c) => (c.y - b.y) ** 2 + (c.x - b.x) ** 2 - ((a.y - b.y) ** 2 + (a.x - b.x) ** 2)); const [from, exit] = findLandPath(b.cell, connect[farthest].cell, null); const segments = restorePath(b.cell, exit, "main", from); segments.forEach(s => paths.push(s)); } - cells.i.forEach(i => cells.s[i] += cells.road[i] / 2); // add roads to suitability score + cells.i.forEach(i => (cells.s[i] += cells.road[i] / 2)); // add roads to suitability score TIME && console.timeEnd("generateMainRoads"); return paths; - } + }; - const getTrails = function() { + const getTrails = function () { TIME && console.time("generateTrails"); - const cells = pack.cells, burgs = pack.burgs.filter(b => b.i && !b.removed); + const cells = pack.cells; + const burgs = pack.burgs.filter(b => b.i && !b.removed); + if (burgs.length < 2) return []; // not enough burgs to build trails let paths = []; // array to store path segments @@ -35,11 +38,11 @@ const isle = burgs.filter(b => b.feature === f.i); // burgs on island if (isle.length < 2) continue; - isle.forEach(function(b, i) { + isle.forEach(function (b, i) { let path = []; if (!i) { // build trail from the first burg on island to the farthest one on the same island - const farthest = d3.scan(isle, (a, c) => ((c.y - b.y) ** 2 + (c.x - b.x) ** 2) - ((a.y - b.y) ** 2 + (a.x - b.x) ** 2)); + const farthest = d3.scan(isle, (a, c) => (c.y - b.y) ** 2 + (c.x - b.x) ** 2 - ((a.y - b.y) ** 2 + (a.x - b.x) ** 2)); const to = isle[farthest].cell; if (cells.road[to]) return; const [from, exit] = findLandPath(b.cell, to, null); @@ -57,26 +60,31 @@ TIME && console.timeEnd("generateTrails"); return paths; - } + }; - const getSearoutes = function() { + const getSearoutes = function () { TIME && console.time("generateSearoutes"); - const allPorts = pack.burgs.filter(b => b.port > 0 && !b.removed); - if (allPorts.length < 2) return []; + const {cells, burgs, features} = pack; + const allPorts = burgs.filter(b => b.port > 0 && !b.removed); - const bodies = new Set(allPorts.map(b => b.port)); // features with ports + if (!allPorts.length) return []; + + const bodies = new Set(allPorts.map(b => b.port)); // water features with ports let paths = []; // array to store path segments const connected = []; // store cell id of connected burgs - bodies.forEach(function(f) { + bodies.forEach(f => { const ports = allPorts.filter(b => b.port === f); // all ports on the same feature - if (ports.length < 2) return; + if (!ports.length) return; - for (let s=0; s < ports.length; s++) { + if (features[f].border) addOverseaRoute(f, ports[0]); + + // get inner-map routes + for (let s = 0; s < ports.length; s++) { const source = ports[s].cell; if (connected[source]) continue; - for (let t=s+1; t < ports.length; t++) { + for (let t = s + 1; t < ports.length; t++) { const target = ports[t].cell; if (connected[target]) continue; @@ -90,61 +98,63 @@ connected[target] = 1; } } - }); + function addOverseaRoute(f, port) { + const {x, y, cell: source} = port; + const dist = p => Math.abs(p[0] - x) + Math.abs(p[1] - y); + const [x1, y1] = [ + [0, y], + [x, 0], + [graphWidth, y], + [x, graphHeight] + ].sort((a, b) => dist(a) - dist(b))[0]; + const target = findCell(x1, y1); + + if (cells.f[target] === f && cells.h[target] < 20) { + const [from, exit, passable] = findOceanPath(target, source, true); + + if (passable) { + const path = restorePath(target, exit, "ocean", from); + paths = paths.concat(path); + last(path).push([x1, y1]); + } + } + } + TIME && console.timeEnd("generateSearoutes"); return paths; - } + }; - const draw = function(main, small, ocean) { + const draw = function (main, small, water) { TIME && console.time("drawRoutes"); - const cells = pack.cells, burgs = pack.burgs; + const {cells, burgs} = pack; + const {burg, p} = cells; + + const getBurgCoords = b => [burgs[b].x, burgs[b].y]; + const getPathPoints = cells => cells.map(i => (Array.isArray(i) ? i : burg[i] ? getBurgCoords(burg[i]) : p[i])); + const getPath = segment => round(lineGen(getPathPoints(segment)), 1); + const getPathsHTML = (paths, type) => paths.map((path, i) => ``).join(""); + lineGen.curve(d3.curveCatmullRom.alpha(0.1)); + roads.html(getPathsHTML(main, "road")); + trails.html(getPathsHTML(small, "trail")); - // main routes - roads.selectAll("path").data(main).enter().append("path") - .attr("id", (d, i) => "road" + i) - .attr("d", d => round(lineGen(d.map(c => { - const b = cells.burg[c]; - const x = b ? burgs[b].x : cells.p[c][0]; - const y = b ? burgs[b].y : cells.p[c][1]; - return [x, y]; - })), 1)); - - // small routes - trails.selectAll("path").data(small).enter().append("path") - .attr("id", (d, i) => "trail" + i) - .attr("d", d => round(lineGen(d.map(c => { - const b = cells.burg[c]; - const x = b ? burgs[b].x : cells.p[c][0]; - const y = b ? burgs[b].y : cells.p[c][1]; - return [x, y]; - })), 1)); - - // ocean routes lineGen.curve(d3.curveBundle.beta(1)); - searoutes.selectAll("path").data(ocean).enter().append("path") - .attr("id", (d, i) => "searoute" + i) - .attr("d", d => round(lineGen(d.map(c => { - const b = cells.burg[c]; - const x = b ? burgs[b].x : cells.p[c][0]; - const y = b ? burgs[b].y : cells.p[c][1]; - return [x, y]; - })), 1)); + searoutes.html(getPathsHTML(water, "searoute")); TIME && console.timeEnd("drawRoutes"); - } + }; - const regenerate = function() { + const regenerate = function () { routes.selectAll("path").remove(); pack.cells.road = new Uint16Array(pack.cells.i.length); pack.cells.crossroad = new Uint16Array(pack.cells.i.length); const main = getRoads(); const small = getTrails(); - const ocean = getSearoutes(); - draw(main, small, ocean); - } + const water = getSearoutes(); + draw(main, small, water); + }; return {getRoads, getTrails, getSearoutes, draw, regenerate}; @@ -152,11 +162,14 @@ function findLandPath(start, exit = null, toRoad = null) { const cells = pack.cells; const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p}); - const cost = [], from = []; + const cost = [], + from = []; queue.queue({e: start, p: 0}); while (queue.length) { - const next = queue.dequeue(), n = next.e, p = next.p; + const next = queue.dequeue(), + n = next.e, + p = next.p; if (toRoad && cells.road[n]) return [from, n]; for (const c of cells.c[n]) { @@ -175,7 +188,6 @@ cost[c] = totalCost; queue.queue({e: c, p: totalCost}); } - } return [from, exit]; } @@ -183,7 +195,9 @@ function restorePath(start, end, type, from) { const cells = pack.cells; const path = []; // to store all segments; - let segment = [], current = end, prev = end; + let segment = [], + current = end, + prev = end; const score = type === "main" ? 5 : 1; // to incrade road score at cell if (type === "ocean" || !cells.road[prev]) segment.push(end); @@ -197,8 +211,14 @@ if (segment.length) { segment.push(current); path.push(segment); - if (segment[0] !== end) {cells.road[segment[0]] += score; cells.crossroad[segment[0]] += score;} - if (current !== start) {cells.road[current] += score; cells.crossroad[current] += score;} + if (segment[0] !== end) { + cells.road[segment[0]] += score; + cells.crossroad[segment[0]] += score; + } + if (current !== start) { + cells.road[current] += score; + cells.crossroad[current] += score; + } } segment = []; prev = current; @@ -218,29 +238,34 @@ // find water paths function findOceanPath(start, exit = null, toRoute = null) { - const cells = pack.cells, temp = grid.cells.temp; + const cells = pack.cells, + temp = grid.cells.temp; const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p}); - const cost = [], from = []; + const cost = [], + from = []; queue.queue({e: start, p: 0}); while (queue.length) { - const next = queue.dequeue(), n = next.e, p = next.p; + const next = queue.dequeue(), + n = next.e, + p = next.p; if (toRoute && n !== start && cells.road[n]) return [from, n, true]; for (const c of cells.c[n]) { - if (c === exit) {from[c] = n; return [from, exit, true];} + if (c === exit) { + from[c] = n; + return [from, exit, true]; + } if (cells.h[c] >= 20) continue; // ignore land cells if (temp[cells.g[c]] <= -5) continue; // ignore cells with term <= -5 const dist2 = (cells.p[c][1] - cells.p[n][1]) ** 2 + (cells.p[c][0] - cells.p[n][0]) ** 2; const totalCost = p + (cells.road[c] ? 1 + dist2 / 2 : dist2 + (cells.t[c] ? 1 : 100)); if (from[c] || totalCost >= cost[c]) continue; - from[c] = n, cost[c] = totalCost; + (from[c] = n), (cost[c] = totalCost); queue.queue({e: c, p: totalCost}); } - } return [from, exit, false]; } - -}))); +});