chore: unify drawFillWithGap

This commit is contained in:
Azgaar 2024-09-02 23:05:47 +02:00
parent 6b3df6c4d8
commit 39516ce782
3 changed files with 17 additions and 101 deletions

View file

@ -212,11 +212,6 @@ t,
mask: url(#land);
}
#statesBody,
#provincesBody {
stroke-width: 3;
}
#borders {
stroke-linejoin: round;
fill: none;

View file

@ -878,96 +878,17 @@ function toggleReligions(event) {
function drawReligions() {
TIME && console.time("drawReligions");
relig.selectAll("path").remove();
const {cells, vertices, religions} = pack;
const n = cells.i.length;
const {cells, religions} = pack;
const used = new Uint8Array(cells.i.length);
const vArray = new Array(religions.length); // store vertices array
const body = new Array(religions.length).fill(""); // store path around each religion
const gap = new Array(religions.length).fill(""); // store path along water for each religion to fill the gaps
for (const i of cells.i) {
if (!cells.religion[i]) continue;
if (used[i]) continue;
used[i] = 1;
const r = cells.religion[i];
const onborder = cells.c[i].filter(n => cells.religion[n] !== r);
if (!onborder.length) continue;
const borderWith = cells.c[i].map(c => cells.religion[c]).find(n => n !== r);
const vertex = cells.v[i].find(v => vertices.c[v].some(i => cells.religion[i] === borderWith));
const chain = connectVertices(vertex, r, borderWith);
if (chain.length < 3) continue;
const points = chain.map(v => vertices.p[v[0]]);
if (!vArray[r]) vArray[r] = [];
vArray[r].push(points);
body[r] += "M" + points.join("L") + "Z";
gap[r] +=
"M" +
vertices.p[chain[0][0]] +
chain.reduce(
(r2, v, i, d) =>
!i ? r2 : !v[2] ? r2 + "L" + vertices.p[v[0]] : d[i + 1] && !d[i + 1][2] ? r2 + "M" + vertices.p[v[0]] : r2,
""
);
const bodyPaths = new Array(religions.length - 1);
const isolines = getIsolines(cellId => cells.religion[cellId], {fill: true, waterGap: true});
for (const [index, {fill, waterGap}] of isolines) {
const color = religions[index].color;
bodyPaths.push(drawFillWithGap("religion", fill, waterGap, color, index));
}
const bodyData = body.map((p, i) => [p.length > 10 ? p : null, i, religions[i].color]).filter(d => d[0]);
relig
.selectAll("path")
.data(bodyData)
.enter()
.append("path")
.attr("d", d => d[0])
.attr("fill", d => d[2])
.attr("id", d => "religion" + d[1]);
const gapData = gap.map((p, i) => [p.length > 10 ? p : null, i, religions[i].color]).filter(d => d[0]);
relig
.selectAll(".path")
.data(gapData)
.enter()
.append("path")
.attr("d", d => d[0])
.attr("fill", "none")
.attr("stroke", d => d[2])
.attr("id", d => "religion-gap" + d[1])
.attr("stroke-width", "10px");
byId("relig").innerHTML = bodyPaths.join("");
// connect vertices to chain
function connectVertices(start, t, religion) {
const chain = []; // vertices chain to form a path
let land = vertices.c[start].some(c => cells.h[c] >= 20 && cells.religion[c] !== t);
function check(i) {
religion = cells.religion[i];
land = cells.h[i] >= 20;
}
for (let i = 0, current = start; i === 0 || (current !== start && i < 20000); i++) {
const prev = chain[chain.length - 1] ? chain[chain.length - 1][0] : -1; // previous vertex in chain
chain.push([current, religion, land]); // add current vertex to sequence
const c = vertices.c[current]; // cells adjacent to vertex
c.filter(c => cells.religion[c] === t).forEach(c => (used[c] = 1));
const c0 = c[0] >= n || cells.religion[c[0]] !== t;
const c1 = c[1] >= n || cells.religion[c[1]] !== t;
const c2 = c[2] >= n || cells.religion[c[2]] !== t;
const v = vertices.v[current]; // neighboring vertices
if (v[0] !== prev && c0 !== c1) {
current = v[0];
check(c0 ? c[0] : c[1]);
} else if (v[1] !== prev && c1 !== c2) {
current = v[1];
check(c1 ? c[1] : c[2]);
} else if (v[2] !== prev && c0 !== c2) {
current = v[2];
check(c2 ? c[2] : c[0]);
}
if (current === chain[chain.length - 1][0]) {
ERROR && console.error("Next vertex is not found");
break;
}
}
return chain;
}
TIME && console.timeEnd("drawReligions");
}
@ -1000,11 +921,7 @@ function drawStates() {
const isolines = getIsolines(cellId => cells.state[cellId], {fill: true, waterGap: true, halo: renderHalo});
for (const [index, {fill, waterGap, halo}] of isolines) {
const color = states[index].color;
bodyPaths.push(
/* html */ `<path d="${waterGap}" fill="none" stroke="${color}" id="state-gap${index}" />`,
/* html */ `<path d="${fill}" fill="${color}" stroke="none" id="state${index}" />`
);
bodyPaths.push(drawFillWithGap("state", fill, waterGap, color, index));
if (renderHalo) {
const haloColor = d3.color(color)?.darker().hex() || "#666666";
@ -1165,10 +1082,7 @@ function drawProvinces() {
const isolines = getIsolines(cellId => cells.province[cellId], {fill: true, waterGap: true});
for (const [index, {fill, waterGap}] of isolines) {
const color = provinces[index].color;
bodyPaths.push(
/* html */ `<path d="${waterGap}" fill="none" stroke="${color}" id="province-gap${index}" />`,
/* html */ `<path d="${fill}" fill="${color}" stroke="none" id="province${index}" />`
);
bodyPaths.push(drawFillWithGap("province", fill, waterGap, color, index));
}
const labels = provinces

View file

@ -84,7 +84,7 @@ function getVertexPoint(vertexId) {
function getFillPath(vertexChain) {
const points = vertexChain.map(getVertexPoint);
const firstPoint = points.shift();
return `M${firstPoint} L${points.join(" ")}`;
return `M${firstPoint} L${points.join(" ")} Z`;
}
// get single path for an non-continuous array of cells
@ -172,3 +172,10 @@ function connectVertices({startingVertex, ofSameType, addToChecked, closeRing})
if (closeRing) chain.push(startingVertex);
return chain;
}
function drawFillWithGap(elementName, fill, waterGap, color, index) {
return /* html */ `
<path d="${fill}" fill="${color}" id="${elementName}${index}" />
<path d="${waterGap}" fill="none" stroke="${color}" stroke-width="5" id="${elementName}-gap${index}" />
`;
}