zone editor - filter by type

This commit is contained in:
Azgaar 2022-02-07 00:05:04 +03:00
parent 5acc3b156b
commit f8d2719218
2 changed files with 49 additions and 32 deletions

View file

@ -177,7 +177,7 @@
<div id="loading"> <div id="loading">
<div id="titleName"><t data-t="titleName">Azgaar's</t></div> <div id="titleName"><t data-t="titleName">Azgaar's</t></div>
<div id="title"><t data-t="title">Fantasy Map Generator</t></div> <div id="title"><t data-t="title">Fantasy Map Generator</t></div>
<div id="version"><t data-t="version">v. </t>1.72</div> <div id="version"><t data-t="version">v. </t>1.73</div>
<p id="loading-text"><t data-t="loading">LOADING</t><span>.</span><span>.</span><span>.</span></p> <p id="loading-text"><t data-t="loading">LOADING</t><span>.</span><span>.</span><span>.</span></p>
</div> </div>
@ -2871,7 +2871,7 @@
<div id="zonesEditor" class="dialog stable" style="display: none"> <div id="zonesEditor" class="dialog stable" style="display: none">
<div id="customHeader" class="header"> <div id="customHeader" class="header">
<div style="left:1.8em" data-tip="Zone description">Description&nbsp;</div> <div style="left:1.8em" data-tip="Zone description">Description&nbsp;</div>
<div style="left:13em" data-tip="Zone type" class="hide">Type&nbsp;</div> <div style="left:13em" data-tip="Zone type">Type&nbsp;</div>
<div style="left:19em" data-tip="Zone cells count" class="hide">Cells&nbsp;</div> <div style="left:19em" data-tip="Zone cells count" class="hide">Cells&nbsp;</div>
<div style="left:23.6em" data-tip="Zone area" class="hide">Area&nbsp;</div> <div style="left:23.6em" data-tip="Zone area" class="hide">Area&nbsp;</div>
<div style="left:30.6em" data-tip="Zone population" class="hide">Population&nbsp;</div> <div style="left:30.6em" data-tip="Zone population" class="hide">Population&nbsp;</div>
@ -2879,11 +2879,6 @@
<div id="zonesBodySection" class="table" data-type="absolute"></div> <div id="zonesBodySection" class="table" data-type="absolute"></div>
<div id="zonesFilters" data-tip="Show only zones of selected type">
Type:
<select id="zonesFilterType"></select>
</div>
<div id="zonesFooter" class="totalLine"> <div id="zonesFooter" class="totalLine">
<div data-tip="Number of zones" style="margin-left: 5px">Zones:&nbsp;<span id="zonesFooterNumber">0</span></div> <div data-tip="Number of zones" style="margin-left: 5px">Zones:&nbsp;<span id="zonesFooterNumber">0</span></div>
<div data-tip="Total cells number" style="margin-left: 12px">Cells:&nbsp;<span id="zonesFooterCells">0</span></div> <div data-tip="Total cells number" style="margin-left: 12px">Cells:&nbsp;<span id="zonesFooterCells">0</span></div>
@ -2900,8 +2895,8 @@
<button id="zonesManually" data-tip="Re-assign zones" class="icon-brush"></button> <button id="zonesManually" data-tip="Re-assign zones" class="icon-brush"></button>
<div id="zonesManuallyButtons" style="display: none"> <div id="zonesManuallyButtons" style="display: none">
<label data-tip="Change brush size. Shortcut: + (increase), (decrease)" class="italic">Brush: <label data-tip="Change brush size. Shortcut: + (increase), (decrease)" class="italic">Brush:
<input id="zonesBrush" oninput="tip('Brush size: '+this.value); zonesBrushNumber.value = this.value" type="range" min=5 max=25 value=7 style="width:7em"> <input id="zonesBrush" oninput="tip('Brush size: '+this.value); zonesBrushNumber.value = this.value" type="range" min=5 max=50 value=7 style="width:9em">
<input id="zonesBrushNumber" oninput="tip('Brush size: '+this.value); zonesBrush.value = this.value" type="number" min=5 max=25 value=7> <input id="zonesBrushNumber" oninput="tip('Brush size: '+this.value); zonesBrush.value = this.value" type="number" min=5 max=50 value=7>
</label><br> </label><br>
<button id="zonesManuallyApply" data-tip="Apply assignment" class="icon-check"></button> <button id="zonesManuallyApply" data-tip="Apply assignment" class="icon-check"></button>
<button id="zonesManuallyCancel" data-tip="Cancel assignment" class="icon-cancel"></button> <button id="zonesManuallyCancel" data-tip="Cancel assignment" class="icon-cancel"></button>
@ -2910,6 +2905,8 @@
<button id="zonesAdd" data-tip="Add new zone layer" class="icon-plus"></button> <button id="zonesAdd" data-tip="Add new zone layer" class="icon-plus"></button>
<button id="zonesExport" data-tip="Download zones-related data" class="icon-download"></button> <button id="zonesExport" data-tip="Download zones-related data" class="icon-download"></button>
<div id="zonesFilters" data-tip="Show only zones of selected type" style="display: inline-block">Type: <select id="zonesFilterType"></select></div>
</div> </div>
</div> </div>

View file

@ -4,6 +4,8 @@ function editZones() {
closeDialogs(); closeDialogs();
if (!layerIsOn("toggleZones")) toggleZones(); if (!layerIsOn("toggleZones")) toggleZones();
const body = document.getElementById("zonesBodySection"); const body = document.getElementById("zonesBodySection");
updateFilters();
zonesEditorAddLines(); zonesEditorAddLines();
if (modules.editZones) return; if (modules.editZones) return;
@ -18,7 +20,8 @@ function editZones() {
}); });
// add listeners // add listeners
document.getElementById("zonesFilterType").addEventListener("change", filterZonesByType); document.getElementById("zonesFilterType").addEventListener("click", updateFilters);
document.getElementById("zonesFilterType").addEventListener("change", zonesEditorAddLines);
document.getElementById("zonesEditorRefresh").addEventListener("click", zonesEditorAddLines); document.getElementById("zonesEditorRefresh").addEventListener("click", zonesEditorAddLines);
document.getElementById("zonesEditStyle").addEventListener("click", () => editStyle("zones")); document.getElementById("zonesEditStyle").addEventListener("click", () => editStyle("zones"));
document.getElementById("zonesLegend").addEventListener("click", toggleLegend); document.getElementById("zonesLegend").addEventListener("click", toggleLegend);
@ -39,39 +42,55 @@ function editZones() {
else if (cl.contains("icon-trash-empty")) zoneRemove(zone); else if (cl.contains("icon-trash-empty")) zoneRemove(zone);
else if (cl.contains("icon-eye")) toggleVisibility(el); else if (cl.contains("icon-eye")) toggleVisibility(el);
else if (cl.contains("icon-pin")) toggleFog(zone, cl); else if (cl.contains("icon-pin")) toggleFog(zone, cl);
if (customization) selectZone(el); if (customization) selectZone(el);
}); });
body.addEventListener("input", function (ev) { body.addEventListener("input", function (ev) {
const el = ev.target, const el = ev.target;
zone = el.parentNode.dataset.id; const zone = zones.select("#" + el.parentNode.dataset.id);
if (el.classList.contains("religionName")) zones.select("#" + zone).attr("data-description", el.value);
if (el.classList.contains("zoneName")) zone.attr("data-description", el.value);
else if (el.classList.contains("zoneType")) zone.attr("data-type", el.value);
}); });
// update type filter with a list of used types
function updateFilters() {
const zones = Array.from(document.querySelectorAll("#zones > g"));
const types = unique(zones.map(zone => zone.dataset.type));
const filterSelect = document.getElementById("zonesFilterType");
const typeToFilterBy = types.includes(zonesFilterType.value) ? zonesFilterType.value : "all";
filterSelect.innerHTML = "<option value='all'>all</option>" + types.map(type => `<option value="${type}">${type}</option>`).join("");
filterSelect.value = typeToFilterBy;
}
// add line for each zone // add line for each zone
function zonesEditorAddLines() { function zonesEditorAddLines() {
const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value; const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value;
let lines = "";
zones.selectAll("g").each(function () { const typeToFilterBy = document.getElementById("zonesFilterType").value;
const c = this.dataset.cells ? this.dataset.cells.split(",").map(c => +c) : []; const zones = Array.from(document.querySelectorAll("#zones > g"));
const description = this.dataset.description; const filteredZones = typeToFilterBy === "all" ? zones : zones.filter(zone => zone.dataset.type === typeToFilterBy);
const type = this.dataset.type;
const fill = this.getAttribute("fill"); const lines = filteredZones.map(zoneEl => {
const c = zoneEl.dataset.cells ? zoneEl.dataset.cells.split(",").map(c => +c) : [];
const description = zoneEl.dataset.description;
const type = zoneEl.dataset.type;
const fill = zoneEl.getAttribute("fill");
const area = d3.sum(c.map(i => pack.cells.area[i])) * distanceScaleInput.value ** 2; const area = d3.sum(c.map(i => pack.cells.area[i])) * distanceScaleInput.value ** 2;
const rural = d3.sum(c.map(i => pack.cells.pop[i])) * populationRate; const rural = d3.sum(c.map(i => pack.cells.pop[i])) * populationRate;
const urban = d3.sum(c.map(i => pack.cells.burg[i]).map(b => pack.burgs[b].population)) * populationRate * urbanization; const urban = d3.sum(c.map(i => pack.cells.burg[i]).map(b => pack.burgs[b].population)) * populationRate * urbanization;
const population = rural + urban; const population = rural + urban;
const populationTip = `Total population: ${si(population)}; Rural population: ${si(rural)}; Urban population: ${si(urban)}. Click to change`; const populationTip = `Total population: ${si(population)}; Rural population: ${si(rural)}; Urban population: ${si(urban)}. Click to change`;
const inactive = this.style.display === "none"; const inactive = zoneEl.style.display === "none";
const focused = defs.select("#fog #focus" + this.id).size(); const focused = defs.select("#fog #focus" + zoneEl.id).size();
lines += `<div class="states" data-id="${this.id}" data-fill="${fill}" data-description="${description}" return `<div class="states" data-id="${zoneEl.id}" data-fill="${fill}" data-description="${description}"
data-cells=${c.length} data-area=${area} data-population=${population}> data-cells=${c.length} data-area=${area} data-population=${population}>
<fill-box fill="${fill}"></fill-box> <fill-box fill="${fill}"></fill-box>
<input data-tip="Zone description. Click and type to change" class="religionName" value="${description}" autocorrect="off" spellcheck="false"> <input data-tip="Zone description. Click and type to change" style="width: 11em" class="zoneName" value="${description}" autocorrect="off" spellcheck="false">
<input data-tip="Zone type. Click and type to change" value="${type}"> <input data-tip="Zone type. Click and type to change" class="zoneType" value="${type}">
<span data-tip="Cells count" class="icon-check-empty hide"></span> <span data-tip="Cells count" class="icon-check-empty hide"></span>
<div data-tip="Cells count" class="stateCells hide">${c.length}</div> <div data-tip="Cells count" class="stateCells hide">${c.length}</div>
<span data-tip="Zone area" style="padding-right:4px" class="icon-map-o hide"></span> <span data-tip="Zone area" style="padding-right:4px" class="icon-map-o hide"></span>
@ -85,13 +104,13 @@ function editZones() {
</div>`; </div>`;
}); });
body.innerHTML = lines; body.innerHTML = lines.join("");
// update footer // update footer
const totalArea = (zonesFooterArea.dataset.area = graphWidth * graphHeight * distanceScaleInput.value ** 2); const totalArea = (zonesFooterArea.dataset.area = graphWidth * graphHeight * distanceScaleInput.value ** 2);
const totalPop = (d3.sum(pack.cells.pop) + d3.sum(pack.burgs.filter(b => !b.removed).map(b => b.population)) * urbanization) * populationRate; const totalPop = (d3.sum(pack.cells.pop) + d3.sum(pack.burgs.filter(b => !b.removed).map(b => b.population)) * urbanization) * populationRate;
zonesFooterPopulation.dataset.population = totalPop; zonesFooterPopulation.dataset.population = totalPop;
zonesFooterNumber.innerHTML = zones.selectAll("g").size(); zonesFooterNumber.innerHTML = `${filteredZones.length} of ${zones.length}`;
zonesFooterCells.innerHTML = pack.cells.i.length; zonesFooterCells.innerHTML = pack.cells.i.length;
zonesFooterArea.innerHTML = si(totalArea) + unit; zonesFooterArea.innerHTML = si(totalArea) + unit;
zonesFooterPopulation.innerHTML = si(totalPop); zonesFooterPopulation.innerHTML = si(totalPop);
@ -107,8 +126,6 @@ function editZones() {
$("#zonesEditor").dialog({width: fitContent()}); $("#zonesEditor").dialog({width: fitContent()});
} }
function filterZonesByType() {}
function zoneHighlightOn(event) { function zoneHighlightOn(event) {
const zone = event.target.dataset.id; const zone = event.target.dataset.id;
zones.select("#" + zone).style("outline", "1px solid red"); zones.select("#" + zone).style("outline", "1px solid red");
@ -134,7 +151,7 @@ function editZones() {
function enterZonesManualAssignent() { function enterZonesManualAssignent() {
if (!layerIsOn("toggleZones")) toggleZones(); if (!layerIsOn("toggleZones")) toggleZones();
customization = 10; customization = 10;
document.querySelectorAll("#zonesBottom > button").forEach(el => (el.style.display = "none")); document.querySelectorAll("#zonesBottom > *").forEach(el => (el.style.display = "none"));
document.getElementById("zonesManuallyButtons").style.display = "inline-block"; document.getElementById("zonesManuallyButtons").style.display = "inline-block";
zonesEditor.querySelectorAll(".hide").forEach(el => el.classList.add("hidden")); zonesEditor.querySelectorAll(".hide").forEach(el => el.classList.add("hidden"));
@ -336,13 +353,15 @@ function editZones() {
function addZonesLayer() { function addZonesLayer() {
const id = getNextId("zone"); const id = getNextId("zone");
const description = "Unknown zone"; const description = "Unknown zone";
const type = "Unknown";
const fill = "url(#hatch" + (id.slice(4) % 42) + ")"; const fill = "url(#hatch" + (id.slice(4) % 42) + ")";
zones.append("g").attr("id", id).attr("data-description", description).attr("data-cells", "").attr("fill", fill); zones.append("g").attr("id", id).attr("data-description", description).attr("data-cells", "").attr("fill", fill);
const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value; const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value;
const line = `<div class="states" data-id="${id}" data-fill="${fill}" data-description="${description}" data-cells=0 data-area=0 data-population=0> const line = `<div class="states" data-id="${id}" data-fill="${fill}" data-description="${description}" data-cells=0 data-area=0 data-population=0>
<fill-box fill="${fill}"></fill-box> <fill-box fill="${fill}"></fill-box>
<input data-tip="Zone description. Click and type to change" class="religionName" value="${description}" autocorrect="off" spellcheck="false"> <input data-tip="Zone description. Click and type to change" style="width: 11em" class="zoneName" value="${description}" autocorrect="off" spellcheck="false">
<input data-tip="Zone type. Click and type to change" value="${type}">
<span data-tip="Cells count" class="icon-check-empty hide"></span> <span data-tip="Cells count" class="icon-check-empty hide"></span>
<div data-tip="Cells count" class="stateCells hide">0</div> <div data-tip="Cells count" class="stateCells hide">0</div>
<span data-tip="Zone area" style="padding-right:4px" class="icon-map-o hide"></span> <span data-tip="Zone area" style="padding-right:4px" class="icon-map-o hide"></span>
@ -361,12 +380,13 @@ function editZones() {
function downloadZonesData() { function downloadZonesData() {
const unit = areaUnit.value === "square" ? distanceUnitInput.value + "2" : areaUnit.value; const unit = areaUnit.value === "square" ? distanceUnitInput.value + "2" : areaUnit.value;
let data = "Id,Fill,Description,Cells,Area " + unit + ",Population\n"; // headers let data = "Id,Fill,Description,Type,Cells,Area " + unit + ",Population\n"; // headers
body.querySelectorAll(":scope > div").forEach(function (el) { body.querySelectorAll(":scope > div").forEach(function (el) {
data += el.dataset.id + ","; data += el.dataset.id + ",";
data += el.dataset.fill + ","; data += el.dataset.fill + ",";
data += el.dataset.description + ","; data += el.dataset.description + ",";
data += el.dataset.type + ",";
data += el.dataset.cells + ","; data += el.dataset.cells + ",";
data += el.dataset.area + ","; data += el.dataset.area + ",";
data += el.dataset.population + "\n"; data += el.dataset.population + "\n";