refactor: drawIce

This commit is contained in:
Azgaar 2024-09-03 01:33:50 +02:00
parent 39516ce782
commit e83726918b
10 changed files with 174 additions and 272 deletions

View file

@ -1,16 +1,16 @@
"use strict";
// get continuous paths (isolines) for all cells at once based on getType(cellId) comparison
function getIsolines(getType, options = {polygons: false, fill: false, halo: false, waterGap: false}) {
const {cells, vertices} = pack;
function getIsolines(graph, getType, options = {polygons: false, fill: false, halo: false, waterGap: false}) {
const {cells, vertices} = graph;
const isolines = {};
const checkedCells = new Uint8Array(cells.c.length);
const checkedCells = new Uint8Array(cells.i.length);
const addToChecked = cellId => (checkedCells[cellId] = 1);
const isChecked = cellId => checkedCells[cellId] === 1;
for (let cellId = 0; cellId < cells.c.length; cellId++) {
if (isChecked(cellId) || getType(cellId) === 0) continue;
for (const cellId of cells.i) {
if (isChecked(cellId) || !getType(cellId)) continue;
addToChecked(cellId);
const type = getType(cellId);
@ -20,73 +20,75 @@ function getIsolines(getType, options = {polygons: false, fill: false, halo: fal
const onborderCell = cells.c[cellId].find(ofDifferentType);
if (onborderCell === undefined) continue;
const feature = pack.features[cells.f[onborderCell]];
if (feature.type === "lake") {
if (!feature.shoreline) Lakes.getShoreline(feature);
if (feature.shoreline.every(ofSameType)) continue; // inner lake
}
// check if inner lake. Note there is no shoreline for grid features
const feature = graph.features[cells.f[onborderCell]];
if (feature.type === "lake" && feature.shoreline?.every(ofSameType)) continue;
const startingVertex = cells.v[cellId].find(v => vertices.c[v].some(ofDifferentType));
if (startingVertex === undefined) throw new Error(`Starting vertex for cell ${cellId} is not found`);
const vertexChain = connectVertices({startingVertex, ofSameType, addToChecked, closeRing: true});
const vertexChain = connectVertices({vertices, startingVertex, ofSameType, addToChecked, closeRing: true});
if (vertexChain.length < 3) continue;
addIsoline(type, vertexChain);
addIsoline(type, vertices, vertexChain);
}
return Object.entries(isolines);
return isolines;
function getBorderPath(vertexChain, discontinue) {
let discontinued = true;
let lastOperation = "";
const path = vertexChain.map(vertex => {
if (discontinue(vertex)) {
discontinued = true;
return "";
}
function addIsoline(type, vertices, vertexChain) {
if (!isolines[type]) isolines[type] = {};
const operation = discontinued ? "M" : "L";
const command = operation === lastOperation ? "" : operation;
if (options.polygons) {
if (!isolines[type].polygons) isolines[type].polygons = [];
isolines[type].polygons.push(vertexChain.map(vertexId => vertices.p[vertexId]));
}
discontinued = false;
lastOperation = operation;
if (options.fill) {
if (!isolines[type].fill) isolines[type].fill = "";
isolines[type].fill += getFillPath(vertices, vertexChain);
}
return ` ${command}${getVertexPoint(vertex)}`;
});
if (options.waterGap) {
if (!isolines[type].waterGap) isolines[type].waterGap = "";
const isLandVertex = vertexId => vertices.c[vertexId].every(i => cells.h[i] >= 20);
isolines[type].waterGap += getBorderPath(vertices, vertexChain, isLandVertex);
}
return path.join("").trim();
}
function isBorderVertex(vertex) {
const adjacentCells = vertices.c[vertex];
return adjacentCells.some(i => cells.b[i]);
}
function isLandVertex(vertex) {
const adjacentCells = vertices.c[vertex];
return adjacentCells.every(i => cells.h[i] >= 20);
}
function addIsoline(index, vertexChain) {
if (!isolines[index]) isolines[index] = {polygons: [], fill: "", waterGap: "", halo: ""};
if (options.polygons) isolines[index].polygons.push(vertexChain.map(getVertexPoint));
if (options.fill) isolines[index].fill += getFillPath(vertexChain);
if (options.halo) isolines[index].halo += getBorderPath(vertexChain, isBorderVertex);
if (options.waterGap) isolines[index].waterGap += getBorderPath(vertexChain, isLandVertex);
if (options.halo) {
if (!isolines[type].halo) isolines[type].halo = "";
const isBorderVertex = vertexId => vertices.c[vertexId].some(i => cells.b[i]);
isolines[type].halo += getBorderPath(vertices, vertexChain, isBorderVertex);
}
}
}
function getVertexPoint(vertexId) {
return pack.vertices.p[vertexId];
}
function getFillPath(vertexChain) {
const points = vertexChain.map(getVertexPoint);
function getFillPath(vertices, vertexChain) {
const points = vertexChain.map(vertexId => vertices.p[vertexId]);
const firstPoint = points.shift();
return `M${firstPoint} L${points.join(" ")} Z`;
}
function getBorderPath(vertices, vertexChain, discontinue) {
let discontinued = true;
let lastOperation = "";
const path = vertexChain.map(vertexId => {
if (discontinue(vertexId)) {
discontinued = true;
return "";
}
const operation = discontinued ? "M" : "L";
const command = operation === lastOperation ? "" : operation;
discontinued = false;
lastOperation = operation;
return ` ${command}${vertices.p[vertexId]}`;
});
return path.join("").trim();
}
// get single path for an non-continuous array of cells
function getVertexPath(cellsArray) {
const {cells, vertices} = pack;
@ -116,7 +118,7 @@ function getVertexPath(cellsArray) {
const startingVertex = cells.v[cellId].find(v => vertices.c[v].some(ofDifferentType));
if (startingVertex === undefined) throw new Error(`Starting vertex for cell ${cellId} is not found`);
const vertexChain = connectVertices({startingVertex, ofSameType, addToChecked, closeRing: true});
const vertexChain = connectVertices({vertices, startingVertex, ofSameType, addToChecked, closeRing: true});
if (vertexChain.length < 3) continue;
path += getFillPath(vertexChain);
@ -125,10 +127,10 @@ function getVertexPath(cellsArray) {
return path;
}
function getPolesOfInaccessibility(getType) {
const isolines = getIsolines(getType, {polygons: true});
function getPolesOfInaccessibility(graph, getType) {
const isolines = getIsolines(graph, getType, {polygons: true});
const poles = isolines.map(([id, isoline]) => {
const poles = Object.entries(isolines).map(([id, isoline]) => {
const multiPolygon = isoline.polygons.sort((a, b) => b.length - a.length);
const [x, y] = polylabel(multiPolygon, 20);
return [id, [rn(x), rn(y)]];
@ -137,9 +139,8 @@ function getPolesOfInaccessibility(getType) {
return Object.fromEntries(poles);
}
function connectVertices({startingVertex, ofSameType, addToChecked, closeRing}) {
const vertices = pack.vertices;
const MAX_ITERATIONS = pack.cells.i.length;
function connectVertices({vertices, startingVertex, ofSameType, addToChecked, closeRing}) {
const MAX_ITERATIONS = vertices.c.length;
const chain = []; // vertices chain to form a path
let next = startingVertex;
@ -172,10 +173,3 @@ 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}" />
`;
}