sparse array implementation with reduced updates

This commit is contained in:
StempunkDev 2026-01-13 23:40:37 +01:00
parent 5600c06381
commit 21710bc426
3 changed files with 90 additions and 31 deletions

View file

@ -65,33 +65,41 @@ window.Ice = (function () {
} }
} }
// Add a new iceberg (manual editing)
function addIceberg(cellId, size) { function addIceberg(cellId, size) {
const [cx, cy] = grid.points[cellId]; const [cx, cy] = grid.points[cellId];
const points = getGridPolygon(cellId).map(([x, y]) => [ const points = getGridPolygon(cellId).map(([x, y]) => [
rn(lerp(cx, x, size), 2), rn(lerp(cx, x, size), 2),
rn(lerp(cy, y, size), 2) rn(lerp(cy, y, size), 2)
]); ]);
//here we use the lose equality to find the first undefined or empty or null slot
pack.ice.icebergs.push({ const nextIndex = pack.ice.icebergs.findIndex(iceberg => iceberg == undefined);
cellId, if (nextIndex !== -1) {
size, pack.ice.icebergs[nextIndex] = {
points cellId,
}); size,
points
return pack.ice.icebergs.length - 1; // return index };
} redrawIceberg(nextIndex);
} else {
// Remove ice element by index pack.ice.icebergs.push({
function removeIce(type, index) { cellId,
if (type === "glacier" && pack.ice.glaciers[index]) { size,
pack.ice.glaciers.splice(index, 1); points
} else if (type === "iceberg" && pack.ice.icebergs[index]) { });
pack.ice.icebergs.splice(index, 1); redrawIceberg(pack.ice.icebergs.length - 1);
}
}
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);
} }
} }
// Update iceberg points and size
function updateIceberg(index, points, size) { function updateIceberg(index, points, size) {
if (pack.ice.icebergs[index]) { if (pack.ice.icebergs[index]) {
pack.ice.icebergs[index].points = points; pack.ice.icebergs[index].points = points;
@ -99,7 +107,6 @@ window.Ice = (function () {
} }
} }
// Randomize iceberg shape
function randomizeIcebergShape(index) { function randomizeIcebergShape(index) {
const iceberg = pack.ice.icebergs[index]; const iceberg = pack.ice.icebergs[index];
if (!iceberg) return; if (!iceberg) return;
@ -120,7 +127,6 @@ window.Ice = (function () {
iceberg.points = points; iceberg.points = points;
} }
// Change iceberg size and recalculate points
function changeIcebergSize(index, newSize) { function changeIcebergSize(index, newSize) {
const iceberg = pack.ice.icebergs[index]; const iceberg = pack.ice.icebergs[index];
if (!iceberg) return; if (!iceberg) return;
@ -129,7 +135,6 @@ window.Ice = (function () {
const [cx, cy] = grid.points[cellId]; const [cx, cy] = grid.points[cellId];
const oldSize = iceberg.size; const oldSize = iceberg.size;
// Recalculate points based on new size
const flat = iceberg.points.flat(); const flat = iceberg.points.flat();
const pairs = []; const pairs = [];
while (flat.length) pairs.push(flat.splice(0, 2)); while (flat.length) pairs.push(flat.splice(0, 2));
@ -143,7 +148,6 @@ window.Ice = (function () {
iceberg.size = newSize; iceberg.size = newSize;
} }
// Get all ice data
function getData() { function getData() {
return pack.ice; return pack.ice;
} }

View file

@ -11,17 +11,73 @@ function drawIce() {
// Draw glaciers // Draw glaciers
pack.ice.glaciers.forEach((glacier, index) => { pack.ice.glaciers.forEach((glacier, index) => {
html += `<polygon points="${glacier.points}" type="iceShield" data-index="${index}" ${glacier.offset ? `transform="translate(${glacier.offset[0]},${glacier.offset[1]})"` : ""} class="glacier"/>`; html += getGlacierHtml(glacier, index);
}); });
// Draw icebergs // Draw icebergs
pack.ice.icebergs.forEach((iceberg, index) => { pack.ice.icebergs.forEach((iceberg, index) => {
html += `<polygon points="${iceberg.points}" cell="${iceberg.cellId}" size="${iceberg.size}" data-index="${index}" ${iceberg.offset ? `transform="translate(${iceberg.offset[0]},${iceberg.offset[1]})"` : ""} class="iceberg"/>`; html += getIcebergHtml(iceberg, index);
}); });
ice.html(html); ice.html(html);
TIME && console.timeEnd("drawIce"); TIME && console.timeEnd("drawIce");
function getGlacierHtml(glacier, index) {
return `<polygon points="${glacier.points}" type="iceShield" data-index="${index}" ${glacier.offset ? `transform="translate(${glacier.offset[0]},${glacier.offset[1]})"` : ""} class="glacier"/>`;
}
function getIcebergHtml(iceberg, index) {
return `<polygon points="${iceberg.points}" cell="${iceberg.cellId}" size="${iceberg.size}" data-index="${index}" ${iceberg.offset ? `transform="translate(${iceberg.offset[0]},${iceberg.offset[1]})"` : ""} class="iceberg"/>`;
}
}
function redrawIceberg(index) {
TIME && console.time("redrawIceberg");
const iceberg = pack.ice.icebergs[index];
let el = ice.selectAll(`.iceberg[data-index="${index}"]`);
if (!iceberg && !el.empty()) {
el.remove();
} else {
if (el.empty()) {
// Create new element if it doesn't exist
const polygon = getIcebergHtml(iceberg, index);
ice.node().insertAdjacentHTML("beforeend", polygon);
el = ice.selectAll(`.iceberg[data-index="${index}"]`);
}
el.attr("points", iceberg.points);
el.attr("size", iceberg.size);
el.attr("cell", iceberg.cellId);
el.attr("transform", iceberg.offset ? `translate(${iceberg.offset[0]},${iceberg.offset[1]})` : null);
}
TIME && console.timeEnd("redrawIceberg");
function getIcebergHtml(iceberg, index) {
return `<polygon points="${iceberg.points}" cell="${iceberg.cellId}" size="${iceberg.size}" data-index="${index}" ${iceberg.offset ? `transform="translate(${iceberg.offset[0]},${iceberg.offset[1]})"` : ""} class="iceberg"/>`;
}
}
function redrawGlacier(index) {
TIME && console.time("redrawGlacier");
const glacier = pack.ice.glaciers[index];
let el = ice.selectAll(`.glacier[data-index="${index}"]`);
if (!glacier && !el.empty()) {
el.remove();
} else {
if (el.empty()) {
// Create new element if it doesn't exist
const polygon = getGlacierHtml(glacier, index);
ice.node().insertAdjacentHTML("beforeend", polygon);
el = ice.selectAll(`.glacier[data-index="${index}"]`);
}
el.attr("points", glacier.points);
el.attr("transform", glacier.offset ? `translate(${glacier.offset[0]},${glacier.offset[1]})` : null);
}
TIME && console.timeEnd("redrawGlacier");
function getGlacierHtml(glacier, index) {
return `<polygon points="${glacier.points}" type="iceShield" data-index="${index}" ${glacier.offset ? `transform="translate(${glacier.offset[0]},${glacier.offset[1]})"` : ""} class="glacier"/>`;
}
} }
// Re-render ice layer from data model // Re-render ice layer from data model

View file

@ -23,7 +23,7 @@ function editIce() {
}); });
if (!modules.editIce) { if (!modules.editIce) {
modules.editIce = {currentIndex: index}; modules.editIce = {currentIndex: index, isGlacier: isGlacier};
// add listeners // add listeners
document.getElementById("iceEditStyle").addEventListener("click", () => editStyle("ice")); document.getElementById("iceEditStyle").addEventListener("click", () => editStyle("ice"));
document.getElementById("iceRandomize").addEventListener("click", randomizeShape); document.getElementById("iceRandomize").addEventListener("click", randomizeShape);
@ -32,19 +32,20 @@ function editIce() {
document.getElementById("iceRemove").addEventListener("click", removeIce); document.getElementById("iceRemove").addEventListener("click", removeIce);
} }
modules.editIce.currentIndex = index; modules.editIce.currentIndex = index;
modules.editIce.isGlacier = isGlacier;
function randomizeShape() { function randomizeShape() {
const idx = modules.editIce.currentIndex; const idx = modules.editIce.currentIndex;
Ice.randomizeIcebergShape(idx); Ice.randomizeIcebergShape(idx);
redrawIce(); redrawIceberg(idx);
} }
function changeSize() { function changeSize() {
const newSize = +this.value; const newSize = +this.value;
const idx = modules.editIce.currentIndex; const idx = modules.editIce.currentIndex;
Ice.changeIcebergSize(idx, newSize); Ice.changeIcebergSize(idx, newSize);
redrawIce(); redrawIceberg(idx);
} }
function toggleAdd() { function toggleAdd() {
@ -64,13 +65,12 @@ function editIce() {
const size = +document.getElementById("iceSize")?.value || 1; const size = +document.getElementById("iceSize")?.value || 1;
Ice.addIceberg(i, size); Ice.addIceberg(i, size);
redrawIce();
if (d3.event.shiftKey === false) toggleAdd(); if (d3.event.shiftKey === false) toggleAdd();
} }
function removeIce() { function removeIce() {
const type = isGlacier ? "Glacier" : "Iceberg"; const type = modules.editIce.isGlacier ? "Glacier" : "Iceberg";
alertMessage.innerHTML = /* html */ `Are you sure you want to remove the ${type}?`; alertMessage.innerHTML = /* html */ `Are you sure you want to remove the ${type}?`;
$("#alert").dialog({ $("#alert").dialog({
resizable: false, resizable: false,
@ -78,8 +78,7 @@ function editIce() {
buttons: { buttons: {
Remove: function () { Remove: function () {
$(this).dialog("close"); $(this).dialog("close");
Ice.removeIce(isGlacier ? "glacier" : "iceberg", index); Ice.removeIce(type.toLowerCase(), modules.editIce.currentIndex);
redrawIce();
$("#iceEditor").dialog("close"); $("#iceEditor").dialog("close");
}, },
Cancel: function () { Cancel: function () {