mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2026-04-05 06:57:24 +02:00
feat: update relief rendering logic and version to 1.114.0
This commit is contained in:
parent
dc06f3d65c
commit
ab7baf83fd
6 changed files with 53 additions and 110 deletions
|
|
@ -1112,4 +1112,23 @@ export function resolveVersionConflicts(mapVersion) {
|
||||||
zone.cells = unique(zone.cells);
|
zone.cells = unique(zone.cells);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isOlderThan("1.114.0")) {
|
||||||
|
// v1.114.0 add reliefIcon to pack data and changed rendering method to WebGL
|
||||||
|
const terrainEl = byId("terrain");
|
||||||
|
if (!terrainEl) return;
|
||||||
|
const relief = [];
|
||||||
|
|
||||||
|
terrainEl.querySelectorAll("use").forEach(u => {
|
||||||
|
const href = u.getAttribute("href") || u.getAttribute("xlink:href") || "";
|
||||||
|
if (!href) return;
|
||||||
|
const x = +u.getAttribute("x");
|
||||||
|
const y = +u.getAttribute("y");
|
||||||
|
const s = +u.getAttribute("width");
|
||||||
|
relief.push({i: relief.length, href, x, y, s});
|
||||||
|
});
|
||||||
|
terrainEl.innerHTML = "";
|
||||||
|
pack.relief = relief;
|
||||||
|
if (layerIsOn("toggleRelief")) drawRelief();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -180,10 +180,7 @@ async function getMapURL(
|
||||||
fullMap = false
|
fullMap = false
|
||||||
} = {}
|
} = {}
|
||||||
) {
|
) {
|
||||||
// Temporarily inject <use> elements so the clone includes relief icon data
|
|
||||||
if (typeof prepareReliefForSave === "function") prepareReliefForSave();
|
|
||||||
const cloneEl = byId("map").cloneNode(true); // clone svg
|
const cloneEl = byId("map").cloneNode(true); // clone svg
|
||||||
if (typeof restoreReliefAfterSave === "function") restoreReliefAfterSave();
|
|
||||||
cloneEl.id = "fantasyMap";
|
cloneEl.id = "fantasyMap";
|
||||||
document.body.appendChild(cloneEl);
|
document.body.appendChild(cloneEl);
|
||||||
const clone = d3.select(cloneEl);
|
const clone = d3.select(cloneEl);
|
||||||
|
|
@ -261,6 +258,19 @@ async function getMapURL(
|
||||||
cloneDefs.querySelector("#defs-emblems")?.remove();
|
cloneDefs.querySelector("#defs-emblems")?.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// render relief icons in svg and add used icons to defs
|
||||||
|
const terrainEl = cloneEl.getElementById("terrain");
|
||||||
|
if (terrainEl) drawRelief("svg", terrainEl);
|
||||||
|
|
||||||
|
const uniqueElements = new Set(pack.relief?.map(r => r.href) || []);
|
||||||
|
const defsRelief = svgDefs.getElementById("defs-relief");
|
||||||
|
for (const terrain of uniqueElements) {
|
||||||
|
const element = defsRelief.querySelector(terrain);
|
||||||
|
if (element) cloneDefs.appendChild(element.cloneNode(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// replace ocean pattern href to base64
|
// replace ocean pattern href to base64
|
||||||
const image = cloneEl.getElementById("oceanicPattern");
|
const image = cloneEl.getElementById("oceanicPattern");
|
||||||
|
|
@ -289,22 +299,6 @@ async function getMapURL(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add relief icons (from <use> elements – canvas <image> is excluded)
|
|
||||||
if (cloneEl.getElementById("terrain")) {
|
|
||||||
const uniqueElements = new Set();
|
|
||||||
const terrainUses = cloneEl.getElementById("terrain").querySelectorAll("use");
|
|
||||||
for (let i = 0; i < terrainUses.length; i++) {
|
|
||||||
const href = terrainUses[i].getAttribute("href") || terrainUses[i].getAttribute("xlink:href");
|
|
||||||
if (href && href.startsWith("#")) uniqueElements.add(href);
|
|
||||||
}
|
|
||||||
|
|
||||||
const defsRelief = svgDefs.getElementById("defs-relief");
|
|
||||||
for (const terrain of [...uniqueElements]) {
|
|
||||||
const element = defsRelief.querySelector(terrain);
|
|
||||||
if (element) cloneDefs.appendChild(element.cloneNode(true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add wind rose
|
// add wind rose
|
||||||
if (cloneEl.getElementById("compass")) {
|
if (cloneEl.getElementById("compass")) {
|
||||||
const rose = svgDefs.getElementById("defs-compass-rose");
|
const rose = svgDefs.getElementById("defs-compass-rose");
|
||||||
|
|
|
||||||
|
|
@ -440,12 +440,7 @@ async function parseLoadedData(data, mapVersion) {
|
||||||
if (hasChildren(coordinates)) turnOn("toggleCoordinates");
|
if (hasChildren(coordinates)) turnOn("toggleCoordinates");
|
||||||
if (isVisible(compass) && hasChild(compass, "use")) turnOn("toggleCompass");
|
if (isVisible(compass) && hasChild(compass, "use")) turnOn("toggleCompass");
|
||||||
if (hasChildren(rivers)) turnOn("toggleRivers");
|
if (hasChildren(rivers)) turnOn("toggleRivers");
|
||||||
if (isVisible(terrain) && hasChildren(terrain)) {
|
if (hasChildren(terrain)) turnOn("toggleRelief");
|
||||||
turnOn("toggleRelief");
|
|
||||||
}
|
|
||||||
// Migrate any legacy SVG <use> elements to canvas rendering
|
|
||||||
// (runs regardless of visibility to handle maps loaded with relief layer off)
|
|
||||||
if (typeof migrateReliefFromSvg === "function") migrateReliefFromSvg();
|
|
||||||
if (hasChildren(relig)) turnOn("toggleReligions");
|
if (hasChildren(relig)) turnOn("toggleReligions");
|
||||||
if (hasChildren(cults)) turnOn("toggleCultures");
|
if (hasChildren(cults)) turnOn("toggleCultures");
|
||||||
if (hasChildren(statesBody)) turnOn("toggleStates");
|
if (hasChildren(statesBody)) turnOn("toggleStates");
|
||||||
|
|
|
||||||
|
|
@ -77,11 +77,7 @@ function prepareMapData() {
|
||||||
const rulersString = rulers.toString();
|
const rulersString = rulers.toString();
|
||||||
const fonts = JSON.stringify(getUsedFonts(svg.node()));
|
const fonts = JSON.stringify(getUsedFonts(svg.node()));
|
||||||
|
|
||||||
// save svg
|
|
||||||
// Temporarily inject <use> elements so the SVG snapshot includes relief icon data
|
|
||||||
if (typeof prepareReliefForSave === "function") prepareReliefForSave();
|
|
||||||
const cloneEl = document.getElementById("map").cloneNode(true);
|
const cloneEl = document.getElementById("map").cloneNode(true);
|
||||||
if (typeof restoreReliefAfterSave === "function") restoreReliefAfterSave();
|
|
||||||
|
|
||||||
// reset transform values to default
|
// reset transform values to default
|
||||||
cloneEl.setAttribute("width", graphWidth);
|
cloneEl.setAttribute("width", graphWidth);
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
* For the changes that may be interesting to end users, update the `latestPublicChanges` array below (new changes on top).
|
* For the changes that may be interesting to end users, update the `latestPublicChanges` array below (new changes on top).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const VERSION = "1.113.5";
|
const VERSION = "1.114.0";
|
||||||
if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format or parsing function");
|
if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format or parsing function");
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -30,6 +30,7 @@ if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format o
|
||||||
}
|
}
|
||||||
|
|
||||||
const latestPublicChanges = [
|
const latestPublicChanges = [
|
||||||
|
"Relief icons: improved performance on huge maps",
|
||||||
"Search input in Overview dialogs",
|
"Search input in Overview dialogs",
|
||||||
"Custom burg grouping and icon selection",
|
"Custom burg grouping and icon selection",
|
||||||
"Ability to set custom image as Marker or Regiment icon",
|
"Ability to set custom image as Marker or Regiment icon",
|
||||||
|
|
@ -41,8 +42,7 @@ if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format o
|
||||||
"New style preset: Dark Seas",
|
"New style preset: Dark Seas",
|
||||||
"New routes generation algorithm",
|
"New routes generation algorithm",
|
||||||
"Routes overview tool",
|
"Routes overview tool",
|
||||||
"Configurable longitude",
|
"Configurable longitude"
|
||||||
"Export zones to GeoJSON"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function showUpdateWindow() {
|
function showUpdateWindow() {
|
||||||
|
|
|
||||||
|
|
@ -232,14 +232,10 @@ function renderFrame(): void {
|
||||||
renderer.render(scene, camera);
|
renderer.render(scene, camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawWebGl(icons: ReliefIcon[]): void {
|
function drawWebGl(icons: ReliefIcon[], parentEl: HTMLElement): void {
|
||||||
const terrainEl = byId("terrain");
|
parentEl.innerHTML = "";
|
||||||
if (!terrainEl) return;
|
parentEl.dataset.mode = "webGL";
|
||||||
if (!icons.length) return;
|
const set = parentEl.getAttribute("set") || "simple";
|
||||||
|
|
||||||
terrainEl.innerHTML = "";
|
|
||||||
terrainEl.dataset.mode = "webGL";
|
|
||||||
const set = terrainEl.getAttribute("set") || "simple";
|
|
||||||
|
|
||||||
if (ensureRenderer()) {
|
if (ensureRenderer()) {
|
||||||
loadTexture(set).then(() => {
|
loadTexture(set).then(() => {
|
||||||
|
|
@ -251,23 +247,26 @@ function drawWebGl(icons: ReliefIcon[]): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawSvg(icons: ReliefIcon[]): void {
|
function drawSvg(icons: ReliefIcon[], parentEl: HTMLElement): void {
|
||||||
const terrainEl = byId("terrain");
|
|
||||||
if (!terrainEl) return;
|
|
||||||
terrainEl.innerHTML = "";
|
|
||||||
|
|
||||||
const html = icons.map(
|
const html = icons.map(
|
||||||
(r) =>
|
(r) =>
|
||||||
`<use href="${r.href}" data-id="${r.i}" x="${r.x}" y="${r.y}" width="${r.s}" height="${r.s}"/>`,
|
`<use href="${r.href}" data-id="${r.i}" x="${r.x}" y="${r.y}" width="${r.s}" height="${r.s}"/>`,
|
||||||
);
|
);
|
||||||
terrainEl.innerHTML = html.join("");
|
parentEl.innerHTML = html.join("");
|
||||||
terrainEl.dataset.mode = "svg";
|
parentEl.dataset.mode = "svg";
|
||||||
}
|
}
|
||||||
|
|
||||||
window.drawRelief = (type: "svg" | "webGL" = "webGL") => {
|
window.drawRelief = (
|
||||||
|
type: "svg" | "webGL" = "webGL",
|
||||||
|
parentEl: HTMLElement | undefined = byId("terrain"),
|
||||||
|
) => {
|
||||||
|
if (!parentEl) throw new Error("Relief: parent element not found");
|
||||||
|
|
||||||
const icons = pack.relief?.length ? pack.relief : generateRelief();
|
const icons = pack.relief?.length ? pack.relief : generateRelief();
|
||||||
if (type === "svg") drawSvg(icons);
|
if (!icons.length) return;
|
||||||
else drawWebGl(icons);
|
|
||||||
|
if (type === "svg") drawSvg(icons, parentEl);
|
||||||
|
else drawWebGl(icons, parentEl);
|
||||||
};
|
};
|
||||||
|
|
||||||
window.undrawRelief = () => {
|
window.undrawRelief = () => {
|
||||||
|
|
@ -294,68 +293,8 @@ window.undrawRelief = () => {
|
||||||
// re-render the current WebGL frame (called on pan/zoom)
|
// re-render the current WebGL frame (called on pan/zoom)
|
||||||
window.rerenderReliefIcons = renderFrame;
|
window.rerenderReliefIcons = renderFrame;
|
||||||
|
|
||||||
// Migrate legacy saves: read <use> elements from the terrain SVG into pack.relief, remove them from the DOM, then render via WebGL.
|
|
||||||
window.migrateReliefFromSvg = () => {
|
|
||||||
const terrainEl = byId("terrain");
|
|
||||||
if (!terrainEl) return;
|
|
||||||
const relief: ReliefIcon[] = [];
|
|
||||||
|
|
||||||
terrainEl.querySelectorAll<SVGUseElement>("use").forEach((u) => {
|
|
||||||
const href = u.getAttribute("href") || u.getAttribute("xlink:href") || "";
|
|
||||||
if (!href) return;
|
|
||||||
relief.push({
|
|
||||||
i: relief.length,
|
|
||||||
href,
|
|
||||||
x: +u.getAttribute("x")!,
|
|
||||||
y: +u.getAttribute("y")!,
|
|
||||||
s: +u.getAttribute("width")!,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
terrainEl.innerHTML = "";
|
|
||||||
pack.relief = relief;
|
|
||||||
drawWebGl(relief);
|
|
||||||
};
|
|
||||||
|
|
||||||
let _reliefSvgInjectedForSave = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Before SVG serialization: ensure <use> elements are in the terrain group.
|
|
||||||
* In WebGL mode, temporarily injects them from pack.relief.
|
|
||||||
* In SVG edit mode, elements are already live in the DOM.
|
|
||||||
*/
|
|
||||||
window.prepareReliefForSave = () => {
|
|
||||||
const terrainEl = byId("terrain");
|
|
||||||
if (!terrainEl) return;
|
|
||||||
if (terrainEl.querySelectorAll("use").length > 0) {
|
|
||||||
_reliefSvgInjectedForSave = false;
|
|
||||||
} else {
|
|
||||||
terrainEl.insertAdjacentHTML(
|
|
||||||
"afterbegin",
|
|
||||||
(pack.relief || [])
|
|
||||||
.map(
|
|
||||||
(r) =>
|
|
||||||
`<use href="${r.href}" x="${r.x}" y="${r.y}" width="${r.s}" height="${r.s}"/>`,
|
|
||||||
)
|
|
||||||
.join(""),
|
|
||||||
);
|
|
||||||
_reliefSvgInjectedForSave = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Remove temporarily injected <use> elements after serialization. */
|
|
||||||
window.restoreReliefAfterSave = () => {
|
|
||||||
if (_reliefSvgInjectedForSave) {
|
|
||||||
for (const el of byId("terrain")?.querySelectorAll("use") ?? [])
|
|
||||||
el.remove();
|
|
||||||
_reliefSvgInjectedForSave = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
var drawRelief: (type?: "svg" | "webGL") => void;
|
var drawRelief: (type?: "svg" | "webGL") => void;
|
||||||
var undrawRelief: () => void;
|
var undrawRelief: () => void;
|
||||||
var rerenderReliefIcons: () => void;
|
var rerenderReliefIcons: () => void;
|
||||||
var migrateReliefFromSvg: () => void;
|
|
||||||
var prepareReliefForSave: () => void;
|
|
||||||
var restoreReliefAfterSave: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue