diff --git a/modules/dynamic/export-json.js b/modules/dynamic/export-json.js
index 4f66dc95..df0d6815 100644
--- a/modules/dynamic/export-json.js
+++ b/modules/dynamic/export-json.js
@@ -86,7 +86,7 @@ function getMapInfo() {
function getSettings() {
return {
distanceUnit: distanceUnitInput.value,
- distanceScale: distanceScaleInput.value,
+ distanceScale,
areaUnit: areaUnit.value,
heightUnit: heightUnit.value,
heightExponent: heightExponentInput.value,
diff --git a/modules/io/save.js b/modules/io/save.js
index fd8c586b..73e6845f 100644
--- a/modules/io/save.js
+++ b/modules/io/save.js
@@ -44,7 +44,7 @@ function prepareMapData() {
const params = [version, license, dateString, seed, graphWidth, graphHeight, mapId].join("|");
const settings = [
distanceUnitInput.value,
- distanceScaleInput.value,
+ distanceScale,
areaUnit.value,
heightUnit.value,
heightExponentInput.value,
diff --git a/modules/routes-generator.js b/modules/routes-generator.js
index ca086222..24b4f02d 100644
--- a/modules/routes-generator.js
+++ b/modules/routes-generator.js
@@ -601,6 +601,11 @@ window.Routes = (function () {
return routePoints;
}
+ function getLength(routeId) {
+ const path = routes.select("#route" + routeId).node();
+ return path.getTotalLength();
+ }
+
function remove(route) {
const routes = pack.cells.routes;
@@ -630,6 +635,7 @@ window.Routes = (function () {
generateName,
preparePointsArray,
getPoints,
+ getLength,
remove
};
})();
diff --git a/modules/ui/battle-screen.js b/modules/ui/battle-screen.js
index 2936549c..12ad7bdd 100644
--- a/modules/ui/battle-screen.js
+++ b/modules/ui/battle-screen.js
@@ -37,12 +37,22 @@ class Battle {
// add listeners
document.getElementById("battleType").addEventListener("click", ev => this.toggleChange(ev));
- document.getElementById("battleType").nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changeType(ev));
- document.getElementById("battleNameShow").addEventListener("click", () => Battle.prototype.context.showNameSection());
- document.getElementById("battleNamePlace").addEventListener("change", ev => (Battle.prototype.context.place = ev.target.value));
+ document
+ .getElementById("battleType")
+ .nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changeType(ev));
+ document
+ .getElementById("battleNameShow")
+ .addEventListener("click", () => Battle.prototype.context.showNameSection());
+ document
+ .getElementById("battleNamePlace")
+ .addEventListener("change", ev => (Battle.prototype.context.place = ev.target.value));
document.getElementById("battleNameFull").addEventListener("change", ev => Battle.prototype.context.changeName(ev));
- document.getElementById("battleNameCulture").addEventListener("click", () => Battle.prototype.context.generateName("culture"));
- document.getElementById("battleNameRandom").addEventListener("click", () => Battle.prototype.context.generateName("random"));
+ document
+ .getElementById("battleNameCulture")
+ .addEventListener("click", () => Battle.prototype.context.generateName("culture"));
+ document
+ .getElementById("battleNameRandom")
+ .addEventListener("click", () => Battle.prototype.context.generateName("random"));
document.getElementById("battleNameHide").addEventListener("click", this.hideNameSection);
document.getElementById("battleAddRegiment").addEventListener("click", this.addSide);
document.getElementById("battleRoll").addEventListener("click", () => Battle.prototype.context.randomize());
@@ -52,11 +62,19 @@ class Battle {
document.getElementById("battleWiki").addEventListener("click", () => wiki("Battle-Simulator"));
document.getElementById("battlePhase_attackers").addEventListener("click", ev => this.toggleChange(ev));
- document.getElementById("battlePhase_attackers").nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changePhase(ev, "attackers"));
+ document
+ .getElementById("battlePhase_attackers")
+ .nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changePhase(ev, "attackers"));
document.getElementById("battlePhase_defenders").addEventListener("click", ev => this.toggleChange(ev));
- document.getElementById("battlePhase_defenders").nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changePhase(ev, "defenders"));
- document.getElementById("battleDie_attackers").addEventListener("click", () => Battle.prototype.context.rollDie("attackers"));
- document.getElementById("battleDie_defenders").addEventListener("click", () => Battle.prototype.context.rollDie("defenders"));
+ document
+ .getElementById("battlePhase_defenders")
+ .nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changePhase(ev, "defenders"));
+ document
+ .getElementById("battleDie_attackers")
+ .addEventListener("click", () => Battle.prototype.context.rollDie("attackers"));
+ document
+ .getElementById("battleDie_defenders")
+ .addEventListener("click", () => Battle.prototype.context.rollDie("defenders"));
}
defineType() {
@@ -82,8 +100,12 @@ class Battle {
document.getElementById("battleType").className = "icon-button-" + this.type;
const sideSpecific = document.getElementById("battlePhases_" + this.type + "_attackers");
- const attackers = sideSpecific ? sideSpecific.content : document.getElementById("battlePhases_" + this.type).content;
- const defenders = sideSpecific ? document.getElementById("battlePhases_" + this.type + "_defenders").content : attackers;
+ const attackers = sideSpecific
+ ? sideSpecific.content
+ : document.getElementById("battlePhases_" + this.type).content;
+ const defenders = sideSpecific
+ ? document.getElementById("battlePhases_" + this.type + "_defenders").content
+ : attackers;
document.getElementById("battlePhase_attackers").nextElementSibling.innerHTML = "";
document.getElementById("battlePhase_defenders").nextElementSibling.innerHTML = "";
@@ -139,26 +161,37 @@ class Battle {
regiment.survivors = Object.assign({}, regiment.u);
const state = pack.states[regiment.state];
- const distance = (Math.hypot(this.y - regiment.by, this.x - regiment.bx) * distanceScaleInput.value) | 0; // distance between regiment and its base
+ const distance = (Math.hypot(this.y - regiment.by, this.x - regiment.bx) * distanceScale) | 0; // distance between regiment and its base
const color = state.color[0] === "#" ? state.color : "#999";
const icon = ``;
const body = `
-
+
${s.name.slice(0, 11)}
${r.icon}
${r.name.slice(0, 24)}
@@ -267,7 +306,10 @@ class Battle {
}
generateName(type) {
- const place = type === "culture" ? Names.getCulture(pack.cells.culture[this.cell], null, null, "") : Names.getBase(rand(nameBases.length - 1));
+ const place =
+ type === "culture"
+ ? Names.getCulture(pack.cells.culture[this.cell], null, null, "")
+ : Names.getBase(rand(nameBases.length - 1));
document.getElementById("battleNamePlace").value = this.place = place;
document.getElementById("battleNameFull").value = this.name = this.defineName();
$("#battleScreen").dialog({title: this.name});
@@ -286,35 +328,161 @@ class Battle {
calculateStrength(side) {
const scheme = {
// field battle phases
- skirmish: {melee: 0.2, ranged: 2.4, mounted: 0.1, machinery: 3, naval: 1, armored: 0.2, aviation: 1.8, magical: 1.8}, // ranged excel
+ skirmish: {
+ melee: 0.2,
+ ranged: 2.4,
+ mounted: 0.1,
+ machinery: 3,
+ naval: 1,
+ armored: 0.2,
+ aviation: 1.8,
+ magical: 1.8
+ }, // ranged excel
melee: {melee: 2, ranged: 1.2, mounted: 1.5, machinery: 0.5, naval: 0.2, armored: 2, aviation: 0.8, magical: 0.8}, // melee excel
pursue: {melee: 1, ranged: 1, mounted: 4, machinery: 0.05, naval: 1, armored: 1, aviation: 1.5, magical: 0.6}, // mounted excel
- retreat: {melee: 0.1, ranged: 0.01, mounted: 0.5, machinery: 0.01, naval: 0.2, armored: 0.1, aviation: 0.8, magical: 0.05}, // reduced
+ retreat: {
+ melee: 0.1,
+ ranged: 0.01,
+ mounted: 0.5,
+ machinery: 0.01,
+ naval: 0.2,
+ armored: 0.1,
+ aviation: 0.8,
+ magical: 0.05
+ }, // reduced
// naval battle phases
shelling: {melee: 0, ranged: 0.2, mounted: 0, machinery: 2, naval: 2, armored: 0, aviation: 0.1, magical: 0.5}, // naval and machinery excel
- boarding: {melee: 1, ranged: 0.5, mounted: 0.5, machinery: 0, naval: 0.5, armored: 0.4, aviation: 0, magical: 0.2}, // melee excel
+ boarding: {
+ melee: 1,
+ ranged: 0.5,
+ mounted: 0.5,
+ machinery: 0,
+ naval: 0.5,
+ armored: 0.4,
+ aviation: 0,
+ magical: 0.2
+ }, // melee excel
chase: {melee: 0, ranged: 0.15, mounted: 0, machinery: 1, naval: 1, armored: 0, aviation: 0.15, magical: 0.5}, // reduced
- withdrawal: {melee: 0, ranged: 0.02, mounted: 0, machinery: 0.5, naval: 0.1, armored: 0, aviation: 0.1, magical: 0.3}, // reduced
+ withdrawal: {
+ melee: 0,
+ ranged: 0.02,
+ mounted: 0,
+ machinery: 0.5,
+ naval: 0.1,
+ armored: 0,
+ aviation: 0.1,
+ magical: 0.3
+ }, // reduced
// siege phases
- blockade: {melee: 0.25, ranged: 0.25, mounted: 0.2, machinery: 0.5, naval: 0.2, armored: 0.1, aviation: 0.25, magical: 0.25}, // no active actions
- sheltering: {melee: 0.3, ranged: 0.5, mounted: 0.2, machinery: 0.5, naval: 0.2, armored: 0.1, aviation: 0.25, magical: 0.25}, // no active actions
+ blockade: {
+ melee: 0.25,
+ ranged: 0.25,
+ mounted: 0.2,
+ machinery: 0.5,
+ naval: 0.2,
+ armored: 0.1,
+ aviation: 0.25,
+ magical: 0.25
+ }, // no active actions
+ sheltering: {
+ melee: 0.3,
+ ranged: 0.5,
+ mounted: 0.2,
+ machinery: 0.5,
+ naval: 0.2,
+ armored: 0.1,
+ aviation: 0.25,
+ magical: 0.25
+ }, // no active actions
sortie: {melee: 2, ranged: 0.5, mounted: 1.2, machinery: 0.2, naval: 0.1, armored: 0.5, aviation: 1, magical: 1}, // melee excel
- bombardment: {melee: 0.2, ranged: 0.5, mounted: 0.2, machinery: 3, naval: 1, armored: 0.5, aviation: 1, magical: 1}, // machinery excel
- storming: {melee: 1, ranged: 0.6, mounted: 0.5, machinery: 1, naval: 0.1, armored: 0.1, aviation: 0.5, magical: 0.5}, // melee excel
+ bombardment: {
+ melee: 0.2,
+ ranged: 0.5,
+ mounted: 0.2,
+ machinery: 3,
+ naval: 1,
+ armored: 0.5,
+ aviation: 1,
+ magical: 1
+ }, // machinery excel
+ storming: {
+ melee: 1,
+ ranged: 0.6,
+ mounted: 0.5,
+ machinery: 1,
+ naval: 0.1,
+ armored: 0.1,
+ aviation: 0.5,
+ magical: 0.5
+ }, // melee excel
defense: {melee: 2, ranged: 3, mounted: 1, machinery: 1, naval: 0.1, armored: 1, aviation: 0.5, magical: 1}, // ranged excel
- looting: {melee: 1.6, ranged: 1.6, mounted: 0.5, machinery: 0.2, naval: 0.02, armored: 0.2, aviation: 0.1, magical: 0.3}, // melee excel
- surrendering: {melee: 0.1, ranged: 0.1, mounted: 0.05, machinery: 0.01, naval: 0.01, armored: 0.02, aviation: 0.01, magical: 0.03}, // reduced
+ looting: {
+ melee: 1.6,
+ ranged: 1.6,
+ mounted: 0.5,
+ machinery: 0.2,
+ naval: 0.02,
+ armored: 0.2,
+ aviation: 0.1,
+ magical: 0.3
+ }, // melee excel
+ surrendering: {
+ melee: 0.1,
+ ranged: 0.1,
+ mounted: 0.05,
+ machinery: 0.01,
+ naval: 0.01,
+ armored: 0.02,
+ aviation: 0.01,
+ magical: 0.03
+ }, // reduced
// ambush phases
surprise: {melee: 2, ranged: 2.4, mounted: 1, machinery: 1, naval: 1, armored: 1, aviation: 0.8, magical: 1.2}, // increased
- shock: {melee: 0.5, ranged: 0.5, mounted: 0.5, machinery: 0.4, naval: 0.3, armored: 0.1, aviation: 0.4, magical: 0.5}, // reduced
+ shock: {
+ melee: 0.5,
+ ranged: 0.5,
+ mounted: 0.5,
+ machinery: 0.4,
+ naval: 0.3,
+ armored: 0.1,
+ aviation: 0.4,
+ magical: 0.5
+ }, // reduced
// langing phases
- landing: {melee: 0.8, ranged: 0.6, mounted: 0.6, machinery: 0.5, naval: 0.5, armored: 0.5, aviation: 0.5, magical: 0.6}, // reduced
- flee: {melee: 0.1, ranged: 0.01, mounted: 0.5, machinery: 0.01, naval: 0.5, armored: 0.1, aviation: 0.2, magical: 0.05}, // reduced
- waiting: {melee: 0.05, ranged: 0.5, mounted: 0.05, machinery: 0.5, naval: 2, armored: 0.05, aviation: 0.5, magical: 0.5}, // reduced
+ landing: {
+ melee: 0.8,
+ ranged: 0.6,
+ mounted: 0.6,
+ machinery: 0.5,
+ naval: 0.5,
+ armored: 0.5,
+ aviation: 0.5,
+ magical: 0.6
+ }, // reduced
+ flee: {
+ melee: 0.1,
+ ranged: 0.01,
+ mounted: 0.5,
+ machinery: 0.01,
+ naval: 0.5,
+ armored: 0.1,
+ aviation: 0.2,
+ magical: 0.05
+ }, // reduced
+ waiting: {
+ melee: 0.05,
+ ranged: 0.5,
+ mounted: 0.05,
+ machinery: 0.5,
+ naval: 2,
+ armored: 0.05,
+ aviation: 0.5,
+ magical: 0.5
+ }, // reduced
// air battle phases
maneuvering: {melee: 0, ranged: 0.1, mounted: 0, machinery: 0.2, naval: 0, armored: 0, aviation: 1, magical: 0.2}, // aviation
@@ -324,7 +492,8 @@ class Battle {
const forces = this.getJoinedForces(this[side].regiments);
const phase = this[side].phase;
const adjuster = Math.max(populationRate / 10, 10); // population adjuster, by default 100
- this[side].power = d3.sum(options.military.map(u => (forces[u.name] || 0) * u.power * scheme[phase][u.type])) / adjuster;
+ this[side].power =
+ d3.sum(options.military.map(u => (forces[u.name] || 0) * u.power * scheme[phase][u.type])) / adjuster;
const UIvalue = this[side].power ? Math.max(this[side].power | 0, 1) : 0;
document.getElementById("battlePower_" + side).innerHTML = UIvalue;
}
@@ -723,11 +892,13 @@ class Battle {
const status = battleStatus[+P(0.7)];
const result = `The ${this.getTypeName(this.type)} ended in ${status}`;
- const legend = `${this.name} took place in ${options.year} ${options.eraShort}. It was fought between ${getSide(this.attackers.regiments, 1)} and ${getSide(
- this.defenders.regiments,
- 0
- )}. ${result}.
- \r\nAttackers losses: ${getLosses(this.attackers.casualties)}%, defenders losses: ${getLosses(this.defenders.casualties)}%`;
+ const legend = `${this.name} took place in ${options.year} ${options.eraShort}. It was fought between ${getSide(
+ this.attackers.regiments,
+ 1
+ )} and ${getSide(this.defenders.regiments, 0)}. ${result}.
+ \r\nAttackers losses: ${getLosses(this.attackers.casualties)}%, defenders losses: ${getLosses(
+ this.defenders.casualties
+ )}%`;
notes.push({id: `marker${i}`, name: this.name, legend});
tip(`${this.name} is over. ${result}`, true, "success", 4000);
diff --git a/modules/ui/editors.js b/modules/ui/editors.js
index ab91e641..79dcef4e 100644
--- a/modules/ui/editors.js
+++ b/modules/ui/editors.js
@@ -1175,7 +1175,6 @@ function getAreaUnit(squareMark = "²") {
}
function getArea(rawArea) {
- const distanceScale = byId("distanceScaleInput")?.value;
return rawArea * distanceScale ** 2;
}
diff --git a/modules/ui/lakes-editor.js b/modules/ui/lakes-editor.js
index 7d766672..e4977624 100644
--- a/modules/ui/lakes-editor.js
+++ b/modules/ui/lakes-editor.js
@@ -48,8 +48,7 @@ function editLake() {
document.getElementById("lakeArea").value = si(getArea(l.area)) + " " + getAreaUnit();
const length = d3.polygonLength(l.vertices.map(v => pack.vertices.p[v]));
- document.getElementById("lakeShoreLength").value =
- si(length * distanceScaleInput.value) + " " + distanceUnitInput.value;
+ document.getElementById("lakeShoreLength").value = si(length * distanceScale) + " " + distanceUnitInput.value;
const lakeCells = Array.from(cells.i.filter(i => cells.f[i] === l.i));
const heights = lakeCells.map(i => cells.h[i]);
diff --git a/modules/ui/layers.js b/modules/ui/layers.js
index fd1e48fd..d6aefff5 100644
--- a/modules/ui/layers.js
+++ b/modules/ui/layers.js
@@ -1792,7 +1792,6 @@ function toggleScaleBar(event) {
function drawScaleBar(scaleBar, scaleLevel) {
if (!scaleBar.size() || scaleBar.style("display") === "none") return;
- const distanceScale = +distanceScaleInput.value;
const unit = distanceUnitInput.value;
const size = +scaleBar.attr("data-bar-size");
diff --git a/modules/ui/measurers.js b/modules/ui/measurers.js
index e083936c..1f51cd7d 100644
--- a/modules/ui/measurers.js
+++ b/modules/ui/measurers.js
@@ -66,7 +66,7 @@ class Measurer {
}
getDash() {
- return rn(30 / distanceScaleInput.value, 2);
+ return rn(30 / distanceScale, 2);
}
drag() {
@@ -205,7 +205,7 @@ class Ruler extends Measurer {
updateLabel() {
const length = this.getLength();
- const text = rn(length * distanceScaleInput.value) + " " + distanceUnitInput.value;
+ const text = rn(length * distanceScale) + " " + distanceUnitInput.value;
const [x, y] = last(this.points);
this.el.select("text").attr("x", x).attr("y", y).text(text);
}
@@ -337,7 +337,7 @@ class Opisometer extends Measurer {
updateLabel() {
const length = this.el.select("path").node().getTotalLength();
- const text = rn(length * distanceScaleInput.value) + " " + distanceUnitInput.value;
+ const text = rn(length * distanceScale) + " " + distanceUnitInput.value;
const [x, y] = last(this.points);
this.el.select("text").attr("x", x).attr("y", y).text(text);
}
@@ -475,7 +475,7 @@ class RouteOpisometer extends Measurer {
updateLabel() {
const length = this.el.select("path").node().getTotalLength();
- const text = rn(length * distanceScaleInput.value) + " " + distanceUnitInput.value;
+ const text = rn(length * distanceScale) + " " + distanceUnitInput.value;
const [x, y] = last(this.points);
this.el.select("text").attr("x", x).attr("y", y).text(text);
}
diff --git a/modules/ui/options.js b/modules/ui/options.js
index f4d95951..a35c5815 100644
--- a/modules/ui/options.js
+++ b/modules/ui/options.js
@@ -545,6 +545,7 @@ function applyStoredOptions() {
lock(key);
if (key === "points") changeCellsDensity(+value);
+ if (key === "distanceScale") distanceScale = +value;
// add saved style presets to options
if (key.slice(0, 5) === "style") applyOption(stylePreset, key, key.slice(5));
@@ -605,7 +606,8 @@ function randomizeOptions() {
// 'Units Editor' settings
const US = navigator.language === "en-US";
- if (randomize || !locked("distanceScale")) distanceScaleOutput.value = distanceScaleInput.value = gauss(3, 1, 1, 5);
+ if (randomize || !locked("distanceScale"))
+ distanceScale = distanceScaleOutput.value = distanceScaleInput.value = gauss(3, 1, 1, 5);
if (!stored("distanceUnit")) distanceUnitInput.value = US ? "mi" : "km";
if (!stored("heightUnit")) heightUnit.value = US ? "ft" : "m";
if (!stored("temperatureScale")) temperatureScale.value = US ? "°F" : "°C";
diff --git a/modules/ui/rivers-editor.js b/modules/ui/rivers-editor.js
index 95f7593e..4a061179 100644
--- a/modules/ui/rivers-editor.js
+++ b/modules/ui/rivers-editor.js
@@ -81,7 +81,7 @@ function editRiver(id) {
function updateRiverLength(river) {
river.length = rn(elSelected.node().getTotalLength() / 2, 2);
- const lengthUI = `${rn(river.length * distanceScaleInput.value)} ${distanceUnitInput.value}`;
+ const lengthUI = `${rn(river.length * distanceScale)} ${distanceUnitInput.value}`;
byId("riverLength").value = lengthUI;
}
@@ -91,7 +91,7 @@ function editRiver(id) {
const meanderedPoints = addMeandering(cells);
river.width = getWidth(getOffset(discharge, meanderedPoints.length, widthFactor, sourceWidth));
- const width = `${rn(river.width * distanceScaleInput.value, 3)} ${distanceUnitInput.value}`;
+ const width = `${rn(river.width * distanceScale, 3)} ${distanceUnitInput.value}`;
byId("riverWidth").value = width;
}
@@ -235,7 +235,7 @@ function editRiver(id) {
.data()
.map(([x, y]) => findCell(x, y));
const river = getRiver();
- const riverLen = rn(river.length * distanceScaleInput.value);
+ const riverLen = rn(river.length * distanceScale);
showElevationProfile(points, riverLen, true);
}
diff --git a/modules/ui/rivers-overview.js b/modules/ui/rivers-overview.js
index 577f702e..7fc32b45 100644
--- a/modules/ui/rivers-overview.js
+++ b/modules/ui/rivers-overview.js
@@ -35,8 +35,8 @@ function overviewRivers() {
for (const r of pack.rivers) {
const discharge = r.discharge + " m³/s";
- const length = rn(r.length * distanceScaleInput.value) + " " + unit;
- const width = rn(r.width * distanceScaleInput.value, 3) + " " + unit;
+ const length = rn(r.length * distanceScale) + " " + unit;
+ const width = rn(r.width * distanceScale, 3) + " " + unit;
const basin = pack.rivers.find(river => river.i === r.basin)?.name;
lines += /* html */ `
r.discharge)));
riversFooterDischarge.innerHTML = averageDischarge + " m³/s";
const averageLength = rn(d3.mean(pack.rivers.map(r => r.length)));
- riversFooterLength.innerHTML = averageLength * distanceScaleInput.value + " " + unit;
+ riversFooterLength.innerHTML = averageLength * distanceScale + " " + unit;
const averageWidth = rn(d3.mean(pack.rivers.map(r => r.width)), 3);
- riversFooterWidth.innerHTML = rn(averageWidth * distanceScaleInput.value, 3) + " " + unit;
+ riversFooterWidth.innerHTML = rn(averageWidth * distanceScale, 3) + " " + unit;
// add listeners
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseenter", ev => riverHighlightOn(ev)));
@@ -143,8 +143,8 @@ function overviewRivers() {
body.querySelectorAll(":scope > div").forEach(function (el) {
const d = el.dataset;
const discharge = d.discharge + " m³/s";
- const length = rn(d.length * distanceScaleInput.value) + " " + distanceUnitInput.value;
- const width = rn(d.width * distanceScaleInput.value, 3) + " " + distanceUnitInput.value;
+ const length = rn(d.length * distanceScale) + " " + distanceUnitInput.value;
+ const width = rn(d.width * distanceScale, 3) + " " + distanceUnitInput.value;
data += [d.id, d.name, d.type, discharge, length, width, d.basin].join(",") + "\n";
});
diff --git a/modules/ui/routes-editor.js b/modules/ui/routes-editor.js
index fe51e002..7400e1ad 100644
--- a/modules/ui/routes-editor.js
+++ b/modules/ui/routes-editor.js
@@ -37,7 +37,7 @@ function editRoute(id) {
// add listeners
byId("routeCreateSelectingCells").on("click", showCreationDialog);
byId("routeSplit").on("click", togglePressed);
- byId("routeJoin").on("click", joinRoutes);
+ byId("routeJoin").on("click", openJoinRoutesDialog);
byId("routeElevationProfile").on("click", showRouteElevationProfile);
byId("routeLegend").on("click", editRouteLegend);
byId("routeRemove").on("click", removeRoute);
@@ -71,9 +71,8 @@ function editRoute(id) {
}
function updateRouteLength(route) {
- route.length = rn(elSelected.node().getTotalLength() / 2, 2);
- const lengthUI = `${rn(route.length * distanceScale)} ${distanceUnitInput.value}`;
- byId("routeLength").value = lengthUI;
+ route.length = Routes.getLength(route.i);
+ byId("routeLength").value = rn(route.length * distanceScale) + " " + distanceUnitInput.value;
}
function drawControlPoints(points) {
@@ -247,61 +246,86 @@ function editRoute(id) {
}
}
- function joinRoutes() {
+ function openJoinRoutesDialog() {
const route = getRoute();
const firstCell = route.cells.at(0);
const lastCell = route.cells.at(-1);
- let joinedRoute = null;
- for (const nextRoute of pack.routes) {
- if (joinedRoute) break;
- if (nextRoute.i === route.i) continue;
- if (nextRoute.cells.at(0) === lastCell) joinedRoute = nextRoute;
- if (nextRoute.cells.at(-1) === firstCell) joinedRoute = nextRoute;
- if (nextRoute.cells.at(0) === firstCell) joinedRoute = nextRoute;
- if (nextRoute.cells.at(-1) === lastCell) joinedRoute = nextRoute;
- }
+ const candidateRoutes = pack.routes.filter(r => {
+ if (r.i === route.i) return false;
+ if (r.group !== route.group) return false;
+ if (r.cells.at(0) === lastCell) return true;
+ if (r.cells.at(-1) === firstCell) return true;
+ if (r.cells.at(0) === firstCell) return true;
+ if (r.cells.at(-1) === lastCell) return true;
+ return false;
+ });
- if (joinedRoute) {
- join(route, joinedRoute);
- tip("Routes joined", false, "success", 5000);
+ if (candidateRoutes.length) {
+ const options = candidateRoutes.map(r => {
+ r.name = r.name || Routes.generateName(r);
+ r.length = r.length || getRouteLength(r.i);
+ const length = rn(r.length * distanceScale) + " " + unit;
+ return `
`;
+ });
+ alertMessage.innerHTML = /* html */ `
Route to join with:
+
+
`;
+
+ $("#alert").dialog({
+ title: "Join routes",
+ width: fitContent(),
+ position: {my: "center", at: "center", of: "svg"},
+ buttons: {
+ Cancel: () => {
+ $("#alert").dialog("close");
+ },
+ Join: () => {
+ const selectedRouteId = +alertMessage.querySelector("select").value;
+ const selectedRoute = pack.routes.find(r => r.i === selectedRouteId);
+ joinRoutes(route, selectedRoute);
+ tip("Routes joined", false, "success", 5000);
+ $("#alert").dialog("close");
+ }
+ }
+ });
} else {
tip("No routes to join with. Route must start or end at current route's start or end cell", false, "error", 4000);
}
+ }
- function join(route, joinedRoute) {
- if (!route.points) route.points = debug.selectAll("#controlPoints > *").data();
- if (!joinedRoute.points) joinedRoute.points = Routes.getPoints(joinedRoute, Routes.preparePointsArray());
+ function joinRoutes(route, joinedRoute) {
+ if (!route.points) route.points = debug.selectAll("#controlPoints > *").data();
+ if (!joinedRoute.points) joinedRoute.points = Routes.getPoints(joinedRoute, Routes.preparePointsArray());
- if (route.cells.at(-1) === joinedRoute.cells.at(0)) {
- // joinedRoute starts at the end of current route
- route.cells = [...route.cells, ...joinedRoute.cells.slice(1)];
- route.points = [...route.points, ...joinedRoute.points.slice(1)];
- } else if (route.cells.at(0) === joinedRoute.cells.at(-1)) {
- // joinedRoute ends at the start of current route
- route.cells = [...joinedRoute.cells, ...route.cells.slice(1)];
- route.points = [...joinedRoute.points, ...route.points.slice(1)];
- } else if (route.cells.at(0) === joinedRoute.cells.at(0)) {
- // joinedRoute and current route both start at the same cell
- route.cells = [...route.cells.reverse(), ...joinedRoute.cells.slice(1)];
- route.points = [...route.points.reverse(), ...joinedRoute.points.slice(1)];
- } else if (route.cells.at(-1) === joinedRoute.cells.at(-1)) {
- // joinedRoute and current route both end at the same cell
- route.cells = [...route.cells, ...joinedRoute.cells.reverse().slice(1)];
- route.points = [...route.points, ...joinedRoute.points.reverse().slice(1)];
- }
-
- for (let i = 0; i < route.cells.length; i++) {
- const cellId = route.cells[i];
- const nextCellId = route.cells[i + 1];
- if (nextCellId) addConnection(cellId, nextCellId, route.i);
- }
-
- Routes.remove(joinedRoute);
- drawControlPoints(route.points);
- drawCells();
- redrawRoute();
+ if (route.cells.at(-1) === joinedRoute.cells.at(0)) {
+ // joinedRoute starts at the end of current route
+ route.cells = [...route.cells, ...joinedRoute.cells.slice(1)];
+ route.points = [...route.points, ...joinedRoute.points.slice(1)];
+ } else if (route.cells.at(0) === joinedRoute.cells.at(-1)) {
+ // joinedRoute ends at the start of current route
+ route.cells = [...joinedRoute.cells, ...route.cells.slice(1)];
+ route.points = [...joinedRoute.points, ...route.points.slice(1)];
+ } else if (route.cells.at(0) === joinedRoute.cells.at(0)) {
+ // joinedRoute and current route both start at the same cell
+ route.cells = [...route.cells.reverse(), ...joinedRoute.cells.slice(1)];
+ route.points = [...route.points.reverse(), ...joinedRoute.points.slice(1)];
+ } else if (route.cells.at(-1) === joinedRoute.cells.at(-1)) {
+ // joinedRoute and current route both end at the same cell
+ route.cells = [...route.cells, ...joinedRoute.cells.reverse().slice(1)];
+ route.points = [...route.points, ...joinedRoute.points.reverse().slice(1)];
}
+
+ for (let i = 0; i < route.cells.length; i++) {
+ const cellId = route.cells[i];
+ const nextCellId = route.cells[i + 1];
+ if (nextCellId) addConnection(cellId, nextCellId, route.i);
+ }
+
+ Routes.remove(joinedRoute);
+ drawControlPoints(route.points);
+ drawCells();
+ redrawRoute();
}
function showCreationDialog() {
@@ -346,8 +370,8 @@ function editRoute(id) {
function showRouteElevationProfile() {
const route = getRoute();
- const routeLen = rn(route.length * distanceScaleInput.value);
- showElevationProfile(route.cells, routeLen, false);
+ const length = rn(route.length * distanceScale);
+ showElevationProfile(route.cells, length, false);
}
function editRouteLegend() {
diff --git a/modules/ui/routes-overview.js b/modules/ui/routes-overview.js
index cb626025..4393eefe 100644
--- a/modules/ui/routes-overview.js
+++ b/modules/ui/routes-overview.js
@@ -29,12 +29,11 @@ function overviewRoutes() {
function routesOverviewAddLines() {
body.innerHTML = "";
let lines = "";
- const unit = distanceUnitInput.value;
for (const route of pack.routes) {
route.name = route.name || Routes.generateName(route);
- route.length = route.length || getRouteLength(route.i);
- const length = rn(route.length * distanceScale) + " " + unit;
+ route.length = route.length || Routes.getLength(route.i);
+ const length = rn(route.length * distanceScale) + " " + distanceUnitInput.value;
lines += /* html */ `
r.length)));
- routesFooterLength.innerHTML = averageLength * distanceScale + " " + unit;
+ routesFooterLength.innerHTML = averageLength * distanceScale + " " + distanceUnitInput.value;
// add listeners
body.querySelectorAll("div.states").forEach(el => el.on("mouseenter", routeHighlightOn));
@@ -68,11 +67,6 @@ function overviewRoutes() {
applySorting(routesHeader);
}
- function getRouteLength(routeId) {
- const path = routes.select("#route" + routeId).node();
- return rn(path.getTotalLength() / 2, 2);
- }
-
function routeHighlightOn(event) {
if (!layerIsOn("toggleRoutes")) toggleRoutes();
const routeId = +event.target.dataset.id;
diff --git a/modules/ui/style.js b/modules/ui/style.js
index 67e87928..f6e5dd02 100644
--- a/modules/ui/style.js
+++ b/modules/ui/style.js
@@ -503,7 +503,7 @@ styleGridScale.addEventListener("input", function () {
function calculateFriendlyGridSize() {
const size = styleGridScale.value * 25;
- const friendly = `${rn(size * distanceScaleInput.value, 2)} ${distanceUnitInput.value}`;
+ const friendly = `${rn(size * distanceScale, 2)} ${distanceUnitInput.value}`;
styleGridSizeFriendly.value = friendly;
}
diff --git a/modules/ui/submap.js b/modules/ui/submap.js
index 2a476faa..0341f8af 100644
--- a/modules/ui/submap.js
+++ b/modules/ui/submap.js
@@ -258,11 +258,16 @@ window.UISubmap = (function () {
byId("latitudeInput").value = latitudeOutput.value;
// fix scale
- distanceScaleInput.value = distanceScaleOutput.value = rn((distanceScale = distanceScaleOutput.value / scale), 2);
+ distanceScale =
+ distanceScaleInput.value =
+ distanceScaleOutput.value =
+ rn((distanceScale = distanceScaleOutput.value / scale), 2);
+
populationRateInput.value = populationRateOutput.value = rn(
(populationRate = populationRateOutput.value / scale),
2
);
+
customization = 0;
startResample(options);
}, 1000);
diff --git a/modules/ui/units-editor.js b/modules/ui/units-editor.js
index 86079026..ff911567 100644
--- a/modules/ui/units-editor.js
+++ b/modules/ui/units-editor.js
@@ -55,6 +55,7 @@ function editUnits() {
}
function changeDistanceScale() {
+ distanceScale = +this.value;
renderScaleBar();
calculateFriendlyGridSize();
}
@@ -90,10 +91,9 @@ function editUnits() {
}
function restoreDefaultUnits() {
- // distanceScale
distanceScale = 3;
- byId("distanceScaleOutput").value = 3;
- byId("distanceScaleInput").value = 3;
+ byId("distanceScaleOutput").value = distanceScale;
+ byId("distanceScaleInput").value = distanceScale;
unlock("distanceScale");
// units
diff --git a/modules/ui/world-configurator.js b/modules/ui/world-configurator.js
index 9ac0062c..67569592 100644
--- a/modules/ui/world-configurator.js
+++ b/modules/ui/world-configurator.js
@@ -105,13 +105,12 @@ function editWorld() {
calculateMapCoordinates();
const mc = mapCoordinates;
- const scale = +distanceScaleInput.value;
const unit = distanceUnitInput.value;
- const meridian = toKilometer(eqD * 2 * scale);
+ const meridian = toKilometer(eqD * 2 * distanceScale);
byId("mapSize").innerHTML = `${graphWidth}x${graphHeight}`;
- byId("mapSizeFriendly").innerHTML = `${rn(graphWidth * scale)}x${rn(graphHeight * scale)} ${unit}`;
+ byId("mapSizeFriendly").innerHTML = `${rn(graphWidth * distanceScale)}x${rn(graphHeight * distanceScale)} ${unit}`;
byId("meridianLength").innerHTML = rn(eqD * 2);
- byId("meridianLengthFriendly").innerHTML = `${rn(eqD * 2 * scale)} ${unit}`;
+ 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`;