This commit is contained in:
Azgaar 2019-09-17 22:25:19 +03:00
parent d5ec72b6b8
commit dd1510e4ff
8 changed files with 265 additions and 61 deletions

View file

@ -20,6 +20,7 @@ function editBurgs() {
// add listeners
document.getElementById("burgsEditorRefresh").addEventListener("click", refreshBurgsEditor);
document.getElementById("burgsChart").addEventListener("click", showBurgsChart);
document.getElementById("burgsFilterState").addEventListener("change", burgsEditorAddLines);
document.getElementById("burgsFilterCulture").addEventListener("change", burgsEditorAddLines);
document.getElementById("regenerateBurgNames").addEventListener("click", regenerateNames);
@ -250,6 +251,133 @@ function editBurgs() {
if (addNewBurg.classList.contains("pressed")) addNewBurg.classList.remove("pressed");
}
function showBurgsChart() {
// build hierarchy tree
const states = pack.states.map(s => {
const color = s.color ? s.color : "#ccc";
const name = s.fullName ? s.fullName : s.name;
return {id:s.i, state: s.i ? 0 : null, color, name}
});
const burgs = pack.burgs.filter(b => b.i && !b.removed).map(b => {
const id = b.i+states.length-1;
const population = b.population;
const capital = b.capital;
const province = pack.cells.province[b.cell];
const parent = province ? province + states.length-1 : b.state;
return {id, i:b.i, state:b.state, culture:b.culture, province, parent, name:b.name, population, capital, x:b.x, y:b.y}
});
const data = states.concat(burgs);
const root = d3.stratify().parentId(d => d.state)(data)
.sum(d => d.population).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: -10, 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="burgsTreeType" style="display:block; margin-left:13px; font-size:11px">
<option value="states" selected>Group by state</option>
<option value="cultures">Group by culture</option>
<option value="parent">Group by province and state</option>
<option value="provinces">Group by province</option></select>`;
alertMessage.innerHTML += `<div id='burgsInfo' class='chartInfo'>&#8205;</div>`;
const svg = d3.select("#alertMessage").insert("svg", "#burgsInfo").attr("id", "burgsTree")
.attr("width", width).attr("height", height-10).attr("stroke-width", 2);
const graph = svg.append("g").attr("transform", `translate(-50, -10)`);
document.getElementById("burgsTreeType").addEventListener("change", updateChart);
treeLayout(root);
const node = graph.selectAll("circle").data(root.leaves())
.join("circle").attr("data-id", d => d.data.i)
.attr("r", d => d.r).attr("fill", d => d.parent.data.color)
.attr("cx", d => d.x).attr("cy", d => d.y)
.on("mouseenter", d => showInfo(event, d))
.on("mouseleave", d => hideInfo(event, d))
.on("click", d => zoomTo(d.data.x, d.data.y, 8, 2000));
function showInfo(ev, d) {
d3.select(ev.target).transition().duration(1500).attr("stroke", "#c13119");
const name = d.data.name;
const parent = d.parent.data.name;
const population = si(d.value * populationRate.value * urbanization.value);
burgsInfo.innerHTML = `${name}. ${parent}. Population: ${population}`;
burgHighlightOn(ev);
tip("Click to zoom into view");
}
function hideInfo(ev) {
burgHighlightOff(ev);
if (!document.getElementById("burgsInfo")) return;
burgsInfo.innerHTML = "&#8205;";
d3.select(ev.target).transition().attr("stroke", "null");
tip("");
}
function updateChart() {
const getStatesData = () => pack.states.map(s => {
const color = s.color ? s.color : "#ccc";
const name = s.fullName ? s.fullName : s.name;
return {id:s.i, state: s.i ? 0 : null, color, name}
});
const getCulturesData = () => pack.cultures.map(c => {
const color = c.color ? c.color : "#ccc";
return {id:c.i, culture: c.i ? 0 : null, color, name:c.name}
});
const getParentData = () => {
const states = pack.states.map(s => {
const color = s.color ? s.color : "#ccc";
const name = s.fullName ? s.fullName : s.name;
return {id:s.i, parent: s.i ? 0 : null, color, name}
});
const provinces = pack.provinces.filter(p => p.i && !p.removed).map(p => {
return {id:p.i + states.length-1, parent: p.state, color:p.color, name:p.fullName}
});
return states.concat(provinces);
}
const getProvincesData = () => pack.provinces.map(p => {
const color = p.color ? p.color : "#ccc";
const name = p.fullName ? p.fullName : p.name;
return {id:p.i ? p.i : 0, province: p.i ? 0 : null, color, name}
});
const value = d => {
if (this.value === "states") return d.state;
if (this.value === "cultures") return d.culture;
if (this.value === "parent") return d.parent;
if (this.value === "provinces") return d.province;
}
const base = this.value === "states" ? getStatesData()
: this.value === "cultures" ? getCulturesData()
: this.value === "parent" ? getParentData() : getProvincesData();
burgs.forEach(b => b.id = b.i+base.length-1);
const data = base.concat(burgs);
const root = d3.stratify().parentId(d => value(d))(data)
.sum(d => d.population).sort((a, b) => b.value - a.value);
node.data(treeLayout(root).leaves()).transition().duration(2000)
.attr("data-id", d => d.data.i).attr("fill", d => d.parent.data.color)
.attr("cx", d => d.x).attr("cy", d => d.y).attr("r", d => d.r);
}
$("#alert").dialog({
title: "Burgs bubble chart", width: fitContent(),
position: {my: "left bottom", at: "left+10 bottom-10", of: "svg"}, buttons: {},
close: () => {alertMessage.innerHTML = "";}
});
}
function downloadBurgsData() {
let data = "Id,Burg,Province,State,Culture,Religion,Population,Longitude,Latitude,Elevation ("+heightUnit.value+"),Capital,Port\n"; // headers
const valid = pack.burgs.filter(b => b.i && !b.removed); // all valid burgs
@ -259,7 +387,7 @@ function editBurgs() {
data += b.name + ",";
const province = pack.cells.province[b.cell];
data += province ? pack.provinces[province].fullName + "," : ",";
data += b.state ? pack.states[b.state].fullName : pack.states[b.state].name + ",";
data += b.state ? pack.states[b.state].fullName +"," : pack.states[b.state].name + ",";
data += pack.cultures[b.culture].name + ",";
data += pack.religions[pack.cells.religion[b.cell]].name + ",";
data += rn(b.population * populationRate.value * urbanization.value) + ",";

View file

@ -229,6 +229,42 @@ function applyOption(select, option) {
select.value = option;
}
// show info about the generator in a popup
function showInfo() {
const Discord = link("https://discordapp.com/invite/X7E84HU", "Discord");
const Reddit = link("https://www.reddit.com/r/FantasyMapGenerator", "Reddit")
const Patreon = link("https://www.patreon.com/azgaar", "Patreon");
const Trello = link("https://trello.com/b/7x832DG4/fantasy-map-generator", "Trello");
const QuickStart = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Quick-Start-Tutorial", "Quick start tutorial");
const QAA = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Q&A", "Q&A page");
alertMessage.innerHTML = `
<b>Fantasy Map Generator</b> (FMG) is an open-source application, it means the code is published an anyone can use it.
In case of FMG is also means that you own all created maps and can use them as you wish, you can even sell them.
<p>The development is supported by community, you can donate on ${Patreon}.
You can also help creating overviews, tutorials and spreding the word about the Generator.</p>
<p>The best way to get help is to contact the community on ${Discord} and ${Reddit}.
Before asking questions, please check out the ${QuickStart} and the ${QAA}.</p>
<p>You can track the development process on ${Trello}.</p>
Links:
<ul style="columns:2">
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator", "GitHub repository")}</li>
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator/blob/master/LICENSE", "License")}</li>
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog", "Changelog")}</li>
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Hotkeys", "Hotkeys")}</li>
</ul>`;
$("#alert").dialog({resizable: false, title: document.title, width: "28em",
buttons: {OK: function() {$(this).dialog("close");}},
position: {my: "center", at: "center", of: "svg"}
});
}
// prevent default browser behavior for FMG-used hotkeys
document.addEventListener("keydown", event => {
if ([112, 113, 117, 120, 9].includes(event.keyCode)) event.preventDefault(); // F1, F2, F6, F9, Tab
@ -242,13 +278,14 @@ document.addEventListener("keyup", event => {
event.stopPropagation();
const key = event.keyCode, ctrl = event.ctrlKey, shift = event.shiftKey, meta = event.metaKey;
if (key === 27) {closeDialogs(); hideOptions();} // Escape to close all dialogs
else if (key === 9) toggleOptions(event); // Tab to toggle options
if (key === 112) showInfo(); // "F1" to show info
else if (key === 113) regeneratePrompt(); // "F2" for new map
else if (key === 46) removeElementOnKey(); // "Delete" to remove the selected element
else if (key === 113) regeneratePrompt(); // "F2" for a new map
else if (key === 117) quickSave(); // "F6" for quick save
else if (key === 120) quickLoad(); // "F9" for quick load
else if (key === 9) toggleOptions(event); // Tab to toggle options
else if (key === 27) {closeDialogs(); hideOptions();} // Escape to close all dialogs
else if (key === 46) removeElementOnKey(); // "Delete" to remove the selected element
else if (ctrl && key === 80) saveAsImage("png"); // Ctrl + "P" to save as PNG
else if (ctrl && key === 83) saveAsImage("svg"); // Ctrl + "S" to save as SVG

View file

@ -11,8 +11,8 @@ function editHeightmap() {
<p>If you need to change the coastline and keep the data, you may try the <i>risk</i> edit option.
The data will be restored as much as possible, but the coastline change can cause unexpected fluctuations and errors.</p>
<p>Check out <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Heightmap-customization" target="_blank">wiki</a> for guidance.</p>
<p>Check out ${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Heightmap-customization", "wiki")} for guidance.</p>
<p>Please <span class="pseudoLink" onclick=saveMap(); editHeightmap();>save the map</span> before edditing the heightmap!</p>`;
$("#alert").dialog({resizable: false, title: "Edit Heightmap", width: "28em",

View file

@ -286,8 +286,10 @@ function editProvinces() {
const states = pack.states.map(s => {
return {id:s.i, state: s.i?0:null, color: s.i && s.color[0] === "#" ? d3.color(s.color).darker() : "#666"}
});
const provinces = pack.provinces.filter(p => p.i && !p.removed);
provinces.forEach(p => p.id = p.i + states.length - 1);
const provinces = pack.provinces.filter(p => p.i && !p.removed).map(p => {
return {id:p.i+states.length-1, i:p.i, state:p.state, color:p.color,
name:p.name, fullName:p.fullName, area:p.area, urban:p.urban, rural:p.rural}
});
const data = states.concat(provinces);
const root = d3.stratify().parentId(d => d.state)(data).sum(d => d.area);
@ -371,9 +373,9 @@ function editProvinces() {
: this.value === "rural" ? d => d.rural
: this.value === "urban" ? d => d.urban
: d => d.rural + d.urban;
const newRoot = d3.stratify().parentId(d => d.state)(data).sum(value);
node.data(treeLayout(newRoot).leaves());
root.sum(value);
node.data(treeLayout(root).leaves());
node.select("rect").transition().duration(1500)
.attr("x", d => d.x0).attr("y", d => d.y0)

View file

@ -419,7 +419,7 @@ function editStates() {
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)
.attr("data-id", d => d.data.i)
.on("mouseenter", d => showInfo(event, d))
.on("mouseleave", d => hideInfo(event, d));