Merge branch 'master' into dev-submaps

This commit is contained in:
Mészáros Gergely 2021-09-08 12:43:23 +02:00
commit 0a7218db99
24 changed files with 904 additions and 515 deletions

View file

@ -447,15 +447,17 @@ function overviewBurgs() {
}
function downloadBurgsData() {
let data = "Id,Burg,Province,State,Culture,Religion,Population,Longitude,Latitude,Elevation (" + heightUnit.value + "),Capital,Port,Citadel,Walls,Plaza,Temple,Shanty Town\n"; // headers
let data = "Id,Burg,Province,Province Full Name,State,State Full Name,Culture,Religion,Population,Longitude,Latitude,Elevation (" + heightUnit.value + "),Capital,Port,Citadel,Walls,Plaza,Temple,Shanty Town\n"; // headers
const valid = pack.burgs.filter(b => b.i && !b.removed); // all valid burgs
valid.forEach(b => {
data += b.i + ",";
data += b.name + ",";
const province = pack.cells.province[b.cell];
data += province ? pack.provinces[province].name + "," : ",";
data += province ? pack.provinces[province].fullName + "," : ",";
data += b.state ? pack.states[b.state].fullName + "," : pack.states[b.state].name + ",";
data += pack.states[b.state].name + ",";
data += pack.states[b.state].fullName + ",";
data += pack.cultures[b.culture].name + ",";
data += pack.religions[pack.cells.religion[b.cell]].name + ",";
data += rn(b.population * populationRate * urbanization) + ",";

View file

@ -7,7 +7,7 @@ function editHeightmap() {
<p><i>Erase</i> mode also allows you Convert an Image into a heightmap or use Template Editor.</p>
<p>You can <i>keep</i> the data, but you won't be able to change the coastline.</p>
<p>Try <i>risk</i> mode to change the coastline and keep the data. The data will be restored as much as possible, but it can cause unpredictable errors.</p>
<p>Please <span class="pseudoLink" onclick=saveMap(); editHeightmap();>save the map</span> before editing the heightmap!</p>
<p>Please <span class="pseudoLink" onclick=dowloadMap(); editHeightmap();>save the map</span> before editing the heightmap!</p>
<p>Check out ${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Heightmap-customization", "wiki")} for guidance.</p>`;
$("#alert").dialog({
@ -328,10 +328,10 @@ function editHeightmap() {
for (const i of pack.cells.i) {
const g = pack.cells.g[i];
const land = pack.cells.h[i] >= 20;
const isLand = pack.cells.h[i] >= 20;
// check biome
pack.cells.biome[i] = land && biome[g] ? biome[g] : getBiomeId(grid.cells.prec[g], pack.cells.h[i]);
pack.cells.biome[i] = isLand && biome[g] ? biome[g] : getBiomeId(grid.cells.prec[g], grid.cells.temp[g], pack.cells.h[i]);
// rivers data
if (!erosionAllowed) {
@ -340,7 +340,7 @@ function editHeightmap() {
pack.cells.fl[i] = fl[g];
}
if (!land) continue;
if (!isLand) continue;
pack.cells.culture[i] = culture[g];
pack.cells.pop[i] = pop[g];
pack.cells.road[i] = road[g];
@ -837,31 +837,27 @@ function editHeightmap() {
const steps = body.querySelectorAll("#templateBody > div");
if (!steps.length) return;
const {addHill, addPit, addRange, addTrough, addStrait, modify, smooth} = HeightmapGenerator;
grid.cells.h = new Uint8Array(grid.cells.i.length); // clean all heights
for (const s of steps) {
if (s.style.opacity == 0.5) continue;
const type = s.dataset.type;
for (const step of steps) {
if (step.style.opacity === "0.5") continue;
const type = step.dataset.type;
const elCount = s.querySelector(".templateCount") || "";
const elHeight = s.querySelector(".templateHeight") || "";
const count = step.querySelector(".templateCount")?.value || "";
const height = step.querySelector(".templateHeight")?.value || "";
const dist = step.querySelector(".templateDist")?.value || null;
const x = step.querySelector(".templateX")?.value || null;
const y = step.querySelector(".templateY")?.value || null;
const elDist = s.querySelector(".templateDist");
const dist = elDist ? elDist.value : null;
const templateX = s.querySelector(".templateX");
const x = templateX ? templateX.value : null;
const templateY = s.querySelector(".templateY");
const y = templateY ? templateY.value : null;
if (type === "Hill") HeightmapGenerator.addHill(elCount.value, elHeight.value, x, y);
else if (type === "Pit") HeightmapGenerator.addPit(elCount.value, elHeight.value, x, y);
else if (type === "Range") HeightmapGenerator.addRange(elCount.value, elHeight.value, x, y);
else if (type === "Trough") HeightmapGenerator.addTrough(elCount.value, elHeight.value, x, y);
else if (type === "Strait") HeightmapGenerator.addStrait(elCount.value, dist);
else if (type === "Add") HeightmapGenerator.modify(dist, +elCount.value, 1);
else if (type === "Multiply") HeightmapGenerator.modify(dist, 0, +elCount.value);
else if (type === "Smooth") HeightmapGenerator.smooth(+elCount.value);
if (type === "Hill") addHill(count, height, x, y);
else if (type === "Pit") addPit(count, height, x, y);
else if (type === "Range") addRange(count, height, x, y);
else if (type === "Trough") addTrough(count, height, x, y);
else if (type === "Strait") addStrait(count, dist);
else if (type === "Add") modify(dist, +count, 1);
else if (type === "Multiply") modify(dist, 0, +count);
else if (type === "Smooth") smooth(+count);
updateHistory("noStat"); // update history every step
}
@ -880,17 +876,13 @@ function editHeightmap() {
let data = "";
for (const s of steps) {
if (s.style.opacity == 0.5) continue;
if (s.style.opacity === "0.5") continue;
const type = s.getAttribute("data-type");
const elCount = s.querySelector(".templateCount");
const count = elCount ? elCount.value : "0";
const elHeight = s.querySelector(".templateHeight");
const elDist = s.querySelector(".templateDist");
const arg3 = elHeight ? elHeight.value : elDist ? elDist.value : "0";
const templateX = s.querySelector(".templateX");
const x = templateX ? templateX.value : "0";
const templateY = s.querySelector(".templateY");
const y = templateY ? templateY.value : "0";
const count = s.querySelector(".templateCount")?.value || "0";
const arg3 = s.querySelector(".templateHeight")?.value || s.querySelector(".templateDist")?.value || "0";
const x = s.querySelector(".templateX")?.value || "0";
const y = s.querySelector(".templateY")?.value || "0";
data += `${type} ${count} ${arg3} ${x} ${y}\r\n`;
}

View file

@ -1456,16 +1456,19 @@ function toggleRivers(event) {
function drawRivers() {
TIME && console.time("drawRivers");
rivers.selectAll("*").remove();
const {addMeandering, getRiverPath} = Rivers;
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
const riverPaths = pack.rivers.map(river => {
const meanderedPoints = addMeandering(river.cells, river.points);
const widthFactor = river.widthFactor || 1;
const startingWidth = river.sourceWidth || 0;
const path = getRiverPath(meanderedPoints, widthFactor, startingWidth);
return `<path id="river${river.i}" d="${path}"/>`;
const riverPaths = pack.rivers.map(({cells, points, i, widthFactor, sourceWidth}) => {
if (!cells || cells.length < 2) return;
const meanderedPoints = addMeandering(cells, points);
const path = getRiverPath(meanderedPoints, widthFactor, sourceWidth);
return `<path id="river${i}" d="${path}"/>`;
});
rivers.html(riverPaths.join(""));
TIME && console.timeEnd("drawRivers");
}

View file

@ -99,7 +99,9 @@ function showSupporters() {
Dust Bunny,Adrian Wright,Eric Alexander Cartaya,GameNight,Thomas Mortensen Hansen,Zklaus,Drinarius,Ed Wright,Lon Varnadore,Crys Cain,Heaven N Lee,
Jeffrey Henning,Lazer Elf,Jordan Bellah,Alex Beard,Kass Frisson,Petro Lombaard,Emanuel Pietri,Rox,PinkEvil,Gavin Madrigal,Martin Lorber,Prince of Morgoth,
Jaryd Armstrong,Andrew Pirkola,ThyHolyDevil,Gary Smith,Tyshaun Wise,Ethan Cook,Jon Stroman,Nobody679,良义 ,Chris Gray,Phoenix Boatwright,Mackenzie,
"Milo Cohen,Jason Matthew Wuerfel,Rasmus Legêne,Andrew Hines,Wexxler,Espen Sæverud,Binks,Dominick Ormsby,Linn Browning,Václav Švec,Alan Buehne,George J.Lekkas"`;
Milo Cohen,Jason Matthew Wuerfel,Rasmus Legêne,Andrew Hines,Wexxler,Espen Sæverud,Binks,Dominick Ormsby,Linn Browning,Václav Švec,Alan Buehne,
George J.Lekkas,Alexandre Boivin,Tommy Mayfield,Skylar Mangum-Turner,Karen Blythe,Stefan Gugerel,Mike Conley,Xavier privé,Hope You're Well,
Mark Sprietsma,Robert Landry,Nick Mowry"`;
const array = supporters
.replace(/(?:\r\n|\r|\n)/g, "")
@ -621,6 +623,7 @@ document.getElementById("sticked").addEventListener("click", function (event) {
const id = event.target.id;
if (id === "newMapButton") regeneratePrompt();
else if (id === "saveButton") showSavePane();
else if (id === "exportButton") showExportPane();
else if (id === "loadButton") showLoadPane();
else if (id === "zoomReset") resetZoom(1000);
});
@ -654,12 +657,13 @@ function regeneratePrompt() {
}
function showSavePane() {
document.getElementById("showLabels").checked = !hideLabels.checked;
const sharableLinkContainer = document.getElementById("sharableLinkContainer");
sharableLinkContainer.style.display = "none";
$("#saveMapData").dialog({
title: "Save map",
resizable: false,
width: "30em",
width: "27em",
position: {my: "center", at: "center", of: "svg"},
buttons: {
Close: function () {
@ -669,21 +673,21 @@ function showSavePane() {
});
}
// download map data as GeoJSON
function saveGeoJSON() {
alertMessage.innerHTML = `You can export map data in GeoJSON format used in GIS tools such as QGIS.
Check out ${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/GIS-data-export", "wiki-page")} for guidance`;
function copyLinkToClickboard() {
const shrableLink = document.getElementById("sharableLink");
const link = shrableLink.getAttribute("href");
navigator.clipboard.writeText(link).then(() => tip("Link is copied to the clipboard", true, "success", 8000));
}
$("#alert").dialog({
title: "GIS data export",
function showExportPane() {
document.getElementById("showLabels").checked = !hideLabels.checked;
$("#exportMapData").dialog({
title: "Export map data",
resizable: false,
width: "35em",
width: "26em",
position: {my: "center", at: "center", of: "svg"},
buttons: {
Cells: saveGeoJSON_Cells,
Routes: saveGeoJSON_Routes,
Rivers: saveGeoJSON_Rivers,
Markers: saveGeoJSON_Markers,
Close: function () {
$(this).dialog("close");
}
@ -691,11 +695,11 @@ function saveGeoJSON() {
});
}
function showLoadPane() {
async function showLoadPane() {
$("#loadMapData").dialog({
title: "Load map",
resizable: false,
width: "17em",
width: "22em",
position: {my: "center", at: "center", of: "svg"},
buttons: {
Close: function () {
@ -703,6 +707,19 @@ function showLoadPane() {
}
}
});
const dpx = document.getElementById("loadFromDropbox");
const dpf = dpx.querySelector("select");
const files = await Cloud.providers.dropbox.list();
dpx.style.display = files? "block" : "none";
if (!files) return;
while(dpf.firstChild) dpf.removeChild(dpf.firstChild);
files.forEach(f => {
const opt = document.createElement('option');
opt.innerText = f.name;
opt.value = f.path;
dpf.appendChild(opt);
});
}
function loadURL() {

View file

@ -934,20 +934,22 @@ function editProvinces() {
function downloadProvincesData() {
const unit = areaUnit.value === "square" ? distanceUnitInput.value + "2" : areaUnit.value;
let data = "Id,Province,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\n"; // headers
body.querySelectorAll(":scope > div").forEach(function (el) {
let key = parseInt(el.dataset.id);
const key = parseInt(el.dataset.id);
const provincePack = pack.provinces[key];
data += el.dataset.id + ",";
data += el.dataset.name + ",";
data += provincePack.fullName + ",";
data += el.dataset.form + ",";
data += el.dataset.state + ",";
data += el.dataset.color + ",";
data += el.dataset.capital + ",";
data += el.dataset.area + ",";
data += el.dataset.population + ",";
data += `${Math.round(pack.provinces[key].rural * populationRate)},`;
data += `${Math.round(pack.provinces[key].urban * populationRate * urbanization)}\n`;
data += `${Math.round(provincePack.rural * populationRate)},`;
data += `${Math.round(provincePack.urban * populationRate * urbanization)}\n`;
});
const name = getFileName("Provinces") + ".csv";

View file

@ -100,16 +100,13 @@ function createRiver() {
const basin = getBasin(parent);
rivers.push({i: riverId, source, mouth, discharge, length, width, widthFactor, sourceWidth, parent, cells: riverCells, basin, name, type: "River"});
const id = "river" + riverId;
// render river
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
viewbox
.select("#rivers")
.append("path")
.attr("id", "river" + riverId)
.attr("d", getRiverPath(meanderedPoints, widthFactor, sourceWidth));
viewbox.select("#rivers").append("path").attr("id", id).attr("d", getRiverPath(meanderedPoints, widthFactor, sourceWidth));
editRiver(riverId);
editRiver(id);
}
function closeRiverCreator() {

View file

@ -8,9 +8,9 @@ function editRiver(id) {
document.getElementById("toggleCells").dataset.forced = +!layerIsOn("toggleCells");
if (!layerIsOn("toggleCells")) toggleCells();
elSelected = d3.select("#" + id);
elSelected = d3.select("#" + id).on("click", addControlPoint);
tip("Drag control points to change the river course. For major changes please create a new river instead", true);
tip("Drag control points to change the river course. Click on point to remove it. Click on river to add additional control point. For major changes please create a new river instead", true);
debug.append("g").attr("id", "controlCells");
debug.append("g").attr("id", "controlPoints");
@ -19,8 +19,8 @@ function editRiver(id) {
const river = getRiver();
const {cells, points} = river;
const riverPoints = Rivers.getRiverPoints(cells, points);
drawControlPoints(riverPoints, cells);
drawCells(cells, "current");
drawControlPoints(riverPoints);
drawCells(cells);
$("#riverEditor").dialog({
title: "Edit River",
@ -92,37 +92,35 @@ function editRiver(id) {
document.getElementById("riverWidth").value = width;
}
function drawControlPoints(points, cells) {
function drawControlPoints(points) {
debug
.select("#controlPoints")
.selectAll("circle")
.data(points)
.enter()
.append("circle")
.join("circle")
.attr("cx", d => d[0])
.attr("cy", d => d[1])
.attr("r", 0.6)
.attr("data-cell", (d, i) => cells[i])
.attr("data-i", (d, i) => i)
.call(d3.drag().on("start", dragControlPoint));
.call(d3.drag().on("start", dragControlPoint))
.on("click", removeControlPoint);
}
function drawCells(cells, type) {
function drawCells(cells) {
const validCells = [...new Set(cells)].filter(i => pack.cells.i[i]);
debug
.select("#controlCells")
.selectAll(`polygon.${type}`)
.data(cells.filter(i => pack.cells.i[i]))
.selectAll(`polygon`)
.data(validCells)
.join("polygon")
.attr("points", d => getPackPolygon(d))
.attr("class", type);
.attr("points", d => getPackPolygon(d));
}
function dragControlPoint() {
const {i, r, fl} = pack.cells;
const {r, fl} = pack.cells;
const river = getRiver();
const initCell = +this.dataset.cell;
const index = +this.dataset.i;
const {x: x0, y: y0} = d3.event;
const initCell = findCell(x0, y0);
let movedToCell = null;
@ -136,22 +134,18 @@ function editRiver(id) {
this.setAttribute("cy", y);
this.__data__ = [rn(x, 1), rn(y, 1)];
redrawRiver();
drawCells(river.cells);
});
d3.event.on("end", () => {
if (movedToCell) {
this.dataset.cell = movedToCell;
river.cells[index] = movedToCell;
drawCells(river.cells, "current");
if (!r[movedToCell]) {
// swap river data
r[initCell] = 0;
r[movedToCell] = river.i;
const sourceFlux = fl[initCell];
fl[initCell] = fl[movedToCell];
fl[movedToCell] = sourceFlux;
}
if (movedToCell && !r[movedToCell]) {
// swap river data
r[initCell] = 0;
r[movedToCell] = river.i;
const sourceFlux = fl[initCell];
fl[initCell] = fl[movedToCell];
fl[movedToCell] = sourceFlux;
redrawRiver();
}
});
}
@ -159,8 +153,10 @@ function editRiver(id) {
function redrawRiver() {
const river = getRiver();
river.points = debug.selectAll("#controlPoints > *").data();
const {cells, widthFactor, sourceWidth} = river;
const meanderedPoints = Rivers.addMeandering(cells, river.points);
river.cells = river.points.map(([x, y]) => findCell(x, y));
const {widthFactor, sourceWidth} = river;
const meanderedPoints = Rivers.addMeandering(river.cells, river.points);
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
const path = Rivers.getRiverPath(meanderedPoints, widthFactor, sourceWidth);
@ -170,6 +166,27 @@ function editRiver(id) {
if (modules.elevation) showEPForRiver(elSelected.node());
}
function addControlPoint() {
const [x, y] = d3.mouse(this);
const point = [rn(x, 1), rn(y, 1)];
const river = getRiver();
if (!river.points) river.points = debug.selectAll("#controlPoints > *").data();
const index = getSegmentId(river.points, point, 2);
river.points.splice(index, 0, point);
drawControlPoints(river.points);
redrawRiver();
}
function removeControlPoint() {
this.remove();
redrawRiver();
const {cells} = getRiver();
drawCells(cells);
}
function changeName() {
getRiver().name = this.value;
}
@ -244,6 +261,8 @@ function editRiver(id) {
function closeRiverEditor() {
debug.select("#controlPoints").remove();
debug.select("#controlCells").remove();
elSelected.on("click", null);
unselect();
clearMainTip();

View file

@ -1028,12 +1028,13 @@ function editStates() {
function downloadStatesData() {
const unit = areaUnit.value === "square" ? distanceUnitInput.value + "2" : areaUnit.value;
let data = "Id,State,Form,Color,Capital,Culture,Type,Expansionism,Cells,Burgs,Area " + unit + ",Total Population,Rural Population,Urban Population\n"; // headers
let data = "Id,State,Full Name,Form,Color,Capital,Culture,Type,Expansionism,Cells,Burgs,Area " + unit + ",Total Population,Rural Population,Urban Population\n"; // headers
body.querySelectorAll(":scope > div").forEach(function (el) {
const key = parseInt(el.dataset.id);
const statePack = pack.states[key];
data += el.dataset.id + ",";
data += el.dataset.name + ",";
data += (statePack.fullName ? statePack.fullName : "") + ",";
data += el.dataset.form + ",";
data += el.dataset.color + ",";
data += el.dataset.capital + ",";
@ -1044,8 +1045,8 @@ function editStates() {
data += el.dataset.burgs + ",";
data += el.dataset.area + ",";
data += el.dataset.population + ",";
data += `${Math.round(pack.states[key].rural * populationRate)},`;
data += `${Math.round(pack.states[key].urban * populationRate * urbanization)}\n`;
data += `${Math.round(statePack.rural * populationRate)},`;
data += `${Math.round(statePack.urban * populationRate * urbanization)}\n`;
});
const name = getFileName("States") + ".csv";

File diff suppressed because one or more lines are too long

View file

@ -136,21 +136,11 @@ function recalculatePopulation() {
function regenerateStates() {
const localSeed = Math.floor(Math.random() * 1e9); // new random seed
Math.random = aleaPRNG(localSeed);
const burgs = pack.burgs.filter(b => b.i && !b.removed);
if (!burgs.length) {
tip("No burgs to generate states. Please create burgs first", false, "error");
return;
}
if (burgs.length < +regionsInput.value) {
tip(`Not enough burgs to generate ${regionsInput.value} states. Will generate only ${burgs.length} states`, false, "warn");
}
// burg local ids sorted by a bit randomized population:
const sorted = burgs
.map((b, i) => [i, b.population * Math.random()])
.sort((a, b) => b[1] - a[1])
.map(b => b[0]);
const capitalsTree = d3.quadtree();
const statesCount = +regionsInput.value;
const burgs = pack.burgs.filter(b => b.i && !b.removed);
if (!burgs.length) return tip("There are no any burgs to generate states. Please create burgs first", false, "error");
if (burgs.length < statesCount) tip(`Not enough burgs to generate ${statesCount} states. Will generate only ${burgs.length} states`, false, "warn");
// turn all old capitals into towns
burgs
@ -167,8 +157,7 @@ function regenerateStates() {
unfog();
// if desired states number is 0
if (regionsInput.value == 0) {
if (!statesCount) {
tip(`Cannot generate zero states. Please check the <i>States Number</i> option`, false, "warn");
pack.states = pack.states.slice(0, 1); // remove all except of neutrals
pack.states[0].diplomacy = []; // clear diplomacy
@ -184,26 +173,34 @@ function regenerateStates() {
return;
}
const neutral = pack.states[0].name;
const count = Math.min(+regionsInput.value, burgs.length);
// burg local ids sorted by a bit randomized population:
const sortedBurgs = burgs
.map((b, i) => [b, b.population * Math.random()])
.sort((a, b) => b[1] - a[1])
.map(b => b[0]);
const capitalsTree = d3.quadtree();
const neutral = pack.states[0].name; // neutrals name
const count = Math.min(statesCount, burgs.length) + 1; // +1 for neutral
let spacing = (graphWidth + graphHeight) / 2 / count; // min distance between capitals
pack.states = d3.range(count).map(i => {
if (!i) return {i, name: neutral};
let capital = null,
x = 0,
y = 0;
for (const i of sorted) {
capital = burgs[i];
(x = capital.x), (y = capital.y);
if (capitalsTree.find(x, y, spacing) === undefined) break;
let capital = null;
for (const burg of sortedBurgs) {
const {x, y} = burg;
if (capitalsTree.find(x, y, spacing) === undefined) {
burg.capital = 1;
capital = burg;
capitalsTree.add([x, y]);
moveBurgToGroup(burg.i, "cities");
break;
}
spacing = Math.max(spacing - 1, 1);
}
capitalsTree.add([x, y]);
capital.capital = 1;
moveBurgToGroup(capital.i, "cities");
const culture = capital.culture;
const basename = capital.name.length < 9 && capital.cell % 5 === 0 ? capital.name : Names.getCulture(culture, 3, 6, "", 0);
const name = Names.getState(basename, culture);

View file

@ -1,13 +1,17 @@
function editWorld() {
if (customization) return;
$("#worldConfigurator").dialog({title: "Configure World", resizable: false, width: "42em",
$("#worldConfigurator").dialog({
title: "Configure World",
resizable: false,
width: "42em",
buttons: {
"Whole World": () => applyWorldPreset(100, 50),
"Northern": () => applyWorldPreset(33, 25),
"Tropical": () => applyWorldPreset(33, 50),
"Southern": () => applyWorldPreset(33, 75),
Northern: () => applyWorldPreset(33, 25),
Tropical: () => applyWorldPreset(33, 50),
Southern: () => applyWorldPreset(33, 75),
"Restore Winds": restoreDefaultWinds
}, open: function() {
},
open: function () {
const buttons = $(this).dialog("widget").find(".ui-dialog-buttonset > button");
buttons[0].addEventListener("mousemove", () => tip("Click to set map size to cover the whole World"));
buttons[1].addEventListener("mousemove", () => tip("Click to set map size to cover the Northern latitudes"));
@ -19,7 +23,8 @@ function editWorld() {
const globe = d3.select("#globe");
const clr = d3.scaleSequential(d3.interpolateSpectral);
const tMax = 30, tMin = -25; // temperature extremes
const tMax = 30,
tMin = -25; // temperature extremes
const projection = d3.geoOrthographic().translate([100, 100]).scale(100);
const path = d3.geoPath(projection);
@ -29,15 +34,15 @@ function editWorld() {
if (modules.editWorld) return;
modules.editWorld = true;
document.getElementById("worldControls").addEventListener("input", (e) => updateWorld(e.target));
document.getElementById("worldControls").addEventListener("input", e => updateWorld(e.target));
globe.select("#globeWindArrows").on("click", changeWind);
globe.select("#globeGraticule").attr("d", round(path(d3.geoGraticule()()))); // globe graticule
updateWindDirections();
function updateWorld(el) {
if (el) {
document.getElementById(el.dataset.stored+"Input").value = el.value;
document.getElementById(el.dataset.stored+"Output").value = el.value;
document.getElementById(el.dataset.stored + "Input").value = el.value;
document.getElementById(el.dataset.stored + "Output").value = el.value;
if (el.dataset.stored) lock(el.dataset.stored);
}
@ -56,16 +61,18 @@ function editWorld() {
if (layerIsOn("togglePrec")) drawPrec();
if (layerIsOn("toggleBiomes")) drawBiomes();
if (layerIsOn("toggleCoordinates")) drawCoordinates();
if (layerIsOn("toggleRivers")) drawRivers();
if (document.getElementById("canvas3d")) setTimeout(ThreeD.update(), 500);
}
function updateGlobePosition() {
const size = +document.getElementById("mapSizeOutput").value;
const eqD = graphHeight / 2 * 100 / size;
const eqD = ((graphHeight / 2) * 100) / size;
calculateMapCoordinates();
const mc = mapCoordinates; // shortcut
const scale = +distanceScaleInput.value, unit = distanceUnitInput.value;
const scale = +distanceScaleInput.value,
unit = distanceUnitInput.value;
const meridian = toKilometer(eqD * 2 * scale);
document.getElementById("mapSize").innerHTML = `${graphWidth}x${graphHeight}`;
document.getElementById("mapSizeFriendly").innerHTML = `${rn(graphWidth * scale)}x${rn(graphHeight * scale)} ${unit}`;
@ -82,27 +89,35 @@ function editWorld() {
return 0; // 0 if distanceUnitInput is a custom unit
}
function lat(lat) {return lat > 0 ? Math.abs(rn(lat)) + "°N" : Math.abs(rn(lat)) + "°S";} // parse latitude value
const area = d3.geoGraticule().extent([[mc.lonW, mc.latN], [mc.lonE, mc.latS]]);
function lat(lat) {
return lat > 0 ? Math.abs(rn(lat)) + "°N" : Math.abs(rn(lat)) + "°S";
} // parse latitude value
const area = d3.geoGraticule().extent([
[mc.lonW, mc.latN],
[mc.lonE, mc.latS]
]);
globe.select("#globeArea").attr("d", round(path(area.outline()))); // map area
}
function updateGlobeTemperature() {
const tEq = +document.getElementById("temperatureEquatorOutput").value;
document.getElementById("temperatureEquatorF").innerHTML = rn(tEq * 9/5 + 32);
document.getElementById("temperatureEquatorF").innerHTML = rn((tEq * 9) / 5 + 32);
const tPole = +document.getElementById("temperaturePoleOutput").value;
document.getElementById("temperaturePoleF").innerHTML = rn(tPole * 9/5 + 32);
document.getElementById("temperaturePoleF").innerHTML = rn((tPole * 9) / 5 + 32);
globe.selectAll(".tempGradient90").attr("stop-color", clr(1 - (tPole - tMin) / (tMax - tMin)));
globe.selectAll(".tempGradient60").attr("stop-color", clr(1 - (tEq - (tEq - tPole) * 2/3 - tMin) / (tMax - tMin)));
globe.selectAll(".tempGradient30").attr("stop-color", clr(1 - (tEq - (tEq - tPole) * 1/3 - tMin) / (tMax - tMin)));
globe.selectAll(".tempGradient60").attr("stop-color", clr(1 - (tEq - ((tEq - tPole) * 2) / 3 - tMin) / (tMax - tMin)));
globe.selectAll(".tempGradient30").attr("stop-color", clr(1 - (tEq - ((tEq - tPole) * 1) / 3 - tMin) / (tMax - tMin)));
globe.select(".tempGradient0").attr("stop-color", clr(1 - (tEq - tMin) / (tMax - tMin)));
}
function updateWindDirections() {
globe.select("#globeWindArrows").selectAll("path").each(function(d, i) {
const tr = parseTransform(this.getAttribute("transform"));
this.setAttribute("transform", `rotate(${options.winds[i]} ${tr[1]} ${tr[2]})`);
});
globe
.select("#globeWindArrows")
.selectAll("path")
.each(function (d, i) {
const tr = parseTransform(this.getAttribute("transform"));
this.setAttribute("transform", `rotate(${options.winds[i]} ${tr[1]} ${tr[2]})`);
});
}
function changeWind() {
@ -112,13 +127,13 @@ function editWorld() {
const tr = parseTransform(arrow.getAttribute("transform"));
arrow.setAttribute("transform", `rotate(${options.winds[tier]} ${tr[1]} ${tr[2]})`);
localStorage.setItem("winds", options.winds);
const mapTiers = d3.range(mapCoordinates.latN, mapCoordinates.latS, -30).map(c => (90-c) / 30 | 0);
const mapTiers = d3.range(mapCoordinates.latN, mapCoordinates.latS, -30).map(c => ((90 - c) / 30) | 0);
if (mapTiers.includes(tier)) updateWorld();
}
function restoreDefaultWinds() {
const defaultWinds = [225, 45, 225, 315, 135, 315];
const mapTiers = d3.range(mapCoordinates.latN, mapCoordinates.latS, -30).map(c => (90-c) / 30 | 0);
const mapTiers = d3.range(mapCoordinates.latN, mapCoordinates.latS, -30).map(c => ((90 - c) / 30) | 0);
const update = mapTiers.some(t => options.winds[t] != defaultWinds[t]);
options.winds = defaultWinds;
updateWindDirections();
@ -132,4 +147,4 @@ function editWorld() {
lock("latitude");
updateWorld();
}
}
}