mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-22 03:51:23 +01:00
Merge branch 'master' into Units-Editor-DistanceUnit
This commit is contained in:
commit
d2bf08041c
6 changed files with 111 additions and 105 deletions
12
index.html
12
index.html
|
|
@ -6034,7 +6034,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="exportToPngTilesScreen" style="display: none" class="dialog">
|
<div id="exportToPngTilesScreen" style="display: none" class="dialog">
|
||||||
<p>Map will be split into tiles and downloaded as a single zip file. Avoid saving to big images</p>
|
<p>Map will be split into tiles and downloaded as a single zip file. Avoid saving too large images</p>
|
||||||
<div data-tip="Number of columns" style="margin-bottom: 0.3em">
|
<div data-tip="Number of columns" style="margin-bottom: 0.3em">
|
||||||
<div class="label">Columns:</div>
|
<div class="label">Columns:</div>
|
||||||
<input
|
<input
|
||||||
|
|
@ -6042,7 +6042,7 @@
|
||||||
data-stored="tileCols"
|
data-stored="tileCols"
|
||||||
type="range"
|
type="range"
|
||||||
min="2"
|
min="2"
|
||||||
max="20"
|
max="26"
|
||||||
value="8"
|
value="8"
|
||||||
style="width: 10em"
|
style="width: 10em"
|
||||||
/>
|
/>
|
||||||
|
|
@ -6055,7 +6055,7 @@
|
||||||
data-stored="tileRows"
|
data-stored="tileRows"
|
||||||
type="range"
|
type="range"
|
||||||
min="2"
|
min="2"
|
||||||
max="20"
|
max="26"
|
||||||
value="8"
|
value="8"
|
||||||
style="width: 10em"
|
style="width: 10em"
|
||||||
/>
|
/>
|
||||||
|
|
@ -6078,7 +6078,7 @@
|
||||||
<div class="label">Total size:</div>
|
<div class="label">Total size:</div>
|
||||||
<div id="tileSize" style="display: inline-block">1000 x 1000 px</div>
|
<div id="tileSize" style="display: inline-block">1000 x 1000 px</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="tileStatus" style="background-color: #33333310; font-style: italic"></div>
|
<div id="tileStatus" style="font-style: italic"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="resampleDialog" style="display: none" class="dialog">
|
<div id="resampleDialog" style="display: none" class="dialog">
|
||||||
|
|
@ -8095,9 +8095,9 @@
|
||||||
<script defer src="libs/rgbquant.min.js"></script>
|
<script defer src="libs/rgbquant.min.js"></script>
|
||||||
<script defer src="libs/jquery.ui.touch-punch.min.js"></script>
|
<script defer src="libs/jquery.ui.touch-punch.min.js"></script>
|
||||||
<script defer src="modules/io/save.js?v=1.96.00"></script>
|
<script defer src="modules/io/save.js?v=1.96.00"></script>
|
||||||
<script defer src="modules/io/load.js?v=1.97.02"></script>
|
<script defer src="modules/io/load.js?v=1.97.04"></script>
|
||||||
<script defer src="modules/io/cloud.js?v=1.96.00"></script>
|
<script defer src="modules/io/cloud.js?v=1.96.00"></script>
|
||||||
<script defer src="modules/io/export.js?v=1.96.00"></script>
|
<script defer src="modules/io/export.js?v=1.97.03"></script>
|
||||||
|
|
||||||
<!-- Web Components -->
|
<!-- Web Components -->
|
||||||
<script defer src="components/fill-box.js"></script>
|
<script defer src="components/fill-box.js"></script>
|
||||||
|
|
|
||||||
16
libs/jquery-ui.css
vendored
16
libs/jquery-ui.css
vendored
|
|
@ -313,8 +313,9 @@ body .ui-dialog {
|
||||||
background-color: inherit;
|
background-color: inherit;
|
||||||
}
|
}
|
||||||
.ui-dialog .ui-dialog-titlebar {
|
.ui-dialog .ui-dialog-titlebar {
|
||||||
padding: 0.4em 1em;
|
display: flex;
|
||||||
position: relative;
|
padding: 0.4em 0.3em;
|
||||||
|
justify-content: space-evenly;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
min-width: 150px;
|
min-width: 150px;
|
||||||
}
|
}
|
||||||
|
|
@ -328,9 +329,6 @@ body .ui-dialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-dialog .ui-dialog-titlebar button {
|
.ui-dialog .ui-dialog-titlebar button {
|
||||||
position: absolute;
|
|
||||||
right: 0.5em;
|
|
||||||
top: 53%;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: 1.8em;
|
width: 1.8em;
|
||||||
height: 1.8em;
|
height: 1.8em;
|
||||||
|
|
@ -340,14 +338,6 @@ body .ui-dialog {
|
||||||
border: 1px solid #c5c5c5;
|
border: 1px solid #c5c5c5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-dialog .ui-dialog-titlebar button.ui-dialog-titlebar-collapse {
|
|
||||||
margin: -1em 2.2em 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui-dialog .ui-dialog-titlebar button.ui-dialog-titlebar-close {
|
|
||||||
margin: -1em 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui-dialog .ui-dialog-titlebar button:active {
|
.ui-dialog .ui-dialog-titlebar button:active {
|
||||||
border: 1px solid #5d4651;
|
border: 1px solid #5d4651;
|
||||||
color: #5d4651;
|
color: #5d4651;
|
||||||
|
|
|
||||||
|
|
@ -743,7 +743,7 @@ export function resolveVersionConflicts(version) {
|
||||||
|
|
||||||
const opacity = terrs.attr("opacity");
|
const opacity = terrs.attr("opacity");
|
||||||
const filter = terrs.attr("filter");
|
const filter = terrs.attr("filter");
|
||||||
const scheme = terrs.attr("scheme");
|
const scheme = terrs.attr("scheme") || "bright";
|
||||||
const terracing = terrs.attr("terracing");
|
const terracing = terrs.attr("terracing");
|
||||||
const skip = terrs.attr("skip");
|
const skip = terrs.attr("skip");
|
||||||
const relax = terrs.attr("relax");
|
const relax = terrs.attr("relax");
|
||||||
|
|
|
||||||
|
|
@ -71,8 +71,9 @@ async function exportToJpeg() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function exportToPngTiles() {
|
async function exportToPngTiles() {
|
||||||
return new Promise(async (resolve, reject) => {
|
const status = byId("tileStatus");
|
||||||
// download schema
|
status.innerHTML = "Preparing files...";
|
||||||
|
|
||||||
const urlSchema = await getMapURL("tiles", {debug: true, fullMap: true});
|
const urlSchema = await getMapURL("tiles", {debug: true, fullMap: true});
|
||||||
await import("../../libs/jszip.min.js");
|
await import("../../libs/jszip.min.js");
|
||||||
const zip = new window.JSZip();
|
const zip = new window.JSZip();
|
||||||
|
|
@ -84,57 +85,80 @@ async function exportToPngTiles() {
|
||||||
|
|
||||||
const imgSchema = new Image();
|
const imgSchema = new Image();
|
||||||
imgSchema.src = urlSchema;
|
imgSchema.src = urlSchema;
|
||||||
imgSchema.onload = function () {
|
await loadImage(imgSchema);
|
||||||
|
|
||||||
|
status.innerHTML = "Drawing schema...";
|
||||||
ctx.drawImage(imgSchema, 0, 0, canvas.width, canvas.height);
|
ctx.drawImage(imgSchema, 0, 0, canvas.width, canvas.height);
|
||||||
canvas.toBlob(blob => zip.file(`fmg_tile_schema.png`, blob));
|
const blob = await canvasToBlob(canvas, "image/png");
|
||||||
};
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
zip.file("schema.png", blob);
|
||||||
|
|
||||||
// download tiles
|
// download tiles
|
||||||
const url = await getMapURL("tiles", {fullMap: true});
|
const url = await getMapURL("tiles", {fullMap: true});
|
||||||
const tilesX = +document.getElementById("tileColsInput").value;
|
const tilesX = +byId("tileColsInput").value;
|
||||||
const tilesY = +document.getElementById("tileRowsInput").value;
|
const tilesY = +byId("tileRowsInput").value;
|
||||||
const scale = +document.getElementById("tileScaleInput").value;
|
const scale = +byId("tileScaleInput").value;
|
||||||
|
const tolesTotal = tilesX * tilesY;
|
||||||
|
|
||||||
const tileW = (graphWidth / tilesX) | 0;
|
const tileW = (graphWidth / tilesX) | 0;
|
||||||
const tileH = (graphHeight / tilesY) | 0;
|
const tileH = (graphHeight / tilesY) | 0;
|
||||||
const tolesTotal = tilesX * tilesY;
|
|
||||||
|
|
||||||
const width = graphWidth * scale;
|
const width = graphWidth * scale;
|
||||||
const height = width * (tileH / tileW);
|
const height = width * (tileH / tileW);
|
||||||
canvas.width = width;
|
canvas.width = width;
|
||||||
canvas.height = height;
|
canvas.height = height;
|
||||||
|
|
||||||
let loaded = 0;
|
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.src = url;
|
img.src = url;
|
||||||
img.onload = function () {
|
await loadImage(img);
|
||||||
for (let y = 0, i = 0; y + tileH <= graphHeight; y += tileH) {
|
|
||||||
for (let x = 0; x + tileW <= graphWidth; x += tileW, i++) {
|
|
||||||
ctx.drawImage(img, x, y, tileW, tileH, 0, 0, width, height);
|
|
||||||
const name = `fmg_tile_${i}.png`;
|
|
||||||
canvas.toBlob(blob => {
|
|
||||||
zip.file(name, blob);
|
|
||||||
loaded += 1;
|
|
||||||
if (loaded === tolesTotal) return downloadZip();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function downloadZip() {
|
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
const name = `${getFileName()}.zip`;
|
for (let y = 0, row = 0, id = 1; y + tileH <= graphHeight; y += tileH, row++) {
|
||||||
|
const rowName = alphabet[row % alphabet.length];
|
||||||
|
|
||||||
|
for (let x = 0, cell = 1; x + tileW <= graphWidth; x += tileW, cell++, id++) {
|
||||||
|
status.innerHTML = `Drawing tile ${rowName}${cell} (${id} of ${tolesTotal})...`;
|
||||||
|
ctx.drawImage(img, x, y, tileW, tileH, 0, 0, width, height);
|
||||||
|
const blob = await canvasToBlob(canvas, "image/png");
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
zip.file(`${rowName}${cell}.png`, blob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status.innerHTML = "Zipping files...";
|
||||||
zip.generateAsync({type: "blob"}).then(blob => {
|
zip.generateAsync({type: "blob"}).then(blob => {
|
||||||
|
status.innerHTML = "Downloading the archive...";
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
link.href = URL.createObjectURL(blob);
|
link.href = URL.createObjectURL(blob);
|
||||||
link.download = name;
|
link.download = getFileName() + ".zip";
|
||||||
link.click();
|
link.click();
|
||||||
link.remove();
|
link.remove();
|
||||||
|
|
||||||
|
status.innerHTML = 'Done. Check .zip file in "Downloads" (crtl + J)';
|
||||||
setTimeout(() => URL.revokeObjectURL(link.href), 5000);
|
setTimeout(() => URL.revokeObjectURL(link.href), 5000);
|
||||||
resolve(true);
|
});
|
||||||
|
|
||||||
|
// promisified img.onload
|
||||||
|
function loadImage(img) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
img.onload = () => resolve();
|
||||||
|
img.onerror = err => reject(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// promisified canvas.toBlob
|
||||||
|
function canvasToBlob(canvas, mimeType, qualityArgument = 1) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
canvas.toBlob(
|
||||||
|
blob => {
|
||||||
|
if (blob) resolve(blob);
|
||||||
|
else reject(new Error("Canvas toBlob() error"));
|
||||||
|
},
|
||||||
|
mimeType,
|
||||||
|
qualityArgument
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse map svg to object url
|
// parse map svg to object url
|
||||||
|
|
@ -148,14 +172,14 @@ async function getMapURL(type, options) {
|
||||||
fullMap = false
|
fullMap = false
|
||||||
} = options || {};
|
} = options || {};
|
||||||
|
|
||||||
const cloneEl = document.getElementById("map").cloneNode(true); // clone svg
|
const cloneEl = byId("map").cloneNode(true); // clone svg
|
||||||
cloneEl.id = "fantasyMap";
|
cloneEl.id = "fantasyMap";
|
||||||
document.body.appendChild(cloneEl);
|
document.body.appendChild(cloneEl);
|
||||||
const clone = d3.select(cloneEl);
|
const clone = d3.select(cloneEl);
|
||||||
if (!debug) clone.select("#debug")?.remove();
|
if (!debug) clone.select("#debug")?.remove();
|
||||||
|
|
||||||
const cloneDefs = cloneEl.getElementsByTagName("defs")[0];
|
const cloneDefs = cloneEl.getElementsByTagName("defs")[0];
|
||||||
const svgDefs = document.getElementById("defElements");
|
const svgDefs = byId("defElements");
|
||||||
|
|
||||||
const isFirefox = navigator.userAgent.toLowerCase().indexOf("firefox") > -1;
|
const isFirefox = navigator.userAgent.toLowerCase().indexOf("firefox") > -1;
|
||||||
if (isFirefox && type === "mesh") clone.select("#oceanPattern")?.remove();
|
if (isFirefox && type === "mesh") clone.select("#oceanPattern")?.remove();
|
||||||
|
|
@ -218,7 +242,7 @@ async function getMapURL(type, options) {
|
||||||
.forEach(el => {
|
.forEach(el => {
|
||||||
const href = el.getAttribute("href") || el.getAttribute("xlink:href");
|
const href = el.getAttribute("href") || el.getAttribute("xlink:href");
|
||||||
if (!href) return;
|
if (!href) return;
|
||||||
const emblem = document.getElementById(href.slice(1));
|
const emblem = byId(href.slice(1));
|
||||||
if (emblem) cloneDefs.append(emblem.cloneNode(true));
|
if (emblem) cloneDefs.append(emblem.cloneNode(true));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -456,16 +456,16 @@ async function parseLoadedData(data, mapVersion) {
|
||||||
{
|
{
|
||||||
// dynamically import and run auto-update script
|
// dynamically import and run auto-update script
|
||||||
const versionNumber = parseFloat(params[0]);
|
const versionNumber = parseFloat(params[0]);
|
||||||
const {resolveVersionConflicts} = await import("../dynamic/auto-update.js?v=1.97.00");
|
const {resolveVersionConflicts} = await import("../dynamic/auto-update.js?v=1.97.04");
|
||||||
resolveVersionConflicts(versionNumber);
|
resolveVersionConflicts(versionNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add custom heightmap color scheme if any
|
// add custom heightmap color scheme if any
|
||||||
if (heightmapColorSchemes) {
|
if (heightmapColorSchemes) {
|
||||||
const oceanScheme = terrs.select("#oceanHeights").attr("scheme");
|
const oceanScheme = byId("oceanHeights")?.getAttribute("scheme");
|
||||||
const landScheme = terrs.select("#landHeights").attr("scheme");
|
if (oceanScheme && !(oceanScheme in heightmapColorSchemes)) addCustomColorScheme(oceanScheme);
|
||||||
if (!(oceanScheme in heightmapColorSchemes)) addCustomColorScheme(oceanScheme);
|
const landScheme = byId("#landHeights")?.getAttribute("scheme");
|
||||||
if (!(landScheme in heightmapColorSchemes)) addCustomColorScheme(landScheme);
|
if (landScheme && !(landScheme in heightmapColorSchemes)) addCustomColorScheme(landScheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -867,11 +867,9 @@ byId("mapToLoad").addEventListener("change", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
function openExportToPngTiles() {
|
function openExportToPngTiles() {
|
||||||
|
byId("tileStatus").innerHTML = "";
|
||||||
closeDialogs();
|
closeDialogs();
|
||||||
updateTilesOptions();
|
updateTilesOptions();
|
||||||
const status = byId("tileStatus");
|
|
||||||
status.innerHTML = "";
|
|
||||||
let loading = null;
|
|
||||||
|
|
||||||
const inputs = byId("exportToPngTilesScreen").querySelectorAll("input");
|
const inputs = byId("exportToPngTilesScreen").querySelectorAll("input");
|
||||||
inputs.forEach(input => input.addEventListener("input", updateTilesOptions));
|
inputs.forEach(input => input.addEventListener("input", updateTilesOptions));
|
||||||
|
|
@ -881,16 +879,7 @@ function openExportToPngTiles() {
|
||||||
title: "Download tiles",
|
title: "Download tiles",
|
||||||
width: "23em",
|
width: "23em",
|
||||||
buttons: {
|
buttons: {
|
||||||
Download: function () {
|
Download: () => exportToPngTiles(),
|
||||||
status.innerHTML = "Preparing for download...";
|
|
||||||
setTimeout(() => (status.innerHTML = "Downloading. It may take some time."), 1000);
|
|
||||||
loading = setInterval(() => (status.innerHTML += "."), 1000);
|
|
||||||
exportToPngTiles().then(() => {
|
|
||||||
clearInterval(loading);
|
|
||||||
status.innerHTML = /* html */ `Done. Check file in "Downloads" (crtl + J)`;
|
|
||||||
setTimeout(() => (status.innerHTML = ""), 8000);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
Cancel: function () {
|
Cancel: function () {
|
||||||
$(this).dialog("close");
|
$(this).dialog("close");
|
||||||
}
|
}
|
||||||
|
|
@ -898,7 +887,6 @@ function openExportToPngTiles() {
|
||||||
close: () => {
|
close: () => {
|
||||||
inputs.forEach(input => input.removeEventListener("input", updateTilesOptions));
|
inputs.forEach(input => input.removeEventListener("input", updateTilesOptions));
|
||||||
debug.selectAll("*").remove();
|
debug.selectAll("*").remove();
|
||||||
clearInterval(loading);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -928,18 +916,22 @@ function updateTilesOptions() {
|
||||||
const labels = [];
|
const labels = [];
|
||||||
const tileW = (graphWidth / tilesX) | 0;
|
const tileW = (graphWidth / tilesX) | 0;
|
||||||
const tileH = (graphHeight / tilesY) | 0;
|
const tileH = (graphHeight / tilesY) | 0;
|
||||||
for (let y = 0, i = 0; y + tileH <= graphHeight; y += tileH) {
|
|
||||||
for (let x = 0; x + tileW <= graphWidth; x += tileW, i++) {
|
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
for (let y = 0, row = 0; y + tileH <= graphHeight; y += tileH, row++) {
|
||||||
|
for (let x = 0, column = 1; x + tileW <= graphWidth; x += tileW, column++) {
|
||||||
rects.push(`<rect x=${x} y=${y} width=${tileW} height=${tileH} />`);
|
rects.push(`<rect x=${x} y=${y} width=${tileW} height=${tileH} />`);
|
||||||
labels.push(`<text x=${x + tileW / 2} y=${y + tileH / 2}>${i}</text>`);
|
const label = alphabet[row % alphabet.length] + column;
|
||||||
|
labels.push(`<text x=${x + tileW / 2} y=${y + tileH / 2}>${label}</text>`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const rectsG = "<g fill='none' stroke='#000'>" + rects.join("") + "</g>";
|
|
||||||
const labelsG =
|
debug.html(`
|
||||||
"<g fill='#000' stroke='none' text-anchor='middle' dominant-baseline='central' font-size='24px'>" +
|
<g fill='none' stroke='#000'>${rects.join("")}</g>
|
||||||
labels.join("") +
|
<g fill='#000' stroke='none' text-anchor='middle' dominant-baseline='central' font-size='18px'>${labels.join(
|
||||||
"</g>";
|
""
|
||||||
debug.html(rectsG + labelsG);
|
)}</g>
|
||||||
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// View mode
|
// View mode
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue