mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2026-03-26 17:17:23 +01:00
Split view and data for labels (#2)
* Initial plan * Implement label view/data separation with pack.labels Co-authored-by: StempunkDev <39553418+StempunkDev@users.noreply.github.com> * Update label editor to sync changes with pack.labels Co-authored-by: StempunkDev <39553418+StempunkDev@users.noreply.github.com> * Address code review feedback: optimize filtering and add pathData property Co-authored-by: StempunkDev <39553418+StempunkDev@users.noreply.github.com> * Move label migration code from load.js to auto-update.js Co-authored-by: StempunkDev <39553418+StempunkDev@users.noreply.github.com> * Implement label generation and rendering for states and burgs * Bump version to 1.113.0 * Remove initialization of labels array in generate function --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: StempunkDev <39553418+StempunkDev@users.noreply.github.com>
This commit is contained in:
parent
be82ddb0a4
commit
25d06265f1
12 changed files with 962 additions and 351 deletions
|
|
@ -1106,4 +1106,105 @@ export function resolveVersionConflicts(mapVersion) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
if (isOlderThan("1.113.0")) {
|
||||
// v1.113 separates label data from view layer by introducing pack.labels array
|
||||
// Migrate labels from SVG to pack.labels for backward compatibility
|
||||
if (!pack.labels || pack.labels.length === 0) {
|
||||
pack.labels = [];
|
||||
|
||||
// Extract burg labels from SVG
|
||||
burgLabels.selectAll("text").each(function() {
|
||||
const textEl = d3.select(this);
|
||||
const burgId = +textEl.attr("data-id");
|
||||
const id = textEl.attr("id");
|
||||
const group = this.parentNode.id;
|
||||
|
||||
if (id && burgId !== undefined) {
|
||||
pack.labels.push({
|
||||
i: id,
|
||||
type: "burg",
|
||||
name: textEl.text(),
|
||||
group: group,
|
||||
burgId: burgId
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Extract state labels from SVG
|
||||
labels.select("g#states").selectAll("text").each(function() {
|
||||
const textEl = d3.select(this);
|
||||
const id = textEl.attr("id");
|
||||
const stateId = id ? +id.replace("stateLabel", "") : null;
|
||||
|
||||
if (id && stateId !== null) {
|
||||
const textPathEl = textEl.select("textPath");
|
||||
const pathId = textPathEl.attr("href")?.replace("#textPath_", "");
|
||||
const path = pathId ? defs.select(`#textPath_${id}`) : null;
|
||||
|
||||
let pathData;
|
||||
if (path && !path.empty()) {
|
||||
pathData = path.attr("d");
|
||||
}
|
||||
|
||||
const lines = [];
|
||||
textPathEl.selectAll("tspan").each(function() {
|
||||
lines.push(d3.select(this).text());
|
||||
});
|
||||
|
||||
pack.labels.push({
|
||||
i: id,
|
||||
type: "state",
|
||||
name: lines.join("|"),
|
||||
stateId: stateId,
|
||||
pathData: pathData,
|
||||
startOffset: parseFloat(textPathEl.attr("startOffset")) || 50,
|
||||
fontSize: parseFloat(textPathEl.attr("font-size")) || 100,
|
||||
letterSpacing: parseFloat(textPathEl.attr("letter-spacing")) || 0,
|
||||
transform: textEl.attr("transform") || ""
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Extract custom labels from other groups
|
||||
labels.selectAll(":scope > g").each(function() {
|
||||
const groupId = this.id;
|
||||
if (groupId === "states" || groupId === "burgLabels") return;
|
||||
|
||||
d3.select(this).selectAll("text").each(function() {
|
||||
const textEl = d3.select(this);
|
||||
const id = textEl.attr("id");
|
||||
|
||||
if (id) {
|
||||
const textPathEl = textEl.select("textPath");
|
||||
const pathId = textPathEl.attr("href")?.replace("#textPath_", "");
|
||||
const path = pathId ? defs.select(`#textPath_${id}`) : null;
|
||||
|
||||
let pathData;
|
||||
if (path && !path.empty()) {
|
||||
pathData = path.attr("d");
|
||||
}
|
||||
|
||||
const lines = [];
|
||||
textPathEl.selectAll("tspan").each(function() {
|
||||
lines.push(d3.select(this).text());
|
||||
});
|
||||
|
||||
pack.labels.push({
|
||||
i: id,
|
||||
type: "custom",
|
||||
name: lines.join("|"),
|
||||
group: groupId,
|
||||
pathData: pathData,
|
||||
startOffset: parseFloat(textPathEl.attr("startOffset")) || 50,
|
||||
fontSize: parseFloat(textPathEl.attr("font-size")) || 100,
|
||||
letterSpacing: parseFloat(textPathEl.attr("letter-spacing")) || 0,
|
||||
transform: textEl.attr("transform") || ""
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -392,6 +392,7 @@ async function parseLoadedData(data, mapVersion) {
|
|||
pack.markers = data[35] ? JSON.parse(data[35]) : [];
|
||||
pack.routes = data[37] ? JSON.parse(data[37]) : [];
|
||||
pack.zones = data[38] ? JSON.parse(data[38]) : [];
|
||||
pack.labels = data[40] ? JSON.parse(data[40]) : [];
|
||||
pack.cells.biome = Uint8Array.from(data[16].split(","));
|
||||
pack.cells.burg = Uint16Array.from(data[17].split(","));
|
||||
pack.cells.conf = Uint8Array.from(data[18].split(","));
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ function prepareMapData() {
|
|||
const routes = JSON.stringify(pack.routes);
|
||||
const zones = JSON.stringify(pack.zones);
|
||||
const ice = JSON.stringify(pack.ice);
|
||||
const labels = JSON.stringify(pack.labels || []);
|
||||
|
||||
// store name array only if not the same as default
|
||||
const defaultNB = Names.getNameBases();
|
||||
|
|
@ -158,7 +159,8 @@ function prepareMapData() {
|
|||
cellRoutes,
|
||||
routes,
|
||||
zones,
|
||||
ice
|
||||
ice,
|
||||
labels
|
||||
].join("\r\n");
|
||||
return mapData;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,77 @@
|
|||
"use strict";
|
||||
|
||||
// Helper function to sync label data with pack.labels
|
||||
function syncLabelToPack(labelId) {
|
||||
if (!pack.labels) pack.labels = [];
|
||||
|
||||
const textEl = document.getElementById(labelId);
|
||||
if (!textEl) return;
|
||||
|
||||
const group = textEl.parentNode.id;
|
||||
const isBurgLabel = labelId.startsWith("burgLabel");
|
||||
const isStateLabel = labelId.startsWith("stateLabel");
|
||||
|
||||
// Remove existing entry
|
||||
const existingIndex = pack.labels.findIndex(l => l.i === labelId);
|
||||
|
||||
// Gather label data
|
||||
const labelData = {
|
||||
i: labelId,
|
||||
type: isBurgLabel ? "burg" : isStateLabel ? "state" : "custom"
|
||||
};
|
||||
|
||||
if (isBurgLabel) {
|
||||
const burgId = +textEl.getAttribute("data-id");
|
||||
labelData.name = textEl.textContent;
|
||||
labelData.group = group;
|
||||
labelData.burgId = burgId;
|
||||
} else {
|
||||
// State or custom label with textPath
|
||||
const textPathEl = textEl.querySelector("textPath");
|
||||
if (textPathEl) {
|
||||
const lines = [];
|
||||
textPathEl.querySelectorAll("tspan").forEach(tspan => {
|
||||
lines.push(tspan.textContent);
|
||||
});
|
||||
labelData.name = lines.join("|");
|
||||
labelData.startOffset = parseFloat(textPathEl.getAttribute("startOffset")) || 50;
|
||||
labelData.fontSize = parseFloat(textPathEl.getAttribute("font-size")) || 100;
|
||||
labelData.letterSpacing = parseFloat(textPathEl.getAttribute("letter-spacing")) || 0;
|
||||
labelData.transform = textEl.getAttribute("transform") || "";
|
||||
|
||||
if (isStateLabel) {
|
||||
const stateId = +labelId.replace("stateLabel", "");
|
||||
labelData.stateId = stateId;
|
||||
} else {
|
||||
labelData.group = group;
|
||||
}
|
||||
|
||||
// Extract path points
|
||||
const pathId = `textPath_${labelId}`;
|
||||
const pathEl = document.getElementById(pathId);
|
||||
if (pathEl) {
|
||||
const pathData = pathEl.getAttribute("d");
|
||||
// Store the path data - ideally we'd parse it into points, but for now store as-is
|
||||
// This will be improved when we implement more sophisticated label editing
|
||||
labelData.pathData = pathData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update or add to pack.labels
|
||||
if (existingIndex >= 0) {
|
||||
pack.labels[existingIndex] = labelData;
|
||||
} else {
|
||||
pack.labels.push(labelData);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to remove label from pack.labels
|
||||
function removeLabelFromPack(labelId) {
|
||||
if (!pack.labels) return;
|
||||
pack.labels = pack.labels.filter(l => l.i !== labelId);
|
||||
}
|
||||
|
||||
function editLabel() {
|
||||
if (customization) return;
|
||||
closeDialogs();
|
||||
|
|
@ -133,6 +206,9 @@ function editLabel() {
|
|||
const d = round(lineGen(points));
|
||||
path.setAttribute("d", d);
|
||||
debug.select("#controlPoints > path").attr("d", d);
|
||||
|
||||
// Sync changes to pack.labels
|
||||
syncLabelToPack(elSelected.attr("id"));
|
||||
}
|
||||
|
||||
function clickControlPoint() {
|
||||
|
|
@ -188,6 +264,11 @@ function editLabel() {
|
|||
elSelected.attr("transform", transform);
|
||||
debug.select("#controlPoints").attr("transform", transform);
|
||||
});
|
||||
|
||||
d3.event.on("end", function () {
|
||||
// Sync changes to pack.labels after drag ends
|
||||
syncLabelToPack(elSelected.attr("id"));
|
||||
});
|
||||
}
|
||||
|
||||
function showGroupSection() {
|
||||
|
|
@ -205,6 +286,9 @@ function editLabel() {
|
|||
|
||||
function changeGroup() {
|
||||
byId(this.value).appendChild(elSelected.node());
|
||||
|
||||
// Sync changes to pack.labels
|
||||
syncLabelToPack(elSelected.attr("id"));
|
||||
}
|
||||
|
||||
function toggleNewGroupInput() {
|
||||
|
|
@ -280,6 +364,10 @@ function editLabel() {
|
|||
.selectAll("text")
|
||||
.each(function () {
|
||||
byId("textPath_" + this.id).remove();
|
||||
|
||||
// Remove from pack.labels
|
||||
removeLabelFromPack(this.id);
|
||||
|
||||
this.remove();
|
||||
});
|
||||
if (!basic) labels.select("#" + group).remove();
|
||||
|
|
@ -313,6 +401,9 @@ function editLabel() {
|
|||
|
||||
if (elSelected.attr("id").slice(0, 10) === "stateLabel")
|
||||
tip("Use States Editor to change an actual state name, not just a label", false, "warning");
|
||||
|
||||
// Sync changes to pack.labels
|
||||
syncLabelToPack(elSelected.attr("id"));
|
||||
}
|
||||
|
||||
function generateRandomName() {
|
||||
|
|
@ -359,6 +450,9 @@ function editLabel() {
|
|||
function changeStartOffset() {
|
||||
elSelected.select("textPath").attr("startOffset", this.value + "%");
|
||||
tip("Label offset: " + this.value + "%");
|
||||
|
||||
// Sync changes to pack.labels
|
||||
syncLabelToPack(elSelected.attr("id"));
|
||||
}
|
||||
|
||||
function changeRelativeSize() {
|
||||
|
|
@ -395,9 +489,13 @@ function editLabel() {
|
|||
buttons: {
|
||||
Remove: function () {
|
||||
$(this).dialog("close");
|
||||
defs.select("#textPath_" + elSelected.attr("id")).remove();
|
||||
const labelId = elSelected.attr("id");
|
||||
defs.select("#textPath_" + labelId).remove();
|
||||
elSelected.remove();
|
||||
$("#labelEditor").dialog("close");
|
||||
|
||||
// Remove from pack.labels
|
||||
removeLabelFromPack(labelId);
|
||||
},
|
||||
Cancel: function () {
|
||||
$(this).dialog("close");
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
* Example: 1.102.2 -> Major version 1, Minor version 102, Patch version 2
|
||||
*/
|
||||
|
||||
const VERSION = "1.112.1";
|
||||
const VERSION = "1.113.0";
|
||||
if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format or parsing function");
|
||||
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue