diff --git a/index.html b/index.html index cf279ba1..b8d13e3e 100644 --- a/index.html +++ b/index.html @@ -1130,6 +1130,21 @@ + + + + + + State labels + + + + +

Generator settings:

diff --git a/main.js b/main.js index 539dea04..6cab8c0a 100644 --- a/main.js +++ b/main.js @@ -150,7 +150,8 @@ const zoom = d3.zoom().scaleExtent([1, 20]).on("zoom", zoomed); let options = { pinNotes: false, showMFCGMap: true, - winds: [225, 45, 225, 315, 135, 315] + winds: [225, 45, 225, 315, 135, 315], + stateLabelsMode: "auto" }; let mapCoordinates = {}; // map coordinates on globe let populationRate = +document.getElementById("populationRateInput").value; diff --git a/modules/burgs-and-states.js b/modules/burgs-and-states.js index 6b8a814c..09d70f2d 100644 --- a/modules/burgs-and-states.js +++ b/modules/burgs-and-states.js @@ -480,6 +480,7 @@ window.BurgsAndStates = (function () { const {cells, features, states} = pack; const paths = []; // text paths lineGen.curve(d3.curveBundle.beta(1)); + const mode = options.stateLabelsMode || "auto"; for (const s of states) { if (!s.i || s.removed || !s.cells || (list && !list.includes(s.i))) continue; @@ -586,7 +587,8 @@ window.BurgsAndStates = (function () { paths.forEach(p => { const id = p[0]; - const s = states[p[0]]; + const state = states[p[0]]; + const {name, fullName} = state; if (list) { t.select("#textPath_stateLabel" + id).remove(); @@ -600,22 +602,7 @@ window.BurgsAndStates = (function () { .attr("id", "textPath_stateLabel" + id); const pathLength = p[1].length > 1 ? textPath.node().getTotalLength() / letterLength : 0; // path length in letters - let lines = []; - let ratio = 100; - - if (pathLength < s.name.length) { - // only short name will fit - lines = splitInTwo(s.name); - ratio = minmax(rn((pathLength / lines[0].length) * 60), 50, 150); - } else if (pathLength > s.fullName.length * 2.5) { - // full name will fit in one line - lines = [s.fullName]; - ratio = minmax(rn((pathLength / lines[0].length) * 70), 70, 170); - } else { - // try miltilined label - lines = splitInTwo(s.fullName); - ratio = minmax(rn((pathLength / lines[0].length) * 60), 70, 150); - } + const [lines, ratio] = getLines(mode, name, fullName, pathLength); // prolongate path if it's too short if (pathLength && pathLength < lines[0].length) { @@ -647,7 +634,7 @@ window.BurgsAndStates = (function () { .node(); el.insertAdjacentHTML("afterbegin", spans.join("")); - if (lines.length < 2) return; + if (mode === "full" || lines.length === 1) return; // check whether multilined label is generally inside the state. If no, replace with short name label const cs = pack.cells.state; @@ -658,21 +645,43 @@ window.BurgsAndStates = (function () { const c4 = () => +cs[findCell(b.x + b.width, b.y + b.height)] === id; const c5 = () => +cs[findCell(b.x + b.width / 2, b.y + b.height)] === id; const c6 = () => +cs[findCell(b.x, b.y + b.height)] === id; - if (c1() + c2() + c3() + c4() + c5() + c6() > 3) return; // generally inside + if (c1() + c2() + c3() + c4() + c5() + c6() > 3) return; // generally inside => exit - // use one-line name - const name = pathLength > s.fullName.length * 1.8 ? s.fullName : s.name; - example.text(name); + // move to one-line name + const text = pathLength > fullName.length * 1.8 ? fullName : name; + example.text(text); const left = example.node().getBBox().width / -2; // x offset - el.innerHTML = `${name}`; - ratio = minmax(rn((pathLength / name.length) * 60), 40, 130); - el.setAttribute("font-size", ratio + "%"); + el.innerHTML = `${text}`; + + const correctedRatio = minmax(rn((pathLength / text.length) * 60), 40, 130); + el.setAttribute("font-size", correctedRatio + "%"); }); example.remove(); if (!displayed) toggleLabels(); })(); + function getLines(mode, name, fullName, pathLength) { + // short name + if (mode === "short" || (mode === "auto" && pathLength < name.length)) { + const lines = splitInTwo(name); + const ratio = pathLength / lines[0].length; + return [lines, minmax(rn(ratio * 60), 50, 150)]; + } + + // full name: one line + if (pathLength > fullName.length * 2.5) { + const lines = [fullName]; + const ratio = pathLength / lines[0].length; + return [lines, minmax(rn(ratio * 70), 70, 170)]; + } + + // full name: two lines + const lines = splitInTwo(fullName); + const ratio = pathLength / lines[0].length; + return [lines, minmax(rn(ratio * 60), 70, 150)]; + } + TIME && console.timeEnd("drawStateLabels"); }; diff --git a/modules/load.js b/modules/load.js index 459abadb..a03b9523 100644 --- a/modules/load.js +++ b/modules/load.js @@ -234,6 +234,10 @@ function parseLoadedData(data) { if (settings[24]) urbanDensity = urbanDensityInput.value = urbanDensityOutput.value = +settings[24]; })(); + void (function applyOptionsToUI() { + stateLabelsModeInput.value = options.stateLabelsMode; + })(); + void (function parseConfiguration() { if (data[2]) mapCoordinates = JSON.parse(data[2]); if (data[4]) notes = JSON.parse(data[4]); diff --git a/modules/ui/options.js b/modules/ui/options.js index 9610202c..0e23f55a 100644 --- a/modules/ui/options.js +++ b/modules/ui/options.js @@ -165,6 +165,7 @@ optionsContent.addEventListener("change", function (event) { if (id === "shapeRendering") viewbox.attr("shape-rendering", value); else if (id === "yearInput") changeYear(); else if (id === "eraInput") changeEra(); + else if (id === "stateLabelsModeInput") options.stateLabelsMode = value; }); optionsContent.addEventListener("click", function (event) { @@ -533,6 +534,8 @@ function applyStoredOptions() { // set shape rendering viewbox.attr("shape-rendering", shapeRendering.value); + + options.stateLabelsMode = stateLabelsModeInput.value; } // randomize options if randomization is allowed (not locked or options='default')