This commit is contained in:
Azgaar 2019-09-16 21:53:20 +03:00
parent ed720300ff
commit f5bc33b171
7 changed files with 110 additions and 5 deletions

View file

@ -205,6 +205,7 @@ i.icon-lock {
} }
#religionHierarchy text, #religionHierarchy text,
#statesTree text,
#provincesTree text { #provincesTree text {
pointer-events: none; pointer-events: none;
user-select: none; user-select: none;
@ -212,6 +213,12 @@ i.icon-lock {
font-size: 11px; font-size: 11px;
} }
#statesTree circle {
filter: url(#dropShadow05);
stroke: #666666;
stroke-width: 1;
}
#provincesTree .selected { #provincesTree .selected {
stroke: #c13119; stroke: #c13119;
stroke-width: 2; stroke-width: 2;

View file

@ -2503,6 +2503,7 @@
<button id="statesEditorRefresh" data-tip="Refresh the Editor" class="icon-cw"></button> <button id="statesEditorRefresh" data-tip="Refresh the Editor" class="icon-cw"></button>
<button id="statesLegend" data-tip="Toggle Legend box" class="icon-list-bullet"></button> <button id="statesLegend" data-tip="Toggle Legend box" class="icon-list-bullet"></button>
<button id="statesPercentage" data-tip="Toggle percentage / absolute values views" class="icon-percent"></button> <button id="statesPercentage" data-tip="Toggle percentage / absolute values views" class="icon-percent"></button>
<button id="statesChart" data-tip="Show states bubble chart" class="icon-chart-area"></button>
<button id="statesRegenerate" data-tip="Show the regeneration menu and more data" class="icon-cog-alt"></button> <button id="statesRegenerate" data-tip="Show the regeneration menu and more data" class="icon-cog-alt"></button>
<div id="statesRegenerateButtons" style="display: none"> <div id="statesRegenerateButtons" style="display: none">

View file

@ -157,7 +157,7 @@
if (b.capital) b.population = rn(b.population * 1.3, 3); // increase capital population if (b.capital) b.population = rn(b.population * 1.3, 3); // increase capital population
if (port) { if (port) {
b.population *= b.population * 1.3; // increase port population b.population = b.population * 1.3; // increase port population
const e = cells.v[i].filter(v => vertices.c[v].some(c => c === cells.haven[i])); // vertices of common edge const e = cells.v[i].filter(v => vertices.c[v].some(c => c === cells.haven[i])); // vertices of common edge
b.x = rn((vertices.p[e[0]][0] + vertices.p[e[1]][0]) / 2, 2); b.x = rn((vertices.p[e[0]][0] + vertices.p[e[1]][0]) / 2, 2);
b.y = rn((vertices.p[e[0]][1] + vertices.p[e[1]][1]) / 2, 2); b.y = rn((vertices.p[e[0]][1] + vertices.p[e[1]][1]) / 2, 2);

View file

@ -377,7 +377,7 @@ const saveReminder = function() {
"Safety is number one priority. Please save the map", "Safety is number one priority. Please save the map",
"Don't forget to save your map on a regular basis!", "Don't forget to save your map on a regular basis!",
"Just a gentle reminder for you to save the map", "Just a gentle reminder for you to save the map",
"Please forget to save your progress (saving as .map is the best option)", "Please don't forget to save your progress (saving as .map is the best option)",
"Don't want to be reminded about need to save? Press CTRL+Q"]; "Don't want to be reminded about need to save? Press CTRL+Q"];
saveReminder.reminder = setInterval(() => { saveReminder.reminder = setInterval(() => {

View file

@ -373,7 +373,7 @@ function editProvinces() {
: d => d.rural + d.urban; : d => d.rural + d.urban;
const newRoot = d3.stratify().parentId(d => d.state)(data).sum(value); const newRoot = d3.stratify().parentId(d => d.state)(data).sum(value);
node.data(treeLayout(newRoot).leaves()) node.data(treeLayout(newRoot).leaves());
node.select("rect").transition().duration(1500) node.select("rect").transition().duration(1500)
.attr("x", d => d.x0).attr("y", d => d.y0) .attr("x", d => d.x0).attr("y", d => d.y0)

View file

@ -151,7 +151,7 @@ function editReligions() {
const urban = r.urban * populationRate.value * urbanization.value; const urban = r.urban * populationRate.value * urbanization.value;
const population = rural + urban > 0 ? ". " + si(rn(rural + urban)) + " believers" : ". Extinct"; const population = rural + urban > 0 ? ". " + si(rn(rural + urban)) + " believers" : ". Extinct";
info.innerHTML = `${r.name}${type}${form}${population}`; info.innerHTML = `${r.name}${type}${form}${population}`;
tip("Drag to change parent. Hold CTRL and click to change abbrebiation"); tip("Drag to change parent. Hold CTRL and click to change abbreviation");
} }
const el = body.querySelector(`div[data-id='${religion}']`); const el = body.querySelector(`div[data-id='${religion}']`);

View file

@ -24,6 +24,7 @@ function editStates() {
document.getElementById("statesEditorRefresh").addEventListener("click", refreshStatesEditor); document.getElementById("statesEditorRefresh").addEventListener("click", refreshStatesEditor);
document.getElementById("statesLegend").addEventListener("click", toggleLegend); document.getElementById("statesLegend").addEventListener("click", toggleLegend);
document.getElementById("statesPercentage").addEventListener("click", togglePercentageMode); document.getElementById("statesPercentage").addEventListener("click", togglePercentageMode);
document.getElementById("statesChart").addEventListener("click", showStatesChart);
document.getElementById("statesRegenerate").addEventListener("click", openRegenerationMenu); document.getElementById("statesRegenerate").addEventListener("click", openRegenerationMenu);
document.getElementById("statesRegenerateBack").addEventListener("click", exitRegenerationMenu); document.getElementById("statesRegenerateBack").addEventListener("click", exitRegenerationMenu);
document.getElementById("statesRecalculate").addEventListener("click", () => recalculateStates(true)); document.getElementById("statesRecalculate").addEventListener("click", () => recalculateStates(true));
@ -387,6 +388,102 @@ function editStates() {
} }
} }
function showStatesChart() {
// build hierarchy tree
const data = pack.states.filter(s => !s.removed);
const root = d3.stratify().id(d => d.i).parentId(d => d.i ? 0 : null)(data)
.sum(d => d.area).sort((a, b) => b.value - a.value);
const width = 150 + 200 * uiSizeOutput.value, height = 150 + 200 * uiSizeOutput.value;
const margin = {top: 0, right: -50, bottom: 0, left: -50};
const w = width - margin.left - margin.right;
const h = height - margin.top - margin.bottom;
const treeLayout = d3.pack().size([w, h]).padding(3);
// prepare svg
alertMessage.innerHTML = `<select id="statesTreeType" style="display:block; margin-left:13px; font-size:11px">
<option value="area" selected>Area</option>
<option value="population">Total population</option>
<option value="rural">Rural population</option>
<option value="urban">Urban population</option>
<option value="burgs">Burgs number</option>
</select>`;
alertMessage.innerHTML += `<div id='statesInfo' class='chartInfo'>&#8205;</div>`;
const svg = d3.select("#alertMessage").insert("svg", "#statesInfo").attr("id", "statesTree")
.attr("width", width).attr("height", height).style("font-family", "Almendra SC")
.attr("text-anchor", "middle").attr("dominant-baseline", "central");
const graph = svg.append("g").attr("transform", `translate(-50, 0)`);
document.getElementById("statesTreeType").addEventListener("change", updateChart);
treeLayout(root);
const node = graph.selectAll("g").data(root.leaves()).enter()
.append("g").attr("transform", d => `translate(${d.x},${d.y})`)
.attr("data-id", d => d.data.id)
.on("mouseenter", d => showInfo(event, d))
.on("mouseleave", d => hideInfo(event, d));
node.append("circle").attr("fill", d => d.data.color).attr("r", d => d.r);
const exp = /(?=[A-Z][^A-Z])/g;
const lp = n => d3.max(n.split(exp).map(p => p.length)) + 1; // longest name part + 1
node.append("text")
.style("font-size", d => rn(d.r ** .97 * 4 / lp(d.data.name), 2) + "px")
.selectAll("tspan").data(d => d.data.name.split(exp))
.join("tspan").attr("x", 0).text(d => d)
.attr("dy", (d, i, n) => `${i ? 1 : (n.length-1) / -2}em`);
function showInfo(ev, d) {
d3.select(ev.target).select("circle").classed("selected", 1);
const state = d.data.fullName;
const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value;
const area = d.data.area * (distanceScaleInput.value ** 2) + unit;
const rural = rn(d.data.rural * populationRate.value);
const urban = rn(d.data.urban * populationRate.value * urbanization.value);
const option = statesTreeType.value;
const value = option === "area" ? "Area: " + area
: option === "rural" ? "Rural population: " + si(rural)
: option === "urban" ? "Urban population: " + si(urban)
: option === "burgs" ? "Burgs number: " + d.data.burgs
: "Population: " + si(rural + urban);
statesInfo.innerHTML = `${state}. ${value}`;
stateHighlightOn(ev);
}
function hideInfo(ev) {
stateHighlightOff(ev);
if (!document.getElementById("statesInfo")) return;
statesInfo.innerHTML = "&#8205;";
d3.select(ev.target).select("circle").classed("selected", 0);
}
function updateChart() {
const value = this.value === "area" ? d => d.area
: this.value === "rural" ? d => d.rural
: this.value === "urban" ? d => d.urban
: this.value === "burgs" ? d => d.burgs
: d => d.rural + d.urban;
root.sum(value);
node.data(treeLayout(root).leaves());
node.transition().duration(1500).attr("transform", d => `translate(${d.x},${d.y})`)
node.select("circle").transition().duration(1500).attr("r", d => d.r);
node.select("text").transition().duration(1500)
.style("font-size", d => rn(d.r ** .97 * 4 / lp(d.data.name), 2) + "px");
}
$("#alert").dialog({
title: "States bubble chart", width: fitContent(),
position: {my: "left bottom", at: "left+10 bottom-10", of: "svg"}, buttons: {},
close: () => {alertMessage.innerHTML = "";}
});
}
function openRegenerationMenu() { function openRegenerationMenu() {
statesBottom.querySelectorAll(":scope > button").forEach(el => el.style.display = "none"); statesBottom.querySelectorAll(":scope > button").forEach(el => el.style.display = "none");
statesRegenerateButtons.style.display = "block"; statesRegenerateButtons.style.display = "block";