mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-19 10:31:24 +01:00
Merge branch 'Azgaar:master' into Province-legend-box
This commit is contained in:
commit
366b12ece6
49 changed files with 1935 additions and 1057 deletions
|
|
@ -21,38 +21,37 @@ function editBurg(id) {
|
|||
modules.editBurg = true;
|
||||
|
||||
// add listeners
|
||||
document.getElementById("burgGroupShow").addEventListener("click", showGroupSection);
|
||||
document.getElementById("burgGroupHide").addEventListener("click", hideGroupSection);
|
||||
document.getElementById("burgSelectGroup").addEventListener("change", changeGroup);
|
||||
document.getElementById("burgInputGroup").addEventListener("change", createNewGroup);
|
||||
document.getElementById("burgAddGroup").addEventListener("click", toggleNewGroupInput);
|
||||
document.getElementById("burgRemoveGroup").addEventListener("click", removeBurgsGroup);
|
||||
byId("burgGroupShow").addEventListener("click", showGroupSection);
|
||||
byId("burgGroupHide").addEventListener("click", hideGroupSection);
|
||||
byId("burgSelectGroup").addEventListener("change", changeGroup);
|
||||
byId("burgInputGroup").addEventListener("change", createNewGroup);
|
||||
byId("burgAddGroup").addEventListener("click", toggleNewGroupInput);
|
||||
byId("burgRemoveGroup").addEventListener("click", removeBurgsGroup);
|
||||
|
||||
document.getElementById("burgName").addEventListener("input", changeName);
|
||||
document.getElementById("burgNameReRandom").addEventListener("click", generateNameRandom);
|
||||
document.getElementById("burgType").addEventListener("input", changeType);
|
||||
document.getElementById("burgCulture").addEventListener("input", changeCulture);
|
||||
document.getElementById("burgNameReCulture").addEventListener("click", generateNameCulture);
|
||||
document.getElementById("burgPopulation").addEventListener("change", changePopulation);
|
||||
byId("burgName").addEventListener("input", changeName);
|
||||
byId("burgNameReRandom").addEventListener("click", generateNameRandom);
|
||||
byId("burgType").addEventListener("input", changeType);
|
||||
byId("burgCulture").addEventListener("input", changeCulture);
|
||||
byId("burgNameReCulture").addEventListener("click", generateNameCulture);
|
||||
byId("burgPopulation").addEventListener("change", changePopulation);
|
||||
burgBody.querySelectorAll(".burgFeature").forEach(el => el.addEventListener("click", toggleFeature));
|
||||
document.getElementById("mfcgBurgSeed").addEventListener("change", changeSeed);
|
||||
document.getElementById("regenerateMFCGBurgSeed").addEventListener("click", randomizeSeed);
|
||||
document.getElementById("addCustomMFCGBurgLink").addEventListener("click", addCustomMfcgLink);
|
||||
byId("burgLinkOpen").addEventListener("click", openBurgLink);
|
||||
byId("burgLinkEdit").addEventListener("click", changeBurgLink);
|
||||
|
||||
document.getElementById("burgStyleShow").addEventListener("click", showStyleSection);
|
||||
document.getElementById("burgStyleHide").addEventListener("click", hideStyleSection);
|
||||
document.getElementById("burgEditLabelStyle").addEventListener("click", editGroupLabelStyle);
|
||||
document.getElementById("burgEditIconStyle").addEventListener("click", editGroupIconStyle);
|
||||
document.getElementById("burgEditAnchorStyle").addEventListener("click", editGroupAnchorStyle);
|
||||
byId("burgStyleShow").addEventListener("click", showStyleSection);
|
||||
byId("burgStyleHide").addEventListener("click", hideStyleSection);
|
||||
byId("burgEditLabelStyle").addEventListener("click", editGroupLabelStyle);
|
||||
byId("burgEditIconStyle").addEventListener("click", editGroupIconStyle);
|
||||
byId("burgEditAnchorStyle").addEventListener("click", editGroupAnchorStyle);
|
||||
|
||||
document.getElementById("burgEmblem").addEventListener("click", openEmblemEdit);
|
||||
document.getElementById("burgToggleMFCGMap").addEventListener("click", toggleMFCGMap);
|
||||
document.getElementById("burgEditEmblem").addEventListener("click", openEmblemEdit);
|
||||
document.getElementById("burgRelocate").addEventListener("click", toggleRelocateBurg);
|
||||
document.getElementById("burglLegend").addEventListener("click", editBurgLegend);
|
||||
document.getElementById("burgLock").addEventListener("click", toggleBurgLockButton);
|
||||
document.getElementById("burgRemove").addEventListener("click", removeSelectedBurg);
|
||||
document.getElementById("burgTemperatureGraph").addEventListener("click", showTemperatureGraph);
|
||||
byId("burgEmblem").addEventListener("click", openEmblemEdit);
|
||||
byId("burgTogglePreview").addEventListener("click", toggleBurgPreview);
|
||||
byId("burgEditEmblem").addEventListener("click", openEmblemEdit);
|
||||
byId("burgRelocate").addEventListener("click", toggleRelocateBurg);
|
||||
byId("burglLegend").addEventListener("click", editBurgLegend);
|
||||
byId("burgLock").addEventListener("click", toggleBurgLockButton);
|
||||
byId("burgRemove").addEventListener("click", removeSelectedBurg);
|
||||
byId("burgTemperatureGraph").addEventListener("click", showTemperatureGraph);
|
||||
|
||||
function updateBurgValues() {
|
||||
const id = +elSelected.attr("data-id");
|
||||
|
|
@ -60,46 +59,46 @@ function editBurg(id) {
|
|||
const province = pack.cells.province[b.cell];
|
||||
const provinceName = province ? pack.provinces[province].fullName + ", " : "";
|
||||
const stateName = pack.states[b.state].fullName || pack.states[b.state].name;
|
||||
document.getElementById("burgProvinceAndState").innerHTML = provinceName + stateName;
|
||||
byId("burgProvinceAndState").innerHTML = provinceName + stateName;
|
||||
|
||||
document.getElementById("burgName").value = b.name;
|
||||
document.getElementById("burgType").value = b.type || "Generic";
|
||||
document.getElementById("burgPopulation").value = rn(b.population * populationRate * urbanization);
|
||||
document.getElementById("burgEditAnchorStyle").style.display = +b.port ? "inline-block" : "none";
|
||||
byId("burgName").value = b.name;
|
||||
byId("burgType").value = b.type || "Generic";
|
||||
byId("burgPopulation").value = rn(b.population * populationRate * urbanization);
|
||||
byId("burgEditAnchorStyle").style.display = +b.port ? "inline-block" : "none";
|
||||
|
||||
// update list and select culture
|
||||
const cultureSelect = document.getElementById("burgCulture");
|
||||
const cultureSelect = byId("burgCulture");
|
||||
cultureSelect.options.length = 0;
|
||||
const cultures = pack.cultures.filter(c => !c.removed);
|
||||
cultures.forEach(c => cultureSelect.options.add(new Option(c.name, c.i, false, c.i === b.culture)));
|
||||
|
||||
const temperature = grid.cells.temp[pack.cells.g[b.cell]];
|
||||
document.getElementById("burgTemperature").innerHTML = convertTemperature(temperature);
|
||||
document.getElementById("burgTemperatureLikeIn").innerHTML = getTemperatureLikeness(temperature);
|
||||
document.getElementById("burgElevation").innerHTML = getHeight(pack.cells.h[b.cell]);
|
||||
byId("burgTemperature").innerHTML = convertTemperature(temperature);
|
||||
byId("burgTemperatureLikeIn").innerHTML = getTemperatureLikeness(temperature);
|
||||
byId("burgElevation").innerHTML = getHeight(pack.cells.h[b.cell]);
|
||||
|
||||
// toggle features
|
||||
if (b.capital) document.getElementById("burgCapital").classList.remove("inactive");
|
||||
else document.getElementById("burgCapital").classList.add("inactive");
|
||||
if (b.port) document.getElementById("burgPort").classList.remove("inactive");
|
||||
else document.getElementById("burgPort").classList.add("inactive");
|
||||
if (b.citadel) document.getElementById("burgCitadel").classList.remove("inactive");
|
||||
else document.getElementById("burgCitadel").classList.add("inactive");
|
||||
if (b.walls) document.getElementById("burgWalls").classList.remove("inactive");
|
||||
else document.getElementById("burgWalls").classList.add("inactive");
|
||||
if (b.plaza) document.getElementById("burgPlaza").classList.remove("inactive");
|
||||
else document.getElementById("burgPlaza").classList.add("inactive");
|
||||
if (b.temple) document.getElementById("burgTemple").classList.remove("inactive");
|
||||
else document.getElementById("burgTemple").classList.add("inactive");
|
||||
if (b.shanty) document.getElementById("burgShanty").classList.remove("inactive");
|
||||
else document.getElementById("burgShanty").classList.add("inactive");
|
||||
if (b.capital) byId("burgCapital").classList.remove("inactive");
|
||||
else byId("burgCapital").classList.add("inactive");
|
||||
if (b.port) byId("burgPort").classList.remove("inactive");
|
||||
else byId("burgPort").classList.add("inactive");
|
||||
if (b.citadel) byId("burgCitadel").classList.remove("inactive");
|
||||
else byId("burgCitadel").classList.add("inactive");
|
||||
if (b.walls) byId("burgWalls").classList.remove("inactive");
|
||||
else byId("burgWalls").classList.add("inactive");
|
||||
if (b.plaza) byId("burgPlaza").classList.remove("inactive");
|
||||
else byId("burgPlaza").classList.add("inactive");
|
||||
if (b.temple) byId("burgTemple").classList.remove("inactive");
|
||||
else byId("burgTemple").classList.add("inactive");
|
||||
if (b.shanty) byId("burgShanty").classList.remove("inactive");
|
||||
else byId("burgShanty").classList.add("inactive");
|
||||
|
||||
//toggle lock
|
||||
updateBurgLockIcon();
|
||||
|
||||
// select group
|
||||
const group = elSelected.node().parentNode.id;
|
||||
const select = document.getElementById("burgSelectGroup");
|
||||
const select = byId("burgSelectGroup");
|
||||
select.options.length = 0; // remove all options
|
||||
|
||||
burgLabels.selectAll("g").each(function () {
|
||||
|
|
@ -109,68 +108,16 @@ function editBurg(id) {
|
|||
// set emlem image
|
||||
const coaID = "burgCOA" + id;
|
||||
COArenderer.trigger(coaID, b.coa);
|
||||
document.getElementById("burgEmblem").setAttribute("href", "#" + coaID);
|
||||
byId("burgEmblem").setAttribute("href", "#" + coaID);
|
||||
|
||||
if (options.showMFCGMap) {
|
||||
document.getElementById("mfcgPreviewSection").style.display = "block";
|
||||
updateMFCGFrame(b);
|
||||
|
||||
if (b.link) {
|
||||
document.getElementById("mfcgBurgSeedSection").style.display = "none";
|
||||
} else {
|
||||
document.getElementById("mfcgBurgSeedSection").style.display = "inline-block";
|
||||
document.getElementById("mfcgBurgSeed").value = getBurgSeed(b);
|
||||
}
|
||||
if (options.showBurgPreview) {
|
||||
byId("burgPreviewSection").style.display = "block";
|
||||
updateBurgPreview(b);
|
||||
} else {
|
||||
document.getElementById("mfcgPreviewSection").style.display = "none";
|
||||
byId("burgPreviewSection").style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
// in °C, array from -1 °C; source: https://en.wikipedia.org/wiki/List_of_cities_by_average_temperature
|
||||
function getTemperatureLikeness(temperature) {
|
||||
if (temperature < -5) return "Yakutsk";
|
||||
const cities = [
|
||||
"Snag (Yukon)",
|
||||
"Yellowknife (Canada)",
|
||||
"Okhotsk (Russia)",
|
||||
"Fairbanks (Alaska)",
|
||||
"Nuuk (Greenland)",
|
||||
"Murmansk", // -5 - 0
|
||||
"Arkhangelsk",
|
||||
"Anchorage",
|
||||
"Tromsø",
|
||||
"Reykjavik",
|
||||
"Riga",
|
||||
"Stockholm",
|
||||
"Halifax",
|
||||
"Prague",
|
||||
"Copenhagen",
|
||||
"London", // 1 - 10
|
||||
"Antwerp",
|
||||
"Paris",
|
||||
"Milan",
|
||||
"Batumi",
|
||||
"Rome",
|
||||
"Dubrovnik",
|
||||
"Lisbon",
|
||||
"Barcelona",
|
||||
"Marrakesh",
|
||||
"Alexandria", // 11 - 20
|
||||
"Tegucigalpa",
|
||||
"Guangzhou",
|
||||
"Rio de Janeiro",
|
||||
"Dakar",
|
||||
"Miami",
|
||||
"Jakarta",
|
||||
"Mogadishu",
|
||||
"Bangkok",
|
||||
"Aden",
|
||||
"Khartoum"
|
||||
]; // 21 - 30
|
||||
if (temperature > 30) return "Mecca";
|
||||
return cities[temperature + 5] || null;
|
||||
}
|
||||
|
||||
function dragBurgLabel() {
|
||||
const tr = parseTransform(this.getAttribute("transform"));
|
||||
const dx = +tr[0] - d3.event.x,
|
||||
|
|
@ -186,15 +133,15 @@ function editBurg(id) {
|
|||
|
||||
function showGroupSection() {
|
||||
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "none"));
|
||||
document.getElementById("burgGroupSection").style.display = "inline-block";
|
||||
byId("burgGroupSection").style.display = "inline-block";
|
||||
}
|
||||
|
||||
function hideGroupSection() {
|
||||
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "inline-block"));
|
||||
document.getElementById("burgGroupSection").style.display = "none";
|
||||
document.getElementById("burgInputGroup").style.display = "none";
|
||||
document.getElementById("burgInputGroup").value = "";
|
||||
document.getElementById("burgSelectGroup").style.display = "inline-block";
|
||||
byId("burgGroupSection").style.display = "none";
|
||||
byId("burgInputGroup").style.display = "none";
|
||||
byId("burgInputGroup").value = "";
|
||||
byId("burgSelectGroup").style.display = "inline-block";
|
||||
}
|
||||
|
||||
function changeGroup() {
|
||||
|
|
@ -223,7 +170,7 @@ function editBurg(id) {
|
|||
.replace(/ /g, "_")
|
||||
.replace(/[^\w\s]/gi, "");
|
||||
|
||||
if (document.getElementById(group)) {
|
||||
if (byId(group)) {
|
||||
tip("Element with this id already exists. Please provide a unique name", false, "error");
|
||||
return;
|
||||
}
|
||||
|
|
@ -251,10 +198,10 @@ function editBurg(id) {
|
|||
// just rename if only 1 element left
|
||||
const count = elSelected.node().parentNode.childElementCount;
|
||||
if (oldGroup !== "cities" && oldGroup !== "towns" && count === 1) {
|
||||
document.getElementById("burgSelectGroup").selectedOptions[0].remove();
|
||||
document.getElementById("burgSelectGroup").options.add(new Option(group, group, false, true));
|
||||
byId("burgSelectGroup").selectedOptions[0].remove();
|
||||
byId("burgSelectGroup").options.add(new Option(group, group, false, true));
|
||||
toggleNewGroupInput();
|
||||
document.getElementById("burgInputGroup").value = "";
|
||||
byId("burgInputGroup").value = "";
|
||||
labelG.id = group;
|
||||
iconG.id = group;
|
||||
if (anchor) anchorG.id = group;
|
||||
|
|
@ -262,9 +209,9 @@ function editBurg(id) {
|
|||
}
|
||||
|
||||
// create new groups
|
||||
document.getElementById("burgSelectGroup").options.add(new Option(group, group, false, true));
|
||||
byId("burgSelectGroup").options.add(new Option(group, group, false, true));
|
||||
toggleNewGroupInput();
|
||||
document.getElementById("burgInputGroup").value = "";
|
||||
byId("burgInputGroup").value = "";
|
||||
|
||||
addBurgsGroup(group);
|
||||
moveBurgToGroup(id, group);
|
||||
|
|
@ -284,7 +231,9 @@ function editBurg(id) {
|
|||
alertMessage.innerHTML = /* html */ `Are you sure you want to remove ${
|
||||
basic || capital ? "all unlocked elements in the burg group" : "the entire burg group"
|
||||
}?
|
||||
<br />Please note that capital or locked burgs will not be deleted. <br /><br />Burgs to be removed: ${burgsToRemove.length}`;
|
||||
<br />Please note that capital or locked burgs will not be deleted. <br /><br />Burgs to be removed: ${
|
||||
burgsToRemove.length
|
||||
}`;
|
||||
$("#alert").dialog({
|
||||
resizable: false,
|
||||
title: "Remove burg group",
|
||||
|
|
@ -343,7 +292,10 @@ function editBurg(id) {
|
|||
|
||||
function changePopulation() {
|
||||
const id = +elSelected.attr("data-id");
|
||||
const burg = pack.burgs[id];
|
||||
|
||||
pack.burgs[id].population = rn(burgPopulation.value / populationRate / urbanization, 4);
|
||||
updateBurgPreview(burg);
|
||||
}
|
||||
|
||||
function toggleFeature() {
|
||||
|
|
@ -357,9 +309,9 @@ function editBurg(id) {
|
|||
if (burg[feature]) this.classList.remove("inactive");
|
||||
else if (!burg[feature]) this.classList.add("inactive");
|
||||
|
||||
if (burg.port) document.getElementById("burgEditAnchorStyle").style.display = "inline-block";
|
||||
else document.getElementById("burgEditAnchorStyle").style.display = "none";
|
||||
updateMFCGFrame(burg);
|
||||
if (burg.port) byId("burgEditAnchorStyle").style.display = "inline-block";
|
||||
else byId("burgEditAnchorStyle").style.display = "none";
|
||||
updateBurgPreview(burg);
|
||||
}
|
||||
|
||||
function toggleBurgLockButton() {
|
||||
|
|
@ -374,22 +326,22 @@ function editBurg(id) {
|
|||
const id = +elSelected.attr("data-id");
|
||||
const b = pack.burgs[id];
|
||||
if (b.lock) {
|
||||
document.getElementById("burgLock").classList.remove("icon-lock-open");
|
||||
document.getElementById("burgLock").classList.add("icon-lock");
|
||||
byId("burgLock").classList.remove("icon-lock-open");
|
||||
byId("burgLock").classList.add("icon-lock");
|
||||
} else {
|
||||
document.getElementById("burgLock").classList.remove("icon-lock");
|
||||
document.getElementById("burgLock").classList.add("icon-lock-open");
|
||||
byId("burgLock").classList.remove("icon-lock");
|
||||
byId("burgLock").classList.add("icon-lock-open");
|
||||
}
|
||||
}
|
||||
|
||||
function showStyleSection() {
|
||||
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "none"));
|
||||
document.getElementById("burgStyleSection").style.display = "inline-block";
|
||||
byId("burgStyleSection").style.display = "inline-block";
|
||||
}
|
||||
|
||||
function hideStyleSection() {
|
||||
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "inline-block"));
|
||||
document.getElementById("burgStyleSection").style.display = "none";
|
||||
byId("burgStyleSection").style.display = "none";
|
||||
}
|
||||
|
||||
function editGroupLabelStyle() {
|
||||
|
|
@ -407,38 +359,38 @@ function editBurg(id) {
|
|||
editStyle("anchors", g);
|
||||
}
|
||||
|
||||
function updateMFCGFrame(burg) {
|
||||
const mfcgURL = getMFCGlink(burg);
|
||||
document.getElementById("mfcgPreview").setAttribute("src", mfcgURL + "&preview=1");
|
||||
document.getElementById("mfcgLink").setAttribute("href", mfcgURL);
|
||||
function updateBurgPreview(burg) {
|
||||
const src = getBurgLink(burg) + "&preview=1";
|
||||
|
||||
// recreate object to force reload (Chrome bug)
|
||||
const container = byId("burgPreviewObject");
|
||||
container.innerHTML = "";
|
||||
const object = document.createElement("object");
|
||||
object.style.width = "100%";
|
||||
object.data = src;
|
||||
container.insertBefore(object, null);
|
||||
}
|
||||
|
||||
function changeSeed() {
|
||||
function openBurgLink() {
|
||||
const id = +elSelected.attr("data-id");
|
||||
const burg = pack.burgs[id];
|
||||
const burgSeed = +this.value;
|
||||
burg.MFCG = burgSeed;
|
||||
updateMFCGFrame(burg);
|
||||
|
||||
openURL(getBurgLink(burg));
|
||||
}
|
||||
|
||||
function randomizeSeed() {
|
||||
function changeBurgLink() {
|
||||
const id = +elSelected.attr("data-id");
|
||||
const burg = pack.burgs[id];
|
||||
const burgSeed = rand(1e9 - 1);
|
||||
burg.MFCG = burgSeed;
|
||||
updateMFCGFrame(burg);
|
||||
document.getElementById("mfcgBurgSeed").value = burgSeed;
|
||||
}
|
||||
|
||||
function addCustomMfcgLink() {
|
||||
const id = +elSelected.attr("data-id");
|
||||
const burg = pack.burgs[id];
|
||||
const message = "Enter custom link to the burg map. It can be a link to Medieval Fantasy City Generator or other tool. Keep empty to use MFCG seed";
|
||||
prompt(message, {default: burg.link || "", required: false}, link => {
|
||||
if (link) burg.link = link;
|
||||
else delete burg.link;
|
||||
updateMFCGFrame(burg);
|
||||
});
|
||||
prompt(
|
||||
"Provide custom link to the burg map. It can be a link to Medieval Fantasy City Generator, a different tool, or just an image. Leave empty to use the default map",
|
||||
{default: getBurgLink(burg), required: false},
|
||||
link => {
|
||||
if (link) burg.link = link;
|
||||
else delete burg.link;
|
||||
updateBurgPreview(burg);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function openEmblemEdit() {
|
||||
|
|
@ -447,16 +399,16 @@ function editBurg(id) {
|
|||
editEmblem("burg", "burgCOA" + id, burg);
|
||||
}
|
||||
|
||||
function toggleMFCGMap() {
|
||||
options.showMFCGMap = !options.showMFCGMap;
|
||||
document.getElementById("mfcgPreviewSection").style.display = options.showMFCGMap ? "block" : "none";
|
||||
document.getElementById("burgToggleMFCGMap").className = options.showMFCGMap ? "icon-map" : "icon-map-o";
|
||||
function toggleBurgPreview() {
|
||||
options.showBurgPreview = !options.showBurgPreview;
|
||||
byId("burgPreviewSection").style.display = options.showBurgPreview ? "block" : "none";
|
||||
byId("burgTogglePreview").className = options.showBurgPreview ? "icon-map" : "icon-map-o";
|
||||
}
|
||||
|
||||
function toggleRelocateBurg() {
|
||||
const toggler = document.getElementById("toggleCells");
|
||||
document.getElementById("burgRelocate").classList.toggle("pressed");
|
||||
if (document.getElementById("burgRelocate").classList.contains("pressed")) {
|
||||
const toggler = byId("toggleCells");
|
||||
byId("burgRelocate").classList.toggle("pressed");
|
||||
if (byId("burgRelocate").classList.contains("pressed")) {
|
||||
viewbox.style("cursor", "crosshair").on("click", relocateBurgOnClick);
|
||||
tip("Click on map to relocate burg. Hold Shift for continuous move", true);
|
||||
if (!layerIsOn("toggleCells")) {
|
||||
|
|
@ -576,8 +528,53 @@ function editBurg(id) {
|
|||
}
|
||||
|
||||
function closeBurgEditor() {
|
||||
document.getElementById("burgRelocate").classList.remove("pressed");
|
||||
byId("burgRelocate").classList.remove("pressed");
|
||||
burgLabels.selectAll("text").call(d3.drag().on("drag", null)).classed("draggable", false);
|
||||
unselect();
|
||||
}
|
||||
}
|
||||
|
||||
// in °C, array from -1 °C; source: https://en.wikipedia.org/wiki/List_of_cities_by_average_temperature
|
||||
function getTemperatureLikeness(temperature) {
|
||||
if (temperature < -5) return "Yakutsk";
|
||||
const cities = [
|
||||
"Snag (Yukon)",
|
||||
"Yellowknife (Canada)",
|
||||
"Okhotsk (Russia)",
|
||||
"Fairbanks (Alaska)",
|
||||
"Nuuk (Greenland)",
|
||||
"Murmansk", // -5 - 0
|
||||
"Arkhangelsk",
|
||||
"Anchorage",
|
||||
"Tromsø",
|
||||
"Reykjavik",
|
||||
"Riga",
|
||||
"Stockholm",
|
||||
"Halifax",
|
||||
"Prague",
|
||||
"Copenhagen",
|
||||
"London", // 1 - 10
|
||||
"Antwerp",
|
||||
"Paris",
|
||||
"Milan",
|
||||
"Batumi",
|
||||
"Rome",
|
||||
"Dubrovnik",
|
||||
"Lisbon",
|
||||
"Barcelona",
|
||||
"Marrakesh",
|
||||
"Alexandria", // 11 - 20
|
||||
"Tegucigalpa",
|
||||
"Guangzhou",
|
||||
"Rio de Janeiro",
|
||||
"Dakar",
|
||||
"Miami",
|
||||
"Jakarta",
|
||||
"Mogadishu",
|
||||
"Bangkok",
|
||||
"Aden",
|
||||
"Khartoum"
|
||||
]; // 21 - 30
|
||||
if (temperature > 30) return "Mecca";
|
||||
return cities[temperature + 5] || null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
"use strict";
|
||||
function overviewBurgs(options = {stateId: null, cultureId: null}) {
|
||||
function overviewBurgs(settings = {stateId: null, cultureId: null}) {
|
||||
if (customization) return;
|
||||
closeDialogs("#burgsOverview, .stable");
|
||||
if (!layerIsOn("toggleIcons")) toggleIcons();
|
||||
|
|
@ -45,7 +45,7 @@ function overviewBurgs(options = {stateId: null, cultureId: null}) {
|
|||
|
||||
function updateFilter() {
|
||||
const stateFilter = byId("burgsFilterState");
|
||||
const selectedState = options.stateId !== null ? options.stateId : stateFilter.value || -1;
|
||||
const selectedState = settings.stateId !== null ? settings.stateId : stateFilter.value || -1;
|
||||
stateFilter.options.length = 0; // remove all options
|
||||
stateFilter.options.add(new Option("all", -1, false, selectedState === -1));
|
||||
stateFilter.options.add(new Option(pack.states[0].name, 0, false, selectedState === 0));
|
||||
|
|
@ -53,7 +53,7 @@ function overviewBurgs(options = {stateId: null, cultureId: null}) {
|
|||
statesSorted.forEach(s => stateFilter.options.add(new Option(s.name, s.i, false, s.i == selectedState)));
|
||||
|
||||
const cultureFilter = byId("burgsFilterCulture");
|
||||
const selectedCulture = options.cultureId !== null ? options.cultureId : cultureFilter.value || -1;
|
||||
const selectedCulture = settings.cultureId !== null ? settings.cultureId : cultureFilter.value || -1;
|
||||
cultureFilter.options.length = 0; // remove all options
|
||||
cultureFilter.options.add(new Option(`all`, -1, false, selectedCulture === -1));
|
||||
cultureFilter.options.add(new Option(pack.cultures[0].name, 0, false, selectedCulture === 0));
|
||||
|
|
@ -480,10 +480,7 @@ function overviewBurgs(options = {stateId: null, cultureId: null}) {
|
|||
}
|
||||
|
||||
function downloadBurgsData() {
|
||||
let data = `Id,Burg,Province,Province Full Name,State,State Full Name,Culture,Religion,Population,X,Y,Latitude,Longitude,Elevation (${heightUnit.value}),Capital,Port,Citadel,Walls,Plaza,Temple,Shanty Town`; // headers
|
||||
if (options.showMFCGMap) data += `,City Generator Link`;
|
||||
data += "\n";
|
||||
|
||||
let data = `Id,Burg,Province,Province Full Name,State,State Full Name,Culture,Religion,Population,X,Y,Latitude,Longitude,Elevation (${heightUnit.value}),Temperature,Temperature likeness,Capital,Port,Citadel,Walls,Plaza,Temple,Shanty Town,Emblem,City Generator Link\n`; // headers
|
||||
const valid = pack.burgs.filter(b => b.i && !b.removed); // all valid burgs
|
||||
|
||||
valid.forEach(b => {
|
||||
|
|
@ -504,6 +501,9 @@ function overviewBurgs(options = {stateId: null, cultureId: null}) {
|
|||
data += getLatitude(b.y, 2) + ",";
|
||||
data += getLongitude(b.x, 2) + ",";
|
||||
data += parseInt(getHeight(pack.cells.h[b.cell])) + ",";
|
||||
const temperature = grid.cells.temp[pack.cells.g[b.cell]];
|
||||
data += convertTemperature(temperature) + ",";
|
||||
data += getTemperatureLikeness(temperature) + ",";
|
||||
|
||||
// add status data
|
||||
data += b.capital ? "capital," : ",";
|
||||
|
|
@ -513,7 +513,9 @@ function overviewBurgs(options = {stateId: null, cultureId: null}) {
|
|||
data += b.plaza ? "plaza," : ",";
|
||||
data += b.temple ? "temple," : ",";
|
||||
data += b.shanty ? "shanty town," : ",";
|
||||
if (options.showMFCGMap) data += getMFCGlink(b);
|
||||
data += b.coa ? JSON.stringify(b.coa).replace(/"/g, "").replace(/,/g, ";") + "," : ",";
|
||||
data += getBurgLink(b);
|
||||
|
||||
data += "\n";
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -243,25 +243,22 @@ function removeBurg(id) {
|
|||
}
|
||||
}
|
||||
|
||||
function toggleCapital(burg) {
|
||||
const state = pack.burgs[burg].state;
|
||||
if (!state) {
|
||||
tip("Neutral lands cannot have a capital", false, "error");
|
||||
return;
|
||||
}
|
||||
if (pack.burgs[burg].capital) {
|
||||
tip("To change capital please assign a capital status to another burg of this state", false, "error");
|
||||
return;
|
||||
}
|
||||
const old = pack.states[state].capital;
|
||||
function toggleCapital(burgId) {
|
||||
const {burgs, states} = pack;
|
||||
if (burgs[burgId].capital)
|
||||
return tip("To change capital please assign a capital status to another burg of this state", false, "error");
|
||||
|
||||
// change statuses
|
||||
pack.states[state].capital = burg;
|
||||
pack.states[state].center = pack.burgs[burg].cell;
|
||||
pack.burgs[burg].capital = 1;
|
||||
pack.burgs[old].capital = 0;
|
||||
moveBurgToGroup(burg, "cities");
|
||||
moveBurgToGroup(old, "towns");
|
||||
const stateId = burgs[burgId].state;
|
||||
if (!stateId) return tip("Neutral lands cannot have a capital", false, "error");
|
||||
|
||||
const prevCapitalId = states[stateId].capital;
|
||||
states[stateId].capital = burgId;
|
||||
states[stateId].center = burgs[burgId].cell;
|
||||
burgs[burgId].capital = 1;
|
||||
burgs[prevCapitalId].capital = 0;
|
||||
|
||||
moveBurgToGroup(burgId, "cities");
|
||||
moveBurgToGroup(prevCapitalId, "towns");
|
||||
}
|
||||
|
||||
function togglePort(burg) {
|
||||
|
|
@ -291,16 +288,20 @@ function togglePort(burg) {
|
|||
.attr("height", size);
|
||||
}
|
||||
|
||||
function getBurgSeed(burg) {
|
||||
return burg.MFCG || Number(`${seed}${String(burg.i).padStart(4, 0)}`);
|
||||
}
|
||||
|
||||
function getMFCGlink(burg) {
|
||||
function getBurgLink(burg) {
|
||||
if (burg.link) return burg.link;
|
||||
|
||||
const population = burg.population * populationRate * urbanization;
|
||||
if (population >= options.villageMaxPopulation || burg.citadel || burg.walls || burg.temple || burg.shanty)
|
||||
return createMfcgLink(burg);
|
||||
|
||||
return createVillageGeneratorLink(burg);
|
||||
}
|
||||
|
||||
function createMfcgLink(burg) {
|
||||
const {cells} = pack;
|
||||
const {i, name, population: burgPopulation, cell} = burg;
|
||||
const seed = getBurgSeed(burg);
|
||||
const burgSeed = burg.MFCG || seed + String(burg.i).padStart(4, 0);
|
||||
|
||||
const sizeRaw = 2.13 * Math.pow((burgPopulation * populationRate) / urbanDensity, 0.385);
|
||||
const size = minmax(Math.ceil(sizeRaw), 6, 100);
|
||||
|
|
@ -308,11 +309,19 @@ function getMFCGlink(burg) {
|
|||
|
||||
const river = cells.r[cell] ? 1 : 0;
|
||||
const coast = Number(burg.port > 0);
|
||||
const sea = coast && cells.haven[cell] ? getSeaDirections(cell) : null;
|
||||
const sea = (() => {
|
||||
if (!coast || !cells.haven[cell]) return null;
|
||||
|
||||
// calculate see direction: 0 = south, 0.5 = west, 1 = north, 1.5 = east
|
||||
const p1 = cells.p[cell];
|
||||
const p2 = cells.p[cells.haven[cell]];
|
||||
let deg = (Math.atan2(p2[1] - p1[1], p2[0] - p1[0]) * 180) / Math.PI - 90;
|
||||
if (deg < 0) deg += 360;
|
||||
return rn(normalize(deg, 0, 360) * 2, 2);
|
||||
})();
|
||||
|
||||
const biome = cells.biome[cell];
|
||||
const arableBiomes = river ? [1, 2, 3, 4, 5, 6, 7, 8] : [5, 6, 7, 8];
|
||||
const farms = +arableBiomes.includes(biome);
|
||||
const farms = +arableBiomes.includes(cells.biome[cell]);
|
||||
|
||||
const citadel = +burg.citadel;
|
||||
const urban_castle = +(citadel && each(2)(i));
|
||||
|
|
@ -324,19 +333,12 @@ function getMFCGlink(burg) {
|
|||
const temple = +burg.temple;
|
||||
const shantytown = +burg.shanty;
|
||||
|
||||
function getSeaDirections(i) {
|
||||
const p1 = cells.p[i];
|
||||
const p2 = cells.p[cells.haven[i]];
|
||||
let deg = (Math.atan2(p2[1] - p1[1], p2[0] - p1[0]) * 180) / Math.PI - 90;
|
||||
if (deg < 0) deg += 360;
|
||||
return rn(normalize(deg, 0, 360) * 2, 2); // 0 = south, 0.5 = west, 1 = north, 1.5 = east
|
||||
}
|
||||
|
||||
const parameters = {
|
||||
const url = new URL("https://watabou.github.io/city-generator/");
|
||||
url.search = new URLSearchParams({
|
||||
name,
|
||||
population,
|
||||
size,
|
||||
seed,
|
||||
seed: burgSeed,
|
||||
river,
|
||||
coast,
|
||||
farms,
|
||||
|
|
@ -348,14 +350,60 @@ function getMFCGlink(burg) {
|
|||
walls,
|
||||
shantytown,
|
||||
gates: -1
|
||||
};
|
||||
const url = new URL("https://watabou.github.io/city-generator/");
|
||||
url.search = new URLSearchParams(parameters);
|
||||
});
|
||||
if (sea) url.searchParams.append("sea", sea);
|
||||
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
function createVillageGeneratorLink(burg) {
|
||||
const {cells, features} = pack;
|
||||
const {i, population, cell} = burg;
|
||||
|
||||
const pop = rn(population * populationRate * urbanization);
|
||||
const burgSeed = seed + String(i).padStart(4, 0);
|
||||
const tags = [];
|
||||
|
||||
if (cells.r[cell] && cells.haven[cell]) tags.push("estuary");
|
||||
else if (cells.haven[cell] && features[cells.f[cell]].cells === 1) tags.push("island,district");
|
||||
else if (burg.port) tags.push("coast");
|
||||
else if (cells.conf[cell]) tags.push("confluence");
|
||||
else if (cells.r[cell]) tags.push("river");
|
||||
else if (pop < 200 && each(4)(cell)) tags.push("pond");
|
||||
|
||||
const roadsAround = cells.c[cell].filter(c => cells.h[c] >= 20 && cells.road[c]).length;
|
||||
if (roadsAround > 1) tags.push("highway");
|
||||
else if (roadsAround === 1) tags.push("dead end");
|
||||
else tags.push("isolated");
|
||||
|
||||
const biome = cells.biome[cell];
|
||||
const arableBiomes = cells.r[cell] ? [1, 2, 3, 4, 5, 6, 7, 8] : [5, 6, 7, 8];
|
||||
if (!arableBiomes.includes(biome)) tags.push("uncultivated");
|
||||
else if (each(6)(cell)) tags.push("farmland");
|
||||
|
||||
const temp = grid.cells.temp[cells.g[cell]];
|
||||
if (temp <= 0 || temp > 28 || (temp > 25 && each(3)(cell))) tags.push("no orchards");
|
||||
|
||||
if (!burg.plaza) tags.push("no square");
|
||||
|
||||
if (pop < 100) tags.push("sparse");
|
||||
else if (pop > 300) tags.push("dense");
|
||||
|
||||
const width = (() => {
|
||||
if (pop > 1500) return 1600;
|
||||
if (pop > 1000) return 1400;
|
||||
if (pop > 500) return 1000;
|
||||
if (pop > 200) return 800;
|
||||
if (pop > 100) return 600;
|
||||
return 400;
|
||||
})();
|
||||
const height = rn(width / 2.2);
|
||||
|
||||
const url = new URL("https://watabou.github.io/village-generator/");
|
||||
url.search = new URLSearchParams({pop, name: "", seed: burgSeed, width, height, tags});
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
// draw legend box
|
||||
function drawLegend(name, data) {
|
||||
legend.selectAll("*").remove(); // fully redraw every time
|
||||
|
|
@ -1096,12 +1144,12 @@ function selectIcon(initial, callback) {
|
|||
input.oninput = e => callback(input.value);
|
||||
table.onclick = e => {
|
||||
if (e.target.tagName === "TD") {
|
||||
input.value = e.target.innerHTML;
|
||||
input.value = e.target.textContent;
|
||||
callback(input.value);
|
||||
}
|
||||
};
|
||||
table.onmouseover = e => {
|
||||
if (e.target.tagName === "TD") tip(`Click to select ${e.target.innerHTML} icon`);
|
||||
if (e.target.tagName === "TD") tip(`Click to select ${e.target.textContent} icon`);
|
||||
};
|
||||
|
||||
$("#iconSelector").dialog({
|
||||
|
|
@ -1176,18 +1224,18 @@ function refreshAllEditors() {
|
|||
// dynamically loaded editors
|
||||
async function editStates() {
|
||||
if (customization) return;
|
||||
const Editor = await import("../dynamic/editors/states-editor.js?v=1.93.10");
|
||||
const Editor = await import("../dynamic/editors/states-editor.js?v=1.96.06");
|
||||
Editor.open();
|
||||
}
|
||||
|
||||
async function editCultures() {
|
||||
if (customization) return;
|
||||
const Editor = await import("../dynamic/editors/cultures-editor.js?v=1.95.04");
|
||||
const Editor = await import("../dynamic/editors/cultures-editor.js?v=1.96.01");
|
||||
Editor.open();
|
||||
}
|
||||
|
||||
async function editReligions() {
|
||||
if (customization) return;
|
||||
const Editor = await import("../dynamic/editors/religions-editor.js?v=1.89.10");
|
||||
const Editor = await import("../dynamic/editors/religions-editor.js?v=1.96.00");
|
||||
Editor.open();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,8 +193,15 @@ function showElevationProfile(data, routeLen, isRiver) {
|
|||
.attr("d", "M0,0 V4 L2,2 Z")
|
||||
.attr("fill", "darkgray");
|
||||
|
||||
let colors = getColorScheme(terrs.attr("scheme"));
|
||||
const landdef = chart.select("defs").append("linearGradient").attr("id", "landdef").attr("x1", "0%").attr("y1", "0%").attr("x2", "0%").attr("y2", "100%");
|
||||
const colors = getColorScheme("natural");
|
||||
const landdef = chart
|
||||
.select("defs")
|
||||
.append("linearGradient")
|
||||
.attr("id", "landdef")
|
||||
.attr("x1", "0%")
|
||||
.attr("y1", "0%")
|
||||
.attr("x2", "0%")
|
||||
.attr("y2", "100%");
|
||||
|
||||
if (chartData.mah == chartData.mih) {
|
||||
landdef
|
||||
|
|
@ -247,7 +254,14 @@ function showElevationProfile(data, routeLen, isRiver) {
|
|||
path += " L" + parseInt(xscale(extra.length) + +xOffset) + "," + parseInt(yscale(0) + +yOffset);
|
||||
path += " L" + parseInt(xscale(0) + +xOffset) + "," + parseInt(yscale(0) + +yOffset);
|
||||
path += "Z";
|
||||
chart.append("g").attr("id", "epland").append("path").attr("d", path).attr("stroke", "purple").attr("stroke-width", "0").attr("fill", "url(#landdef)");
|
||||
chart
|
||||
.append("g")
|
||||
.attr("id", "epland")
|
||||
.append("path")
|
||||
.attr("d", path)
|
||||
.attr("stroke", "purple")
|
||||
.attr("stroke-width", "0")
|
||||
.attr("fill", "url(#landdef)");
|
||||
|
||||
// biome / heights
|
||||
let g = chart.append("g").attr("id", "epbiomes");
|
||||
|
|
@ -289,7 +303,14 @@ function showElevationProfile(data, routeLen, isRiver) {
|
|||
chartData.cell[k] +
|
||||
")";
|
||||
|
||||
g.append("rect").attr("stroke", c).attr("fill", c).attr("x", x).attr("y", y).attr("width", xscale(1)).attr("height", 15).attr("data-tip", dataTip);
|
||||
g.append("rect")
|
||||
.attr("stroke", c)
|
||||
.attr("fill", c)
|
||||
.attr("x", x)
|
||||
.attr("y", y)
|
||||
.attr("width", xscale(1))
|
||||
.attr("height", 15)
|
||||
.attr("data-tip", dataTip);
|
||||
}
|
||||
|
||||
const xAxis = d3
|
||||
|
|
@ -371,7 +392,17 @@ function showElevationProfile(data, routeLen, isRiver) {
|
|||
// arrow from burg name to graph line
|
||||
g.append("path")
|
||||
.attr("id", "eparrow" + b)
|
||||
.attr("d", "M" + x1.toString() + "," + (y1 + 3).toString() + "L" + x1.toString() + "," + parseInt(chartData.points[k][1] - 3).toString())
|
||||
.attr(
|
||||
"d",
|
||||
"M" +
|
||||
x1.toString() +
|
||||
"," +
|
||||
(y1 + 3).toString() +
|
||||
"L" +
|
||||
x1.toString() +
|
||||
"," +
|
||||
parseInt(chartData.points[k][1] - 3).toString()
|
||||
)
|
||||
.attr("stroke", "darkgray")
|
||||
.attr("fill", "lightgray")
|
||||
.attr("stroke-width", "1")
|
||||
|
|
|
|||
|
|
@ -188,92 +188,135 @@ function restoreLayers() {
|
|||
}
|
||||
|
||||
function toggleHeight(event) {
|
||||
if (customization === 1) {
|
||||
tip("You cannot turn off the layer when heightmap is in edit mode", false, "error");
|
||||
return;
|
||||
}
|
||||
if (customization === 1) return tip("You cannot turn off the layer when heightmap is in edit mode", false, "error");
|
||||
|
||||
if (!terrs.selectAll("*").size()) {
|
||||
const children = terrs.selectAll("#oceanHeights > *, #landHeights > *");
|
||||
if (!children.size()) {
|
||||
turnButtonOn("toggleHeight");
|
||||
drawHeightmap();
|
||||
if (event && isCtrlClick(event)) editStyle("terrs");
|
||||
} else {
|
||||
if (event && isCtrlClick(event)) {
|
||||
editStyle("terrs");
|
||||
return;
|
||||
}
|
||||
if (event && isCtrlClick(event)) return editStyle("terrs");
|
||||
turnButtonOff("toggleHeight");
|
||||
terrs.selectAll("*").remove();
|
||||
children.remove();
|
||||
}
|
||||
}
|
||||
|
||||
function drawHeightmap() {
|
||||
TIME && console.time("drawHeightmap");
|
||||
terrs.selectAll("*").remove();
|
||||
|
||||
const {cells, vertices} = pack;
|
||||
const n = cells.i.length;
|
||||
const used = new Uint8Array(cells.i.length);
|
||||
const paths = new Array(101).fill("");
|
||||
const ocean = terrs.select("#oceanHeights");
|
||||
const land = terrs.select("#landHeights");
|
||||
|
||||
const scheme = getColorScheme(terrs.attr("scheme"));
|
||||
const terracing = terrs.attr("terracing") / 10; // add additional shifted darker layer for pseudo-3d effect
|
||||
const skip = +terrs.attr("skip") + 1;
|
||||
const simplification = +terrs.attr("relax");
|
||||
ocean.selectAll("*").remove();
|
||||
land.selectAll("*").remove();
|
||||
|
||||
switch (+terrs.attr("curve")) {
|
||||
case 0:
|
||||
lineGen.curve(d3.curveBasisClosed);
|
||||
break;
|
||||
case 1:
|
||||
lineGen.curve(d3.curveLinear);
|
||||
break;
|
||||
case 2:
|
||||
lineGen.curve(d3.curveStep);
|
||||
break;
|
||||
default:
|
||||
lineGen.curve(d3.curveBasisClosed);
|
||||
const paths = new Array(101);
|
||||
|
||||
// ocean cells
|
||||
const renderOceanCells = Boolean(+ocean.attr("data-render"));
|
||||
if (renderOceanCells) {
|
||||
const {cells, vertices} = grid;
|
||||
const used = new Uint8Array(cells.i.length);
|
||||
|
||||
const skip = +ocean.attr("skip") + 1 || 1;
|
||||
const relax = +ocean.attr("relax") || 0;
|
||||
lineGen.curve(d3[ocean.attr("curve") || "curveBasisClosed"]);
|
||||
|
||||
let currentLayer = 0;
|
||||
const heights = Array.from(cells.i).sort((a, b) => cells.h[a] - cells.h[b]);
|
||||
|
||||
for (const i of heights) {
|
||||
const h = cells.h[i];
|
||||
if (h > currentLayer) currentLayer += skip;
|
||||
if (h < currentLayer) continue;
|
||||
if (currentLayer >= 20) break;
|
||||
if (used[i]) continue; // already marked
|
||||
const onborder = cells.c[i].some(n => cells.h[n] < h);
|
||||
if (!onborder) continue;
|
||||
const vertex = cells.v[i].find(v => vertices.c[v].some(i => cells.h[i] < h));
|
||||
const chain = connectVertices(cells, vertices, vertex, h, used);
|
||||
if (chain.length < 3) continue;
|
||||
const points = simplifyLine(chain, relax).map(v => vertices.p[v]);
|
||||
if (!paths[h]) paths[h] = "";
|
||||
paths[h] += round(lineGen(points));
|
||||
}
|
||||
}
|
||||
|
||||
let currentLayer = 20;
|
||||
const heights = cells.i.sort((a, b) => cells.h[a] - cells.h[b]);
|
||||
for (const i of heights) {
|
||||
const h = cells.h[i];
|
||||
if (h > currentLayer) currentLayer += skip;
|
||||
if (currentLayer > 100) break; // no layers possible with height > 100
|
||||
if (h < currentLayer) continue;
|
||||
if (used[i]) continue; // already marked
|
||||
const onborder = cells.c[i].some(n => cells.h[n] < h);
|
||||
if (!onborder) continue;
|
||||
const vertex = cells.v[i].find(v => vertices.c[v].some(i => cells.h[i] < h));
|
||||
const chain = connectVertices(vertex, h);
|
||||
if (chain.length < 3) continue;
|
||||
const points = simplifyLine(chain).map(v => vertices.p[v]);
|
||||
paths[h] += round(lineGen(points));
|
||||
// land cells
|
||||
{
|
||||
const {cells, vertices} = pack;
|
||||
const used = new Uint8Array(cells.i.length);
|
||||
|
||||
const skip = +land.attr("skip") + 1 || 1;
|
||||
const relax = +land.attr("relax") || 0;
|
||||
lineGen.curve(d3[land.attr("curve") || "curveBasisClosed"]);
|
||||
|
||||
let currentLayer = 20;
|
||||
const heights = Array.from(cells.i).sort((a, b) => cells.h[a] - cells.h[b]);
|
||||
for (const i of heights) {
|
||||
const h = cells.h[i];
|
||||
if (h > currentLayer) currentLayer += skip;
|
||||
if (h < currentLayer) continue;
|
||||
if (currentLayer > 100) break; // no layers possible with height > 100
|
||||
if (used[i]) continue; // already marked
|
||||
const onborder = cells.c[i].some(n => cells.h[n] < h);
|
||||
if (!onborder) continue;
|
||||
const vertex = cells.v[i].find(v => vertices.c[v].some(i => cells.h[i] < h));
|
||||
const chain = connectVertices(cells, vertices, vertex, h, used);
|
||||
if (chain.length < 3) continue;
|
||||
const points = simplifyLine(chain, relax).map(v => vertices.p[v]);
|
||||
if (!paths[h]) paths[h] = "";
|
||||
paths[h] += round(lineGen(points));
|
||||
}
|
||||
}
|
||||
|
||||
terrs
|
||||
.append("rect")
|
||||
.attr("x", 0)
|
||||
.attr("y", 0)
|
||||
.attr("width", graphWidth)
|
||||
.attr("height", graphHeight)
|
||||
.attr("fill", scheme(0.8)); // draw base layer
|
||||
for (const i of d3.range(20, 101)) {
|
||||
if (paths[i].length < 10) continue;
|
||||
const color = getColor(i, scheme);
|
||||
if (terracing)
|
||||
terrs
|
||||
.append("path")
|
||||
.attr("d", paths[i])
|
||||
.attr("transform", "translate(.7,1.4)")
|
||||
.attr("fill", d3.color(color).darker(terracing))
|
||||
.attr("data-height", i);
|
||||
terrs.append("path").attr("d", paths[i]).attr("fill", color).attr("data-height", i);
|
||||
// render paths
|
||||
for (const height of d3.range(0, 101)) {
|
||||
const group = height < 20 ? ocean : land;
|
||||
const scheme = getColorScheme(group.attr("scheme"));
|
||||
|
||||
if (height === 0 && renderOceanCells) {
|
||||
// draw base ocean layer
|
||||
group
|
||||
.append("rect")
|
||||
.attr("x", 0)
|
||||
.attr("y", 0)
|
||||
.attr("width", graphWidth)
|
||||
.attr("height", graphHeight)
|
||||
.attr("fill", scheme(1));
|
||||
}
|
||||
|
||||
if (height === 20) {
|
||||
// draw base land layer
|
||||
group
|
||||
.append("rect")
|
||||
.attr("x", 0)
|
||||
.attr("y", 0)
|
||||
.attr("width", graphWidth)
|
||||
.attr("height", graphHeight)
|
||||
.attr("fill", scheme(0.8));
|
||||
}
|
||||
|
||||
if (paths[height] && paths[height].length >= 10) {
|
||||
const terracing = group.attr("terracing") / 10 || 0;
|
||||
const color = getColor(height, scheme);
|
||||
|
||||
if (terracing) {
|
||||
group
|
||||
.append("path")
|
||||
.attr("d", paths[height])
|
||||
.attr("transform", "translate(.7,1.4)")
|
||||
.attr("fill", d3.color(color).darker(terracing))
|
||||
.attr("data-height", height);
|
||||
}
|
||||
group.append("path").attr("d", paths[height]).attr("fill", color).attr("data-height", height);
|
||||
}
|
||||
}
|
||||
|
||||
// connect vertices to chain
|
||||
function connectVertices(start, h) {
|
||||
function connectVertices(cells, vertices, start, h, used) {
|
||||
const n = cells.i.length;
|
||||
const chain = []; // vertices chain to form a path
|
||||
for (let i = 0, current = start; i === 0 || (current !== start && i < 20000); i++) {
|
||||
const prev = chain[chain.length - 1]; // previous vertex in chain
|
||||
|
|
@ -295,7 +338,7 @@ function drawHeightmap() {
|
|||
return chain;
|
||||
}
|
||||
|
||||
function simplifyLine(chain) {
|
||||
function simplifyLine(chain, simplification) {
|
||||
if (!simplification) return chain;
|
||||
const n = simplification + 1; // filter each nth element
|
||||
return chain.filter((d, i) => i % n === 0);
|
||||
|
|
@ -1670,10 +1713,7 @@ function toggleLabels(event) {
|
|||
invokeActiveZooming();
|
||||
if (event && isCtrlClick(event)) editStyle("labels");
|
||||
} else {
|
||||
if (event && isCtrlClick(event)) {
|
||||
editStyle("labels");
|
||||
return;
|
||||
}
|
||||
if (event && isCtrlClick(event)) return editStyle("labels");
|
||||
turnButtonOff("toggleLabels");
|
||||
labels.style("display", "none");
|
||||
}
|
||||
|
|
@ -1685,10 +1725,7 @@ function toggleIcons(event) {
|
|||
$("#icons").fadeIn();
|
||||
if (event && isCtrlClick(event)) editStyle("burgIcons");
|
||||
} else {
|
||||
if (event && isCtrlClick(event)) {
|
||||
editStyle("burgIcons");
|
||||
return;
|
||||
}
|
||||
if (event && isCtrlClick(event)) return editStyle("burgIcons");
|
||||
turnButtonOff("toggleIcons");
|
||||
$("#icons").fadeOut();
|
||||
}
|
||||
|
|
@ -1701,10 +1738,7 @@ function toggleRulers(event) {
|
|||
rulers.draw();
|
||||
ruler.style("display", null);
|
||||
} else {
|
||||
if (event && isCtrlClick(event)) {
|
||||
editStyle("ruler");
|
||||
return;
|
||||
}
|
||||
if (event && isCtrlClick(event)) return editStyle("ruler");
|
||||
turnButtonOff("toggleRulers");
|
||||
ruler.selectAll("*").remove();
|
||||
ruler.style("display", "none");
|
||||
|
|
@ -1715,17 +1749,113 @@ function toggleScaleBar(event) {
|
|||
if (!layerIsOn("toggleScaleBar")) {
|
||||
turnButtonOn("toggleScaleBar");
|
||||
$("#scaleBar").fadeIn();
|
||||
if (event && isCtrlClick(event)) editUnits();
|
||||
if (event && isCtrlClick(event)) editStyle("scaleBar");
|
||||
} else {
|
||||
if (event && isCtrlClick(event)) {
|
||||
editUnits();
|
||||
return;
|
||||
}
|
||||
if (event && isCtrlClick(event)) return editStyle("scaleBar");
|
||||
$("#scaleBar").fadeOut();
|
||||
turnButtonOff("toggleScaleBar");
|
||||
}
|
||||
}
|
||||
|
||||
function drawScaleBar(scaleBar, scaleLevel) {
|
||||
if (!scaleBar.size() || scaleBar.style("display") === "none") return;
|
||||
|
||||
const distanceScale = +distanceScaleInput.value;
|
||||
const unit = distanceUnitInput.value;
|
||||
const size = +scaleBar.attr("data-bar-size");
|
||||
|
||||
const length = (function () {
|
||||
const init = 100;
|
||||
let val = (init * size * distanceScale) / scaleLevel; // bar length in distance unit
|
||||
if (val > 900) val = rn(val, -3); // round to 1000
|
||||
else if (val > 90) val = rn(val, -2); // round to 100
|
||||
else if (val > 9) val = rn(val, -1); // round to 10
|
||||
else val = rn(val); // round to 1
|
||||
const length = (val * scaleLevel) / distanceScale; // actual length in pixels on this scale
|
||||
return length;
|
||||
})();
|
||||
|
||||
scaleBar.select("#scaleBarContent").remove(); // redraw content every time
|
||||
const content = scaleBar.append("g").attr("id", "scaleBarContent");
|
||||
|
||||
const lines = content.append("g");
|
||||
lines
|
||||
.append("line")
|
||||
.attr("x1", 0.5)
|
||||
.attr("y1", 0)
|
||||
.attr("x2", length + size - 0.5)
|
||||
.attr("y2", 0)
|
||||
.attr("stroke-width", size)
|
||||
.attr("stroke", "white");
|
||||
lines
|
||||
.append("line")
|
||||
.attr("x1", 0)
|
||||
.attr("y1", size)
|
||||
.attr("x2", length + size)
|
||||
.attr("y2", size)
|
||||
.attr("stroke-width", size)
|
||||
.attr("stroke", "#3d3d3d");
|
||||
lines
|
||||
.append("line")
|
||||
.attr("x1", 0)
|
||||
.attr("y1", 0)
|
||||
.attr("x2", length + size)
|
||||
.attr("y2", 0)
|
||||
.attr("stroke-width", rn(size * 3, 2))
|
||||
.attr("stroke-dasharray", size + " " + rn(length / 5 - size, 2))
|
||||
.attr("stroke", "#3d3d3d");
|
||||
|
||||
const texts = content.append("g").attr("text-anchor", "middle").attr("font-family", "var(--serif)");
|
||||
texts
|
||||
.selectAll("text")
|
||||
.data(d3.range(0, 6))
|
||||
.enter()
|
||||
.append("text")
|
||||
.attr("x", d => rn((d * length) / 5, 2))
|
||||
.attr("y", 0)
|
||||
.attr("dy", "-.6em")
|
||||
.text(d => rn((((d * length) / 5) * distanceScale) / scaleLevel) + (d < 5 ? "" : " " + unit));
|
||||
|
||||
const label = scaleBar.attr("data-label");
|
||||
if (label) {
|
||||
texts
|
||||
.append("text")
|
||||
.attr("x", (length + 1) / 2)
|
||||
.attr("dy", ".6em")
|
||||
.attr("dominant-baseline", "text-before-edge")
|
||||
.text(label);
|
||||
}
|
||||
|
||||
const scaleBarBack = scaleBar.select("#scaleBarBack");
|
||||
if (scaleBarBack.size()) {
|
||||
const bbox = content.node().getBBox();
|
||||
const paddingTop = +scaleBarBack.attr("data-top") || 0;
|
||||
const paddingLeft = +scaleBarBack.attr("data-left") || 0;
|
||||
const paddingRight = +scaleBarBack.attr("data-right") || 0;
|
||||
const paddingBottom = +scaleBarBack.attr("data-bottom") || 0;
|
||||
|
||||
scaleBar
|
||||
.select("#scaleBarBack")
|
||||
.attr("x", -paddingLeft)
|
||||
.attr("y", -paddingTop)
|
||||
.attr("width", bbox.width + paddingRight)
|
||||
.attr("height", bbox.height + paddingBottom);
|
||||
}
|
||||
}
|
||||
|
||||
// fit ScaleBar to screen size
|
||||
function fitScaleBar(scaleBar, fullWidth, fullHeight) {
|
||||
if (!scaleBar.select("rect").size() || scaleBar.style("display") === "none") return;
|
||||
|
||||
const posX = +scaleBar.attr("data-x") || 99;
|
||||
const posY = +scaleBar.attr("data-y") || 99;
|
||||
const bbox = scaleBar.select("rect").node().getBBox();
|
||||
|
||||
const x = rn((fullWidth * posX) / 100 - bbox.width + 10);
|
||||
const y = rn((fullHeight * posY) / 100 - bbox.height + 20);
|
||||
scaleBar.attr("transform", `translate(${x},${y})`);
|
||||
}
|
||||
|
||||
function toggleZones(event) {
|
||||
if (!layerIsOn("toggleZones")) {
|
||||
turnButtonOn("toggleZones");
|
||||
|
|
@ -1748,10 +1878,7 @@ function toggleEmblems(event) {
|
|||
$("#emblems").fadeIn();
|
||||
if (event && isCtrlClick(event)) editStyle("emblems");
|
||||
} else {
|
||||
if (event && isCtrlClick(event)) {
|
||||
editStyle("emblems");
|
||||
return;
|
||||
}
|
||||
if (event && isCtrlClick(event)) return editStyle("emblems");
|
||||
$("#emblems").fadeOut();
|
||||
turnButtonOff("toggleEmblems");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -532,101 +532,3 @@ class Planimeter extends Measurer {
|
|||
this.el.select("text").attr("x", c[0]).attr("y", c[1]).text(area);
|
||||
}
|
||||
}
|
||||
|
||||
// Scale bar
|
||||
function drawScaleBar(scaleBar, scaleLevel) {
|
||||
if (!scaleBar.size() || scaleBar.style("display") === "none") return;
|
||||
scaleBar.selectAll("*").remove(); // fully redraw every time
|
||||
|
||||
const distanceScale = +distanceScaleInput.value;
|
||||
const unit = distanceUnitInput.value;
|
||||
const size = +barSizeInput.value;
|
||||
|
||||
// calculate size
|
||||
const init = 100;
|
||||
let val = (init * size * distanceScale) / scaleLevel; // bar length in distance unit
|
||||
if (val > 900) val = rn(val, -3);
|
||||
// round to 1000
|
||||
else if (val > 90) val = rn(val, -2);
|
||||
// round to 100
|
||||
else if (val > 9) val = rn(val, -1);
|
||||
// round to 10
|
||||
else val = rn(val); // round to 1
|
||||
const length = (val * scaleLevel) / distanceScale; // actual length in pixels on this scale
|
||||
|
||||
scaleBar
|
||||
.append("line")
|
||||
.attr("x1", 0.5)
|
||||
.attr("y1", 0)
|
||||
.attr("x2", length + size - 0.5)
|
||||
.attr("y2", 0)
|
||||
.attr("stroke-width", size)
|
||||
.attr("stroke", "white");
|
||||
scaleBar
|
||||
.append("line")
|
||||
.attr("x1", 0)
|
||||
.attr("y1", size)
|
||||
.attr("x2", length + size)
|
||||
.attr("y2", size)
|
||||
.attr("stroke-width", size)
|
||||
.attr("stroke", "#3d3d3d");
|
||||
const dash = size + " " + rn(length / 5 - size, 2);
|
||||
scaleBar
|
||||
.append("line")
|
||||
.attr("x1", 0)
|
||||
.attr("y1", 0)
|
||||
.attr("x2", length + size)
|
||||
.attr("y2", 0)
|
||||
.attr("stroke-width", rn(size * 3, 2))
|
||||
.attr("stroke-dasharray", dash)
|
||||
.attr("stroke", "#3d3d3d");
|
||||
|
||||
const fontSize = rn(5 * size, 1);
|
||||
scaleBar
|
||||
.selectAll("text")
|
||||
.data(d3.range(0, 6))
|
||||
.enter()
|
||||
.append("text")
|
||||
.attr("x", d => rn((d * length) / 5, 2))
|
||||
.attr("y", 0)
|
||||
.attr("dy", "-.6em")
|
||||
.attr("font-size", fontSize)
|
||||
.text(d => rn((((d * length) / 5) * distanceScale) / scaleLevel) + (d < 5 ? "" : " " + unit));
|
||||
|
||||
if (barLabel.value !== "") {
|
||||
scaleBar
|
||||
.append("text")
|
||||
.attr("x", (length + 1) / 2)
|
||||
.attr("y", 2 * size)
|
||||
.attr("dominant-baseline", "text-before-edge")
|
||||
.attr("font-size", fontSize)
|
||||
.text(barLabel.value);
|
||||
}
|
||||
|
||||
const bbox = scaleBar.node().getBBox();
|
||||
// append backbround rectangle
|
||||
scaleBar
|
||||
.insert("rect", ":first-child")
|
||||
.attr("x", -10)
|
||||
.attr("y", -20)
|
||||
.attr("width", bbox.width + 10)
|
||||
.attr("height", bbox.height + 15)
|
||||
.attr("stroke-width", size)
|
||||
.attr("stroke", "none")
|
||||
.attr("filter", "url(#blur5)")
|
||||
.attr("fill", barBackColor.value)
|
||||
.attr("opacity", +barBackOpacity.value);
|
||||
}
|
||||
|
||||
// fit ScaleBar to screen size
|
||||
function fitScaleBar(scaleBar, fullWidth, fullHeight) {
|
||||
if (!scaleBar.select("rect").size() || scaleBar.style("display") === "none") return;
|
||||
|
||||
const px = isNaN(+barPosX.value) ? 0.99 : barPosX.value / 100;
|
||||
const py = isNaN(+barPosY.value) ? 0.99 : barPosY.value / 100;
|
||||
const bbox = scaleBar.select("rect").node().getBBox();
|
||||
|
||||
const x = rn(fullWidth * px - bbox.width + 10);
|
||||
const y = rn(fullHeight * py - bbox.height + 20);
|
||||
scaleBar.attr("transform", `translate(${x},${y})`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,9 @@ function overviewMilitary() {
|
|||
const insert = html => document.getElementById("militaryTotal").insertAdjacentHTML("beforebegin", html);
|
||||
for (const u of options.military) {
|
||||
const label = capitalize(u.name.replace(/_/g, " "));
|
||||
insert(`<div data-tip="State ${u.name} units number. Click to sort" class="sortable removable" data-sortby="${u.name}">${label} </div>`);
|
||||
insert(
|
||||
`<div data-tip="State ${u.name} units number. Click to sort" class="sortable removable" data-sortby="${u.name}">${label} </div>`
|
||||
);
|
||||
}
|
||||
header.querySelectorAll(".removable").forEach(function (e) {
|
||||
e.addEventListener("click", function () {
|
||||
|
|
@ -76,7 +78,9 @@ function overviewMilitary() {
|
|||
const rate = (total / population) * 100;
|
||||
|
||||
const sortData = options.military.map(u => `data-${u.name}="${getForces(u)}"`).join(" ");
|
||||
const lineData = options.military.map(u => `<div data-type="${u.name}" data-tip="State ${u.name} units number">${getForces(u)}</div>`).join(" ");
|
||||
const lineData = options.military
|
||||
.map(u => `<div data-type="${u.name}" data-tip="State ${u.name} units number">${getForces(u)}</div>`)
|
||||
.join(" ");
|
||||
|
||||
lines += /* html */ `<div
|
||||
class="states"
|
||||
|
|
@ -91,9 +95,14 @@ function overviewMilitary() {
|
|||
<fill-box data-tip="${s.fullName}" fill="${s.color}" disabled></fill-box>
|
||||
<input data-tip="${s.fullName}" style="width:6em" value="${s.name}" readonly />
|
||||
${lineData}
|
||||
<div data-type="total" data-tip="Total state military personnel (considering crew)" style="font-weight: bold">${si(total)}</div>
|
||||
<div data-type="total" data-tip="Total state military personnel (considering crew)" style="font-weight: bold">${si(
|
||||
total
|
||||
)}</div>
|
||||
<div data-type="population" data-tip="State population">${si(population)}</div>
|
||||
<div data-type="rate" data-tip="Military personnel rate (% of state population). Depends on war alert">${rn(rate, 2)}%</div>
|
||||
<div data-type="rate" data-tip="Military personnel rate (% of state population). Depends on war alert">${rn(
|
||||
rate,
|
||||
2
|
||||
)}%</div>
|
||||
<input
|
||||
data-tip="War Alert. Editable modifier to military forces number, depends of political situation"
|
||||
style="width:4.1em"
|
||||
|
|
@ -131,7 +140,9 @@ function overviewMilitary() {
|
|||
});
|
||||
|
||||
const getForces = u => s.military.reduce((s, r) => s + (r.u[u.name] || 0), 0);
|
||||
options.military.forEach(u => (line.dataset[u.name] = line.querySelector(`div[data-type='${u.name}']`).innerHTML = getForces(u)));
|
||||
options.military.forEach(
|
||||
u => (line.dataset[u.name] = line.querySelector(`div[data-type='${u.name}']`).innerHTML = getForces(u))
|
||||
);
|
||||
|
||||
const population = rn((s.rural + s.urban * urbanization) * populationRate);
|
||||
const total = (line.dataset.total = options.military.reduce((s, u) => s + getForces(u) * u.crew, 0));
|
||||
|
|
@ -237,7 +248,16 @@ function overviewMilitary() {
|
|||
position: {my: "center", at: "center", of: "svg"},
|
||||
buttons: {
|
||||
Apply: applyMilitaryOptions,
|
||||
Add: () => addUnitLine({icon: "🛡️", name: "custom" + militaryOptionsTable.rows.length, rural: 0.2, urban: 0.5, crew: 1, power: 1, type: "melee"}),
|
||||
Add: () =>
|
||||
addUnitLine({
|
||||
icon: "🛡️",
|
||||
name: "custom" + militaryOptionsTable.rows.length,
|
||||
rural: 0.2,
|
||||
urban: 0.5,
|
||||
crew: 1,
|
||||
power: 1,
|
||||
type: "melee"
|
||||
}),
|
||||
Restore: restoreDefaultUnits,
|
||||
Cancel: function () {
|
||||
$(this).dialog("close");
|
||||
|
|
@ -262,7 +282,7 @@ function overviewMilitary() {
|
|||
if (el.tagName !== "BUTTON") return;
|
||||
const type = el.dataset.type;
|
||||
|
||||
if (type === "icon") return selectIcon(el.innerHTML, v => (el.innerHTML = v));
|
||||
if (type === "icon") return selectIcon(el.textContent, v => (el.textContent = v));
|
||||
if (type === "biomes") {
|
||||
const {i, name, color} = biomesData;
|
||||
const biomesArray = Array(i.length).fill(null);
|
||||
|
|
@ -294,7 +314,9 @@ function overviewMilitary() {
|
|||
function addUnitLine(unit) {
|
||||
const {type, icon, name, rural, urban, power, crew, separate} = unit;
|
||||
const row = document.createElement("tr");
|
||||
const typeOptions = types.map(t => `<option ${type === t ? "selected" : ""} value="${t}">${t}</option>`).join(" ");
|
||||
const typeOptions = types
|
||||
.map(t => `<option ${type === t ? "selected" : ""} value="${t}">${t}</option>`)
|
||||
.join(" ");
|
||||
|
||||
const getLimitButton = attr =>
|
||||
`<button
|
||||
|
|
@ -305,7 +327,9 @@ function overviewMilitary() {
|
|||
${getLimitText(unit[attr])}
|
||||
</button>`;
|
||||
|
||||
row.innerHTML = /* html */ `<td><button data-type="icon" data-tip="Click to select unit icon">${icon || " "}</button></td>
|
||||
row.innerHTML = /* html */ `<td><button data-type="icon" data-tip="Click to select unit icon">${
|
||||
icon || " "
|
||||
}</button></td>
|
||||
<td><input data-tip="Type unit name. If name is changed for existing unit, old unit will be replaced" value="${name}" /></td>
|
||||
<td>${getLimitButton("biomes")}</td>
|
||||
<td>${getLimitButton("states")}</td>
|
||||
|
|
@ -344,7 +368,9 @@ function overviewMilitary() {
|
|||
const lines = filtered.map(
|
||||
({i, name, fullName, color}) =>
|
||||
`<tr data-tip="${name}"><td><span style="color:${color}">⬤</span></td>
|
||||
<td><input data-i="${i}" id="el${i}" type="checkbox" class="checkbox" ${!initial.length || initial.includes(i) ? "checked" : ""} >
|
||||
<td><input data-i="${i}" id="el${i}" type="checkbox" class="checkbox" ${
|
||||
!initial.length || initial.includes(i) ? "checked" : ""
|
||||
} >
|
||||
<label for="el${i}" class="checkbox-label">${fullName || name}</label>
|
||||
</td></tr>`
|
||||
);
|
||||
|
|
@ -387,22 +413,21 @@ function overviewMilitary() {
|
|||
function applyMilitaryOptions() {
|
||||
const unitLines = Array.from(tableBody.querySelectorAll("tr"));
|
||||
const names = unitLines.map(r => r.querySelector("input").value.replace(/[&\/\\#, +()$~%.'":*?<>{}]/g, "_"));
|
||||
if (new Set(names).size !== names.length) {
|
||||
tip("All units should have unique names", false, "error");
|
||||
return;
|
||||
}
|
||||
if (new Set(names).size !== names.length) return tip("All units should have unique names", false, "error");
|
||||
|
||||
$("#militaryOptions").dialog("close");
|
||||
|
||||
options.military = unitLines.map((r, i) => {
|
||||
const elements = Array.from(r.querySelectorAll("input, button, select"));
|
||||
const [icon, name, biomes, states, cultures, religions, rural, urban, crew, power, type, separate] = elements.map(el => {
|
||||
const {type, value} = el.dataset || {};
|
||||
if (type === "icon") return el.innerHTML || "⠀";
|
||||
if (type) return value ? value.split(",").map(v => parseInt(v)) : null;
|
||||
if (el.type === "number") return +el.value || 0;
|
||||
if (el.type === "checkbox") return +el.checked || 0;
|
||||
return el.value;
|
||||
});
|
||||
const [icon, name, biomes, states, cultures, religions, rural, urban, crew, power, type, separate] =
|
||||
elements.map(el => {
|
||||
const {type, value} = el.dataset || {};
|
||||
if (type === "icon") return el.textContent || "⠀";
|
||||
if (type) return value ? value.split(",").map(v => parseInt(v)) : null;
|
||||
if (el.type === "number") return +el.value || 0;
|
||||
if (el.type === "checkbox") return +el.checked || 0;
|
||||
return el.value;
|
||||
});
|
||||
|
||||
const unit = {icon, name: names[i], rural, urban, crew, power, type, separate};
|
||||
if (biomes) unit.biomes = biomes;
|
||||
|
|
@ -419,7 +444,8 @@ function overviewMilitary() {
|
|||
}
|
||||
|
||||
function militaryRecalculate() {
|
||||
alertMessage.innerHTML = "Are you sure you want to recalculate military forces for all states?<br>Regiments for all states will be regenerated";
|
||||
alertMessage.innerHTML =
|
||||
"Are you sure you want to recalculate military forces for all states?<br>Regiments for all states will be regenerated";
|
||||
$("#alert").dialog({
|
||||
resizable: false,
|
||||
title: "Remove regiment",
|
||||
|
|
|
|||
|
|
@ -691,7 +691,7 @@ function changeEra() {
|
|||
}
|
||||
|
||||
async function openTemplateSelectionDialog() {
|
||||
const HeightmapSelectionDialog = await import("../dynamic/heightmap-selection.js?v=1.93.12");
|
||||
const HeightmapSelectionDialog = await import("../dynamic/heightmap-selection.js?v=1.96.00");
|
||||
HeightmapSelectionDialog.open();
|
||||
}
|
||||
|
||||
|
|
@ -774,7 +774,7 @@ function showExportPane() {
|
|||
}
|
||||
|
||||
async function exportToJson(type) {
|
||||
const {exportToJson} = await import("../dynamic/export-json.js?v=1.93.03");
|
||||
const {exportToJson} = await import("../dynamic/export-json.js?v=1.96.00");
|
||||
exportToJson(type);
|
||||
}
|
||||
|
||||
|
|
@ -867,11 +867,9 @@ byId("mapToLoad").addEventListener("change", function () {
|
|||
});
|
||||
|
||||
function openExportToPngTiles() {
|
||||
byId("tileStatus").innerHTML = "";
|
||||
closeDialogs();
|
||||
updateTilesOptions();
|
||||
const status = byId("tileStatus");
|
||||
status.innerHTML = "";
|
||||
let loading = null;
|
||||
|
||||
const inputs = byId("exportToPngTilesScreen").querySelectorAll("input");
|
||||
inputs.forEach(input => input.addEventListener("input", updateTilesOptions));
|
||||
|
|
@ -881,16 +879,7 @@ function openExportToPngTiles() {
|
|||
title: "Download tiles",
|
||||
width: "23em",
|
||||
buttons: {
|
||||
Download: function () {
|
||||
status.innerHTML = "Preparing for download...";
|
||||
setTimeout(() => (status.innerHTML = "Downloading. It may take some time."), 1000);
|
||||
loading = setInterval(() => (status.innerHTML += "."), 1000);
|
||||
exportToPngTiles().then(() => {
|
||||
clearInterval(loading);
|
||||
status.innerHTML = /* html */ `Done. Check file in "Downloads" (crtl + J)`;
|
||||
setTimeout(() => (status.innerHTML = ""), 8000);
|
||||
});
|
||||
},
|
||||
Download: () => exportToPngTiles(),
|
||||
Cancel: function () {
|
||||
$(this).dialog("close");
|
||||
}
|
||||
|
|
@ -898,7 +887,6 @@ function openExportToPngTiles() {
|
|||
close: () => {
|
||||
inputs.forEach(input => input.removeEventListener("input", updateTilesOptions));
|
||||
debug.selectAll("*").remove();
|
||||
clearInterval(loading);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -928,18 +916,22 @@ function updateTilesOptions() {
|
|||
const labels = [];
|
||||
const tileW = (graphWidth / tilesX) | 0;
|
||||
const tileH = (graphHeight / tilesY) | 0;
|
||||
for (let y = 0, i = 0; y + tileH <= graphHeight; y += tileH) {
|
||||
for (let x = 0; x + tileW <= graphWidth; x += tileW, i++) {
|
||||
|
||||
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
for (let y = 0, row = 0; y + tileH <= graphHeight; y += tileH, row++) {
|
||||
for (let x = 0, column = 1; x + tileW <= graphWidth; x += tileW, column++) {
|
||||
rects.push(`<rect x=${x} y=${y} width=${tileW} height=${tileH} />`);
|
||||
labels.push(`<text x=${x + tileW / 2} y=${y + tileH / 2}>${i}</text>`);
|
||||
const label = alphabet[row % alphabet.length] + column;
|
||||
labels.push(`<text x=${x + tileW / 2} y=${y + tileH / 2}>${label}</text>`);
|
||||
}
|
||||
}
|
||||
const rectsG = "<g fill='none' stroke='#000'>" + rects.join("") + "</g>";
|
||||
const labelsG =
|
||||
"<g fill='#000' stroke='none' text-anchor='middle' dominant-baseline='central' font-size='24px'>" +
|
||||
labels.join("") +
|
||||
"</g>";
|
||||
debug.html(rectsG + labelsG);
|
||||
|
||||
debug.html(`
|
||||
<g fill='none' stroke='#000'>${rects.join("")}</g>
|
||||
<g fill='#000' stroke='none' text-anchor='middle' dominant-baseline='central' font-size='18px'>${labels.join(
|
||||
""
|
||||
)}</g>
|
||||
`);
|
||||
}
|
||||
|
||||
// View mode
|
||||
|
|
|
|||
|
|
@ -45,12 +45,14 @@ function editProvinces() {
|
|||
cl = el.classList,
|
||||
line = el.parentNode,
|
||||
p = +line.dataset.id;
|
||||
const stateId = pack.provinces[p].state;
|
||||
|
||||
if (el.tagName === "FILL-BOX") changeFill(el);
|
||||
else if (cl.contains("name")) editProvinceName(p);
|
||||
else if (cl.contains("coaIcon")) editEmblem("province", "provinceCOA" + p, pack.provinces[p]);
|
||||
else if (cl.contains("icon-star-empty")) capitalZoomIn(p);
|
||||
else if (cl.contains("icon-flag-empty")) triggerIndependencePromps(p);
|
||||
else if (cl.contains("icon-dot-circled")) overviewBurgs({stateId});
|
||||
else if (cl.contains("culturePopulation")) changePopulation(p);
|
||||
else if (cl.contains("icon-pin")) toggleFog(p, cl);
|
||||
else if (cl.contains("icon-trash-empty")) removeProvince(p);
|
||||
|
|
@ -72,9 +74,8 @@ function editProvinces() {
|
|||
}
|
||||
|
||||
function collectStatistics() {
|
||||
const cells = pack.cells,
|
||||
provinces = pack.provinces,
|
||||
burgs = pack.burgs;
|
||||
const {cells, provinces, burgs} = pack;
|
||||
|
||||
provinces.forEach(p => {
|
||||
if (!p.i || p.removed) return;
|
||||
p.area = p.rural = p.urban = 0;
|
||||
|
|
@ -108,16 +109,18 @@ function editProvinces() {
|
|||
statesSorted.forEach(s => stateFilter.options.add(new Option(s.name, s.i, false, s.i == selectedState)));
|
||||
}
|
||||
|
||||
// add line for each state
|
||||
// add line for each province
|
||||
function provincesEditorAddLines() {
|
||||
const unit = " " + getAreaUnit();
|
||||
const selectedState = +document.getElementById("provincesFilterState").value;
|
||||
let filtered = pack.provinces.filter(p => p.i && !p.removed); // all valid burgs
|
||||
if (selectedState != -1) filtered = filtered.filter(p => p.state === selectedState); // filtered by state
|
||||
body.innerHTML = "";
|
||||
let lines = "",
|
||||
totalArea = 0,
|
||||
totalPopulation = 0;
|
||||
|
||||
let lines = "";
|
||||
let totalArea = 0;
|
||||
let totalPopulation = 0;
|
||||
let totalBurgs = 0;
|
||||
|
||||
for (const p of filtered) {
|
||||
const area = getArea(p.area);
|
||||
|
|
@ -129,6 +132,7 @@ function editProvinces() {
|
|||
rural
|
||||
)}; Urban population: ${si(urban)}`;
|
||||
totalPopulation += population;
|
||||
totalBurgs += p.burgs.length;
|
||||
|
||||
const stateName = pack.states[p.state].name;
|
||||
const capital = p.burg ? pack.burgs[p.burg].name : "";
|
||||
|
|
@ -145,6 +149,7 @@ function editProvinces() {
|
|||
data-state="${stateName}"
|
||||
data-area=${area}
|
||||
data-population=${population}
|
||||
data-burgs=${p.burgs.length}
|
||||
>
|
||||
<fill-box fill="${p.color}"></fill-box>
|
||||
<input data-tip="Province name. Click to change" class="name pointer" value="${p.name}" readonly />
|
||||
|
|
@ -164,6 +169,8 @@ function editProvinces() {
|
|||
${p.burgs.length ? getCapitalOptions(p.burgs, p.burg) : ""}
|
||||
</select>
|
||||
<input data-tip="Province owner" class="provinceOwner" value="${stateName}" disabled">
|
||||
<span data-tip="Click to overview province burgs" style="padding-right: 1px" class="icon-dot-circled pointer hide"></span>
|
||||
<div data-tip="Burgs count" class="provinceBurgs hide">${p.burgs.length}</div>
|
||||
<span data-tip="Province area" style="padding-right: 4px" class="icon-map-o hide"></span>
|
||||
<div data-tip="Province area" class="biomeArea hide">${si(area) + unit}</div>
|
||||
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
||||
|
|
@ -180,11 +187,12 @@ function editProvinces() {
|
|||
body.innerHTML = lines;
|
||||
|
||||
// update footer
|
||||
provincesFooterNumber.innerHTML = filtered.length;
|
||||
provincesFooterArea.innerHTML = filtered.length ? si(totalArea / filtered.length) + unit : 0 + unit;
|
||||
provincesFooterPopulation.innerHTML = filtered.length ? si(totalPopulation / filtered.length) : 0;
|
||||
provincesFooterArea.dataset.area = totalArea;
|
||||
provincesFooterPopulation.dataset.population = totalPopulation;
|
||||
byId("provincesFooterNumber").innerHTML = filtered.length;
|
||||
byId("provincesFooterBurgs").innerHTML = totalBurgs;
|
||||
byId("provincesFooterArea").innerHTML = filtered.length ? si(totalArea / filtered.length) + unit : 0 + unit;
|
||||
byId("provincesFooterPopulation").innerHTML = filtered.length ? si(totalPopulation / filtered.length) : 0;
|
||||
byId("provincesFooterArea").dataset.area = totalArea;
|
||||
byId("provincesFooterPopulation").dataset.population = totalPopulation;
|
||||
|
||||
body.querySelectorAll("div.states").forEach(el => {
|
||||
el.addEventListener("click", selectProvinceOnLineClick);
|
||||
|
|
@ -295,7 +303,7 @@ function editProvinces() {
|
|||
// move all burgs to a new state
|
||||
province.burgs.forEach(b => (burgs[b].state = newStateId));
|
||||
|
||||
// difine new state attributes
|
||||
// define new state attributes
|
||||
const {cell: center, culture} = burgs[burgId];
|
||||
const color = getRandomColor();
|
||||
const coa = province.coa;
|
||||
|
|
@ -502,6 +510,9 @@ function editProvinces() {
|
|||
applyOption(provinceNameEditorSelectForm, p.formName);
|
||||
document.getElementById("provinceNameEditorFull").value = p.fullName;
|
||||
|
||||
const cultureId = pack.cells.culture[p.center];
|
||||
document.getElementById("provinceCultureDisplay").innerText = pack.cultures[cultureId].name;
|
||||
|
||||
$("#provinceNameEditor").dialog({
|
||||
resizable: false,
|
||||
title: "Change province name",
|
||||
|
|
@ -588,12 +599,15 @@ function editProvinces() {
|
|||
function togglePercentageMode() {
|
||||
if (body.dataset.type === "absolute") {
|
||||
body.dataset.type = "percentage";
|
||||
const totalBurgs = +byId("provincesFooterBurgs").innerText;
|
||||
const totalArea = +provincesFooterArea.dataset.area;
|
||||
const totalPopulation = +provincesFooterPopulation.dataset.population;
|
||||
|
||||
body.querySelectorAll(":scope > div").forEach(function (el) {
|
||||
el.querySelector(".biomeArea").innerHTML = rn((+el.dataset.area / totalArea) * 100) + "%";
|
||||
el.querySelector(".culturePopulation").innerHTML = rn((+el.dataset.population / totalPopulation) * 100) + "%";
|
||||
const {cells, burgs, area, population} = el.dataset;
|
||||
el.querySelector(".provinceBurgs").innerText = rn((+burgs / totalBurgs) * 100) + "%";
|
||||
el.querySelector(".biomeArea").innerHTML = rn((+area / totalArea) * 100) + "%";
|
||||
el.querySelector(".culturePopulation").innerHTML = rn((+population / totalPopulation) * 100) + "%";
|
||||
});
|
||||
} else {
|
||||
body.dataset.type = "absolute";
|
||||
|
|
@ -1076,10 +1090,7 @@ function editProvinces() {
|
|||
|
||||
function downloadProvincesData() {
|
||||
const unit = areaUnit.value === "square" ? distanceUnitInput.value + "2" : areaUnit.value;
|
||||
let data =
|
||||
"Id,Province,Full Name,Form,State,Color,Capital,Area " +
|
||||
unit +
|
||||
",Total Population,Rural Population,Urban Population\n"; // headers
|
||||
let data = `Id,Province,Full Name,Form,State,Color,Capital,Area ${unit},Total Population,Rural Population,Urban Population,Burgs\n`; // headers
|
||||
|
||||
body.querySelectorAll(":scope > div").forEach(function (el) {
|
||||
const key = parseInt(el.dataset.id);
|
||||
|
|
@ -1093,8 +1104,9 @@ function editProvinces() {
|
|||
data += el.dataset.capital + ",";
|
||||
data += el.dataset.area + ",";
|
||||
data += el.dataset.population + ",";
|
||||
data += `${Math.round(provincePack.rural * populationRate)},`;
|
||||
data += `${Math.round(provincePack.urban * populationRate * urbanization)}\n`;
|
||||
data += Math.round(provincePack.rural * populationRate) + ",";
|
||||
data += Math.round(provincePack.urban * populationRate * urbanization) + ",";
|
||||
data += el.dataset.burgs + "\n";
|
||||
});
|
||||
|
||||
const name = getFileName("Provinces") + ".csv";
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
byId("styleFilterInput").innerHTML = allOptions;
|
||||
byId("styleStatesBodyFilter").innerHTML = allOptions;
|
||||
byId("styleScaleBarBackgroundFilter").innerHTML = allOptions;
|
||||
}
|
||||
|
||||
// store some style inputs as options
|
||||
|
|
@ -31,6 +32,7 @@ function editStyle(element, group) {
|
|||
|
||||
styleElementSelect.classList.add("glow");
|
||||
if (group) styleGroupSelect.classList.add("glow");
|
||||
|
||||
setTimeout(() => {
|
||||
styleElementSelect.classList.remove("glow");
|
||||
if (group) styleGroupSelect.classList.remove("glow");
|
||||
|
|
@ -81,10 +83,10 @@ function selectStyleElement() {
|
|||
styleIsOff.style.display = isLayerOff ? "block" : "none";
|
||||
|
||||
// active group element
|
||||
const group = styleGroupSelect.value;
|
||||
if (["routes", "labels", "coastline", "lakes", "anchors", "burgIcons", "borders"].includes(styleElement)) {
|
||||
const gEl = group && el.select("#" + group);
|
||||
el = group && gEl.size() ? gEl : el.select("g");
|
||||
if (["routes", "labels", "coastline", "lakes", "anchors", "burgIcons", "borders", "terrs"].includes(styleElement)) {
|
||||
const group = styleGroupSelect.value;
|
||||
const defaultGroupSelector = styleElement === "terrs" ? "#landHeights" : "g";
|
||||
el = group && el.select("#" + group).size() ? el.select("#" + group) : el.select(defaultGroupSelector);
|
||||
}
|
||||
|
||||
// opacity
|
||||
|
|
@ -94,13 +96,13 @@ function selectStyleElement() {
|
|||
}
|
||||
|
||||
// filter
|
||||
if (!["landmass", "legend", "regions"].includes(styleElement)) {
|
||||
if (!["landmass", "legend", "regions", "scaleBar"].includes(styleElement)) {
|
||||
styleFilter.style.display = "block";
|
||||
styleFilterInput.value = el.attr("filter") || "";
|
||||
}
|
||||
|
||||
// fill
|
||||
if (["rivers", "lakes", "landmass", "prec", "ice", "fogging", "vignette"].includes(styleElement)) {
|
||||
if (["rivers", "lakes", "landmass", "prec", "ice", "fogging", "scaleBar", "vignette"].includes(styleElement)) {
|
||||
styleFill.style.display = "block";
|
||||
styleFillInput.value = styleFillOutput.value = el.attr("fill");
|
||||
}
|
||||
|
|
@ -170,11 +172,14 @@ function selectStyleElement() {
|
|||
|
||||
if (styleElement === "terrs") {
|
||||
styleHeightmap.style.display = "block";
|
||||
styleHeightmapScheme.value = terrs.attr("scheme");
|
||||
styleHeightmapTerracingInput.value = styleHeightmapTerracingOutput.value = terrs.attr("terracing");
|
||||
styleHeightmapSkipInput.value = styleHeightmapSkipOutput.value = terrs.attr("skip");
|
||||
styleHeightmapSimplificationInput.value = styleHeightmapSimplificationOutput.value = terrs.attr("relax");
|
||||
styleHeightmapCurve.value = terrs.attr("curve");
|
||||
styleHeightmapRenderOceanOption.style.display = el.attr("id") === "oceanHeights" ? "block" : "none";
|
||||
styleHeightmapRenderOcean.checked = +el.attr("data-render");
|
||||
|
||||
styleHeightmapScheme.value = el.attr("scheme");
|
||||
styleHeightmapTerracingInput.value = styleHeightmapTerracingOutput.value = el.attr("terracing");
|
||||
styleHeightmapSkipInput.value = styleHeightmapSkipOutput.value = el.attr("skip");
|
||||
styleHeightmapSimplificationInput.value = styleHeightmapSimplificationOutput.value = el.attr("relax");
|
||||
styleHeightmapCurve.value = el.attr("curve");
|
||||
}
|
||||
|
||||
if (styleElement === "markers") {
|
||||
|
|
@ -336,7 +341,7 @@ function selectStyleElement() {
|
|||
|
||||
// update group options
|
||||
styleGroupSelect.options.length = 0; // remove all options
|
||||
if (["routes", "labels", "coastline", "lakes", "anchors", "burgIcons", "borders"].includes(styleElement)) {
|
||||
if (["routes", "labels", "coastline", "lakes", "anchors", "burgIcons", "borders", "terrs"].includes(styleElement)) {
|
||||
const groups = byId(styleElement).querySelectorAll("g");
|
||||
groups.forEach(el => {
|
||||
if (el.id === "burgLabels") return;
|
||||
|
|
@ -356,6 +361,31 @@ function selectStyleElement() {
|
|||
if (auto) styleFilter.style.display = "none";
|
||||
}
|
||||
|
||||
if (styleElement === "scaleBar") {
|
||||
styleScaleBar.style.display = "block";
|
||||
|
||||
styleScaleBarSize.value = el.attr("data-bar-size");
|
||||
styleScaleBarFontSize.value = el.attr("font-size");
|
||||
styleScaleBarPositionX.value = el.attr("data-x") || "99";
|
||||
styleScaleBarPositionY.value = el.attr("data-y") || "99";
|
||||
styleScaleBarLabel.value = el.attr("data-label") || "";
|
||||
|
||||
const scaleBarBack = el.select("#scaleBarBack");
|
||||
if (scaleBarBack.size()) {
|
||||
styleScaleBarBackgroundOpacityInput.value = styleScaleBarBackgroundOpacityOutput.value =
|
||||
scaleBarBack.attr("opacity");
|
||||
styleScaleBarBackgroundFillInput.value = styleScaleBarBackgroundFillOutput.value = scaleBarBack.attr("fill");
|
||||
styleScaleBarBackgroundStrokeInput.value = styleScaleBarBackgroundStrokeOutput.value =
|
||||
scaleBarBack.attr("stroke");
|
||||
styleScaleBarBackgroundStrokeWidth.value = scaleBarBack.attr("stroke-width");
|
||||
styleScaleBarBackgroundFilter.value = scaleBarBack.attr("filter");
|
||||
styleScaleBarBackgroundPaddingTop.value = scaleBarBack.attr("data-top");
|
||||
styleScaleBarBackgroundPaddingRight.value = scaleBarBack.attr("data-right");
|
||||
styleScaleBarBackgroundPaddingBottom.value = scaleBarBack.attr("data-bottom");
|
||||
styleScaleBarBackgroundPaddingLeft.value = scaleBarBack.attr("data-left");
|
||||
}
|
||||
}
|
||||
|
||||
if (styleElement === "vignette") {
|
||||
styleVignette.style.display = "block";
|
||||
|
||||
|
|
@ -519,18 +549,16 @@ outlineLayers.addEventListener("change", function () {
|
|||
});
|
||||
|
||||
styleHeightmapScheme.addEventListener("change", function () {
|
||||
terrs.attr("scheme", this.value);
|
||||
getEl().attr("scheme", this.value);
|
||||
drawHeightmap();
|
||||
});
|
||||
|
||||
openCreateHeightmapSchemeButton.addEventListener("click", function () {
|
||||
// start with current scheme
|
||||
this.dataset.stops = terrs.attr("scheme").startsWith("#")
|
||||
? terrs.attr("scheme")
|
||||
: (function () {
|
||||
const scheme = heightmapColorSchemes[terrs.attr("scheme")];
|
||||
return [0, 0.25, 0.5, 0.75, 1].map(scheme).map(toHEX).join(",");
|
||||
})();
|
||||
const scheme = getEl().attr("scheme");
|
||||
this.dataset.stops = scheme.startsWith("#")
|
||||
? scheme
|
||||
: (() => [0, 0.25, 0.5, 0.75, 1].map(heightmapColorSchemes[scheme]).map(toHEX).join(","))();
|
||||
|
||||
// render dialog base structure
|
||||
alertMessage.innerHTML = /* html */ `<div>
|
||||
|
|
@ -622,7 +650,7 @@ openCreateHeightmapSchemeButton.addEventListener("click", function () {
|
|||
if (stops in heightmapColorSchemes) return tip("This scheme already exists", false, "error");
|
||||
|
||||
addCustomColorScheme(stops);
|
||||
terrs.attr("scheme", stops);
|
||||
getEl().attr("scheme", stops);
|
||||
drawHeightmap();
|
||||
|
||||
handleClose();
|
||||
|
|
@ -644,23 +672,28 @@ openCreateHeightmapSchemeButton.addEventListener("click", function () {
|
|||
});
|
||||
});
|
||||
|
||||
styleHeightmapRenderOcean.addEventListener("change", function () {
|
||||
getEl().attr("data-render", +this.checked);
|
||||
drawHeightmap();
|
||||
});
|
||||
|
||||
styleHeightmapTerracingInput.addEventListener("input", function () {
|
||||
terrs.attr("terracing", this.value);
|
||||
getEl().attr("terracing", this.value);
|
||||
drawHeightmap();
|
||||
});
|
||||
|
||||
styleHeightmapSkipInput.addEventListener("input", function () {
|
||||
terrs.attr("skip", this.value);
|
||||
getEl().attr("skip", this.value);
|
||||
drawHeightmap();
|
||||
});
|
||||
|
||||
styleHeightmapSimplificationInput.addEventListener("input", function () {
|
||||
terrs.attr("relax", this.value);
|
||||
getEl().attr("relax", this.value);
|
||||
drawHeightmap();
|
||||
});
|
||||
|
||||
styleHeightmapCurve.addEventListener("change", function () {
|
||||
terrs.attr("curve", this.value);
|
||||
getEl().attr("curve", this.value);
|
||||
drawHeightmap();
|
||||
});
|
||||
|
||||
|
|
@ -957,7 +990,7 @@ function textureProvideURL() {
|
|||
}
|
||||
|
||||
function fetchTextureURL(url) {
|
||||
INFO && console.log("Provided URL is", url);
|
||||
INFO && console.info("Provided URL is", url);
|
||||
const img = new Image();
|
||||
img.onload = function () {
|
||||
const canvas = byId("texturePreview");
|
||||
|
|
@ -1043,6 +1076,44 @@ styleVignetteBlur.addEventListener("input", function () {
|
|||
byId("vignette-rect")?.setAttribute("filter", `blur(${this.value}px)`);
|
||||
});
|
||||
|
||||
styleScaleBar.addEventListener("input", function (event) {
|
||||
const scaleBarBack = scaleBar.select("#scaleBarBack");
|
||||
if (!scaleBarBack.size()) return;
|
||||
|
||||
const {id, value} = event.target;
|
||||
|
||||
if (id === "styleScaleBarSize") scaleBar.attr("data-bar-size", value);
|
||||
else if (id === "styleScaleBarFontSize") scaleBar.attr("font-size", value);
|
||||
else if (id === "styleScaleBarPositionX") scaleBar.attr("data-x", value);
|
||||
else if (id === "styleScaleBarPositionY") scaleBar.attr("data-y", value);
|
||||
else if (id === "styleScaleBarLabel") scaleBar.attr("data-label", value);
|
||||
else if (id === "styleScaleBarBackgroundOpacityInput") scaleBarBack.attr("opacity", value);
|
||||
else if (id === "styleScaleBarBackgroundFillInput") scaleBarBack.attr("fill", value);
|
||||
else if (id === "styleScaleBarBackgroundStrokeInput") scaleBarBack.attr("stroke", value);
|
||||
else if (id === "styleScaleBarBackgroundStrokeWidth") scaleBarBack.attr("stroke-width", value);
|
||||
else if (id === "styleScaleBarBackgroundFilter") scaleBarBack.attr("filter", value);
|
||||
else if (id === "styleScaleBarBackgroundPaddingTop") scaleBarBack.attr("data-top", value);
|
||||
else if (id === "styleScaleBarBackgroundPaddingRight") scaleBarBack.attr("data-right", value);
|
||||
else if (id === "styleScaleBarBackgroundPaddingBottom") scaleBarBack.attr("data-bottom", value);
|
||||
else if (id === "styleScaleBarBackgroundPaddingLeft") scaleBarBack.attr("data-left", value);
|
||||
|
||||
if (
|
||||
[
|
||||
"styleScaleBarSize",
|
||||
"styleScaleBarPositionX",
|
||||
"styleScaleBarPositionY",
|
||||
"styleScaleBarLabel",
|
||||
"styleScaleBarBackgroundPaddingLeft",
|
||||
"styleScaleBarBackgroundPaddingTop",
|
||||
"styleScaleBarBackgroundPaddingRight",
|
||||
"styleScaleBarBackgroundPaddingBottom"
|
||||
].includes(id)
|
||||
) {
|
||||
drawScaleBar(scaleBar, scale);
|
||||
fitScaleBar(scaleBar, svgWidth, svgHeight);
|
||||
}
|
||||
});
|
||||
|
||||
function updateElements() {
|
||||
// burgIcons to desired size
|
||||
burgIcons.selectAll("g").each(function () {
|
||||
|
|
|
|||
|
|
@ -140,6 +140,9 @@ function applyStyleWithUiRefresh(style) {
|
|||
|
||||
invokeActiveZooming();
|
||||
setPresetRemoveButtonVisibiliy();
|
||||
|
||||
drawScaleBar(scaleBar, scale);
|
||||
fitScaleBar(scaleBar, svgWidth, svgHeight);
|
||||
}
|
||||
|
||||
function addStylePreset() {
|
||||
|
|
@ -239,7 +242,18 @@ function addStylePreset() {
|
|||
"#oceanLayers": ["filter", "layers"],
|
||||
"#oceanBase": ["fill"],
|
||||
"#oceanicPattern": ["href", "opacity"],
|
||||
"#terrs": ["opacity", "scheme", "terracing", "skip", "relax", "curve", "filter", "mask"],
|
||||
"#terrs #oceanHeights": [
|
||||
"data-render",
|
||||
"opacity",
|
||||
"scheme",
|
||||
"terracing",
|
||||
"skip",
|
||||
"relax",
|
||||
"curve",
|
||||
"filter",
|
||||
"mask"
|
||||
],
|
||||
"#terrs #landHeights": ["opacity", "scheme", "terracing", "skip", "relax", "curve", "filter", "mask"],
|
||||
"#legend": [
|
||||
"data-size",
|
||||
"font-size",
|
||||
|
|
@ -301,7 +315,19 @@ function addStylePreset() {
|
|||
],
|
||||
"#fogging": ["opacity", "fill", "filter"],
|
||||
"#vignette": ["opacity", "fill", "filter"],
|
||||
"#vignette-rect": ["x", "y", "width", "height", "rx", "ry", "filter"]
|
||||
"#vignette-rect": ["x", "y", "width", "height", "rx", "ry", "filter"],
|
||||
"#scaleBar": ["opacity", "fill", "font-size", "data-bar-size", "data-x", "data-y", "data-label"],
|
||||
"#scaleBarBack": [
|
||||
"opacity",
|
||||
"fill",
|
||||
"stroke",
|
||||
"stroke-width",
|
||||
"filter",
|
||||
"data-top",
|
||||
"data-right",
|
||||
"data-bottom",
|
||||
"data-left"
|
||||
]
|
||||
};
|
||||
|
||||
for (const selector in attributes) {
|
||||
|
|
|
|||
|
|
@ -247,13 +247,16 @@ function recreateStates() {
|
|||
capitalsTree.add([x, y]);
|
||||
|
||||
// update label id reference
|
||||
labels
|
||||
.select("#states")
|
||||
.select(`#stateLabel${state.i}`)
|
||||
.attr("id", `stateLabel${newId}`)
|
||||
.select("textPath")
|
||||
.attr("xlink:href", `#textPath_stateLabel${newId}`);
|
||||
defs.select("#textPaths").select(`#textPath_stateLabel${state.i}`).attr("id", `textPath_stateLabel${newId}`);
|
||||
byId(`textPath_stateLabel${state.i}`)?.setAttribute("id", `textPath_stateLabel${newId}`);
|
||||
const $label = byId(`stateLabel${state.i}`);
|
||||
if ($label) {
|
||||
$label.setAttribute("id", `stateLabel${newId}`);
|
||||
const $textPath = $label.querySelector("textPath");
|
||||
if ($textPath) {
|
||||
$textPath.removeAttribute("href");
|
||||
$textPath.setAttribute("href", `#textPath_stateLabel${newId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// update emblem id reference
|
||||
byId(`stateCOA${state.i}`)?.setAttribute("id", `stateCOA${newId}`);
|
||||
|
|
|
|||
|
|
@ -24,13 +24,6 @@ function editUnits() {
|
|||
byId("heightExponentInput").addEventListener("input", changeHeightExponent);
|
||||
byId("heightExponentOutput").addEventListener("input", changeHeightExponent);
|
||||
byId("temperatureScale").addEventListener("change", changeTemperatureScale);
|
||||
byId("barSizeOutput").addEventListener("input", renderScaleBar);
|
||||
byId("barSizeInput").addEventListener("input", renderScaleBar);
|
||||
byId("barLabel").addEventListener("input", renderScaleBar);
|
||||
byId("barPosX").addEventListener("input", fitScaleBar);
|
||||
byId("barPosY").addEventListener("input", fitScaleBar);
|
||||
byId("barBackOpacity").addEventListener("input", changeScaleBarOpacity);
|
||||
byId("barBackColor").addEventListener("input", changeScaleBarColor);
|
||||
|
||||
byId("populationRateOutput").addEventListener("input", changePopulationRate);
|
||||
byId("populationRateInput").addEventListener("change", changePopulationRate);
|
||||
|
|
@ -84,14 +77,6 @@ function editUnits() {
|
|||
if (layerIsOn("toggleTemp")) drawTemp();
|
||||
}
|
||||
|
||||
function changeScaleBarOpacity() {
|
||||
scaleBar.select("rect").attr("opacity", this.value);
|
||||
}
|
||||
|
||||
function changeScaleBarColor() {
|
||||
scaleBar.select("rect").attr("fill", this.value);
|
||||
}
|
||||
|
||||
function changePopulationRate() {
|
||||
populationRate = +this.value;
|
||||
}
|
||||
|
|
@ -129,19 +114,6 @@ function editUnits() {
|
|||
localStorage.removeItem("heightExponent");
|
||||
calculateTemperatures();
|
||||
|
||||
// scale bar
|
||||
barSizeOutput.value = barSizeInput.value = 2;
|
||||
barLabel.value = "";
|
||||
barBackOpacity.value = 0.2;
|
||||
barBackColor.value = "#ffffff";
|
||||
barPosX.value = barPosY.value = 99;
|
||||
|
||||
localStorage.removeItem("barSize");
|
||||
localStorage.removeItem("barLabel");
|
||||
localStorage.removeItem("barBackOpacity");
|
||||
localStorage.removeItem("barBackColor");
|
||||
localStorage.removeItem("barPosX");
|
||||
localStorage.removeItem("barPosY");
|
||||
renderScaleBar();
|
||||
|
||||
// population
|
||||
|
|
|
|||
|
|
@ -113,9 +113,11 @@ function editWorld() {
|
|||
|
||||
function toKilometer(v) {
|
||||
if (unit === "km") return v;
|
||||
else if (unit === "mi") return v * 1.60934;
|
||||
else if (unit === "lg") return v * 5.556;
|
||||
else if (unit === "vr") return v * 1.0668;
|
||||
if (unit === "mi") return v * 1.60934;
|
||||
if (unit === "lg") return v * 4.828;
|
||||
if (unit === "vr") return v * 1.0668;
|
||||
if (unit === "nmi") return v * 1.852;
|
||||
if (unit === "nlg") return v * 5.556;
|
||||
return 0; // 0 if distanceUnitInput is a custom unit
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue