From 7bf33b6e654205ab9f275aa3bf3faf16c0b678aa Mon Sep 17 00:00:00 2001 From: StempunkDev Date: Tue, 10 Feb 2026 22:38:38 +0100 Subject: [PATCH] feat: add labels module and integrate into PackedGraph --- src/modules/index.ts | 1 + src/modules/labels.ts | 152 +++++++++++++++++++++++++++++++++++++++ src/types/PackedGraph.ts | 2 + 3 files changed, 155 insertions(+) create mode 100644 src/modules/labels.ts diff --git a/src/modules/index.ts b/src/modules/index.ts index a9ebf2b8..46ee6759 100644 --- a/src/modules/index.ts +++ b/src/modules/index.ts @@ -12,4 +12,5 @@ import "./routes-generator"; import "./states-generator"; import "./zones-generator"; import "./religions-generator"; +import "./labels"; import "./provinces-generator"; diff --git a/src/modules/labels.ts b/src/modules/labels.ts new file mode 100644 index 00000000..a6862263 --- /dev/null +++ b/src/modules/labels.ts @@ -0,0 +1,152 @@ +declare global { + var Labels: LabelsModule; +} + +// --- Types --- + +export interface StateLabelData { + i: number; + type: "state"; + stateId: number; + text: string; + pathPoints: [number, number][]; + startOffset: number; + fontSize: number; + letterSpacing: number; + transform: string; +} + +export interface BurgLabelData { + i: number; + type: "burg"; + burgId: number; + group: string; + text: string; + x: number; + y: number; + dx: number; + dy: number; +} + +export interface CustomLabelData { + i: number; + type: "custom"; + group: string; + text: string; + pathPoints: [number, number][]; + startOffset: number; + fontSize: number; + letterSpacing: number; + transform: string; +} + +export type LabelData = StateLabelData | BurgLabelData | CustomLabelData; + +// --- Implementation --- + +class LabelsModule { + private getNextId(): number { + const labels = pack.labels; + if (labels.length === 0) return 0; + + const existingIds = labels.map((l) => l.i).sort((a, b) => a - b); + for (let id = 0; id < existingIds[existingIds.length - 1]; id++) { + if (!existingIds.includes(id)) return id; + } + return existingIds[existingIds.length - 1] + 1; + } + + getAll(): LabelData[] { + return pack.labels; + } + + get(id: number): LabelData | undefined { + return pack.labels.find((l) => l.i === id); + } + + getByType(type: LabelData["type"]): LabelData[] { + return pack.labels.filter((l) => l.type === type); + } + + getByGroup(group: string): LabelData[] { + return pack.labels.filter( + (l) => (l.type === "burg" || l.type === "custom") && l.group === group, + ); + } + + getStateLabel(stateId: number): StateLabelData | undefined { + return pack.labels.find( + (l) => l.type === "state" && l.stateId === stateId, + ) as StateLabelData | undefined; + } + + getBurgLabel(burgId: number): BurgLabelData | undefined { + return pack.labels.find( + (l) => l.type === "burg" && l.burgId === burgId, + ) as BurgLabelData | undefined; + } + + addStateLabel( + data: Omit, + ): StateLabelData { + const label: StateLabelData = { i: this.getNextId(), type: "state", ...data }; + pack.labels.push(label); + return label; + } + + addBurgLabel(data: Omit): BurgLabelData { + const label: BurgLabelData = { i: this.getNextId(), type: "burg", ...data }; + pack.labels.push(label); + return label; + } + + addCustomLabel( + data: Omit, + ): CustomLabelData { + const label: CustomLabelData = { i: this.getNextId(), type: "custom", ...data }; + pack.labels.push(label); + return label; + } + + updateLabel(id: number, updates: Partial): void { + const label = pack.labels.find((l) => l.i === id); + if (!label) return; + Object.assign(label, updates, { i: label.i, type: label.type }); + } + + removeLabel(id: number): void { + const index = pack.labels.findIndex((l) => l.i === id); + if (index !== -1) pack.labels.splice(index, 1); + } + + removeByType(type: LabelData["type"]): void { + pack.labels = pack.labels.filter((l) => l.type !== type); + } + + removeByGroup(group: string): void { + pack.labels = pack.labels.filter( + (l) => + !((l.type === "burg" || l.type === "custom") && l.group === group), + ); + } + + removeStateLabel(stateId: number): void { + const index = pack.labels.findIndex( + (l) => l.type === "state" && l.stateId === stateId, + ); + if (index !== -1) pack.labels.splice(index, 1); + } + + removeBurgLabel(burgId: number): void { + const index = pack.labels.findIndex( + (l) => l.type === "burg" && l.burgId === burgId, + ); + if (index !== -1) pack.labels.splice(index, 1); + } + + clear(): void { + pack.labels = []; + } +} + +window.Labels = new LabelsModule(); diff --git a/src/types/PackedGraph.ts b/src/types/PackedGraph.ts index b8749f0a..f54d8157 100644 --- a/src/types/PackedGraph.ts +++ b/src/types/PackedGraph.ts @@ -5,6 +5,7 @@ import type { Province } from "../modules/provinces-generator"; import type { River } from "../modules/river-generator"; import type { Route } from "../modules/routes-generator"; import type { State } from "../modules/states-generator"; +import type { LabelData } from "../modules/labels"; import type { Zone } from "../modules/zones-generator"; type TypedArray = @@ -62,5 +63,6 @@ export interface PackedGraph { zones: Zone[]; markers: any[]; ice: any[]; + labels: LabelData[]; provinces: Province[]; }