mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 09:41:24 +01:00
v 1.21
This commit is contained in:
parent
a3fa5443d6
commit
5ffd30dee8
19 changed files with 686 additions and 412 deletions
|
|
@ -73,12 +73,16 @@
|
|||
riversData.push({river: riverNext, cell: i, x, y});
|
||||
riverNext++;
|
||||
}
|
||||
|
||||
|
||||
if (cells.r[min]) { // downhill cell already has river assigned
|
||||
if (cells.fl[min] < cells.fl[i]) {
|
||||
cells.conf[min] = cells.fl[min]; // confluence
|
||||
cells.conf[min] = cells.fl[min]; // mark confluence
|
||||
if (h[min] >= 20) riversData.find(r => r.river === cells.r[min]).parent = cells.r[i]; // min river is a tributary of current river
|
||||
cells.r[min] = cells.r[i]; // re-assign river if downhill part has less flux
|
||||
} else cells.conf[min] += cells.fl[i]; // confluence
|
||||
} else {
|
||||
cells.conf[min] += cells.fl[i]; // mark confluence
|
||||
if (h[min] >= 20) riversData.find(r => r.river === cells.r[i]).parent = cells.r[min]; // current river is a tributary of min river
|
||||
}
|
||||
} else cells.r[min] = cells.r[i]; // assign the river to the downhill cell
|
||||
|
||||
const nx = p[min][0], ny = p[min][1];
|
||||
|
|
@ -99,25 +103,29 @@
|
|||
|
||||
});
|
||||
}()
|
||||
|
||||
void function drawRivers() {
|
||||
const riverPaths = []; // to store data for all rivers
|
||||
|
||||
void function defineRivers() {
|
||||
pack.rivers = []; // rivers data
|
||||
const riverPaths = []; // temporary data for all rivers
|
||||
|
||||
for (let r = 1; r <= riverNext; r++) {
|
||||
const riverSegments = riversData.filter(d => d.river === r);
|
||||
|
||||
|
||||
if (riverSegments.length > 2) {
|
||||
const riverEnhanced = addMeandring(riverSegments);
|
||||
const width = rn(0.8 + Math.random() * 0.4, 1); // river width modifier
|
||||
const increment = rn(0.8 + Math.random() * 0.6, 1); // river bed widening modifier
|
||||
const path = getPath(riverEnhanced, width, increment);
|
||||
const width = rn(.8 + Math.random() * .4, 1); // river width modifier
|
||||
const increment = rn(.8 + Math.random() * .6, 1); // river bed widening modifier
|
||||
const [path, length] = getPath(riverEnhanced, width, increment);
|
||||
riverPaths.push([r, path, width, increment]);
|
||||
const parent = riverSegments[0].parent || 0;
|
||||
pack.rivers.push({i:r, parent, length, source:riverSegments[0].cell, mouth:last(riverSegments).cell});
|
||||
} else {
|
||||
// remove too short rivers
|
||||
riverSegments.filter(s => cells.r[s.cell] === r).forEach(s => cells.r[s.cell] = 0);
|
||||
}
|
||||
}
|
||||
|
||||
// drawRivers
|
||||
rivers.selectAll("path").remove();
|
||||
rivers.selectAll("path").data(riverPaths).enter()
|
||||
.append("path").attr("d", d => d[1]).attr("id", d => "river"+d[0])
|
||||
|
|
@ -129,12 +137,10 @@
|
|||
|
||||
// depression filling algorithm (for a correct water flux modeling)
|
||||
const resolveDepressions = function(h) {
|
||||
console.time('resolveDepressions');
|
||||
const cells = pack.cells;
|
||||
const land = cells.i.filter(i => h[i] >= 20 && h[i] < 100 && !cells.b[i]); // exclude near-border cells
|
||||
land.sort((a,b) => h[b] - h[a]); // highest cells go first
|
||||
let depressed = false;
|
||||
const depressions = [];
|
||||
|
||||
for (let l = 0, depression = Infinity; depression && l < 100; l++) {
|
||||
depression = 0;
|
||||
|
|
@ -147,12 +153,8 @@
|
|||
depressed = true;
|
||||
}
|
||||
}
|
||||
depressions.push(depression);
|
||||
}
|
||||
|
||||
console.log(depressions);
|
||||
|
||||
console.timeEnd('resolveDepressions');
|
||||
return depressed;
|
||||
}
|
||||
|
||||
|
|
@ -242,9 +244,56 @@
|
|||
const right = lineGen(riverPointsRight);
|
||||
let left = lineGen(riverPointsLeft);
|
||||
left = left.substring(left.indexOf("C"));
|
||||
return round(right + left, 2);
|
||||
return [round(right + left, 2), rn(riverLength, 2)];
|
||||
}
|
||||
|
||||
return {generate, resolveDepressions, addMeandring, getPath};
|
||||
const specify = function() {
|
||||
if (!pack.rivers.length) return;
|
||||
const smallLength = pack.rivers.map(r => r.length||0).sort((a,b) => a-b)[Math.ceil(pack.rivers.length * .15)];
|
||||
const smallType = {"Creek":9, "River":3, "Brook":3, "Stream":1}; // weighted small river types
|
||||
|
||||
for (const r of pack.rivers) {
|
||||
r.basin = getBasin(r.i, r.parent);
|
||||
r.name = getName(r.mouth);
|
||||
const small = r.length < smallLength;
|
||||
r.type = r.parent && !(r.i%6) ? small ? "Branch" : "Fork" : small ? rw(smallType) : "River";
|
||||
}
|
||||
|
||||
return;
|
||||
const basins = [...(new Set(pack.rivers.map(r=>r.basin)))];
|
||||
const colors = getColors(basins.length);
|
||||
basins.forEach((b,i) => {
|
||||
pack.rivers.filter(r => r.basin === b).forEach(r => {
|
||||
rivers.select("#river"+r.i).attr("fill", colors[i]);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
const getName = function(cell) {
|
||||
return Names.getCulture(pack.cells.culture[cell]);
|
||||
}
|
||||
|
||||
// remove river and all its tributaries
|
||||
const remove = function(id) {
|
||||
const riversToRemove = pack.rivers.filter(r => r.i === id || getBasin(r.i, r.parent, id) === id).map(r => r.i);
|
||||
riversToRemove.forEach(r => rivers.select("#river"+r).remove());
|
||||
pack.cells.r.forEach((r, i) => {
|
||||
if (r && riversToRemove.includes(r)) pack.cells.r[i] = 0;
|
||||
});
|
||||
pack.rivers = pack.rivers.filter(r => !riversToRemove.includes(r.i));
|
||||
}
|
||||
|
||||
const getBasin = function(r, p, e) {
|
||||
while (p) {
|
||||
const parent = pack.rivers.find(r => r.i === p);
|
||||
if (parent) r = parent.i;
|
||||
p = parent ? parent.parent : 0;
|
||||
if (r === e) return r;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
return {generate, resolveDepressions, addMeandring, getPath, specify, getName, getBasin, remove};
|
||||
|
||||
})));
|
||||
|
|
@ -250,6 +250,7 @@ function getMapData() {
|
|||
const burgs = JSON.stringify(pack.burgs);
|
||||
const religions = JSON.stringify(pack.religions);
|
||||
const provinces = JSON.stringify(pack.provinces);
|
||||
const rivers = JSON.stringify(pack.rivers);
|
||||
|
||||
// store name array only if it is not the same as default
|
||||
const defaultNB = Names.getNameBases();
|
||||
|
|
@ -268,7 +269,7 @@ function getMapData() {
|
|||
pack.cells.biome, pack.cells.burg, pack.cells.conf, pack.cells.culture, pack.cells.fl,
|
||||
pop, pack.cells.r, pack.cells.road, pack.cells.s, pack.cells.state,
|
||||
pack.cells.religion, pack.cells.province, pack.cells.crossroad, religions, provinces,
|
||||
namesData].join("\r\n");
|
||||
namesData, rivers].join("\r\n");
|
||||
const blob = new Blob([data], {type: "text/plain"});
|
||||
|
||||
console.timeEnd("createMapDataBlob");
|
||||
|
|
@ -293,23 +294,6 @@ async function saveMap() {
|
|||
window.setTimeout(() => window.URL.revokeObjectURL(URL), 5000);
|
||||
}
|
||||
|
||||
// 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`;
|
||||
|
||||
$("#alert").dialog({title: "GIS data export", resizable: false, width: "32em", position: {my: "center", at: "center", of: "svg"},
|
||||
buttons: {
|
||||
Cells: saveGeoJSON_Cells,
|
||||
Routes: saveGeoJSON_Roads,
|
||||
Rivers: saveGeoJSON_Rivers,
|
||||
Markers: saveGeoJSON_Markers,
|
||||
Close: function() {$(this).dialog("close");}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function saveGeoJSON_Cells() {
|
||||
let data = "{ \"type\": \"FeatureCollection\", \"features\": [\n";
|
||||
const cells = pack.cells, v = pack.vertices;
|
||||
|
|
@ -686,6 +670,7 @@ function parseLoadedData(data) {
|
|||
pack.burgs = JSON.parse(data[15]);
|
||||
pack.religions = data[29] ? JSON.parse(data[29]) : [{i: 0, name: "No religion"}];
|
||||
pack.provinces = data[30] ? JSON.parse(data[30]) : [0];
|
||||
pack.rivers = data[32] ? JSON.stringify(data[32]) : [];
|
||||
|
||||
const cells = pack.cells;
|
||||
cells.biome = Uint8Array.from(data[16].split(","));
|
||||
|
|
@ -899,14 +884,6 @@ function parseLoadedData(data) {
|
|||
});
|
||||
}
|
||||
|
||||
// v 1.11 replaced "display" attribute by "display" style
|
||||
viewbox.selectAll("g").each(function() {
|
||||
if (this.hasAttribute("display")) {
|
||||
this.removeAttribute("display");
|
||||
this.style.display = "none";
|
||||
}
|
||||
});
|
||||
|
||||
// v 1.11 had an issue with fogging being displayed on load
|
||||
unfog();
|
||||
|
||||
|
|
@ -916,6 +893,30 @@ function parseLoadedData(data) {
|
|||
if (!terrain.attr("density")) terrain.attr("density", .4);
|
||||
}
|
||||
|
||||
if (version < 1.21) {
|
||||
// v 1.11 replaced "display" attribute by "display" style
|
||||
viewbox.selectAll("g").each(function() {
|
||||
if (this.hasAttribute("display")) {
|
||||
this.removeAttribute("display");
|
||||
this.style.display = "none";
|
||||
}
|
||||
});
|
||||
|
||||
// v 1.21 added rivers data to pack
|
||||
|
||||
pack.rivers = []; // rivers data
|
||||
rivers.selectAll("path").each(function() {
|
||||
const i = +this.id.slice(5);
|
||||
const length = this.getTotalLength() / 2;
|
||||
const s = this.getPointAtLength(length), e = this.getPointAtLength(0);
|
||||
const source = findCell(s.x, s.y), mouth = findCell(e.x, e.y);
|
||||
const name = Rivers.getName(mouth);
|
||||
const type = length < 25 ? rw({"Creek":9, "River":3, "Brook":3, "Stream":1}) : "River";
|
||||
pack.rivers.push({i, parent:0, length, source, mouth, basin:i, name, type});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
changeMapSize();
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ function editBurg(id) {
|
|||
document.getElementById("burgAddGroup").addEventListener("click", toggleNewGroupInput);
|
||||
document.getElementById("burgRemoveGroup").addEventListener("click", removeBurgsGroup);
|
||||
|
||||
document.getElementById("burgName").addEventListener("input", changeName);
|
||||
document.getElementById("burgName").addEventListener("input", changeName);
|
||||
document.getElementById("burgNameReCulture").addEventListener("click", generateNameCulture);
|
||||
document.getElementById("burgNameReRandom").addEventListener("click", generateNameRandom);
|
||||
document.getElementById("burgPopulation").addEventListener("change", changePopulation);
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
"use strict";
|
||||
function editBurgs() {
|
||||
function overviewBurgs() {
|
||||
if (customization) return;
|
||||
closeDialogs("#burgsEditor, .stable");
|
||||
closeDialogs("#burgsOverview, .stable");
|
||||
if (!layerIsOn("toggleIcons")) toggleIcons();
|
||||
if (!layerIsOn("toggleLabels")) toggleLabels();
|
||||
|
||||
const body = document.getElementById("burgsBody");
|
||||
updateFilter();
|
||||
burgsEditorAddLines();
|
||||
$("#burgsEditor").dialog();
|
||||
burgsOverviewAddLines();
|
||||
$("#burgsOverview").dialog();
|
||||
|
||||
if (modules.editBurgs) return;
|
||||
modules.editBurgs = true;
|
||||
if (modules.overviewBurgs) return;
|
||||
modules.overviewBurgs = true;
|
||||
|
||||
$("#burgsEditor").dialog({
|
||||
$("#burgsOverview").dialog({
|
||||
title: "Burgs Overview", resizable: false, width: fitContent(), close: exitAddBurgMode,
|
||||
position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}
|
||||
});
|
||||
|
||||
// add listeners
|
||||
document.getElementById("burgsEditorRefresh").addEventListener("click", refreshBurgsEditor);
|
||||
document.getElementById("burgsOverviewRefresh").addEventListener("click", refreshBurgsEditor);
|
||||
document.getElementById("burgsChart").addEventListener("click", showBurgsChart);
|
||||
document.getElementById("burgsFilterState").addEventListener("change", burgsEditorAddLines);
|
||||
document.getElementById("burgsFilterCulture").addEventListener("change", burgsEditorAddLines);
|
||||
document.getElementById("burgsFilterState").addEventListener("change", burgsOverviewAddLines);
|
||||
document.getElementById("burgsFilterCulture").addEventListener("change", burgsOverviewAddLines);
|
||||
document.getElementById("regenerateBurgNames").addEventListener("click", regenerateNames);
|
||||
document.getElementById("addNewBurg").addEventListener("click", enterAddBurgMode);
|
||||
document.getElementById("burgsExport").addEventListener("click", downloadBurgsData);
|
||||
|
|
@ -32,7 +32,7 @@ function editBurgs() {
|
|||
|
||||
function refreshBurgsEditor() {
|
||||
updateFilter();
|
||||
burgsEditorAddLines();
|
||||
burgsOverviewAddLines();
|
||||
}
|
||||
|
||||
function updateFilter() {
|
||||
|
|
@ -53,8 +53,8 @@ function editBurgs() {
|
|||
culturesSorted.forEach(c => cultureFilter.options.add(new Option(c.name, c.i, false, c.i == selectedCulture)));
|
||||
}
|
||||
|
||||
// add line for each state
|
||||
function burgsEditorAddLines() {
|
||||
// add line for each burg
|
||||
function burgsOverviewAddLines() {
|
||||
const selectedState = +document.getElementById("burgsFilterState").value;
|
||||
const selectedCulture = +document.getElementById("burgsFilterCulture").value;
|
||||
let filtered = pack.burgs.filter(b => b.i && !b.removed); // all valid burgs
|
||||
|
|
@ -168,7 +168,7 @@ function editBurgs() {
|
|||
function toggleCapitalStatus() {
|
||||
const burg = +this.parentNode.parentNode.dataset.id;
|
||||
toggleCapital(burg);
|
||||
burgsEditorAddLines();
|
||||
burgsOverviewAddLines();
|
||||
}
|
||||
|
||||
function togglePortStatus() {
|
||||
|
|
@ -193,7 +193,7 @@ function editBurgs() {
|
|||
Remove: function() {
|
||||
$(this).dialog("close");
|
||||
removeBurg(burg);
|
||||
burgsEditorAddLines();
|
||||
burgsOverviewAddLines();
|
||||
},
|
||||
Cancel: function() {$(this).dialog("close");}
|
||||
}
|
||||
|
|
@ -228,7 +228,7 @@ function editBurgs() {
|
|||
|
||||
if (d3.event.shiftKey === false) {
|
||||
exitAddBurgMode();
|
||||
burgsEditorAddLines();
|
||||
burgsOverviewAddLines();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -448,7 +448,7 @@ function editBurgs() {
|
|||
burgLabels.select("[data-id='" + id + "']").text(change[i].name);
|
||||
}
|
||||
$(this).dialog("close");
|
||||
burgsEditorAddLines();
|
||||
burgsOverviewAddLines();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -470,7 +470,7 @@ function editBurgs() {
|
|||
|
||||
function removeAllBurgs() {
|
||||
pack.burgs.filter(b => b.i && !b.capital).forEach(b => removeBurg(b.i));
|
||||
burgsEditorAddLines();
|
||||
burgsOverviewAddLines();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ function editCultures() {
|
|||
title: "Cultures Editor", resizable: false, width: fitContent(), close: closeCulturesEditor,
|
||||
position: {my: "right top", at: "right-10 top+10", of: "svg"}
|
||||
});
|
||||
body.focus();
|
||||
|
||||
// add listeners
|
||||
document.getElementById("culturesEditorRefresh").addEventListener("click", refreshCulturesEditor);
|
||||
|
|
|
|||
|
|
@ -383,7 +383,7 @@ function createPicker() {
|
|||
|
||||
function updateSelectedRect(fill) {
|
||||
document.getElementById("picker").querySelector("rect.selected").classList.remove("selected");
|
||||
document.getElementById("picker").querySelector("rect[fill='"+fill+"']").classList.add("selected");
|
||||
document.getElementById("picker").querySelector("rect[fill='"+fill.toLowerCase()+"']").classList.add("selected");
|
||||
}
|
||||
|
||||
function updateSpaces() {
|
||||
|
|
@ -558,3 +558,25 @@ function uploadFile(el, callback) {
|
|||
el.value = "";
|
||||
fileReader.onload = loaded => callback(loaded.target.result);
|
||||
}
|
||||
|
||||
function highlightElement(element) {
|
||||
if (debug.select(".highlighted").size()) return; // allow only 1 highlight element simultaniosly
|
||||
const box = element.getBBox();
|
||||
const transform = element.getAttribute("transform") || null;
|
||||
const enter = d3.transition().duration(1000).ease(d3.easeBounceOut);
|
||||
const exit = d3.transition().duration(500).ease(d3.easeLinear);
|
||||
|
||||
const highlight = debug.append("rect").attr("x", box.x).attr("y", box.y)
|
||||
.attr("width", box.width).attr("height", box.height).attr("transform", transform);
|
||||
|
||||
highlight.classed("highlighted", 1)
|
||||
.transition(enter).style("outline-offset", "0px")
|
||||
.transition(exit).style("outline-color", "transparent").delay(1000).remove();
|
||||
|
||||
const tr = parseTransform(transform);
|
||||
let x = box.x + box.width / 2;
|
||||
if (tr[0]) x += tr[0];
|
||||
let y = box.y + box.height / 2;
|
||||
if (tr[1]) y += tr[1];
|
||||
if (scale >= 2) zoomTo(x, y, scale, 1600);
|
||||
}
|
||||
|
|
@ -80,10 +80,9 @@ function showMapTooltip(point, e, i, g) {
|
|||
const group = path[path.length - 7].id;
|
||||
const subgroup = path[path.length - 8].id;
|
||||
const land = pack.cells.h[i] >= 20;
|
||||
//const type = pack.features[cells.f[i]].type;
|
||||
|
||||
// specific elements
|
||||
if (group === "rivers") {tip("Click to edit the River"); return;}
|
||||
if (group === "rivers") {tip(getRiverName(e.target.id) + "Click to edit"); return;}
|
||||
if (group === "routes") {tip("Click to edit the Route"); return;}
|
||||
if (group === "terrain") {tip("Click to edit the Relief Icon"); return;}
|
||||
if (subgroup === "burgLabels" || subgroup === "burgIcons") {tip("Click to open Burg Editor"); return;}
|
||||
|
|
@ -121,6 +120,11 @@ function showMapTooltip(point, e, i, g) {
|
|||
if (layerIsOn("toggleHeight")) tip("Height: " + getFriendlyHeight(point));
|
||||
}
|
||||
|
||||
function getRiverName(id) {
|
||||
const r = pack.rivers.find(r => r.i == id.slice(5));
|
||||
return r ? r.name + " " + r.type + ". " : "";
|
||||
}
|
||||
|
||||
// get cell info on mouse move
|
||||
function updateCellInfo(point, i, g) {
|
||||
const cells = pack.cells;
|
||||
|
|
@ -133,6 +137,7 @@ function updateCellInfo(point, i, g) {
|
|||
infoHeight.innerHTML = getFriendlyHeight(point) + " (" + h + ")";
|
||||
infoTemp.innerHTML = convertTemperature(grid.cells.temp[g]);
|
||||
infoPrec.innerHTML = cells.h[i] >= 20 ? getFriendlyPrecipitation(i) : "n/a";
|
||||
infoRiver.innerHTML = cells.h[i] >= 20 && cells.r[i] ? getRiverInfo(cells.r[i]) : "no";
|
||||
infoState.innerHTML = cells.h[i] >= 20 ? cells.state[i] ? `${pack.states[cells.state[i]].fullName} (${cells.state[i]})` : "neutral lands (0)" : "no";
|
||||
infoProvince.innerHTML = cells.province[i] ? `${pack.provinces[cells.province[i]].fullName} (${cells.province[i]})` : "no";
|
||||
infoCulture.innerHTML = cells.culture[i] ? `${pack.cultures[cells.culture[i]].name} (${cells.culture[i]})` : "no";
|
||||
|
|
@ -171,6 +176,11 @@ function getFriendlyPrecipitation(i) {
|
|||
return prec * 100 + " mm";
|
||||
}
|
||||
|
||||
function getRiverInfo(id) {
|
||||
const r = pack.rivers.find(r => r.i == id);
|
||||
return r ? `${r.name} ${r.type} (${id})` : "n/a";
|
||||
}
|
||||
|
||||
// get user-friendly (real-world) population value from map data
|
||||
function getFriendlyPopulation(i) {
|
||||
const rural = pack.cells.pop[i] * populationRate.value;
|
||||
|
|
@ -274,6 +284,7 @@ function showInfo() {
|
|||
|
||||
// prevent default browser behavior for FMG-used hotkeys
|
||||
document.addEventListener("keydown", event => {
|
||||
if (event.altKey && event.keyCode !== 18) event.preventDefault(); // disallowalt key combinations
|
||||
if ([112, 113, 117, 120, 9].includes(event.keyCode)) event.preventDefault(); // F1, F2, F6, F9, Tab
|
||||
});
|
||||
|
||||
|
|
@ -286,7 +297,7 @@ document.addEventListener("keyup", event => {
|
|||
if (active === "DIV" && document.activeElement.contentEditable === "true") return; // don't trigger if user inputs a text
|
||||
event.stopPropagation();
|
||||
|
||||
const key = event.keyCode, ctrl = event.ctrlKey, shift = event.shiftKey, meta = event.metaKey;
|
||||
const key = event.keyCode, ctrl = event.ctrlKey || event.metaKey, shift = event.shiftKey, alt = event.altKey;
|
||||
|
||||
if (key === 112) showInfo(); // "F1" to show info
|
||||
else if (key === 113) regeneratePrompt(); // "F2" for new map
|
||||
|
|
@ -321,22 +332,23 @@ document.addEventListener("keyup", event => {
|
|||
else if (shift && key === 78) editNamesbase(); // Shift + "N" to edit Namesbase
|
||||
else if (shift && key === 90) editZones(); // Shift + "Z" to edit Zones
|
||||
else if (shift && key === 82) editReligions(); // Shift + "R" to edit Religions
|
||||
else if (shift && key === 84) editBurgs(); // Shift + "T" to edit Burgs
|
||||
else if (shift && key === 85) editUnits(); // Shift + "U" to edit Units
|
||||
else if (shift && key === 81) editUnits(); // Shift + "Q" to edit Units
|
||||
else if (shift && key === 79) editNotes(); // Shift + "O" to edit Notes
|
||||
else if (shift && key === 84) overviewBurgs(); // Shift + "T" to open Burgs overview
|
||||
else if (shift && key === 86) overviewRivers(); // Shift + "V" to open Rivers overview
|
||||
else if (shift && key === 69) viewCellDetails(); // Shift + "E" to open Cell Details
|
||||
|
||||
else if (shift && key === 71) toggleAddBurg(); // Shift + "G" to click to add Burg
|
||||
else if (shift && key === 65) toggleAddLabel(); // Shift + "A" to click to add Label
|
||||
else if (shift && key === 73) toggleAddRiver(); // Shift + "I" to click to add River
|
||||
else if (shift && key === 69) toggleAddRoute(); // Shift + "E" to click to add Route
|
||||
else if (shift && key === 75) toggleAddMarker(); // Shift + "K" to click to add Marker
|
||||
else if (shift && key === 49) toggleAddBurg(); // Shift + "1" to click to add Burg
|
||||
else if (shift && key === 50) toggleAddLabel(); // Shift + "2" to click to add Label
|
||||
else if (shift && key === 51) toggleAddRiver(); // Shift + "3" to click to add River
|
||||
else if (shift && key === 52) toggleAddRoute(); // Shift + "4" to click to add Route
|
||||
else if (shift && key === 53) toggleAddMarker(); // Shift + "5" to click to add Marker
|
||||
|
||||
else if (meta && key === 192) console.log(pack.cells); // Metakey + "`" to log cells data
|
||||
else if (meta && key === 66) console.table(pack.burgs); // Metakey + "B" to log burgs data
|
||||
else if (meta && key === 83) console.table(pack.states); // Metakey + "S" to log states data
|
||||
else if (meta && key === 67) console.table(pack.cultures); // Metakey + "C" to log cultures data
|
||||
else if (meta && key === 82) console.table(pack.religions); // Metakey + "R" to log religions data
|
||||
else if (meta && key === 70) console.table(pack.features); // Metakey + "F" to log features data
|
||||
else if (alt && key === 66) console.table(pack.burgs); // Alt + "B" to log burgs data
|
||||
else if (alt && key === 83) console.table(pack.states); // Alt + "S" to log states data
|
||||
else if (alt && key === 67) console.table(pack.cultures); // Alt + "C" to log cultures data
|
||||
else if (alt && key === 82) console.table(pack.religions); // Alt + "R" to log religions data
|
||||
else if (alt && key === 70) console.table(pack.features); // Alt + "F" to log features data
|
||||
|
||||
else if (key === 88) toggleTexture(); // "X" to toggle Texture layer
|
||||
else if (key === 72) toggleHeight(); // "H" to toggle Heightmap layer
|
||||
|
|
|
|||
|
|
@ -4,16 +4,12 @@
|
|||
function editHeightmap() {
|
||||
void function selectEditMode() {
|
||||
alertMessage.innerHTML = `<p>Heightmap is a core element on which all other data (rivers, burgs, states etc) is based.
|
||||
So the best edit approach is to <i>erase</i> the secondary data and let the system automatically regenerate it on edit completion.</p>
|
||||
|
||||
<p>You can also <i>keep</i> all the data, but you won't be able to change the coastline.</p>
|
||||
|
||||
<p>If you need to change the coastline and keep the data, you may try the <i>risk</i> edit option.
|
||||
The data will be restored as much as possible, but the coastline change can cause unexpected fluctuations and errors.</p>
|
||||
|
||||
<p>Check out ${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Heightmap-customization", "wiki")} for guidance.</p>
|
||||
|
||||
<p>Please <span class="pseudoLink" onclick=saveMap(); editHeightmap();>save the map</span> before edditing the heightmap!</p>`;
|
||||
So the best edit approach is to <i>erase</i> the secondary data and let the system automatically regenerate it on edit completion.</p>
|
||||
<p>You can also <i>keep</i> all the data, but you won't be able to change the coastline.</p>
|
||||
<p>If you need to change the coastline and keep the data, you may try the <i>risk</i> edit option.
|
||||
The data will be restored as much as possible, but the coastline change can cause unexpected fluctuations and errors.</p>
|
||||
<p>Check out ${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Heightmap-customization", "wiki")} for guidance.</p>
|
||||
<p>Please <span class="pseudoLink" onclick=saveMap(); editHeightmap();>save the map</span> before edditing the heightmap!</p>`;
|
||||
|
||||
$("#alert").dialog({resizable: false, title: "Edit Heightmap", width: "28em",
|
||||
buttons: {
|
||||
|
|
@ -179,6 +175,8 @@ function editHeightmap() {
|
|||
drawStates();
|
||||
drawBorders();
|
||||
BurgsAndStates.drawStateLabels();
|
||||
|
||||
Rivers.specify();
|
||||
addMarkers();
|
||||
addZones();
|
||||
console.timeEnd("regenerateErasedData");
|
||||
|
|
@ -338,6 +336,8 @@ function editHeightmap() {
|
|||
drawStates();
|
||||
drawBorders();
|
||||
|
||||
if (changeHeights.checked) Rivers.specify();
|
||||
|
||||
// restore zones from grid
|
||||
zones.selectAll("g").each(function() {
|
||||
const zone = d3.select(this);
|
||||
|
|
@ -378,8 +378,9 @@ function editHeightmap() {
|
|||
function mockHeightmap() {
|
||||
const data = renderOcean.checked ? grid.cells.i : grid.cells.i.filter(i => grid.cells.h[i] >= 20);
|
||||
const scheme = getColorScheme();
|
||||
viewbox.select("#heights").selectAll("polygon").data(data).join("polygon").attr("points", d => getGridPolygon(d))
|
||||
.attr("id", d => "cell"+d).attr("fill", d => getColor(grid.cells.h[d], scheme));
|
||||
viewbox.select("#heights").selectAll("polygon").data(data).join("polygon")
|
||||
.attr("points", d => getGridPolygon(d)).attr("id", d => "cell"+d)
|
||||
.attr("fill", d => getColor(grid.cells.h[d], scheme));
|
||||
}
|
||||
|
||||
// draw or update heightmap for a selection of cells
|
||||
|
|
@ -456,11 +457,11 @@ function editHeightmap() {
|
|||
document.getElementById("redo").addEventListener("click", () => restoreHistory(edits.n+1));
|
||||
document.getElementById("rescaleShow").addEventListener("click", () => {
|
||||
document.getElementById("modifyButtons").style.display = "none";
|
||||
document.getElementById("rescaleSection").style.display = "block";
|
||||
document.getElementById("rescaleSection").style.display = "block";
|
||||
});
|
||||
document.getElementById("rescaleHide").addEventListener("click", () => {
|
||||
document.getElementById("modifyButtons").style.display = "block";
|
||||
document.getElementById("rescaleSection").style.display = "none";
|
||||
document.getElementById("rescaleSection").style.display = "none";
|
||||
});
|
||||
document.getElementById("rescaler").addEventListener("change", (e) => rescale(e.target.valueAsNumber));
|
||||
document.getElementById("rescaleCondShow").addEventListener("click", () => {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ function editNotes(id, name) {
|
|||
}
|
||||
|
||||
// select an object
|
||||
if (id) {
|
||||
if (notes.length) {
|
||||
if (!id) id = notes[0].id;
|
||||
let note = notes.find(note => note.id === id);
|
||||
if (note === undefined) {
|
||||
if (!name) name = id;
|
||||
|
|
@ -81,28 +82,6 @@ function editNotes(id, name) {
|
|||
highlightElement(element); // if element is found
|
||||
}
|
||||
|
||||
function highlightElement(element) {
|
||||
if (debug.select(".highlighted").size()) return; // allow only 1 highlight element simultaniosly
|
||||
const box = element.getBBox();
|
||||
const transform = element.getAttribute("transform") || null;
|
||||
const t = d3.transition().duration(1000).ease(d3.easeBounceOut);
|
||||
const r = d3.transition().duration(500).ease(d3.easeLinear);
|
||||
|
||||
const highlight = debug.append("rect").attr("x", box.x).attr("y", box.y)
|
||||
.attr("width", box.width).attr("height", box.height).attr("transform", transform);
|
||||
|
||||
highlight.classed("highlighted", 1)
|
||||
.transition(t).style("outline-offset", "0px")
|
||||
.transition(r).style("outline-color", "transparent").remove();
|
||||
|
||||
const tr = parseTransform(transform);
|
||||
let x = box.x + box.width / 2;
|
||||
if (tr[0]) x += tr[0];
|
||||
let y = box.y + box.height / 2;
|
||||
if (tr[1]) y += tr[1];
|
||||
if (scale >= 2) zoomTo(x, y, scale, 1600);
|
||||
}
|
||||
|
||||
function downloadLegends() {
|
||||
const data = JSON.stringify(notes);
|
||||
const name = getFileName("Notes") + ".txt";
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ function changeMapSize() {
|
|||
landmass.select("rect").attr("x", 0).attr("y", 0).attr("width", maxWidth).attr("height", maxHeight);
|
||||
oceanPattern.select("rect").attr("x", 0).attr("y", 0).attr("width", maxWidth).attr("height", maxHeight);
|
||||
oceanLayers.select("rect").attr("x", 0).attr("y", 0).attr("width", maxWidth).attr("height", maxHeight);
|
||||
defs.select("#mapClip > rect").attr("width", maxWidth).attr("height", maxHeight);
|
||||
//defs.select("#mapClip > rect").attr("width", maxWidth).attr("height", maxHeight);
|
||||
|
||||
fitScaleBar();
|
||||
if (window.fitLegendBox) fitLegendBox();
|
||||
|
|
@ -157,8 +157,8 @@ function applyMapSize() {
|
|||
svgHeight = Math.min(graphHeight, window.innerHeight)
|
||||
svg.attr("width", svgWidth).attr("height", svgHeight);
|
||||
zoom.translateExtent([[0, 0],[graphWidth, graphHeight]]).scaleExtent([1, 20]).scaleTo(svg, 1);
|
||||
viewbox.attr("transform", null).attr("clip-path", "url(#mapClip)");
|
||||
defs.append("clipPath").attr("id", "mapClip").append("rect").attr("x", 0).attr("y", 0).attr("width", graphWidth).attr("height", graphHeight);
|
||||
//viewbox.attr("transform", null).attr("clip-path", "url(#mapClip)");
|
||||
//defs.append("clipPath").attr("id", "mapClip").append("rect").attr("x", 0).attr("y", 0).attr("width", graphWidth).attr("height", graphHeight);
|
||||
//zoom.translateExtent([[-svgWidth*.2, -graphHeight*.2], [svgWidth*1.2, graphHeight*1.2]]);
|
||||
}
|
||||
|
||||
|
|
@ -348,22 +348,9 @@ function restoreDefaultOptions() {
|
|||
document.getElementById("sticked").addEventListener("click", function(event) {
|
||||
const id = event.target.id;
|
||||
if (id === "newMapButton") regeneratePrompt();
|
||||
else if (id === "saveButton") toggleSavePane();
|
||||
else if (id === "loadButton") toggleLoadPane();
|
||||
else if (id === "saveButton") showSavePane();
|
||||
else if (id === "loadButton") showLoadPane();
|
||||
else if (id === "zoomReset") resetZoom(1000);
|
||||
else if (id === "quickSave") quickSave();
|
||||
else if (id === "saveMap") saveMap();
|
||||
else if (id === "saveSVG") saveSVG();
|
||||
else if (id === "savePNG") savePNG();
|
||||
else if (id === "saveJPEG") saveJPEG();
|
||||
else if (id === "saveGeo") saveGeoJSON();
|
||||
else if (id === "saveDropbox") saveDropbox();
|
||||
if (id === "quickSave" || id === "saveMap" || id === "saveSVG" || id === "savePNG" || id === "saveGeo" || id === "saveDropbox") toggleSavePane();
|
||||
if (id === "loadMap") mapToLoad.click();
|
||||
else if (id === "quickLoad") quickLoad();
|
||||
else if (id === "loadURL") loadURL();
|
||||
else if (id === "loadDropbox") loadDropbox();
|
||||
if (id === "quickLoad" || id === "loadURL" || id === "loadMap" || id === "loadDropbox") toggleLoadPane();
|
||||
});
|
||||
|
||||
function regeneratePrompt() {
|
||||
|
|
@ -381,28 +368,34 @@ function regeneratePrompt() {
|
|||
});
|
||||
}
|
||||
|
||||
function toggleSavePane() {
|
||||
if (saveDropdown.style.display === "block") {saveDropdown.style.display = "none"; return;}
|
||||
saveDropdown.style.display = "block";
|
||||
|
||||
// ask users to allow popups
|
||||
if (!localStorage.getItem("dns_allow_popup_message")) {
|
||||
alertMessage.innerHTML = `Generator uses pop-up window to download files.
|
||||
<br>Please ensure your browser does not block popups.
|
||||
<br>Please check browser settings and turn off adBlocker if it is enabled`;
|
||||
|
||||
$("#alert").dialog({title: "File saver", resizable: false, position: {my: "center", at: "center", of: "svg"},
|
||||
buttons: {OK: function() {
|
||||
localStorage.setItem("dns_allow_popup_message", true);
|
||||
$(this).dialog("close");
|
||||
}}
|
||||
});
|
||||
}
|
||||
function showSavePane() {
|
||||
$("#saveMapData").dialog({title: "Save map", resizable: false, width: "27em",
|
||||
position: {my: "center", at: "center", of: "svg"},
|
||||
buttons: {Close: function() {$(this).dialog("close");}}
|
||||
});
|
||||
}
|
||||
|
||||
function toggleLoadPane() {
|
||||
if (loadDropdown.style.display === "block") {loadDropdown.style.display = "none"; return;}
|
||||
loadDropdown.style.display = "block";
|
||||
// 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`;
|
||||
|
||||
$("#alert").dialog({title: "GIS data export", resizable: false, width: "35em", position: {my: "center", at: "center", of: "svg"},
|
||||
buttons: {
|
||||
Cells: saveGeoJSON_Cells,
|
||||
Routes: saveGeoJSON_Roads,
|
||||
Rivers: saveGeoJSON_Rivers,
|
||||
Markers: saveGeoJSON_Markers,
|
||||
Close: function() {$(this).dialog("close");}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showLoadPane() {
|
||||
$("#loadMapData").dialog({title: "Load map", resizable: false, width: "17em",
|
||||
position: {my: "center", at: "center", of: "svg"},
|
||||
buttons: {Close: function() {$(this).dialog("close");}}
|
||||
});
|
||||
}
|
||||
|
||||
function loadURL() {
|
||||
|
|
@ -411,7 +404,7 @@ function loadURL() {
|
|||
<input id="mapURL" type="url" style="width: 24em" placeholder="https://e-cloud.com/test.map">
|
||||
<br><i>Please note server should allow CORS for file to be loaded. If CORS is not allowed, save file to Dropbox and provide a direct link</i>`;
|
||||
alertMessage.innerHTML = inner;
|
||||
$("#alert").dialog({resizable: false, title: "Load map from URL", width: "26em",
|
||||
$("#alert").dialog({resizable: false, title: "Load map from URL", width: "27em",
|
||||
buttons: {
|
||||
Load: function() {
|
||||
const value = mapURL.value;
|
||||
|
|
|
|||
|
|
@ -1,21 +1,19 @@
|
|||
"use strict";
|
||||
function editRiver() {
|
||||
function editRiver(id) {
|
||||
if (customization) return;
|
||||
if (elSelected && d3.event.target.id === elSelected.attr("id")) return;
|
||||
if (elSelected && d3.event && d3.event.target.id === elSelected.attr("id")) return;
|
||||
closeDialogs(".stable");
|
||||
if (!layerIsOn("toggleRivers")) toggleRivers();
|
||||
|
||||
const node = d3.event.target;
|
||||
elSelected = d3.select(node).on("click", addInterimControlPoint)
|
||||
.call(d3.drag().on("start", dragRiver)).classed("draggable", true);
|
||||
const node = id ? document.getElementById(id) : d3.event.target;
|
||||
elSelected = d3.select(node).on("click", addInterimControlPoint);
|
||||
viewbox.on("touchmove mousemove", showEditorTips);
|
||||
debug.append("g").attr("id", "controlPoints").attr("transform", elSelected.attr("transform"));
|
||||
drawControlPoints(node);
|
||||
updateValues(node);
|
||||
|
||||
$("#riverEditor").dialog({
|
||||
title: "Edit River", resizable: false,
|
||||
position: {my: "center top+20", at: "top", of: d3.event, collision: "fit"},
|
||||
position: {my: "center top+20", at: "top", of: node, collision: "fit"},
|
||||
close: closeRiverEditor
|
||||
});
|
||||
|
||||
|
|
@ -23,19 +21,19 @@ function editRiver() {
|
|||
modules.editRiver = true;
|
||||
|
||||
// add listeners
|
||||
document.getElementById("riverNameShow").addEventListener("click", showRiverName);
|
||||
document.getElementById("riverNameHide").addEventListener("click", hideRiverName);
|
||||
document.getElementById("riverName").addEventListener("input", changeName);
|
||||
document.getElementById("riverType").addEventListener("input", changeType);
|
||||
document.getElementById("riverNameCulture").addEventListener("click", generateNameCulture);
|
||||
document.getElementById("riverNameRandom").addEventListener("click", generateNameRandom);
|
||||
|
||||
document.getElementById("riverWidthShow").addEventListener("click", showRiverWidth);
|
||||
document.getElementById("riverWidthHide").addEventListener("click", hideRiverWidth);
|
||||
document.getElementById("riverWidthInput").addEventListener("input", changeWidth);
|
||||
document.getElementById("riverIncrement").addEventListener("input", changeIncrement);
|
||||
|
||||
document.getElementById("riverResizeShow").addEventListener("click", showRiverSize);
|
||||
document.getElementById("riverResizeHide").addEventListener("click", hideRiverSize);
|
||||
document.getElementById("riverAngle").addEventListener("input", changeAngle);
|
||||
document.getElementById("riverScale").addEventListener("input", changeScale);
|
||||
document.getElementById("riverReset").addEventListener("click", resetTransformation);
|
||||
|
||||
document.getElementById("riverEditStyle").addEventListener("click", () => editStyle("rivers"));
|
||||
document.getElementById("riverCopy").addEventListener("click", copyRiver);
|
||||
document.getElementById("riverNew").addEventListener("click", toggleRiverCreationMode);
|
||||
document.getElementById("riverLegend").addEventListener("click", editRiverLegend);
|
||||
document.getElementById("riverRemove").addEventListener("click", removeRiver);
|
||||
|
|
@ -46,27 +44,6 @@ function editRiver() {
|
|||
if (d3.event.target.parentNode.id === "controlPoints") tip("Drag to move, click to delete the control point");
|
||||
}
|
||||
|
||||
function updateValues(node) {
|
||||
const tr = parseTransform(node.getAttribute("transform"));
|
||||
document.getElementById("riverAngle").value = tr[2];
|
||||
document.getElementById("riverAngleValue").innerHTML = Math.abs(+tr[2]) + "°";
|
||||
document.getElementById("riverScale").value = tr[5];
|
||||
document.getElementById("riverWidthInput").value = node.dataset.width;
|
||||
document.getElementById("riverIncrement").value = node.dataset.increment;
|
||||
}
|
||||
|
||||
function dragRiver() {
|
||||
const x = d3.event.x, y = d3.event.y;
|
||||
const tr = parseTransform(elSelected.attr("transform"));
|
||||
|
||||
d3.event.on("drag", function() {
|
||||
let xc = d3.event.x, yc = d3.event.y;
|
||||
let transform = `translate(${(+tr[0]+xc-x)},${(+tr[1]+yc-y)}) rotate(${tr[2]} ${tr[3]} ${tr[4]}) scale(${tr[5]})`;
|
||||
elSelected.attr("transform", transform);
|
||||
debug.select("#controlPoints").attr("transform", transform);
|
||||
});
|
||||
}
|
||||
|
||||
function drawControlPoints(node) {
|
||||
const l = node.getTotalLength() / 2;
|
||||
const segments = Math.ceil(l / 8);
|
||||
|
|
@ -99,19 +76,29 @@ function editRiver() {
|
|||
});
|
||||
|
||||
if (points.length === 1) return;
|
||||
if (points.length === 2) {elSelected.attr("d", `M${points[0][0]},${points[0][1]} L${points[1][0]},${points[1][1]}`); return;}
|
||||
const d = Rivers.getPath(points, +riverWidthInput.value, +riverIncrement.value);
|
||||
if (points.length === 2) {
|
||||
const p0 = points[0], p1 = points[1];
|
||||
const angle = Math.atan2(p1[1] - p0[1], p1[0] - p0[0]);
|
||||
const sin = Math.sin(angle), cos = Math.cos(angle);
|
||||
elSelected.attr("d", `M${p0[0]},${p0[1]} L${p1[0]},${p1[1]} l${-sin/2},${cos/2} Z`);
|
||||
return;
|
||||
}
|
||||
const [d, length] = Rivers.getPath(points, +riverWidthInput.value, +riverIncrement.value);
|
||||
elSelected.attr("d", d);
|
||||
updateRiverLength();
|
||||
updateRiverLength(length);
|
||||
}
|
||||
|
||||
function updateRiverLength(l = elSelected.node().getTotalLength() / 2) {
|
||||
const tr = parseTransform(elSelected.attr("transform"));
|
||||
riverLength.innerHTML = rn(l * tr[5] * distanceScaleInput.value) + " " + distanceUnitInput.value;
|
||||
const length = l * tr[5];
|
||||
riverLength.innerHTML = rn(length * distanceScaleInput.value) + " " + distanceUnitInput.value;
|
||||
const river = +elSelected.attr("id").slice(5);
|
||||
const r = pack.rivers.find(r => r.i === river);
|
||||
if (r) r.length = length;
|
||||
}
|
||||
|
||||
function clickControlPoint() {
|
||||
this.remove();
|
||||
this.remove();
|
||||
redrawRiver();
|
||||
}
|
||||
|
||||
|
|
@ -142,9 +129,50 @@ function editRiver() {
|
|||
redrawRiver();
|
||||
}
|
||||
|
||||
function showRiverName() {
|
||||
document.querySelectorAll("#riverEditor > button").forEach(el => el.style.display = "none");
|
||||
document.getElementById("riverNameSection").style.display = "inline-block";
|
||||
const river = +elSelected.attr("id").slice(5);
|
||||
const r = pack.rivers.find(r => r.i === river);
|
||||
if (!r) return;
|
||||
document.getElementById("riverName").value = r.name;
|
||||
document.getElementById("riverType").value = r.type;
|
||||
}
|
||||
|
||||
function hideRiverName() {
|
||||
document.querySelectorAll("#riverEditor > button").forEach(el => el.style.display = "inline-block");
|
||||
document.getElementById("riverNameSection").style.display = "none";
|
||||
}
|
||||
|
||||
function changeName() {
|
||||
const river = +elSelected.attr("id").slice(5);
|
||||
const r = pack.rivers.find(r => r.i === river);
|
||||
if (r) r.name = this.value;
|
||||
}
|
||||
|
||||
function changeType() {
|
||||
const river = +elSelected.attr("id").slice(5);
|
||||
const r = pack.rivers.find(r => r.i === river);
|
||||
if (r) r.type = this.value;
|
||||
}
|
||||
|
||||
function generateNameCulture() {
|
||||
const river = +elSelected.attr("id").slice(5);
|
||||
const r = pack.rivers.find(r => r.i === river);
|
||||
if (r) r.name = riverName.value = Rivers.getName(r.mouth);
|
||||
}
|
||||
|
||||
function generateNameRandom() {
|
||||
const river = +elSelected.attr("id").slice(5);
|
||||
const r = pack.rivers.find(r => r.i === river);
|
||||
if (r) r.name = riverName.value = Names.getBase(rand(nameBases.length-1));
|
||||
}
|
||||
|
||||
function showRiverWidth() {
|
||||
document.querySelectorAll("#riverEditor > button").forEach(el => el.style.display = "none");
|
||||
document.getElementById("riverWidthSection").style.display = "inline-block";
|
||||
document.getElementById("riverWidthInput").value = elSelected.attr("data-width");
|
||||
document.getElementById("riverIncrement").value = elSelected.attr("data-increment");
|
||||
}
|
||||
|
||||
function hideRiverWidth() {
|
||||
|
|
@ -161,76 +189,14 @@ function editRiver() {
|
|||
elSelected.attr("data-increment", this.value);
|
||||
redrawRiver();
|
||||
}
|
||||
|
||||
function showRiverSize() {
|
||||
document.querySelectorAll("#riverEditor > button").forEach(el => el.style.display = "none");
|
||||
document.getElementById("riverResizeSection").style.display = "inline-block";
|
||||
}
|
||||
|
||||
function hideRiverSize() {
|
||||
document.querySelectorAll("#riverEditor > button").forEach(el => el.style.display = "inline-block");
|
||||
document.getElementById("riverResizeSection").style.display = "none";
|
||||
}
|
||||
|
||||
function changeAngle() {
|
||||
const tr = parseTransform(elSelected.attr("transform"));
|
||||
riverAngleValue.innerHTML = Math.abs(+this.value) + "°";
|
||||
const c = elSelected.node().getBBox();
|
||||
const angle = +this.value, scale = +tr[5];
|
||||
const transform = `translate(${tr[0]},${tr[1]}) rotate(${angle} ${(c.x+c.width/2)*scale} ${(c.y+c.height/2)*scale}) scale(${scale})`;
|
||||
elSelected.attr("transform", transform);
|
||||
debug.select("#controlPoints").attr("transform", transform);
|
||||
}
|
||||
|
||||
function changeScale() {
|
||||
const tr = parseTransform(elSelected.attr("transform"));
|
||||
const scaleOld = +tr[5],scale = +this.value;
|
||||
const c = elSelected.node().getBBox();
|
||||
const cx = c.x + c.width / 2, cy = c.y + c.height / 2;
|
||||
const trX = +tr[0] + cx * (scaleOld - scale);
|
||||
const trY = +tr[1] + cy * (scaleOld - scale);
|
||||
const scX = +tr[3] * scale / scaleOld;
|
||||
const scY = +tr[4] * scale / scaleOld;
|
||||
const transform = `translate(${trX},${trY}) rotate(${tr[2]} ${scX} ${scY}) scale(${scale})`;
|
||||
elSelected.attr("transform", transform);
|
||||
debug.select("#controlPoints").attr("transform", transform);
|
||||
updateRiverLength();
|
||||
}
|
||||
|
||||
function resetTransformation() {
|
||||
elSelected.attr("transform", null);
|
||||
debug.select("#controlPoints").attr("transform", null);
|
||||
riverAngle.value = 0;
|
||||
riverAngleValue.innerHTML = "0°";
|
||||
riverScale.value = 1;
|
||||
updateRiverLength();
|
||||
}
|
||||
|
||||
function copyRiver() {
|
||||
const tr = parseTransform(elSelected.attr("transform"));
|
||||
const d = elSelected.attr("d");
|
||||
let x = 2, y = 2;
|
||||
let transform = `translate(${tr[0]-x},${tr[1]-y}) rotate(${tr[2]} ${tr[3]} ${tr[4]}) scale(${tr[5]})`;
|
||||
while (rivers.selectAll("[transform='" + transform + "'][d='" + d + "']").size() > 0) {
|
||||
x += 2; y += 2;
|
||||
transform = `translate(${tr[0]-x},${tr[1]-y}) rotate(${tr[2]} ${tr[3]} ${tr[4]}) scale(${tr[5]})`;
|
||||
}
|
||||
|
||||
rivers.append("path").attr("d", d).attr("transform", transform).attr("id", getNextId("river"))
|
||||
.attr("data-width", elSelected.attr("data-width")).attr("data-increment", elSelected.attr("data-increment"));
|
||||
}
|
||||
|
||||
function toggleRiverCreationMode() {
|
||||
document.getElementById("riverNew").classList.toggle("pressed");
|
||||
if (document.getElementById("riverNew").classList.contains("pressed")) {
|
||||
tip("Click on map to add control points", true);
|
||||
if (document.getElementById("riverNew").classList.contains("pressed")) exitRiverCreationMode();
|
||||
else {
|
||||
document.getElementById("riverNew").classList.add("pressed");
|
||||
tip("Click on map to add control points", true, "warn");
|
||||
viewbox.on("click", addPointOnClick).style("cursor", "crosshair");
|
||||
elSelected.on("click", null);
|
||||
} else {
|
||||
clearMainTip();
|
||||
viewbox.on("click", clicked).style("cursor", "default");
|
||||
elSelected.on("click", addInterimControlPoint).attr("data-new", null)
|
||||
.call(d3.drag().on("start", dragRiver)).classed("draggable", true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -239,7 +205,7 @@ function editRiver() {
|
|||
debug.select("#controlPoints").selectAll("circle").remove();
|
||||
const id = getNextId("river");
|
||||
elSelected = d3.select(elSelected.node().parentNode).append("path").attr("id", id)
|
||||
.attr("data-new", 1).attr("data-width", 2).attr("data-increment", 1);
|
||||
.attr("data-new", 1).attr("data-width", 1).attr("data-increment", .5);
|
||||
}
|
||||
|
||||
// add control point
|
||||
|
|
@ -252,15 +218,16 @@ function editRiver() {
|
|||
function editRiverLegend() {
|
||||
const id = elSelected.attr("id");
|
||||
editNotes(id, id);
|
||||
}
|
||||
}
|
||||
|
||||
function removeRiver() {
|
||||
alertMessage.innerHTML = "Are you sure you want to remove the river?";
|
||||
$("#alert").dialog({resizable: false, title: "Remove river",
|
||||
alertMessage.innerHTML = "Are you sure you want to remove the river? All tributaries will be auto-removed";
|
||||
$("#alert").dialog({resizable: false, width: "22em", title: "Remove river",
|
||||
buttons: {
|
||||
Remove: function() {
|
||||
$(this).dialog("close");
|
||||
elSelected.remove();
|
||||
const river = +elSelected.attr("id").slice(5);
|
||||
Rivers.remove(river);
|
||||
$("#riverEditor").dialog("close");
|
||||
},
|
||||
Cancel: function() {$(this).dialog("close");}
|
||||
|
|
@ -268,10 +235,38 @@ function editRiver() {
|
|||
});
|
||||
}
|
||||
|
||||
function closeRiverEditor() {
|
||||
elSelected.attr("data-new", null).on("click", null);
|
||||
function exitRiverCreationMode() {
|
||||
riverNew.classList.remove("pressed");
|
||||
clearMainTip();
|
||||
riverNew.classList.remove("pressed");
|
||||
viewbox.on("click", clicked).style("cursor", "default");
|
||||
elSelected.on("click", addInterimControlPoint);
|
||||
|
||||
if (!elSelected.attr("data-new")) return; // no need to create a new river
|
||||
elSelected.attr("data-new", null);
|
||||
|
||||
// add a river
|
||||
const r = +elSelected.attr("id").slice(5);
|
||||
const node = elSelected.node(), length = node.getTotalLength() / 2;
|
||||
|
||||
const cells = [];
|
||||
const segments = Math.ceil(length / 8), increment = rn(length / segments * 1e5);
|
||||
for (let i=increment*segments, c=i; i >= 0; i -= increment, c += increment) {
|
||||
const p = node.getPointAtLength(i / 1e5);
|
||||
const cell = findCell(p.x, p.y);
|
||||
if (!pack.cells.r[cell]) pack.cells.r[cell] = r;
|
||||
cells.push(cell);
|
||||
}
|
||||
|
||||
const source = cells[0], mouth = last(cells);
|
||||
const name = Rivers.getName(mouth);
|
||||
const smallLength = pack.rivers.map(r => r.length||0).sort((a,b) => a-b)[Math.ceil(pack.rivers.length * .15)];
|
||||
const type = length < smallLength ? rw({"Creek":9, "River":3, "Brook":3, "Stream":1}) : "River";
|
||||
pack.rivers.push({i:r, parent:0, length, source, mouth, basin:r, name, type});
|
||||
}
|
||||
|
||||
function closeRiverEditor() {
|
||||
exitRiverCreationMode();
|
||||
elSelected.on("click", null);
|
||||
debug.select("#controlPoints").remove();
|
||||
unselect();
|
||||
}
|
||||
|
|
|
|||
157
modules/ui/rivers-overview.js
Normal file
157
modules/ui/rivers-overview.js
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
"use strict";
|
||||
function overviewRivers() {
|
||||
if (customization) return;
|
||||
closeDialogs("#riversOverview, .stable");
|
||||
if (!layerIsOn("toggleRivers")) toggleRivers();
|
||||
|
||||
const body = document.getElementById("riversBody");
|
||||
riversOverviewAddLines();
|
||||
$("#riversOverview").dialog();
|
||||
|
||||
if (modules.overviewRivers) return;
|
||||
modules.overviewRivers = true;
|
||||
|
||||
$("#riversOverview").dialog({
|
||||
title: "Rivers Overview", resizable: false, width: fitContent(),
|
||||
position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}
|
||||
});
|
||||
|
||||
// add listeners
|
||||
document.getElementById("riversOverviewRefresh").addEventListener("click", riversOverviewAddLines);
|
||||
document.getElementById("addNewRiver").addEventListener("click", toggleAddRiver);
|
||||
document.getElementById("riversExport").addEventListener("click", downloadRiversData);
|
||||
document.getElementById("riversRemoveAll").addEventListener("click", triggerAllRiversRemove);
|
||||
|
||||
// add line for each river
|
||||
function riversOverviewAddLines() {
|
||||
body.innerHTML = "";
|
||||
let lines = "";
|
||||
|
||||
for (const r of pack.rivers) {
|
||||
const length = rn(r.length * distanceScaleInput.value) + " " + distanceUnitInput.value;
|
||||
const basin = pack.rivers.find(river => river.i === r.basin).name;
|
||||
|
||||
lines += `<div class="states" data-id=${r.i} data-name="${r.name}" data-type="${r.type}" data-length="${r.length}" data-basin="${basin}">
|
||||
<span data-tip="Click to focus on river" class="icon-dot-circled pointer"></span>
|
||||
<input data-tip="River proper name. Click to change. Ctrl + click to regenerate" class="riverName" value="${r.name}" autocorrect="off" spellcheck="false">
|
||||
<input data-tip="River type name. Click to change" class="riverType" value="${r.type}">
|
||||
<div data-tip="River length" class="biomeArea">${length}</div>
|
||||
<input data-tip="River basin (name of the main stem)" class="stateName" value="${basin}" disabled>
|
||||
<span data-tip="Edit river" class="icon-pencil"></span>
|
||||
<span data-tip="Remove river" class="icon-trash-empty"></span>
|
||||
</div>`;
|
||||
}
|
||||
body.insertAdjacentHTML("beforeend", lines);
|
||||
|
||||
// update footer
|
||||
riversFooterNumber.innerHTML = pack.rivers.length;
|
||||
const averageLength = rn(d3.sum(pack.rivers.map(r => r.length)) / pack.rivers.length);
|
||||
riversFooterLength.innerHTML = (averageLength * distanceScaleInput.value) + " " + distanceUnitInput.value;
|
||||
|
||||
// add listeners
|
||||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseenter", ev => riverHighlightOn(ev)));
|
||||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseleave", ev => riverHighlightOff(ev)));
|
||||
body.querySelectorAll("div > input.riverName").forEach(el => el.addEventListener("input", changeRiverName));
|
||||
body.querySelectorAll("div > input.riverName").forEach(el => el.addEventListener("click", regenerateRiverName));
|
||||
body.querySelectorAll("div > input.riverType").forEach(el => el.addEventListener("input", changeRiverType));
|
||||
body.querySelectorAll("div > span.icon-dot-circled").forEach(el => el.addEventListener("click", zoomToRiver));
|
||||
body.querySelectorAll("div > span.icon-pencil").forEach(el => el.addEventListener("click", openRiverEditor));
|
||||
body.querySelectorAll("div > span.icon-trash-empty").forEach(el => el.addEventListener("click", triggerRiverRemove));
|
||||
|
||||
applySorting(riversHeader);
|
||||
}
|
||||
|
||||
function riverHighlightOn(event) {
|
||||
if (!layerIsOn("toggleRivers")) toggleRivers();
|
||||
const r = +event.target.dataset.id;
|
||||
rivers.select("#river"+r).attr("stroke", "red").attr("stroke-width", 1);
|
||||
}
|
||||
|
||||
function riverHighlightOff() {
|
||||
const r = +event.target.dataset.id;
|
||||
rivers.select("#river"+r).attr("stroke", null).attr("stroke-width", null);
|
||||
}
|
||||
|
||||
function changeRiverName() {
|
||||
if (this.value == "") tip("Please provide a proper name", false, "error");
|
||||
const river = +this.parentNode.dataset.id;
|
||||
pack.rivers.find(r => r.i === river).name = this.value;
|
||||
this.parentNode.dataset.name = this.value;
|
||||
}
|
||||
|
||||
function regenerateRiverName(event) {
|
||||
if (!event.ctrlKey) return;
|
||||
const river = +this.parentNode.dataset.id;
|
||||
const r = pack.rivers.find(r => r.i === river);
|
||||
r.name = this.value = this.parentNode.dataset.name = Rivers.getName(r.mouth);
|
||||
}
|
||||
|
||||
function changeRiverType() {
|
||||
if (this.value == "") tip("Please provide a type name", false, "error");
|
||||
const river = +this.parentNode.dataset.id;
|
||||
pack.rivers.find(r => r.i === river).type = this.value;
|
||||
this.parentNode.dataset.type = this.value;
|
||||
}
|
||||
|
||||
function zoomToRiver() {
|
||||
const r = +this.parentNode.dataset.id;
|
||||
const river = rivers.select("#river"+r).node();
|
||||
highlightElement(river);
|
||||
}
|
||||
|
||||
function downloadRiversData() {
|
||||
let data = "Id,River,Type,Length,Basin\n"; // headers
|
||||
|
||||
body.querySelectorAll(":scope > div").forEach(function(el) {
|
||||
data += el.dataset.id + ",";
|
||||
data += el.dataset.name + ",";
|
||||
data += el.dataset.type + ",";
|
||||
data += el.querySelector(".biomeArea").innerHTML + ",";
|
||||
data += el.dataset.basin + "\n";
|
||||
});
|
||||
|
||||
const name = getFileName("Rivers") + ".csv";
|
||||
downloadFile(data, name);
|
||||
}
|
||||
|
||||
function openRiverEditor() {
|
||||
editRiver("river"+this.parentNode.dataset.id);
|
||||
}
|
||||
|
||||
function triggerRiverRemove() {
|
||||
const river = +this.parentNode.dataset.id;
|
||||
alertMessage.innerHTML = `Are you sure you want to remove the river?
|
||||
All tributaries will be auto-removed`;
|
||||
|
||||
$("#alert").dialog({resizable: false, width: "22em", title: "Remove river",
|
||||
buttons: {
|
||||
Remove: function() {
|
||||
Rivers.remove(river);
|
||||
riversOverviewAddLines();
|
||||
$(this).dialog("close");
|
||||
},
|
||||
Cancel: function() {$(this).dialog("close");}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function triggerAllRiversRemove() {
|
||||
alertMessage.innerHTML = `Are you sure you want to remove all rivers?`;
|
||||
$("#alert").dialog({resizable: false, title: "Remove all rivers",
|
||||
buttons: {
|
||||
Remove: function() {
|
||||
$(this).dialog("close");
|
||||
removeAllRivers();
|
||||
},
|
||||
Cancel: function() {$(this).dialog("close");}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function removeAllRivers() {
|
||||
pack.rivers = [];
|
||||
rivers.selectAll("*").remove();
|
||||
riversOverviewAddLines();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -872,7 +872,7 @@ function editStates() {
|
|||
let data = "Id,State,Color,Capital,Culture,Type,Expansionism,Cells,Burgs,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);
|
||||
data += el.dataset.id + ",";
|
||||
data += el.dataset.name + ",";
|
||||
data += el.dataset.color + ",";
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ toolsContent.addEventListener("click", function(event) {
|
|||
if (button === "editUnitsButton") editUnits(); else
|
||||
if (button === "editNotesButton") editNotes(); else
|
||||
if (button === "editZonesButton") editZones(); else
|
||||
if (button === "overviewBurgsButton") editBurgs(); else
|
||||
if (button === "overviewBurgsButton") overviewBurgs(); else
|
||||
if (button === "overviewRiversButton") overviewRivers(); else
|
||||
if (button === "overviewCellsButton") viewCellDetails();
|
||||
|
||||
// Click to Regenerate buttons
|
||||
|
|
@ -67,9 +68,13 @@ function processFeatureRegeneration(event, button) {
|
|||
}
|
||||
|
||||
function regenerateRivers() {
|
||||
const heights = new Float32Array(pack.cells.h);
|
||||
elevateLakes();
|
||||
Rivers.generate();
|
||||
pack.cells.h = new Float32Array(heights);
|
||||
for (const i of pack.cells.i) {
|
||||
const f = pack.features[pack.cells.f[i]]; // feature
|
||||
if (f.group === "freshwater") pack.cells.h[i] = 19; // de-elevate lakes
|
||||
}
|
||||
Rivers.specify();
|
||||
if (!layerIsOn("toggleRivers")) toggleRivers();
|
||||
}
|
||||
|
||||
|
|
@ -134,7 +139,7 @@ function regenerateBurgs() {
|
|||
BurgsAndStates.drawBurgs();
|
||||
Routes.regenerate();
|
||||
|
||||
if (document.getElementById("burgsEditorRefresh").offsetParent) burgsEditorRefresh.click();
|
||||
if (document.getElementById("burgsOverviewRefresh").offsetParent) burgsOverviewRefresh.click();
|
||||
if (document.getElementById("statesEditorRefresh").offsetParent) statesEditorRefresh.click();
|
||||
}
|
||||
|
||||
|
|
@ -173,7 +178,7 @@ function regenerateStates() {
|
|||
labels.select("#states").selectAll("text"); // remove state labels
|
||||
defs.select("#textPaths").selectAll("path[id*='stateLabel']").remove(); // remove state labels paths
|
||||
|
||||
if (document.getElementById("burgsEditorRefresh").offsetParent) burgsEditorRefresh.click();
|
||||
if (document.getElementById("burgsOverviewRefresh").offsetParent) burgsOverviewRefresh.click();
|
||||
if (document.getElementById("statesEditorRefresh").offsetParent) statesEditorRefresh.click();
|
||||
return;
|
||||
}
|
||||
|
|
@ -216,7 +221,7 @@ function regenerateStates() {
|
|||
if (!layerIsOn("toggleBorders")) toggleBorders(); else drawBorders();
|
||||
BurgsAndStates.drawStateLabels();
|
||||
|
||||
if (document.getElementById("burgsEditorRefresh").offsetParent) burgsEditorRefresh.click();
|
||||
if (document.getElementById("burgsOverviewRefresh").offsetParent) burgsOverviewRefresh.click();
|
||||
if (document.getElementById("statesEditorRefresh").offsetParent) statesEditorRefresh.click();
|
||||
}
|
||||
|
||||
|
|
@ -325,19 +330,24 @@ function addLabelOnClick() {
|
|||
function toggleAddBurg() {
|
||||
unpressClickToAddButton();
|
||||
document.getElementById("addBurgTool").classList.add("pressed");
|
||||
editBurgs();
|
||||
overviewBurgs();
|
||||
document.getElementById("addNewBurg").click();
|
||||
}
|
||||
|
||||
function toggleAddRiver() {
|
||||
const pressed = document.getElementById("addRiver").classList.contains("pressed");
|
||||
if (pressed) {unpressClickToAddButton(); return;}
|
||||
if (pressed) {
|
||||
unpressClickToAddButton();
|
||||
document.getElementById("addNewRiver").classList.remove("pressed");
|
||||
return;
|
||||
}
|
||||
|
||||
addFeature.querySelectorAll("button.pressed").forEach(b => b.classList.remove("pressed"));
|
||||
addRiver.classList.add('pressed');
|
||||
document.getElementById("addNewRiver").classList.add("pressed");
|
||||
closeDialogs(".stable");
|
||||
viewbox.style("cursor", "crosshair").on("click", addRiverOnClick);
|
||||
tip("Click on map to place new river or extend an existing one. Hold Shift to place multiple rivers", true);
|
||||
tip("Click on map to place new river or extend an existing one. Hold Shift to place multiple rivers", true, "warn");
|
||||
if (!layerIsOn("toggleRivers")) toggleRivers();
|
||||
}
|
||||
|
||||
|
|
@ -348,57 +358,55 @@ function addRiverOnClick() {
|
|||
if (cells.r[i] || cells.h[i] < 20 || cells.b[i]) return;
|
||||
|
||||
const dataRiver = []; // to store river points
|
||||
const river = +getNextId("river").slice(5); // river id
|
||||
let river = +getNextId("river").slice(5); // river id
|
||||
cells.fl[i] = grid.cells.prec[cells.g[i]]; // initial flux
|
||||
let depressed = false;
|
||||
const heights = new Uint8Array(pack.cells.h); // initial heights
|
||||
|
||||
// height with added t value to make map less depressed
|
||||
const h = Array.from(cells.h)
|
||||
.map((h, i) => h < 20 || cells.t[i] < 1 ? h : h + cells.t[i] / 100)
|
||||
.map((h, i) => h < 20 || cells.t[i] < 1 ? h : h + d3.mean(cells.c[i].map(c => cells.t[c])) / 10000);
|
||||
Rivers.resolveDepressions(h);
|
||||
|
||||
while (i) {
|
||||
cells.r[i] = river;
|
||||
const x = cells.p[i][0], y = cells.p[i][1];
|
||||
dataRiver.push({x, y, cell:i});
|
||||
|
||||
let min = cells.c[i][d3.scan(cells.c[i], (a, b) => cells.h[a] - cells.h[b])]; // downhill cell
|
||||
|
||||
if (cells.h[i] <= cells.h[min]) {
|
||||
if (depressed) {tip("The heightmap is too depressed, please try again", false, "error"); return;}
|
||||
depressed = Rivers.resolveDepressions();
|
||||
min = cells.c[i][d3.scan(cells.c[i], (a, b) => cells.h[a] - cells.h[b])];
|
||||
}
|
||||
|
||||
const min = cells.c[i][d3.scan(cells.c[i], (a, b) => h[a] - h[b])]; // downhill cell
|
||||
if (h[i] <= h[min]) {tip(`Cell ${i} is depressed, river cannot flow further`, false, "error"); return;}
|
||||
const tx = cells.p[min][0], ty = cells.p[min][1];
|
||||
|
||||
if (cells.h[min] < 20) {
|
||||
const px = (x + tx) / 2;
|
||||
const py = (y + ty) / 2;
|
||||
dataRiver.push({x: px, y: py, cell:i});
|
||||
if (h[min] < 20) {
|
||||
// pour to water body
|
||||
dataRiver.push({x: tx, y: ty, cell:i});
|
||||
break;
|
||||
}
|
||||
|
||||
if (!cells.r[min]) {
|
||||
// continue if next cell has not river
|
||||
cells.fl[min] += cells.fl[i];
|
||||
i = min;
|
||||
continue;
|
||||
}
|
||||
|
||||
// hadnle case when lowest cell already has a river
|
||||
const r = cells.r[min];
|
||||
const riverCellsUpper = cells.i.filter(i => cells.r[i] === r && cells.h[i] > cells.h[min]);
|
||||
const riverCells = cells.i.filter(i => cells.r[i] === r);
|
||||
const riverCellsUpper = riverCells.filter(i => h[i] > h[min]);
|
||||
|
||||
// new river is not perspective
|
||||
// finish new river if old river is longer
|
||||
if (dataRiver.length <= riverCellsUpper.length) {
|
||||
cells.conf[min] += cells.fl[i];
|
||||
dataRiver.push({x: tx, y: ty, cell: min});
|
||||
dataRiver[0].parent = r; // new river is tributary
|
||||
break;
|
||||
}
|
||||
|
||||
// new river is more perspective
|
||||
// extend old river
|
||||
rivers.select("#river"+r).remove();
|
||||
riverCellsUpper.forEach(i => cells.r[i] = 0);
|
||||
if (riverCellsUpper.length > 1) {
|
||||
// redraw upper part of the old river
|
||||
}
|
||||
|
||||
cells.conf[min] = cells.fl[min];
|
||||
cells.i.filter(i => cells.r[i] === river).forEach(i => cells.r[i] = r);
|
||||
riverCells.forEach(i => cells.r[i] = 0);
|
||||
river = r;
|
||||
cells.fl[min] = cells.fl[i] + grid.cells.prec[cells.g[min]];
|
||||
i = min;
|
||||
}
|
||||
|
|
@ -406,27 +414,30 @@ function addRiverOnClick() {
|
|||
const points = Rivers.addMeandring(dataRiver, Math.random() * .5 + .1);
|
||||
const width = Math.random() * .5 + .9;
|
||||
const increment = Math.random() * .4 + .8;
|
||||
const d = Rivers.getPath(points, width, increment);
|
||||
rivers.append("path").attr("d", d).attr("id", "river"+river).attr("data-width", width).attr("data-increment", increment);
|
||||
const [path, length] = Rivers.getPath(points, width, increment);
|
||||
rivers.append("path").attr("d", path).attr("id", "river"+river).attr("data-width", width).attr("data-increment", increment);
|
||||
|
||||
if (depressed) {
|
||||
if (layerIsOn("toggleHeight")) drawHeightmap();
|
||||
alertMessage.innerHTML = `<p>Heightmap is depressed and the system had to change the heightmap to allow water flux.</p>
|
||||
Would you like to <i>keep</i> the changes or <i>restore</i> the initial heightmap?`;
|
||||
|
||||
$("#alert").dialog({resizable: false, title: "Heightmap is changed", width: "30em", modal: true,
|
||||
buttons: {
|
||||
Keep: function() {$(this).dialog("close");},
|
||||
Restore: function() {
|
||||
$(this).dialog("close");
|
||||
pack.cells.h = new Float32Array(heights);
|
||||
if (layerIsOn("toggleHeight")) drawHeightmap();
|
||||
}
|
||||
}
|
||||
});
|
||||
// add new river to data or change extended river attributes
|
||||
const r = pack.rivers.find(r => r.i === river);
|
||||
if (r) {
|
||||
r.source = dataRiver[0].cell;
|
||||
r.length = length;
|
||||
} else {
|
||||
const parent = dataRiver[0].parent || 0;
|
||||
const basin = Rivers.getBasin(river, parent);
|
||||
const source = dataRiver[0].cell;
|
||||
const mouth = last(dataRiver).cell;
|
||||
const name = Rivers.getName(mouth);
|
||||
const smallLength = pack.rivers.map(r => r.length||0).sort((a,b) => a-b)[Math.ceil(pack.rivers.length * .15)];
|
||||
const type = length < smallLength ? rw({"Creek":9, "River":3, "Brook":3, "Stream":1}) : "River";
|
||||
pack.rivers.push({i:river, parent, length, source, mouth, basin, name, type});
|
||||
}
|
||||
|
||||
if (d3.event.shiftKey === false) unpressClickToAddButton();
|
||||
if (d3.event.shiftKey === false) {
|
||||
unpressClickToAddButton();
|
||||
document.getElementById("addNewRiver").classList.remove("pressed");
|
||||
if (addNewRiver.offsetParent) riversOverviewRefresh.click();
|
||||
}
|
||||
}
|
||||
|
||||
function toggleAddRoute() {
|
||||
|
|
@ -482,5 +493,8 @@ function addMarkerOnClick() {
|
|||
}
|
||||
|
||||
function viewCellDetails() {
|
||||
$("#cellInfo").dialog({resizable: false, width: "22em", title: "Cell Details"});
|
||||
$("#cellInfo").dialog({
|
||||
resizable: false, width: "22em", title: "Cell Details",
|
||||
position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}
|
||||
});
|
||||
}
|
||||
|
|
@ -48,6 +48,7 @@ function editWorld() {
|
|||
elevateLakes();
|
||||
const heights = new Uint8Array(pack.cells.h);
|
||||
Rivers.generate();
|
||||
Rivers.specify();
|
||||
pack.cells.h = new Float32Array(heights);
|
||||
defineBiomes();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue