diff --git a/public/modules/dynamic/auto-update.js b/public/modules/dynamic/auto-update.js index 8e3ef5bc..060bb2d0 100644 --- a/public/modules/dynamic/auto-update.js +++ b/public/modules/dynamic/auto-update.js @@ -1041,7 +1041,8 @@ export function resolveVersionConflicts(mapVersion) { // v1.110.0 moved ice data from SVG to data model // Migrate old ice SVG elements to new pack.ice structure if (!pack.ice) { - pack.ice = { glaciers: [], icebergs: [] }; + pack.ice = []; + let iceId = 0; const iceLayer = document.getElementById("ice"); if (iceLayer) { @@ -1051,15 +1052,15 @@ export function resolveVersionConflicts(mapVersion) { const points = [...polygon.points].map(svgPoint => [svgPoint.x, svgPoint.y]); const transform = polygon.getAttribute("transform"); - - if (transform) { - pack.ice.glaciers.push({ + const iceElement = { + i: iceId++, points, - offset: parseTransform(transform) - }); - } else { - pack.ice.glaciers.push({ points }); + type: "glacier" + }; + if (transform) { + iceElement.offset = parseTransform(transform); } + pack.ice.push(iceElement); }); // Migrate icebergs @@ -1074,21 +1075,17 @@ export function resolveVersionConflicts(mapVersion) { const points = [...polygon.points].map(svgPoint => [svgPoint.x, svgPoint.y]); const transform = polygon.getAttribute("transform"); + const iceElement = { + i: iceId++, + points, + type: "iceberg", + cellId, + size + }; if (transform) { - pack.ice.icebergs.push({ - cellId, - size, - points, - offset: parseTransform(transform) - }); - } else { - pack.ice.icebergs.push({ - cellId, - size, - points - }); + iceElement.offset = parseTransform(transform); } - + pack.ice.push(iceElement); }); // Clear old SVG elements diff --git a/public/modules/ice.js b/public/modules/ice.js index d3d7ab36..e1e86b8e 100644 --- a/public/modules/ice.js +++ b/public/modules/ice.js @@ -2,17 +2,16 @@ // Ice layer data model - separates ice data from SVG rendering window.Ice = (function () { - // Initialize ice data structure - function initialize() { - pack.ice = { - glaciers: [], // auto-generated glaciers on cold land - icebergs: [] // manually edited and auto-generated icebergs on cold water - }; + + // Find next available id for new ice element + function getNextId() { + if (pack.ice.length === 0) return 0; + return Math.max(...pack.ice.map(element => element.i)) + 1; } // Generate glaciers and icebergs based on temperature and height function generate() { - initialize(); + clear(); const { cells, features } = grid; const { temp, h } = cells; Math.random = aleaPRNG(seed); @@ -31,8 +30,10 @@ window.Ice = (function () { if (isolines[type]?.polygons) { isolines[type].polygons.forEach(points => { const clipped = clipPoly(points); - pack.ice.glaciers.push({ - points: clipped + pack.ice.push({ + i: getNextId(), + points: clipped, + type: "glacier" }); }); } @@ -57,10 +58,12 @@ window.Ice = (function () { rn(lerp(cy, y, size), 2) ]); - pack.ice.icebergs.push({ + pack.ice.push({ + i: getNextId(), + points, + type: "iceberg", cellId, - size, - points + size }); } } @@ -71,44 +74,41 @@ window.Ice = (function () { rn(lerp(cx, x, size), 2), rn(lerp(cy, y, size), 2) ]); - //here we use the lose equality to find the first undefined or empty or null slot - const nextIndex = pack.ice.icebergs.findIndex(iceberg => iceberg == undefined); - if (nextIndex !== -1) { - pack.ice.icebergs[nextIndex] = { - cellId, - size, - points - }; - redrawIceberg(nextIndex); - } else { - pack.ice.icebergs.push({ - cellId, - size, - points - }); - redrawIceberg(pack.ice.icebergs.length - 1); + const id = getNextId(); + pack.ice.push({ + i: id, + points, + type: "iceberg", + cellId, + size + }); + redrawIceberg(id); + } + + function removeIce(id) { + const index = pack.ice.findIndex(element => element.i === id); + if (index !== -1) { + const type = pack.ice.find(element => element.i === id).type; + pack.ice.splice(index, 1); + if (type === "glacier") { + redrawGlacier(id); + } else { + redrawIceberg(id); + } + } } - function removeIce(type, index) { - if (type === "glacier" && pack.ice.glaciers[index]) { - delete pack.ice.glaciers[index]; - redrawGlacier(index); - } else if (type === "iceberg" && pack.ice.icebergs[index]) { - delete pack.ice.icebergs[index]; - redrawIceberg(index); + function updateIceberg(id, points, size) { + const iceberg = pack.ice.find(element => element.i === id); + if (iceberg) { + iceberg.points = points; + iceberg.size = size; } } - function updateIceberg(index, points, size) { - if (pack.ice.icebergs[index]) { - pack.ice.icebergs[index].points = points; - pack.ice.icebergs[index].size = size; - } - } - - function randomizeIcebergShape(index) { - const iceberg = pack.ice.icebergs[index]; + function randomizeIcebergShape(id) { + const iceberg = pack.ice.find(element => element.i === id); if (!iceberg) return; const cellId = iceberg.cellId; @@ -127,8 +127,8 @@ window.Ice = (function () { iceberg.points = points; } - function changeIcebergSize(index, newSize) { - const iceberg = pack.ice.icebergs[index]; + function changeIcebergSize(id, newSize) { + const iceberg = pack.ice.find(element => element.i === id); if (!iceberg) return; const cellId = iceberg.cellId; @@ -150,12 +150,10 @@ window.Ice = (function () { // Clear all ice function clear() { - pack.ice.glaciers = []; - pack.ice.icebergs = []; + pack.ice = []; } return { - initialize, generate, addIceberg, removeIce, diff --git a/public/modules/io/load.js b/public/modules/io/load.js index 371547ec..9b401733 100644 --- a/public/modules/io/load.js +++ b/public/modules/io/load.js @@ -406,7 +406,7 @@ async function parseLoadedData(data, mapVersion) { pack.cells.province = data[27] ? Uint16Array.from(data[27].split(",")) : new Uint16Array(pack.cells.i.length); // data[28] had deprecated cells.crossroad pack.cells.routes = data[36] ? JSON.parse(data[36]) : {}; - pack.ice = data[39] ? JSON.parse(data[39]) : {glaciers: [], icebergs: []}; + pack.ice = data[39] ? JSON.parse(data[39]) : []; if (data[31]) { const namesDL = data[31].split("/"); diff --git a/public/modules/io/save.js b/public/modules/io/save.js index 33ded975..25cd7493 100644 --- a/public/modules/io/save.js +++ b/public/modules/io/save.js @@ -32,7 +32,7 @@ async function saveMap(method) { $(this).dialog("close"); } }, - position: {my: "center", at: "center", of: "svg"} + position: { my: "center", at: "center", of: "svg" } }); } } @@ -90,8 +90,8 @@ function prepareMapData() { const serializedSVG = new XMLSerializer().serializeToString(cloneEl); - const {spacing, cellsX, cellsY, boundary, points, features, cellsDesired} = grid; - const gridGeneral = JSON.stringify({spacing, cellsX, cellsY, boundary, points, features, cellsDesired}); + const { spacing, cellsX, cellsY, boundary, points, features, cellsDesired } = grid; + const gridGeneral = JSON.stringify({ spacing, cellsX, cellsY, boundary, points, features, cellsDesired }); const packFeatures = JSON.stringify(pack.features); const cultures = JSON.stringify(pack.cultures); const states = JSON.stringify(pack.states); @@ -103,10 +103,7 @@ function prepareMapData() { const cellRoutes = JSON.stringify(pack.cells.routes); const routes = JSON.stringify(pack.routes); const zones = JSON.stringify(pack.zones); - - const icebergs = pack.ice.icebergs.filter(iceberg => iceberg !== undefined); - const glaciers = pack.ice.glaciers.filter(glacier => glacier !== undefined); - const ice = JSON.stringify({icebergs, glaciers}); + const ice = JSON.stringify(pack.ice); // store name array only if not the same as default const defaultNB = Names.getNameBases(); @@ -168,14 +165,14 @@ function prepareMapData() { // save map file to indexedDB async function saveToStorage(mapData, showTip = false) { - const blob = new Blob([mapData], {type: "text/plain"}); + const blob = new Blob([mapData], { type: "text/plain" }); await ldb.set("lastMap", blob); showTip && tip("Map is saved to the browser storage", false, "success"); } // download map file function saveToMachine(mapData, filename) { - const blob = new Blob([mapData], {type: "text/plain"}); + const blob = new Blob([mapData], { type: "text/plain" }); const URL = window.URL.createObjectURL(blob); const link = document.createElement("a"); diff --git a/public/modules/renderers/draw-ice.js b/public/modules/renderers/draw-ice.js index ede5d8fc..4b35f75c 100644 --- a/public/modules/renderers/draw-ice.js +++ b/public/modules/renderers/draw-ice.js @@ -9,14 +9,13 @@ function drawIce() { let html = ""; - // Draw glaciers - pack.ice.glaciers.forEach((glacier, index) => { - html += getGlacierHtml(glacier, index); - }); - - // Draw icebergs - pack.ice.icebergs.forEach((iceberg, index) => { - html += getIcebergHtml(iceberg, index); + // Draw all ice elements + pack.ice.forEach(iceElement => { + if (iceElement.type === "glacier") { + html += getGlacierHtml(iceElement); + } else if (iceElement.type === "iceberg") { + html += getIcebergHtml(iceElement); + } }); ice.html(html); @@ -24,18 +23,18 @@ function drawIce() { TIME && console.timeEnd("drawIce"); } -function redrawIceberg(index) { +function redrawIceberg(id) { TIME && console.time("redrawIceberg"); - const iceberg = pack.ice.icebergs[index]; - let el = ice.selectAll(`polygon[data-index="${index}"]:not([type="glacier"])`); + const iceberg = pack.ice.find(element => element.i === id); + let el = ice.selectAll(`polygon[data-id="${id}"]:not([type="glacier"])`); if (!iceberg && !el.empty()) { el.remove(); } else { if (el.empty()) { // Create new element if it doesn't exist - const polygon = getIcebergHtml(iceberg, index); + const polygon = getIcebergHtml(iceberg); ice.node().insertAdjacentHTML("beforeend", polygon); - el = ice.selectAll(`polygon[data-index="${index}"]:not([type="glacier"])`); + el = ice.selectAll(`polygon[data-id="${id}"]:not([type="glacier"])`); } el.attr("points", iceberg.points); el.attr("transform", iceberg.offset ? `translate(${iceberg.offset[0]},${iceberg.offset[1]})` : null); @@ -43,18 +42,18 @@ function redrawIceberg(index) { TIME && console.timeEnd("redrawIceberg"); } -function redrawGlacier(index) { +function redrawGlacier(id) { TIME && console.time("redrawGlacier"); - const glacier = pack.ice.glaciers[index]; - let el = ice.selectAll(`polygon[data-index="${index}"][type="glacier"]`); + const glacier = pack.ice.find(element => element.i === id); + let el = ice.selectAll(`polygon[data-id="${id}"][type="glacier"]`); if (!glacier && !el.empty()) { el.remove(); } else { if (el.empty()) { // Create new element if it doesn't exist - const polygon = getGlacierHtml(glacier, index); + const polygon = getGlacierHtml(glacier); ice.node().insertAdjacentHTML("beforeend", polygon); - el = ice.selectAll(`polygon[data-index="${index}"][type="glacier"]`); + el = ice.selectAll(`polygon[data-id="${id}"][type="glacier"]`); } el.attr("points", glacier.points); el.attr("transform", glacier.offset ? `translate(${glacier.offset[0]},${glacier.offset[1]})` : null); @@ -62,10 +61,10 @@ function redrawGlacier(index) { TIME && console.timeEnd("redrawGlacier"); } -function getGlacierHtml(glacier, index) { - return ``; +function getGlacierHtml(glacier) { + return ``; } -function getIcebergHtml(iceberg, index) { - return ``; +function getIcebergHtml(iceberg) { + return ``; } \ No newline at end of file diff --git a/public/modules/ui/ice-editor.js b/public/modules/ui/ice-editor.js index 1a23e304..16818b4c 100644 --- a/public/modules/ui/ice-editor.js +++ b/public/modules/ui/ice-editor.js @@ -7,13 +7,14 @@ function editIce(element) { if (!layerIsOn("toggleIce")) toggleIce(); elSelected = d3.select(d3.event.target); - const index = +elSelected.attr("data-index"); + const id = +elSelected.attr("data-id"); + const iceElement = pack.ice.find(el => el.i === id); const isGlacier = elSelected.attr("type") === "glacier"; const type = isGlacier ? "Glacier" : "Iceberg"; document.getElementById("iceRandomize").style.display = isGlacier ? "none" : "inline-block"; document.getElementById("iceSize").style.display = isGlacier ? "none" : "inline-block"; - if (!isGlacier) document.getElementById("iceSize").value = isGlacier ? "" : pack.ice.icebergs[index].size; + if (!isGlacier) document.getElementById("iceSize").value = iceElement?.size || ""; ice.selectAll("*").classed("draggable", true).call(d3.drag().on("drag", dragElement)); @@ -35,16 +36,16 @@ function editIce(element) { function randomizeShape() { - const idx = +elSelected.attr("data-index"); - Ice.randomizeIcebergShape(idx); - redrawIceberg(idx); + const selectedId = +elSelected.attr("data-id"); + Ice.randomizeIcebergShape(selectedId); + redrawIceberg(selectedId); } function changeSize() { const newSize = +this.value; - const idx = +elSelected.attr("data-index"); - Ice.changeIcebergSize(idx, newSize); - redrawIceberg(idx); + const selectedId = +elSelected.attr("data-id"); + Ice.changeIcebergSize(selectedId, newSize); + redrawIceberg(selectedId); } function toggleAdd() { @@ -77,7 +78,7 @@ function editIce(element) { buttons: { Remove: function () { $(this).dialog("close"); - Ice.removeIce(type.toLowerCase(), +elSelected.attr("data-index")); + Ice.removeIce(+elSelected.attr("data-id")); $("#iceEditor").dialog("close"); }, Cancel: function () { @@ -88,7 +89,7 @@ function editIce(element) { } function dragElement() { - const idx = +elSelected.attr("data-index"); + const selectedId = +elSelected.attr("data-id"); const initialTransform = parseTransform(this.getAttribute("transform")); const dx = initialTransform[0] - d3.event.x; const dy = initialTransform[1] - d3.event.y; @@ -101,7 +102,7 @@ function editIce(element) { // Update data model with new position const offset = [dx + x, dy + y]; - const iceData = elSelected.attr("type") === "glacier" ? pack.ice.glaciers[idx] : pack.ice.icebergs[idx]; + const iceData = pack.ice.find(element => element.i === selectedId); if (iceData) { // Store offset for visual positioning, actual geometry stays in points iceData.offset = offset;