mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-18 02:01:22 +01:00
refactor: drawBorders
This commit is contained in:
parent
e83726918b
commit
3b1e993e9a
11 changed files with 144 additions and 143 deletions
|
|
@ -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>
|
||||
|
|
|
|||
1
main.js
1
main.js
|
|
@ -654,7 +654,6 @@ async function generate(options) {
|
|||
Provinces.getPoles();
|
||||
BurgsAndStates.defineBurgFeatures();
|
||||
|
||||
drawBorders();
|
||||
drawStateLabels();
|
||||
|
||||
Rivers.specify();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
120
modules/renderers/draw-borders.js
Normal file
120
modules/renderers/draw-borders.js
Normal 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");
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
|
@ -273,7 +273,6 @@ window.Submap = (function () {
|
|||
stage("Regenerating routes network.");
|
||||
regenerateRoutes();
|
||||
|
||||
drawBorders();
|
||||
drawStateLabels();
|
||||
|
||||
Rivers.specify();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -336,7 +336,8 @@ function regenerateProvinces() {
|
|||
|
||||
Provinces.generate(true, true);
|
||||
Provinces.getPoles();
|
||||
drawBorders();
|
||||
|
||||
if (layerIsOn("toggleBorders")) drawBorders();
|
||||
if (layerIsOn("toggleProvinces")) drawProvinces();
|
||||
|
||||
// remove emblems
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue