mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-22 03:51:23 +01:00
feat: routes overview - fix distanceScale value
This commit is contained in:
parent
28bc6ccde6
commit
834d6f6cc7
17 changed files with 328 additions and 130 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -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 = `<svg width="1.4em" height="1.4em" style="margin-bottom: -.6em; stroke: #333">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="${color}"></rect>
|
||||
<text x="0" y="1.04em" style="">${regiment.icon}</text></svg>`;
|
||||
const body = `<tbody id="battle${state.i}-${regiment.i}">`;
|
||||
|
||||
let initial = `<tr class="battleInitial"><td>${icon}</td><td class="regiment" data-tip="${regiment.name}">${regiment.name.slice(0, 24)}</td>`;
|
||||
let casualties = `<tr class="battleCasualties"><td></td><td data-tip="${state.fullName}">${state.fullName.slice(0, 26)}</td>`;
|
||||
let initial = `<tr class="battleInitial"><td>${icon}</td><td class="regiment" data-tip="${
|
||||
regiment.name
|
||||
}">${regiment.name.slice(0, 24)}</td>`;
|
||||
let casualties = `<tr class="battleCasualties"><td></td><td data-tip="${state.fullName}">${state.fullName.slice(
|
||||
0,
|
||||
26
|
||||
)}</td>`;
|
||||
let survivors = `<tr class="battleSurvivors"><td></td><td data-tip="Supply line length, affects morale">Distance to base: ${distance} ${distanceUnitInput.value}</td>`;
|
||||
|
||||
for (const u of options.military) {
|
||||
initial += `<td data-tip="Initial forces" style="width: 2.5em; text-align: center">${regiment.u[u.name] || 0}</td>`;
|
||||
initial += `<td data-tip="Initial forces" style="width: 2.5em; text-align: center">${
|
||||
regiment.u[u.name] || 0
|
||||
}</td>`;
|
||||
casualties += `<td data-tip="Casualties" style="width: 2.5em; text-align: center; color: red">0</td>`;
|
||||
survivors += `<td data-tip="Survivors" style="width: 2.5em; text-align: center; color: green">${regiment.u[u.name] || 0}</td>`;
|
||||
survivors += `<td data-tip="Survivors" style="width: 2.5em; text-align: center; color: green">${
|
||||
regiment.u[u.name] || 0
|
||||
}</td>`;
|
||||
}
|
||||
|
||||
initial += `<td data-tip="Initial forces" style="width: 2.5em; text-align: center">${regiment.a || 0}</td></tr>`;
|
||||
casualties += `<td data-tip="Casualties" style="width: 2.5em; text-align: center; color: red">0</td></tr>`;
|
||||
survivors += `<td data-tip="Survivors" style="width: 2.5em; text-align: center; color: green">${regiment.a || 0}</td></tr>`;
|
||||
survivors += `<td data-tip="Survivors" style="width: 2.5em; text-align: center; color: green">${
|
||||
regiment.a || 0
|
||||
}</td></tr>`;
|
||||
|
||||
const div = side === "attackers" ? battleAttackers : battleDefenders;
|
||||
div.innerHTML += body + initial + casualties + survivors + "</tbody>";
|
||||
|
|
@ -173,17 +206,23 @@ class Battle {
|
|||
.filter(s => s.military && !s.removed)
|
||||
.map(s => s.military)
|
||||
.flat();
|
||||
const distance = reg => rn(Math.hypot(context.y - reg.y, context.x - reg.x) * distanceScaleInput.value) + " " + distanceUnitInput.value;
|
||||
const isAdded = reg => context.defenders.regiments.some(r => r === reg) || context.attackers.regiments.some(r => r === reg);
|
||||
const distance = reg =>
|
||||
rn(Math.hypot(context.y - reg.y, context.x - reg.x) * distanceScale) + " " + distanceUnitInput.value;
|
||||
const isAdded = reg =>
|
||||
context.defenders.regiments.some(r => r === reg) || context.attackers.regiments.some(r => r === reg);
|
||||
|
||||
body.innerHTML = regiments
|
||||
.map(r => {
|
||||
const s = pack.states[r.state],
|
||||
added = isAdded(r),
|
||||
dist = added ? "0 " + distanceUnitInput.value : distance(r);
|
||||
return `<div ${added ? "class='inactive'" : ""} data-s=${s.i} data-i=${r.i} data-state=${s.name} data-regiment=${r.name}
|
||||
return `<div ${added ? "class='inactive'" : ""} data-s=${s.i} data-i=${r.i} data-state=${
|
||||
s.name
|
||||
} data-regiment=${r.name}
|
||||
data-total=${r.a} data-distance=${dist} data-tip="Click to select regiment">
|
||||
<svg width=".9em" height=".9em" style="margin-bottom:-1px; stroke: #333"><rect x="0" y="0" width="100%" height="100%" fill="${s.color}" ></svg>
|
||||
<svg width=".9em" height=".9em" style="margin-bottom:-1px; stroke: #333"><rect x="0" y="0" width="100%" height="100%" fill="${
|
||||
s.color
|
||||
}" ></svg>
|
||||
<div style="width:6em">${s.name.slice(0, 11)}</div>
|
||||
<div style="width:1.2em">${r.icon}</div>
|
||||
<div style="width:13em">${r.name.slice(0, 24)}</div>
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -1175,7 +1175,6 @@ function getAreaUnit(squareMark = "²") {
|
|||
}
|
||||
|
||||
function getArea(rawArea) {
|
||||
const distanceScale = byId("distanceScaleInput")?.value;
|
||||
return rawArea * distanceScale ** 2;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */ `<div
|
||||
|
|
@ -67,9 +67,9 @@ function overviewRivers() {
|
|||
const averageDischarge = rn(d3.mean(pack.rivers.map(r => 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";
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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,29 +246,55 @@ 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);
|
||||
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 `<option value="${r.i}">${r.name} (${length})</option>`;
|
||||
});
|
||||
alertMessage.innerHTML = /* html */ `<div>Route to join with:
|
||||
<select>${options.join("")}</select>
|
||||
</div>`;
|
||||
|
||||
$("#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) {
|
||||
function joinRoutes(route, joinedRoute) {
|
||||
if (!route.points) route.points = debug.selectAll("#controlPoints > *").data();
|
||||
if (!joinedRoute.points) joinedRoute.points = Routes.getPoints(joinedRoute, Routes.preparePointsArray());
|
||||
|
||||
|
|
@ -302,7 +327,6 @@ function editRoute(id) {
|
|||
drawCells();
|
||||
redrawRoute();
|
||||
}
|
||||
}
|
||||
|
||||
function showCreationDialog() {
|
||||
const route = getRoute();
|
||||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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 */ `<div
|
||||
class="states"
|
||||
|
|
@ -56,7 +55,7 @@ function overviewRoutes() {
|
|||
// update footer
|
||||
routesFooterNumber.innerHTML = pack.routes.length;
|
||||
const averageLength = rn(d3.mean(pack.routes.map(r => 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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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`;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue