mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2026-02-05 01:51:23 +01:00
chore: add npm + vite for progressive enhancement
This commit is contained in:
parent
0c26f0831f
commit
3490ad9865
1374 changed files with 111037 additions and 19 deletions
199
src/modules/ui/world-configurator.js
Normal file
199
src/modules/ui/world-configurator.js
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
function editWorld() {
|
||||
if (customization) return;
|
||||
|
||||
$("#worldConfigurator").dialog({
|
||||
title: "Configure World",
|
||||
resizable: false,
|
||||
width: "minmax(40em, 85vw)",
|
||||
buttons: {"Update world": updateWorld},
|
||||
open: function () {
|
||||
const checkbox = /* html */ `<div class="dontAsk" data-tip="Automatically update world on input changes and button clicks">
|
||||
<input id="wcAutoChange" class="checkbox" type="checkbox" checked />
|
||||
<label for="wcAutoChange" class="checkbox-label"><i>auto-apply changes</i></label>
|
||||
</div>`;
|
||||
const pane = this.parentElement.querySelector(".ui-dialog-buttonpane");
|
||||
pane.insertAdjacentHTML("afterbegin", checkbox);
|
||||
|
||||
const button = this.parentElement.querySelector(".ui-dialog-buttonset > button");
|
||||
button.on("mousemove", () => tip("Apply current settings to the map"));
|
||||
},
|
||||
close: function () {
|
||||
$(this).dialog("destroy");
|
||||
}
|
||||
});
|
||||
|
||||
const globe = d3.select("#globe");
|
||||
const projection = d3.geoOrthographic().translate([100, 100]).scale(100);
|
||||
const path = d3.geoPath(projection);
|
||||
|
||||
updateInputValues();
|
||||
updateGlobeTemperature();
|
||||
updateGlobePosition();
|
||||
|
||||
if (modules.editWorld) return;
|
||||
modules.editWorld = true;
|
||||
|
||||
const graticule = d3.geoGraticule();
|
||||
globe.select("#globeWindArrows").on("click", handleWindChange);
|
||||
globe.select("#globeGraticule").attr("d", round(path(graticule()))); // globe graticule
|
||||
updateWindDirections();
|
||||
|
||||
byId("worldControls").on("input", handleControlsChange);
|
||||
byId("restoreWinds").on("click", restoreDefaultWinds);
|
||||
byId("wcWholeWorld").on("click", () => applyWorldPreset(100, 50));
|
||||
byId("wcNorthern").on("click", () => applyWorldPreset(33, 25));
|
||||
byId("wcTropical").on("click", () => applyWorldPreset(33, 50));
|
||||
byId("wcSouthern").on("click", () => applyWorldPreset(33, 75));
|
||||
|
||||
function updateInputValues() {
|
||||
byId("temperatureEquatorInput").value = options.temperatureEquator;
|
||||
byId("temperatureEquatorOutput").value = options.temperatureEquator;
|
||||
byId("temperatureEquatorF").innerText = convertTemperature(options.temperatureEquator, "°F");
|
||||
|
||||
byId("temperatureNorthPoleInput").value = options.temperatureNorthPole;
|
||||
byId("temperatureNorthPoleOutput").value = options.temperatureNorthPole;
|
||||
byId("temperatureNorthPoleF").innerText = convertTemperature(options.temperatureNorthPole, "°F");
|
||||
|
||||
byId("temperatureSouthPoleInput").value = options.temperatureSouthPole;
|
||||
byId("temperatureSouthPoleOutput").value = options.temperatureSouthPole;
|
||||
byId("temperatureSouthPoleF").innerText = convertTemperature(options.temperatureSouthPole, "°F");
|
||||
}
|
||||
|
||||
function handleControlsChange({target}) {
|
||||
const stored = target.dataset.stored;
|
||||
byId(stored + "Input").value = target.value;
|
||||
byId(stored + "Output").value = target.value;
|
||||
lock(stored);
|
||||
|
||||
if (stored === "temperatureEquator") {
|
||||
options.temperatureEquator = Number(target.value);
|
||||
byId("temperatureEquatorF").innerText = convertTemperature(options.temperatureEquator, "°F");
|
||||
} else if (stored === "temperatureNorthPole") {
|
||||
options.temperatureNorthPole = Number(target.value);
|
||||
byId("temperatureNorthPoleF").innerText = convertTemperature(options.temperatureNorthPole, "°F");
|
||||
} else if (stored === "temperatureSouthPole") {
|
||||
options.temperatureSouthPole = Number(target.value);
|
||||
byId("temperatureSouthPoleF").innerText = convertTemperature(options.temperatureSouthPole, "°F");
|
||||
}
|
||||
|
||||
if (byId("wcAutoChange").checked) updateWorld();
|
||||
}
|
||||
|
||||
function updateWorld() {
|
||||
updateGlobeTemperature();
|
||||
updateGlobePosition();
|
||||
calculateTemperatures();
|
||||
generatePrecipitation();
|
||||
const heights = new Uint8Array(pack.cells.h);
|
||||
Rivers.generate();
|
||||
Rivers.specify();
|
||||
pack.cells.h = new Float32Array(heights);
|
||||
Biomes.define();
|
||||
Features.defineGroups();
|
||||
Lakes.defineNames();
|
||||
|
||||
if (layerIsOn("toggleTemperature")) drawTemperature();
|
||||
if (layerIsOn("togglePrecipitation")) drawPrecipitation();
|
||||
if (layerIsOn("toggleBiomes")) drawBiomes();
|
||||
if (layerIsOn("toggleCoordinates")) drawCoordinates();
|
||||
if (layerIsOn("toggleRivers")) drawRivers();
|
||||
if (byId("canvas3d")) setTimeout(ThreeD.update(), 500);
|
||||
}
|
||||
|
||||
function updateGlobePosition() {
|
||||
const size = +byId("mapSizeOutput").value;
|
||||
const eqD = ((graphHeight / 2) * 100) / size;
|
||||
|
||||
calculateMapCoordinates();
|
||||
const mc = mapCoordinates;
|
||||
const unit = distanceUnitInput.value;
|
||||
const meridian = toKilometer(eqD * 2 * distanceScale);
|
||||
byId("mapSize").innerHTML = `${graphWidth}x${graphHeight}`;
|
||||
byId("mapSizeFriendly").innerHTML = `${rn(graphWidth * distanceScale)}x${rn(graphHeight * distanceScale)} ${unit}`;
|
||||
byId("meridianLength").innerHTML = rn(eqD * 2);
|
||||
byId("meridianLengthFriendly").innerHTML = `${rn(eqD * 2 * distanceScale)} ${unit}`;
|
||||
byId("meridianLengthEarth").innerHTML = meridian ? " = " + rn(meridian / 200) + "%🌏" : "";
|
||||
byId("mapCoordinates").innerHTML = `${lat(mc.latN)} ${Math.abs(rn(mc.lonW))}°W; ${lat(mc.latS)} ${rn(mc.lonE)}°E`;
|
||||
|
||||
function toKilometer(v) {
|
||||
if (unit === "km") return v;
|
||||
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
|
||||
}
|
||||
|
||||
// parse latitude value
|
||||
function lat(lat) {
|
||||
return lat > 0 ? Math.abs(rn(lat)) + "°N" : Math.abs(rn(lat)) + "°S";
|
||||
}
|
||||
|
||||
const area = d3.geoGraticule().extent([
|
||||
[mc.lonW, mc.latN],
|
||||
[mc.lonE, mc.latS]
|
||||
]);
|
||||
|
||||
globe.select("#globeArea").attr("d", round(path(area.outline()))); // map area
|
||||
}
|
||||
|
||||
// update temperatures on globe (visual-only)
|
||||
function updateGlobeTemperature() {
|
||||
const tEq = options.temperatureEquator;
|
||||
const tNP = options.temperatureNorthPole;
|
||||
const tSP = options.temperatureSouthPole;
|
||||
|
||||
const scale = d3.scaleSequential(d3.interpolateSpectral);
|
||||
const getColor = value => scale(1 - value);
|
||||
const [tMin, tMax] = [-25, 30]; // temperature extremes
|
||||
const tDelta = tMax - tMin;
|
||||
|
||||
globe.select("#grad90").attr("stop-color", getColor((tNP - tMin) / tDelta));
|
||||
globe.select("#grad60").attr("stop-color", getColor((tEq - ((tEq - tNP) * 2) / 3 - tMin) / tDelta));
|
||||
globe.select("#grad30").attr("stop-color", getColor((tEq - ((tEq - tNP) * 1) / 4 - tMin) / tDelta));
|
||||
globe.select("#grad0").attr("stop-color", getColor((tEq - tMin) / tDelta));
|
||||
globe.select("#grad-30").attr("stop-color", getColor((tEq - ((tEq - tSP) * 1) / 4 - tMin) / tDelta));
|
||||
globe.select("#grad-60").attr("stop-color", getColor((tEq - ((tEq - tSP) * 2) / 3 - tMin) / tDelta));
|
||||
globe.select("#grad-90").attr("stop-color", getColor((tSP - tMin) / tDelta));
|
||||
}
|
||||
|
||||
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]})`);
|
||||
});
|
||||
}
|
||||
|
||||
function handleWindChange() {
|
||||
const arrow = d3.event.target.nextElementSibling;
|
||||
const tier = +arrow.dataset.tier;
|
||||
options.winds[tier] = (options.winds[tier] + 45) % 360;
|
||||
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);
|
||||
if (byId("wcAutoChange").checked && 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 update = byId("wcAutoChange").checked && mapTiers.some(t => options.winds[t] != defaultWinds[t]);
|
||||
options.winds = defaultWinds;
|
||||
updateWindDirections();
|
||||
if (update) updateWorld();
|
||||
}
|
||||
|
||||
function applyWorldPreset(size, lat) {
|
||||
byId("mapSizeInput").value = byId("mapSizeOutput").value = size;
|
||||
byId("latitudeInput").value = byId("latitudeOutput").value = lat;
|
||||
lock("mapSize");
|
||||
lock("latitude");
|
||||
if (byId("wcAutoChange").checked) updateWorld();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue