State labels: new label placing algorithm (#977)

* feat: draw state labels start

* feat: update old .map files

* chore: update version hash

* fear: add change to the user's changelog
This commit is contained in:
Azgaar 2023-08-11 18:56:42 +04:00 committed by GitHub
parent 1bb90251cd
commit 87599d1530
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 395 additions and 281 deletions

View file

@ -1176,7 +1176,7 @@ function refreshAllEditors() {
// dynamically loaded editors
async function editStates() {
if (customization) return;
const Editor = await import("../dynamic/editors/states-editor.js?v=1.89.35");
const Editor = await import("../dynamic/editors/states-editor.js?v=1.92.00");
Editor.open();
}

View file

@ -253,7 +253,7 @@ function editHeightmap(options) {
drawStates();
drawBorders();
BurgsAndStates.drawStateLabels();
drawStateLabels();
Rivers.specify();
Lakes.generateName();
@ -442,7 +442,7 @@ function editHeightmap(options) {
c.center = findCell(c.x, c.y);
}
BurgsAndStates.drawStateLabels();
drawStateLabels();
drawStates();
drawBorders();

View file

@ -78,7 +78,9 @@ function editLabel() {
}
function updateValues(textPath) {
document.getElementById("labelText").value = [...textPath.querySelectorAll("tspan")].map(tspan => tspan.textContent).join("|");
document.getElementById("labelText").value = [...textPath.querySelectorAll("tspan")]
.map(tspan => tspan.textContent)
.join("|");
document.getElementById("labelStartOffset").value = parseFloat(textPath.getAttribute("startOffset"));
document.getElementById("labelRelativeSize").value = parseFloat(textPath.getAttribute("font-size"));
}
@ -298,22 +300,15 @@ function editLabel() {
function changeText() {
const input = document.getElementById("labelText").value;
const el = elSelected.select("textPath").node();
const example = d3.select(elSelected.node().parentNode).append("text").attr("x", 0).attr("x", 0).attr("font-size", el.getAttribute("font-size")).node();
const lines = input.split("|");
const top = (lines.length - 1) / -2; // y offset
const inner = lines
.map((l, d) => {
example.innerHTML = l;
const left = example.getBBox().width / -2; // x offset
return `<tspan x="${left}px" dy="${d ? 1 : top}em">${l}</tspan>`;
})
.join("");
if (lines.length > 1) {
const top = (lines.length - 1) / -2; // y offset
el.innerHTML = lines.map((line, index) => `<tspan x="0" dy="${index ? 1 : top}em">${line}</tspan>`).join("");
} else el.innerHTML = `<tspan x="0">${lines}</tspan>`;
el.innerHTML = inner;
example.remove();
if (elSelected.attr("id").slice(0, 10) === "stateLabel") tip("Use States Editor to change an actual state name, not just a label", false, "warning");
if (elSelected.attr("id").slice(0, 10) === "stateLabel")
tip("Use States Editor to change an actual state name, not just a label", false, "warning");
}
function generateRandomName() {

View file

@ -124,7 +124,9 @@ function editProvinces() {
const rural = p.rural * populationRate;
const urban = p.urban * populationRate * urbanization;
const population = rn(rural + urban);
const populationTip = `Total population: ${si(population)}; Rural population: ${si(rural)}; Urban population: ${si(urban)}`;
const populationTip = `Total population: ${si(population)}; Rural population: ${si(
rural
)}; Urban population: ${si(urban)}`;
totalPopulation += population;
const stateName = pack.states[p.state].name;
@ -145,9 +147,15 @@ function editProvinces() {
>
<fill-box fill="${p.color}"></fill-box>
<input data-tip="Province name. Click to change" class="name pointer" value="${p.name}" readonly />
<svg data-tip="Click to show and edit province emblem" class="coaIcon pointer hide" viewBox="0 0 200 200"><use href="#provinceCOA${p.i}"></use></svg>
<input data-tip="Province form name. Click to change" class="name pointer hide" value="${p.formName}" readonly />
<span data-tip="Province capital. Click to zoom into view" class="icon-star-empty pointer hide ${p.burg ? "" : "placeholder"}"></span>
<svg data-tip="Click to show and edit province emblem" class="coaIcon pointer hide" viewBox="0 0 200 200"><use href="#provinceCOA${
p.i
}"></use></svg>
<input data-tip="Province form name. Click to change" class="name pointer hide" value="${
p.formName
}" readonly />
<span data-tip="Province capital. Click to zoom into view" class="icon-star-empty pointer hide ${
p.burg ? "" : "placeholder"
}"></span>
<select
data-tip="Province capital. Click to select from burgs within the state. No capital means the province is governed from the state capital"
class="cultureBase hide ${p.burgs.length ? "" : "placeholder"}"
@ -164,7 +172,7 @@ function editProvinces() {
class="icon-flag-empty ${separable ? "" : "placeholder"} hide"
></span>
<span data-tip="Toggle province focus" class="icon-pin ${focused ? "" : " inactive"} hide"></span>
<span data-tip="Lock the province" class="icon-lock${p.lock ? '' : '-open'} hide"></span>
<span data-tip="Lock the province" class="icon-lock${p.lock ? "" : "-open"} hide"></span>
<span data-tip="Remove the province" class="icon-trash-empty hide"></span>
</div>`;
}
@ -193,7 +201,9 @@ function editProvinces() {
function getCapitalOptions(burgs, capital) {
let options = "";
burgs.forEach(b => (options += `<option ${b === capital ? "selected" : ""} value="${b}">${pack.burgs[b].name}</option>`));
burgs.forEach(
b => (options += `<option ${b === capital ? "selected" : ""} value="${b}">${pack.burgs[b].name}</option>`)
);
return options;
}
@ -267,7 +277,11 @@ function editProvinces() {
const {name, burg: burgId, burgs: provinceBurgs} = province;
if (provinceBurgs.some(b => burgs[b].capital))
return tip("Cannot declare independence of a province having capital burg. Please change capital first", false, "error");
return tip(
"Cannot declare independence of a province having capital burg. Please change capital first",
false,
"error"
);
if (!burgId) return tip("Cannot declare independence of a province without burg", false, "error");
const oldStateId = province.state;
@ -313,7 +327,10 @@ function editProvinces() {
return relations;
});
diplomacy.push("x");
states[0].diplomacy.push([`Independance declaration`, `${name} declared its independance from ${states[oldStateId].name}`]);
states[0].diplomacy.push([
`Independance declaration`,
`${name} declared its independance from ${states[oldStateId].name}`
]);
// create new state
states.push({
@ -348,7 +365,7 @@ function editProvinces() {
BurgsAndStates.collectStatistics();
BurgsAndStates.defineStateForms(newStates);
BurgsAndStates.drawStateLabels(allStates);
drawStateLabels(allStates);
// redraw emblems
allStates.forEach(stateId => {
@ -375,8 +392,12 @@ function editProvinces() {
const l = n => Number(n).toLocaleString();
alertMessage.innerHTML = /* html */ ` Rural: <input type="number" min="0" step="1" id="ruralPop" value=${rural} style="width:6em" /> Urban:
<input type="number" min="0" step="1" id="urbanPop" value=${urban} style="width:6em" ${p.burgs.length ? "" : "disabled"} />
<p>Total population: ${l(total)} <span id="totalPop">${l(total)}</span> (<span id="totalPopPerc">100</span>%)</p>`;
<input type="number" min="0" step="1" id="urbanPop" value=${urban} style="width:6em" ${
p.burgs.length ? "" : "disabled"
} />
<p>Total population: ${l(total)} <span id="totalPop">${l(
total
)}</span> (<span id="totalPopPerc">100</span>%)</p>`;
const update = function () {
const totalNew = ruralPop.valueAsNumber + urbanPop.valueAsNumber;
@ -694,7 +715,13 @@ function editProvinces() {
function updateChart() {
const value =
this.value === "area" ? d => d.area : this.value === "rural" ? d => d.rural : this.value === "urban" ? d => d.urban : d => d.rural + d.urban;
this.value === "area"
? d => d.area
: this.value === "rural"
? d => d.rural
: this.value === "urban"
? d => d.urban
: d => d.rural + d.urban;
root.sum(value);
node.data(treeLayout(root).leaves());
@ -776,7 +803,13 @@ function editProvinces() {
customization = 11;
provs.select("g#provincesBody").append("g").attr("id", "temp");
provs.select("g#provincesBody").append("g").attr("id", "centers").attr("fill", "none").attr("stroke", "#ff0000").attr("stroke-width", 1);
provs
.select("g#provincesBody")
.append("g")
.attr("id", "centers")
.attr("fill", "none")
.attr("stroke", "#ff0000")
.attr("stroke-width", 1);
document.querySelectorAll("#provincesBottom > *").forEach(el => (el.style.display = "none"));
document.getElementById("provincesManuallyButtons").style.display = "inline-block";
@ -788,7 +821,11 @@ function editProvinces() {
$("#provincesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}});
tip("Click on a province to select, drag the circle to change province", true);
viewbox.style("cursor", "crosshair").on("click", selectProvinceOnMapClick).call(d3.drag().on("start", dragBrush)).on("touchmove mousemove", moveBrush);
viewbox
.style("cursor", "crosshair")
.on("click", selectProvinceOnMapClick)
.call(d3.drag().on("start", dragBrush))
.on("touchmove mousemove", moveBrush);
body.querySelector("div").classList.add("selected");
selectProvince(+body.querySelector("div").dataset.id);
@ -859,7 +896,11 @@ function editProvinces() {
if (i === pack.provinces[provinceOld].center) {
const center = centers.select("polygon[data-center='" + i + "']");
if (!center.size()) centers.append("polygon").attr("data-center", i).attr("points", getPackPolygon(i));
tip("Province center cannot be assigned to a different region. Please remove the province first", false, "error");
tip(
"Province center cannot be assigned to a different region. Please remove the province first",
false,
"error"
);
return;
}
@ -921,7 +962,8 @@ function editProvinces() {
provincesHeader.querySelector("div[data-sortby='state']").style.left = "22em";
provincesFooter.style.display = "block";
body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "all"));
if (!close) $("#provincesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}});
if (!close)
$("#provincesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}});
restoreDefaultEvents();
clearMainTip();
@ -943,14 +985,20 @@ function editProvinces() {
const {cells, provinces} = pack;
const point = d3.mouse(this);
const center = findCell(point[0], point[1]);
if (cells.h[center] < 20) return tip("You cannot place province into the water. Please click on a land cell", false, "error");
if (cells.h[center] < 20)
return tip("You cannot place province into the water. Please click on a land cell", false, "error");
const oldProvince = cells.province[center];
if (oldProvince && provinces[oldProvince].center === center)
return tip("The cell is already a center of a different province. Select other cell", false, "error");
const state = cells.state[center];
if (!state) return tip("You cannot create a province in neutral lands. Please assign this land to a state first", false, "error");
if (!state)
return tip(
"You cannot create a province in neutral lands. Please assign this land to a state first",
false,
"error"
);
if (d3.event.shiftKey === false) exitAddProvinceMode();
@ -1016,7 +1064,10 @@ function editProvinces() {
function downloadProvincesData() {
const unit = areaUnit.value === "square" ? distanceUnitInput.value + "2" : areaUnit.value;
let data = "Id,Province,Full Name,Form,State,Color,Capital,Area " + unit + ",Total Population,Rural Population,Urban Population\n"; // headers
let data =
"Id,Province,Full Name,Form,State,Color,Capital,Area " +
unit +
",Total Population,Rural Population,Urban Population\n"; // headers
body.querySelectorAll(":scope > div").forEach(function (el) {
const key = parseInt(el.dataset.id);

View file

@ -74,7 +74,7 @@ toolsContent.addEventListener("click", function (event) {
});
function processFeatureRegeneration(event, button) {
if (button === "regenerateStateLabels") BurgsAndStates.drawStateLabels();
if (button === "regenerateStateLabels") drawStateLabels();
else if (button === "regenerateReliefIcons") {
ReliefIcons();
if (!layerIsOn("toggleRelief")) toggleRelief();
@ -154,7 +154,7 @@ function regenerateStates() {
layerIsOn("toggleBorders") ? drawBorders() : toggleBorders();
if (layerIsOn("toggleProvinces")) drawProvinces();
BurgsAndStates.drawStateLabels();
drawStateLabels();
Military.generate();
if (layerIsOn("toggleEmblems")) drawEmblems();
@ -570,9 +570,8 @@ function addLabelOnClick() {
.attr("data-size", 18)
.attr("filter", null);
const example = group.append("text").attr("x", 0).attr("x", 0).text(name);
const example = group.append("text").attr("x", 0).attr("y", 0).text(name);
const width = example.node().getBBox().width;
const x = width / -2; // x offset;
example.remove();
group.classed("hidden", false);
@ -584,7 +583,7 @@ function addLabelOnClick() {
.attr("startOffset", "50%")
.attr("font-size", "100%")
.append("tspan")
.attr("x", x)
.attr("x", 0)
.text(name);
defs
@ -836,7 +835,7 @@ function addMarkerOnClick() {
const marker = Markers.add({...baseMarker, x, y, cell});
if (selectedConfig && selectedConfig.add) {
selectedConfig.add("marker"+marker.i, cell);
selectedConfig.add("marker" + marker.i, cell);
}
const markersElement = document.getElementById("markers");