Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
7bf33b6
feat: add labels module and integrate into PackedGraph
StempunkDev Feb 10, 2026
dac231f
refactor: clean up label-related code and introduce raycasting utilities
StempunkDev Feb 10, 2026
c467f87
refactor: change exported functions to internal functions in label-ra…
StempunkDev Feb 10, 2026
94b638f
feat: integrate label generation into main flow and enhance label dat…
StempunkDev Feb 11, 2026
689fef0
feat: enhance label editing functionality and improve data model sync…
StempunkDev Feb 13, 2026
2379770
refactor: clean up code formatting and improve timing logic in label …
StempunkDev Feb 13, 2026
471e865
refactor: update import path for findClosestCell utility
StempunkDev Feb 13, 2026
ca6d01f
feat: synchronize label data model with burg name and position updates
StempunkDev Feb 16, 2026
3ab40ad
feat: migrate label data structure from SVG to data model and update …
StempunkDev Feb 17, 2026
6ab2c03
fix: prevent error on rendering removed or neutral states in stateLab…
StempunkDev Feb 17, 2026
861db87
refactor: encapsulate label generation functions within LabelsModule
StempunkDev Feb 17, 2026
0d56479
refactor: update import path for label-raycast and improve state labe…
StempunkDev Feb 17, 2026
c22e6eb
chore: update script version numbers to 1.113.0 in index.html
StempunkDev Feb 17, 2026
32e7049
refactor: replace currentLabelData with direct calls to getLabelData …
StempunkDev Feb 19, 2026
6d99d82
Fix spelling in label-raycast.ts [Copilot]
StempunkDev Feb 19, 2026
6fa3f78
refactor: improve code formatting and organization in labels and rend…
StempunkDev Feb 19, 2026
e174056
refactor: optimize label rendering by building HTML string for batch …
StempunkDev Feb 21, 2026
ba0ce8e
Merge branch 'master' into feature/split-label-view-data
StempunkDev Feb 24, 2026
fd32007
apply format
StempunkDev Feb 24, 2026
3927a76
chore: update version to 1.114.0 and adjust related script references…
StempunkDev Feb 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions public/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,8 @@ async function generate(options) {
Provinces.generate();
Provinces.getPoles();

Labels.generate();

Rivers.specify();
Lakes.defineNames();

Expand Down
157 changes: 157 additions & 0 deletions public/modules/dynamic/auto-update.js
Original file line number Diff line number Diff line change
Expand Up @@ -1106,4 +1106,161 @@ export function resolveVersionConflicts(mapVersion) {
}

}

if (isOlderThan("1.114.0")) {
// v1.114.0 moved labels data from SVG to data model
// Migrate old SVG labels to pack.labels structure
if (!pack.labels || !pack.labels.length) {
pack.labels = [];
let labelId = 0;

// Migrate state labels
const stateLabelsGroup = document.querySelector("#labels > #states");
if (stateLabelsGroup) {
stateLabelsGroup.querySelectorAll("text").forEach(textElement => {
const id = textElement.getAttribute("id");
if (!id || !id.startsWith("stateLabel")) return;

const stateIdMatch = id.match(/stateLabel(\d+)/);
if (!stateIdMatch) return;

const stateId = +stateIdMatch[1];
const state = pack.states[stateId];
if (!state || state.removed) return;

const textPath = textElement.querySelector("textPath");
if (!textPath) return;

const text = textPath.textContent.trim();
const fontSizeAttr = textPath.getAttribute("font-size");
const fontSize = fontSizeAttr ? parseFloat(fontSizeAttr) : 100;

pack.labels.push({
i: labelId++,
type: "state",
stateId: stateId,
text: text,
fontSize: fontSize
});
});
}

// Migrate burg labels
const burgLabelsGroup = document.querySelector("#burgLabels");
if (burgLabelsGroup) {
burgLabelsGroup.querySelectorAll("g").forEach(groupElement => {
const group = groupElement.getAttribute("id");
if (!group) return;

const dxAttr = groupElement.getAttribute("data-dx");
const dyAttr = groupElement.getAttribute("data-dy");
const dx = dxAttr ? parseFloat(dxAttr) : 0;
const dy = dyAttr ? parseFloat(dyAttr) : 0;

groupElement.querySelectorAll("text").forEach(textElement => {
const burgId = +textElement.getAttribute("data-id");
if (!burgId) return;

const burg = pack.burgs[burgId];
if (!burg || burg.removed) return;

const text = textElement.textContent.trim();
// Use burg coordinates, not SVG text coordinates
// SVG coordinates may be affected by viewbox transforms
const x = burg.x;
const y = burg.y;

pack.labels.push({
i: labelId++,
type: "burg",
burgId: burgId,
group: group,
text: text,
x: x,
y: y,
dx: dx,
dy: dy
});
});
});
}

// Migrate custom labels
const customLabelsGroup = document.querySelector("#labels > #addedLabels");
if (customLabelsGroup) {
customLabelsGroup.querySelectorAll("text").forEach(textElement => {
const id = textElement.getAttribute("id");
if (!id) return;

const group = "custom";
const textPath = textElement.querySelector("textPath");
if (!textPath) return;

const text = textPath.textContent.trim();
const fontSizeAttr = textPath.getAttribute("font-size");
const fontSize = fontSizeAttr ? parseFloat(fontSizeAttr) : 100;
const letterSpacingAttr = textPath.getAttribute("letter-spacing");
const letterSpacing = letterSpacingAttr ? parseFloat(letterSpacingAttr) : 0;
const startOffsetAttr = textPath.getAttribute("startOffset");
const startOffset = startOffsetAttr ? parseFloat(startOffsetAttr) : 50;
const transform = textPath.getAttribute("transform");

// Get path points from the referenced path
const href = textPath.getAttribute("xlink:href") || textPath.getAttribute("href");
if (!href) return;

const pathId = href.replace("#", "");
const pathElement = document.getElementById(pathId);
if (!pathElement) return;

const d = pathElement.getAttribute("d");
if (!d) return;

// Parse path data to extract points(M, L and C commands)
const pathPoints = [];
const commands = d.match(/[MLC][^MLC]*/g);
if (commands) {
commands.forEach(cmd => {
const type = cmd[0];
if (type === "M" || type === "L") {
const coords = cmd.slice(1).trim().split(/[\s,]+/).map(Number);
if (coords.length >= 2) {
pathPoints.push([coords[0], coords[1]]);
}
} else if (type === "C") {
const coords = cmd.slice(1).trim().split(/[\s,]+/).map(Number);
if (coords.length >= 6) {
pathPoints.push([coords[4], coords[5]]);
}
}
});
}

if (pathPoints.length > 0) {
pack.labels.push({
i: labelId++,
type: "custom",
group: group,
text: text,
pathPoints: pathPoints,
startOffset: startOffset,
fontSize: fontSize,
letterSpacing: letterSpacing,
transform: transform || undefined
});
}
});
}

// Clear old SVG labels and redraw from data
if (stateLabelsGroup) stateLabelsGroup.querySelectorAll("*").forEach(el => el.remove());
if (burgLabelsGroup) burgLabelsGroup.querySelectorAll("text").forEach(el => el.remove());

// Regenerate labels from data
if (layerIsOn("toggleLabels")) {
drawStateLabels();
drawBurgLabels();
}
}
}
}
3 changes: 2 additions & 1 deletion public/modules/io/load.js
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ async function parseLoadedData(data, mapVersion) {
// data[28] had deprecated cells.crossroad
pack.cells.routes = data[36] ? JSON.parse(data[36]) : {};
pack.ice = data[39] ? JSON.parse(data[39]) : [];
pack.labels = data[40] ? JSON.parse(data[40]) : [];

if (data[31]) {
const namesDL = data[31].split("/");
Expand Down Expand Up @@ -473,7 +474,7 @@ async function parseLoadedData(data, mapVersion) {

{
// dynamically import and run auto-update script
const {resolveVersionConflicts} = await import("../dynamic/auto-update.js?v=1.109.4");
const {resolveVersionConflicts} = await import("../dynamic/auto-update.js?v=1.113.0");
resolveVersionConflicts(mapVersion);
}

Expand Down
4 changes: 3 additions & 1 deletion public/modules/io/save.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -158,7 +159,8 @@ function prepareMapData() {
cellRoutes,
routes,
zones,
ice
ice,
labels
].join("\r\n");
return mapData;
}
Expand Down
7 changes: 7 additions & 0 deletions public/modules/ui/burg-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ function editBurg(id) {
const id = +elSelected.attr("data-id");
pack.burgs[id].name = burgName.value;
elSelected.text(burgName.value);
// Sync to Labels data model
const labelData = Labels.getBurgLabel(id);
if (labelData) Labels.updateLabel(labelData.i, {text: burgName.value});
}

function generateNameRandom() {
Expand Down Expand Up @@ -382,6 +385,10 @@ function editBurg(id) {
burg.y = y;
if (burg.capital) pack.states[newState].center = burg.cell;

// Sync position to Labels data model
const labelData = Labels.getBurgLabel(id);
if (labelData) Labels.updateLabel(labelData.i, {x, y});

if (d3.event.shiftKey === false) toggleRelocateBurg();
}

Expand Down
Loading