refactor: roads generation - reduce curves on routes rendering

This commit is contained in:
max 2022-08-21 00:02:32 +03:00
parent fc3e3b844d
commit e427050369
5 changed files with 68 additions and 37 deletions

View file

@ -1,5 +1,4 @@
import * as d3 from "d3"; import * as d3 from "d3";
import {drawPoint} from "utils/debugUtils";
import {round} from "utils/stringUtils"; import {round} from "utils/stringUtils";
@ -9,35 +8,16 @@ export function drawRoutes() {
const {cells, burgs} = pack; const {cells, burgs} = pack;
const lineGen = d3.line().curve(d3.curveCatmullRom.alpha(0.1)); const lineGen = d3.line().curve(d3.curveCatmullRom.alpha(0.1));
const getBurgCoords = (burgId: number): TPoint => { const SHARP_ANGLE = 135;
if (!burgId) throw new Error("burgId must be positive"); const VERY_SHARP_ANGLE = 115;
const burg = burgs[burgId] as IBurg;
return [burg.x, burg.y];
};
const getPathPoints = (cellIds: number[]): TPoints =>
cellIds.map(cellId => {
const burgId = cells.burg[cellId];
if (burgId) return getBurgCoords(burgId);
return cells.p[cellId];
});
const normalizePoints = (points: TPoints): TPoints =>
points.map(([x, y], index) => {
return [x, y];
if (i === 17) {
cells.forEach(cellId => drawPoint(pack.cells.p[cellId]));
}
});
const points = adjustBurgPoints(); // mutable array of points
const routePaths: Dict<string[]> = {}; const routePaths: Dict<string[]> = {};
for (const {i, type, cells} of pack.routes) { for (const {i, type, cells} of pack.routes) {
const points = getPathPoints(cells); straightenPathAngles(cells); // mutates points
const normalizedPoints = normalizePoints(points); const pathPoints = cells.map(cellId => points[cellId]);
const path = round(lineGen(normalizedPoints)!, 1); const path = round(lineGen(pathPoints)!, 1);
if (!routePaths[type]) routePaths[type] = []; if (!routePaths[type]) routePaths[type] = [];
routePaths[type].push(`<path id="${type}${i}" d="${path}"/>`); routePaths[type].push(`<path id="${type}${i}" d="${path}"/>`);
@ -46,4 +26,49 @@ export function drawRoutes() {
for (const type in routePaths) { for (const type in routePaths) {
routes.select(`[data-type=${type}]`).html(routePaths[type].join("")); routes.select(`[data-type=${type}]`).html(routePaths[type].join(""));
} }
function adjustBurgPoints() {
const points = Array.from(cells.p);
for (const burg of burgs) {
if (burg.i === 0) continue;
const {cell, x, y} = burg as IBurg;
points[cell] = [x, y];
}
return points;
}
function straightenPathAngles(cellIds: number[]) {
for (let i = 1; i < cellIds.length - 1; i++) {
const cellId = cellIds[i];
if (cells.burg[cellId]) continue;
const prev = points[cellIds[i - 1]];
const that = points[cellId];
const next = points[cellIds[i + 1]];
const dAx = prev[0] - that[0];
const dAy = prev[1] - that[1];
const dBx = next[0] - that[0];
const dBy = next[1] - that[1];
const angle = (Math.atan2(dAx * dBy - dAy * dBx, dAx * dBx + dAy * dBy) * 180) / Math.PI;
if (Math.abs(angle) < SHARP_ANGLE) {
const middleX = (prev[0] + next[0]) / 2;
const middleY = (prev[1] + next[1]) / 2;
if (Math.abs(angle) < VERY_SHARP_ANGLE) {
const newX = (that[0] + middleX * 2) / 3;
const newY = (that[1] + middleY * 2) / 3;
points[cellId] = [newX, newY];
continue;
}
const newX = (that[0] + middleX) / 2;
const newY = (that[1] + middleY) / 2;
points[cellId] = [newX, newY];
}
}
}
} }

View file

@ -1,8 +1,7 @@
import * as d3 from "d3"; import * as d3 from "d3";
import {TIME} from "config/logging"; import {TIME, DEBUG} from "config/logging";
import {last} from "utils/arrayUtils"; import {last} from "utils/arrayUtils";
import {rn} from "utils/numberUtils";
import {rand, P, gauss, ra, rw} from "utils/probabilityUtils"; import {rand, P, gauss, ra, rw} from "utils/probabilityUtils";
import {capitalize} from "utils/stringUtils"; import {capitalize} from "utils/stringUtils";
import {convertTemperature, getFriendlyHeight, getBurgPopulation} from "utils/unitUtils"; import {convertTemperature, getFriendlyHeight, getBurgPopulation} from "utils/unitUtils";
@ -112,8 +111,9 @@ window.Markers = (function () {
let candidates = Array.from(list(pack)); let candidates = Array.from(list(pack));
let quantity = getQuantity(candidates, min, each, multiplier); let quantity = getQuantity(candidates, min, each, multiplier);
// uncomment for debugging:
// console.log(`${icon} ${type}: each ${each} of ${candidates.length}, min ${min} candidates. Got ${quantity}`); DEBUG &&
console.info(`${icon} ${type}: each ${each} of ${candidates.length}, min ${min} candidates. Got ${quantity}`);
while (quantity && candidates.length) { while (quantity && candidates.length) {
const [cell] = extractAnyElement(candidates); const [cell] = extractAnyElement(candidates);

View file

@ -1,4 +1,4 @@
import {TIME} from "config/logging"; import {INFO, TIME} from "config/logging";
import {getInputNumber} from "utils/nodeUtils"; import {getInputNumber} from "utils/nodeUtils";
import {MAX_HEIGHT, MIN_LAND_HEIGHT} from "config/generation"; import {MAX_HEIGHT, MIN_LAND_HEIGHT} from "config/generation";
@ -46,7 +46,7 @@ export function addLakesInDeepDepressions(heights: Uint8Array, neighbours: numbe
if (inDeepDepression) { if (inDeepDepression) {
currentHeights[cellId] = MIN_LAND_HEIGHT - 1; currentHeights[cellId] = MIN_LAND_HEIGHT - 1;
console.log(`ⓘ Added lake at deep depression. Cell: ${cellId}`); INFO && console.info(`ⓘ Added lake at deep depression. Cell: ${cellId}`);
} }
} }

View file

@ -4,7 +4,6 @@ import FlatQueue from "flatqueue";
import {TIME} from "config/logging"; import {TIME} from "config/logging";
import {ELEVATION, MIN_LAND_HEIGHT, ROUTES} from "config/generation"; import {ELEVATION, MIN_LAND_HEIGHT, ROUTES} from "config/generation";
import {dist2} from "utils/functionUtils"; import {dist2} from "utils/functionUtils";
import {drawLine} from "utils/debugUtils";
export function generateRoutes( export function generateRoutes(
burgs: TBurgs, burgs: TBurgs,
@ -37,8 +36,6 @@ export function generateRoutes(
const points: TPoints = featureCapitals.map(burg => [burg.x, burg.y]); const points: TPoints = featureCapitals.map(burg => [burg.x, burg.y]);
const urquhartEdges = calculateUrquhartEdges(points); const urquhartEdges = calculateUrquhartEdges(points);
urquhartEdges.forEach(([fromId, toId]) => { urquhartEdges.forEach(([fromId, toId]) => {
drawLine(points[fromId], points[toId], {stroke: "red", strokeWidth: 0.01});
const start = featureCapitals[fromId].cell; const start = featureCapitals[fromId].cell;
const exit = featureCapitals[toId].cell; const exit = featureCapitals[toId].cell;
@ -70,8 +67,6 @@ export function generateRoutes(
const points: TPoints = featureBurgs.map(burg => [burg.x, burg.y]); const points: TPoints = featureBurgs.map(burg => [burg.x, burg.y]);
const urquhartEdges = calculateUrquhartEdges(points); const urquhartEdges = calculateUrquhartEdges(points);
urquhartEdges.forEach(([fromId, toId]) => { urquhartEdges.forEach(([fromId, toId]) => {
drawLine(points[fromId], points[toId], {strokeWidth: 0.01});
const start = featureBurgs[fromId].cell; const start = featureBurgs[fromId].cell;
const exit = featureBurgs[toId].cell; const exit = featureBurgs[toId].cell;

View file

@ -44,3 +44,14 @@ export function drawArrow([x1, y1]: TPoint, [x2, y2]: TPoint, {width = 1, color
.attr("stroke", color) .attr("stroke", color)
.attr("stroke-width", width / 2); .attr("stroke-width", width / 2);
} }
export function drawText(text: string | number, [x, y]: TPoint, {size = 6, color = "black"} = {}) {
debug
.append("text")
.attr("x", x)
.attr("y", y)
.attr("font-size", size)
.attr("fill", color)
.attr("stroke", "none")
.text(text);
}