refactor: drawBorders

This commit is contained in:
Azgaar 2024-09-03 12:43:11 +02:00
parent e83726918b
commit 3b1e993e9a
11 changed files with 144 additions and 143 deletions

View file

@ -603,6 +603,7 @@
id="toggleBorders"
data-tip="State borders: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style"
data-shortcut="D"
class="buttonoff"
onclick="toggleBorders(event)"
>
Bor<u>d</u>ers
@ -8030,7 +8031,8 @@
<script src="modules/biomes.js?v=1.99.00"></script>
<script src="modules/names-generator.js?v=1.87.14"></script>
<script src="modules/cultures-generator.js?v=1.99.05"></script>
<script src="modules/renderers/state-labels.js?v=1.96.04"></script>
<script src="modules/renderers/draw-state-labels.js?v=1.103.00"></script>
<script src="modules/renderers/draw-borders.js?v=1.103.00"></script>
<script src="modules/burgs-and-states.js?v=1.103.00"></script>
<script src="modules/provinces-generator.js?v=1.103.00"></script>
<script src="modules/routes-generator.js?v=1.99.04"></script>

View file

@ -654,7 +654,6 @@ async function generate(options) {
Provinces.getPoles();
BurgsAndStates.defineBurgFeatures();
drawBorders();
drawStateLabels();
Rivers.specify();

View file

@ -53,7 +53,6 @@ export function resolveVersionConflicts(mapVersion) {
BurgsAndStates.defineStateForms();
Provinces.generate();
Provinces.getPoles();
drawBorders();
if (!layerIsOn("toggleBorders")) $("#borders").fadeOut();
if (!layerIsOn("toggleStates")) regions.attr("display", "none").selectAll("path").remove();

View file

@ -643,13 +643,10 @@ function stateRemove(stateId) {
debug.selectAll(".highlight").remove();
if (!layerIsOn("toggleStates")) toggleStates();
else drawStates();
if (!layerIsOn("toggleBorders")) toggleBorders();
else drawBorders();
if (layerIsOn("toggleStates")) drawStates();
if (layerIsOn("toggleBorders")) drawBorders();
if (layerIsOn("toggleProvinces")) drawProvinces();
refreshStatesEditor();
}
@ -844,13 +841,13 @@ function recalculateStates(must) {
BurgsAndStates.expandStates();
Provinces.generate();
Provinces.getPoles();
if (!layerIsOn("toggleStates")) toggleStates();
else drawStates();
if (!layerIsOn("toggleBorders")) toggleBorders();
else drawBorders();
if (layerIsOn("toggleProvinces")) drawProvinces();
BurgsAndStates.getPoles();
if (layerIsOn("toggleStates")) drawStates();
if (layerIsOn("toggleBorders")) drawBorders();
if (layerIsOn("toggleProvinces")) drawProvinces();
if (adjustLabels.checked) drawStateLabels();
refreshStatesEditor();
}

View file

@ -0,0 +1,120 @@
"use strict";
function drawBorders() {
TIME && console.time("drawBorders");
const {cells, vertices} = pack;
const statePath = [];
const provincePath = [];
const checked = {};
const isLand = cellId => cells.h[cellId] >= 20;
for (let cellId = 0; cellId < cells.i.length; cellId++) {
if (!cells.state[cellId]) continue;
const provinceId = cells.province[cellId];
const stateId = cells.state[cellId];
// bordering cell of another province
if (provinceId) {
const provToCell = cells.c[cellId].find(neibId => {
const neibProvinceId = cells.province[neibId];
return (
neibProvinceId &&
provinceId > neibProvinceId &&
!checked[`prov-${provinceId}-${neibProvinceId}-${cellId}`] &&
cells.state[neibId] === stateId
);
});
if (provToCell !== undefined) {
const addToChecked = cellId => (checked[`prov-${provinceId}-${cells.province[provToCell]}-${cellId}`] = true);
const border = getBorder({type: "province", fromCell: cellId, toCell: provToCell, addToChecked});
if (border) {
provincePath.push(border);
cellId--; // check the same cell again
continue;
}
}
}
// if cell is on state border
const stateToCell = cells.c[cellId].find(neibId => {
const neibStateId = cells.state[neibId];
return isLand(neibId) && stateId > neibStateId && !checked[`state-${stateId}-${neibStateId}-${cellId}`];
});
if (stateToCell !== undefined) {
const addToChecked = cellId => (checked[`state-${stateId}-${cells.state[stateToCell]}-${cellId}`] = true);
const border = getBorder({type: "state", fromCell: cellId, toCell: stateToCell, addToChecked});
if (border) {
statePath.push(border);
cellId--; // check the same cell again
continue;
}
}
}
svg.select("#borders").selectAll("path").remove();
svg.select("#stateBorders").append("path").attr("d", statePath.join(" "));
svg.select("#provinceBorders").append("path").attr("d", provincePath.join(" "));
function getBorder({type, fromCell, toCell, addToChecked}) {
const getType = cellId => cells[type][cellId];
const isTypeFrom = cellId => cellId < cells.i.length && getType(cellId) === getType(fromCell);
const isTypeTo = cellId => cellId < cells.i.length && getType(cellId) === getType(toCell);
addToChecked(fromCell);
const startingVertex = cells.v[fromCell].find(v => vertices.c[v].some(i => isLand(i) && isTypeTo(i)));
if (startingVertex === undefined) return null;
const checkVertex = vertex =>
vertices.c[vertex].some(isTypeFrom) && vertices.c[vertex].some(c => isLand(c) && isTypeTo(c));
const chain = getVerticesLine({vertices, startingVertex, checkCell: isTypeFrom, checkVertex, addToChecked});
if (chain.length > 1) return "M" + chain.map(cellId => vertices.p[cellId]).join(" ");
return null;
}
// connect vertices to chain to form a border
function getVerticesLine({vertices, startingVertex, checkCell, checkVertex, addToChecked}) {
let chain = []; // vertices chain to form a path
let next = startingVertex;
const MAX_ITERATIONS = vertices.c.length;
for (let run = 0; run < 2; run++) {
// first run: from any vertex to a border edge
// second run: from found border edge to another edge
chain = [];
for (let i = 0; i < MAX_ITERATIONS; i++) {
const previous = chain.at(-1);
const current = next;
chain.push(current);
const neibCells = vertices.c[current];
neibCells.map(addToChecked);
const [c1, c2, c3] = neibCells.map(checkCell);
const [v1, v2, v3] = vertices.v[current].map(checkVertex);
const [vertex1, vertex2, vertex3] = vertices.v[current];
if (v1 && vertex1 !== previous && c1 !== c2) next = vertex1;
else if (v2 && vertex2 !== previous && c2 !== c3) next = vertex2;
else if (v3 && vertex3 !== previous && c1 !== c3) next = vertex3;
if (next === current || next === startingVertex) {
if (next === startingVertex) chain.push(startingVertex);
startingVertex = next;
break;
}
}
}
return chain;
}
TIME && console.timeEnd("drawBorders");
}

View file

@ -2,7 +2,7 @@
// list - an optional array of stateIds to regenerate
function drawStateLabels(list) {
console.time("drawStateLabels");
TIME && console.time("drawStateLabels");
// temporary make the labels visible
const layerDisplay = labels.style("display");
@ -289,5 +289,5 @@ function drawStateLabels(list) {
return false;
}
console.timeEnd("drawStateLabels");
TIME && console.timeEnd("drawStateLabels");
}

View file

@ -273,7 +273,6 @@ window.Submap = (function () {
stage("Regenerating routes network.");
regenerateRoutes();
drawBorders();
drawStateLabels();
Rivers.specify();

View file

@ -253,7 +253,6 @@ function editHeightmap(options) {
Provinces.getPoles();
BurgsAndStates.defineBurgFeatures();
drawBorders();
drawStateLabels();
Rivers.specify();
@ -440,7 +439,6 @@ function editHeightmap(options) {
}
drawStateLabels();
drawBorders();
if (erosionAllowed) {
Rivers.specify();

View file

@ -182,10 +182,10 @@ function restoreLayers() {
if (layerIsOn("toggleEmblems")) drawEmblems();
if (layerIsOn("toggleMarkers")) drawMarkers();
if (layerIsOn("toggleZones")) drawZones();
if (layerIsOn("toggleBorders")) drawBorders();
if (layerIsOn("toggleStates")) drawStates();
// some layers are rendered each time, remove them if they are not on
if (!layerIsOn("toggleBorders")) borders.selectAll("path").remove();
if (!layerIsOn("toggleStates")) regions.selectAll("path").remove();
if (!layerIsOn("toggleRivers")) rivers.selectAll("*").remove();
}
@ -871,114 +871,6 @@ function toggleBorders(event) {
}
}
// draw state and province borders
function drawBorders() {
TIME && console.time("drawBorders");
borders.selectAll("path").remove();
const {cells, vertices} = pack;
const n = cells.i.length;
const sPath = [];
const pPath = [];
const sUsed = new Array(pack.states.length).fill("").map(_ => []);
const pUsed = new Array(pack.provinces.length).fill("").map(_ => []);
for (let i = 0; i < cells.i.length; i++) {
if (!cells.state[i]) continue;
const p = cells.province[i];
const s = cells.state[i];
// if cell is on province border
const provToCell = cells.c[i].find(
n => cells.state[n] === s && p > cells.province[n] && pUsed[p][n] !== cells.province[n]
);
if (provToCell) {
const provTo = cells.province[provToCell];
pUsed[p][provToCell] = provTo;
const vertex = cells.v[i].find(v => vertices.c[v].some(i => cells.province[i] === provTo));
const chain = connectVertices(vertex, p, cells.province, provTo, pUsed);
if (chain.length > 1) {
pPath.push("M" + chain.map(c => vertices.p[c]).join(" "));
i--;
continue;
}
}
// if cell is on state border
const stateToCell = cells.c[i].find(n => cells.h[n] >= 20 && s > cells.state[n] && sUsed[s][n] !== cells.state[n]);
if (stateToCell !== undefined) {
const stateTo = cells.state[stateToCell];
sUsed[s][stateToCell] = stateTo;
const vertex = cells.v[i].find(v => vertices.c[v].some(i => cells.h[i] >= 20 && cells.state[i] === stateTo));
const chain = connectVertices(vertex, s, cells.state, stateTo, sUsed);
if (chain.length > 1) {
sPath.push("M" + chain.map(c => vertices.p[c]).join(" "));
i--;
continue;
}
}
}
stateBorders.append("path").attr("d", sPath.join(" "));
provinceBorders.append("path").attr("d", pPath.join(" "));
// connect vertices to chain
function connectVertices(current, f, array, t, used) {
let chain = [];
const checkCell = c => c >= n || array[c] !== f;
const checkVertex = v =>
vertices.c[v].some(c => array[c] === f) && vertices.c[v].some(c => array[c] === t && cells.h[c] >= 20);
// find starting vertex
for (let i = 0; i < 1000; i++) {
if (i === 999) ERROR && console.error("Find starting vertex: limit is reached", current, f, t);
const p = chain[chain.length - 2] || -1; // previous vertex
const v = vertices.v[current],
c = vertices.c[current];
const v0 = checkCell(c[0]) !== checkCell(c[1]) && checkVertex(v[0]);
const v1 = checkCell(c[1]) !== checkCell(c[2]) && checkVertex(v[1]);
const v2 = checkCell(c[0]) !== checkCell(c[2]) && checkVertex(v[2]);
if (v0 + v1 + v2 === 1) break;
current = v0 && p !== v[0] ? v[0] : v1 && p !== v[1] ? v[1] : v[2];
if (current === chain[0]) break;
if (current === p) return [];
chain.push(current);
}
chain = [current]; // vertices chain to form a path
// find path
for (let i = 0; i < 1000; i++) {
if (i === 999) ERROR && console.error("Find path: limit is reached", current, f, t);
const p = chain[chain.length - 2] || -1; // previous vertex
const v = vertices.v[current],
c = vertices.c[current];
c.filter(c => array[c] === t).forEach(c => (used[f][c] = t));
const v0 = checkCell(c[0]) !== checkCell(c[1]) && checkVertex(v[0]);
const v1 = checkCell(c[1]) !== checkCell(c[2]) && checkVertex(v[1]);
const v2 = checkCell(c[0]) !== checkCell(c[2]) && checkVertex(v[2]);
current = v0 && p !== v[0] ? v[0] : v1 && p !== v[1] ? v[1] : v[2];
if (current === p) break;
if (current === chain[chain.length - 1]) break;
if (chain.length > 1 && v0 + v1 + v2 < 2) break;
chain.push(current);
if (current === chain[0]) break;
}
return chain;
}
TIME && console.timeEnd("drawBorders");
}
function toggleProvinces(event) {
if (!layerIsOn("toggleProvinces")) {
turnButtonOn("toggleProvinces");

View file

@ -490,8 +490,7 @@ function editProvinces() {
const g = provs.select("#provincesBody");
g.select("#province" + p).remove();
g.select("#province-gap" + p).remove();
if (!layerIsOn("toggleBorders")) toggleBorders();
else drawBorders();
if (layerIsOn("toggleBorders")) drawBorders();
refreshProvincesEditor();
$(this).dialog("close");
},
@ -950,12 +949,9 @@ function editProvinces() {
pack.cells.province[i] = +this.dataset.province;
});
if (!layerIsOn("toggleBorders")) toggleBorders();
else drawBorders();
Provinces.getPoles();
if (!layerIsOn("toggleProvinces")) toggleProvinces();
else drawProvinces();
if (layerIsOn("toggleBorders")) drawBorders();
if (layerIsOn("toggleProvinces")) drawProvinces();
exitProvincesManualAssignment();
refreshProvincesEditor();
@ -1047,10 +1043,9 @@ function editProvinces() {
cells.province[c] = province;
});
if (!layerIsOn("toggleBorders")) toggleBorders();
else drawBorders();
if (!layerIsOn("toggleProvinces")) toggleProvinces();
else drawProvinces();
if (layerIsOn("toggleBorders")) drawBorders();
if (layerIsOn("toggleProvinces")) drawProvinces();
collectStatistics();
byId("provincesFilterState").value = state;
provincesEditorAddLines();
@ -1123,8 +1118,7 @@ function editProvinces() {
pack.states.forEach(s => (s.provinces = []));
unfog();
if (!layerIsOn("toggleBorders")) toggleBorders();
else drawBorders();
if (layerIsOn("toggleBorders")) drawBorders();
provs.select("#provincesBody").remove();
turnButtonOff("toggleProvinces");

View file

@ -336,7 +336,8 @@ function regenerateProvinces() {
Provinces.generate(true, true);
Provinces.getPoles();
drawBorders();
if (layerIsOn("toggleBorders")) drawBorders();
if (layerIsOn("toggleProvinces")) drawProvinces();
// remove emblems