mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 17:51:24 +01:00
v1.5.04 - emblem gallery generator
This commit is contained in:
parent
3aaddcf1a7
commit
68dc822121
7 changed files with 174 additions and 42 deletions
11
index.css
11
index.css
|
|
@ -1581,9 +1581,18 @@ rect.fillRect {
|
||||||
width: 9em;
|
width: 9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#emblemsBottom {
|
#emblemsBottom {
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#emblemDownloadControl {
|
||||||
|
margin-top: .3em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#emblemDownloadControl > input {
|
||||||
|
width: 4.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#picker text {
|
#picker text {
|
||||||
|
|
|
||||||
11
index.html
11
index.html
|
|
@ -3343,6 +3343,7 @@
|
||||||
<div>
|
<div>
|
||||||
<b id="emblemArmiger"></b>
|
<b id="emblemArmiger"></b>
|
||||||
</div>
|
</div>
|
||||||
|
<hr/>
|
||||||
<div data-tip="Select state">
|
<div data-tip="Select state">
|
||||||
<div class="label">State:</div>
|
<div class="label">State:</div>
|
||||||
<select id="emblemStates"></select>
|
<select id="emblemStates"></select>
|
||||||
|
|
@ -3424,10 +3425,16 @@
|
||||||
<button id="emblemsRegenerate" data-tip="Regenerate emblem" class="icon-shuffle"></button>
|
<button id="emblemsRegenerate" data-tip="Regenerate emblem" class="icon-shuffle"></button>
|
||||||
<button id="emblemsArmoria" data-tip="Edit the emblem in Armoria: our dedicated rich heraldry editor" class="icon-brush"></button>
|
<button id="emblemsArmoria" data-tip="Edit the emblem in Armoria: our dedicated rich heraldry editor" class="icon-brush"></button>
|
||||||
<button id="emblemsUpload" data-tip="Upload png, jpg or svg image from Armoria or other sources as emblem" class="icon-upload"></button>
|
<button id="emblemsUpload" data-tip="Upload png, jpg or svg image from Armoria or other sources as emblem" class="icon-upload"></button>
|
||||||
<button id="emblemsDownload" data-tip="Download emblem as png image" class="icon-download"></button>
|
<button id="emblemsDownload" data-tip="Set size, select file format and download emblem image" class="icon-download"></button>
|
||||||
<button id="emblemsGallery" data-tip="Download emblems gallery as html table (open in browser)" class="icon-layer-group"></button>
|
<button id="emblemsGallery" data-tip="Download emblems gallery as html document (open in browser; downloading takes some time)" class="icon-layer-group"></button>
|
||||||
<button id="emblemsFocus" data-tip="Show emblem associated area or place" class="icon-target"></button>
|
<button id="emblemsFocus" data-tip="Show emblem associated area or place" class="icon-target"></button>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="emblemDownloadControl" class="hidden">
|
||||||
|
<input id="emblemsDownloadSize" data-tip="Set image size in pixels" type="number" value="500" step="100" min="100" max="10000"/>
|
||||||
|
<button id="emblemsDownloadSVG" data-tip="Download as SVG: scalable vector image. Best quality, can be opened in browser or Inkscape">SVG</button>
|
||||||
|
<button id="emblemsDownloadPNG" data-tip="Download as PNG: lossless raster image with transparent background">PNG</button>
|
||||||
|
<button id="emblemsDownloadJPG" data-tip="Download as JPG: lossy compressed raster image with solid white background">JPG</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="unitsEditor" class="dialog stable" style="display: none">
|
<div id="unitsEditor" class="dialog stable" style="display: none">
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,8 @@
|
||||||
// test in FF
|
// test in FF
|
||||||
// generate all?
|
// generate all?
|
||||||
// layout preset
|
// layout preset
|
||||||
|
// burg editor - add emblem
|
||||||
|
// other editors
|
||||||
|
|
||||||
const t1 = P(kinship) ? parent.t1 : getTincture("field");
|
const t1 = P(kinship) ? parent.t1 : getTincture("field");
|
||||||
const coa = {t1};
|
const coa = {t1};
|
||||||
|
|
|
||||||
|
|
@ -672,7 +672,7 @@
|
||||||
moriaOrc: "0 0 200 200"
|
moriaOrc: "0 0 200 200"
|
||||||
}
|
}
|
||||||
|
|
||||||
async function draw(id, coa) {
|
function draw(id, coa) {
|
||||||
const {division, ordinaries = [], charges = []} = coa;
|
const {division, ordinaries = [], charges = []} = coa;
|
||||||
const ordinariesRegular = ordinaries.filter(o => !o.above);
|
const ordinariesRegular = ordinaries.filter(o => !o.above);
|
||||||
const ordinariesAboveCharges = ordinaries.filter(o => o.above);
|
const ordinariesAboveCharges = ordinaries.filter(o => o.above);
|
||||||
|
|
@ -877,7 +877,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// async render coa if it does not exist
|
// async render coa if it does not exist
|
||||||
const trigger = async function(id, coa) {
|
const trigger = function(id, coa) {
|
||||||
if (!document.getElementById(id)) draw(id, coa);
|
if (!document.getElementById(id)) draw(id, coa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,11 @@ function editEmblem(type, id, el) {
|
||||||
document.getElementById("emblemShapeSelector").oninput = changeShape;
|
document.getElementById("emblemShapeSelector").oninput = changeShape;
|
||||||
document.getElementById("emblemsRegenerate").onclick = regenerate;
|
document.getElementById("emblemsRegenerate").onclick = regenerate;
|
||||||
document.getElementById("emblemsArmoria").onclick = openInArmoria;
|
document.getElementById("emblemsArmoria").onclick = openInArmoria;
|
||||||
document.getElementById("emblemsDownload").onclick = download;
|
document.getElementById("emblemsDownload").onclick = toggleDownload;
|
||||||
|
document.getElementById("emblemsDownloadSVG").onclick = () => download("svg");
|
||||||
|
document.getElementById("emblemsDownloadPNG").onclick = () => download("png");
|
||||||
|
document.getElementById("emblemsDownloadJPG").onclick = () => download("jpeg");
|
||||||
|
document.getElementById("emblemsGallery").onclick = downloadGallery;
|
||||||
document.getElementById("emblemsFocus").onclick = showArea;
|
document.getElementById("emblemsFocus").onclick = showArea;
|
||||||
|
|
||||||
function defineEmblemData(e) {
|
function defineEmblemData(e) {
|
||||||
|
|
@ -164,51 +168,161 @@ function editEmblem(type, id, el) {
|
||||||
openURL(url);
|
openURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
function download() {
|
function toggleDownload() {
|
||||||
const coa = document.getElementById(id);
|
const buttons = document.getElementById("emblemDownloadControl");
|
||||||
const canvas = document.createElement("canvas");
|
buttons.classList.toggle("hidden");
|
||||||
const ctx = canvas.getContext("2d");
|
|
||||||
canvas.width = 500;
|
|
||||||
canvas.height = 500;
|
|
||||||
|
|
||||||
const url = getURL(coa, el.coa);
|
|
||||||
const img = new Image();
|
|
||||||
img.src = url;
|
|
||||||
img.onload = () => {
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
||||||
drawCanvas(canvas, el);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawCanvas(canvas, el) {
|
function download(format) {
|
||||||
|
const coa = document.getElementById(id);
|
||||||
|
const size = +emblemsDownloadSize.value;
|
||||||
|
const url = getURL(coa, el.coa, size);
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
link.download = getFileName(`Emblem ${el.fullName || el.name}`) + ".png";
|
link.download = getFileName(`Emblem ${el.fullName || el.name}`) + "." + format;
|
||||||
canvas.toBlob(function (blob) {
|
|
||||||
link.href = window.URL.createObjectURL(blob);
|
if (format === "svg") downloadSVG(url, link); else downloadRaster(format, url, link, size);
|
||||||
|
document.getElementById("emblemDownloadControl").classList.add("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadSVG(url, link) {
|
||||||
|
link.href = url;
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
link.click();
|
link.click();
|
||||||
setTimeout(function () {
|
window.setTimeout(() => window.URL.revokeObjectURL(URL), 5000);
|
||||||
canvas.remove();
|
}
|
||||||
window.URL.revokeObjectURL(link.href);
|
|
||||||
}, 5000);
|
function downloadRaster(format, url, link, size) {
|
||||||
});
|
const canvas = document.createElement("canvas");
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
canvas.width = size;
|
||||||
|
canvas.height = size;
|
||||||
|
|
||||||
|
const img = new Image();
|
||||||
|
img.src = url;
|
||||||
|
img.onload = async function() {
|
||||||
|
if (format === "jpeg") {
|
||||||
|
ctx.fillStyle = "#fff";
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
}
|
||||||
|
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||||
|
const URL = await canvas.toDataURL("image/" + format, .92);
|
||||||
|
link.href = URL;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
window.setTimeout(() => window.URL.revokeObjectURL(URL), 5000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getURL(svg, coa) {
|
function getURL(svg, coa, size) {
|
||||||
|
const serialized = getSVG(svg, coa, size);
|
||||||
|
const blob = new Blob([serialized], { type: 'image/svg+xml;charset=utf-8' });
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSVG(svg, coa, size) {
|
||||||
const clone = svg.cloneNode(true); // clone svg
|
const clone = svg.cloneNode(true); // clone svg
|
||||||
const d = clone.getElementsByTagName("defs")[0];
|
const d = clone.getElementsByTagName("defs")[0];
|
||||||
|
|
||||||
|
clone.removeAttribute("id");
|
||||||
|
clone.setAttribute("width", size);
|
||||||
|
clone.setAttribute("height", size);
|
||||||
|
|
||||||
d.insertAdjacentHTML("beforeend", document.getElementById(coa.shield).outerHTML); // copy shield to defs
|
d.insertAdjacentHTML("beforeend", document.getElementById(coa.shield).outerHTML); // copy shield to defs
|
||||||
svg.querySelectorAll("[fill^=url]").forEach(el => {
|
clone.querySelectorAll("[fill^=url]").forEach(el => {
|
||||||
const id = el.getAttribute("fill").match(/\#([^)]+)\)/)[1];
|
const id = el.getAttribute("fill").match(/\#([^)]+)\)/)[1];
|
||||||
d.insertAdjacentHTML("beforeend", document.getElementById(id).outerHTML);
|
d.insertAdjacentHTML("beforeend", document.getElementById(id).outerHTML);
|
||||||
});
|
});
|
||||||
|
|
||||||
const serialized = (new XMLSerializer()).serializeToString(clone);
|
return (new XMLSerializer()).serializeToString(clone);
|
||||||
const blob = new Blob([serialized], { type: 'image/svg+xml;charset=utf-8' });
|
}
|
||||||
const url = window.URL.createObjectURL(blob);
|
|
||||||
return url;
|
function downloadGallery() {
|
||||||
|
const name = getFileName("Emblems Gallery");
|
||||||
|
const validStates = states.filter(s => s.i && !s.removed && s.coa);
|
||||||
|
const validProvinces = provinces.filter(p => p.i && !p.removed && p.coa);
|
||||||
|
const validBurgs = burgs.filter(b => b.i && !b.removed && b.coa);
|
||||||
|
triggerCOALoad(validStates, validProvinces, validBurgs);
|
||||||
|
const timeout = (validStates.length + validProvinces.length + validBurgs.length) * 8;
|
||||||
|
tip("Preparing to download...", true, "warn", timeout);
|
||||||
|
d3.timeout(runDownload, timeout);
|
||||||
|
|
||||||
|
function runDownload() {
|
||||||
|
const stateSection = `<div><h2>States</h2>` + validStates.map(state => {
|
||||||
|
const el = document.getElementById("stateCOA"+state.i);
|
||||||
|
const svg = getSVG(el, state.coa, 200);
|
||||||
|
return `<figure id="state_${state.i}"><a href="#provinces_${state.i}"><figcaption>${state.fullName}</figcaption>${svg}</a></figure>`;
|
||||||
|
}).join("") + `</div>`;
|
||||||
|
|
||||||
|
const provinceSections = validStates.map(state => {
|
||||||
|
const figures = validProvinces.filter(p => p.state === state.i).map(province => {
|
||||||
|
const el = document.getElementById("provinceCOA"+province.i);
|
||||||
|
const svg = getSVG(el, province.coa, 200);
|
||||||
|
return `<figure id="province_${province.i}"><a href="#burgs_${province.i}"><figcaption>${province.fullName}</figcaption>${svg}</a></figure>`;
|
||||||
|
}).join("");
|
||||||
|
return `<div id="provinces_${state.i}"><h2>${state.fullName} provinces</h2>${figures}</div>`;
|
||||||
|
}).join("");
|
||||||
|
|
||||||
|
const burgSections = validStates.map(state => {
|
||||||
|
const stateBurgs = validBurgs.filter(b => b.state === state.i);
|
||||||
|
let stateBurgSections = validProvinces.filter(p => p.state === state.i).map(province => {
|
||||||
|
const provinceBurgs = stateBurgs.filter(b => cells.province[b.cell] === province.i);
|
||||||
|
if (!provinceBurgs.length) return "";
|
||||||
|
const provinceBurgFigures = provinceBurgs.map(burg => {
|
||||||
|
const el = document.getElementById("burgCOA"+burg.i);
|
||||||
|
const svg = getSVG(el, burg.coa, 200);
|
||||||
|
return `<figure id="burg_${burg.i}"><figcaption>${burg.name}</figcaption>${svg}</figure>`;
|
||||||
|
}).join("");
|
||||||
|
return `<div id="burgs_${province.i}"><h2>${province.fullName} burgs</h2>${provinceBurgFigures}</div>`;
|
||||||
|
}).join("");
|
||||||
|
|
||||||
|
const stateBurgOutOfProvinces = stateBurgs.filter(b => !cells.province[b.cell]);
|
||||||
|
const stateBurgOutOfProvincesFigures = stateBurgOutOfProvinces.map(burg => {
|
||||||
|
const el = document.getElementById("burgCOA"+burg.i);
|
||||||
|
const svg = getSVG(el, burg.coa, 200);
|
||||||
|
return `<figure id="burg_${burg.i}"><figcaption>${burg.name}</figcaption>${svg}</figure>`;
|
||||||
|
}).join("");
|
||||||
|
if (stateBurgOutOfProvincesFigures) stateBurgSections += `<div><h2>${state.fullName} burgs under direct control</h2>${stateBurgOutOfProvincesFigures}</div>`;
|
||||||
|
return stateBurgSections;
|
||||||
|
}).join("");
|
||||||
|
|
||||||
|
const neutralBurgs = validBurgs.filter(b => !b.state);
|
||||||
|
const neutralsSection = neutralBurgs.length ? "<div><h2>Independent burgs</h2>" + neutralBurgs.map(burg => {
|
||||||
|
const el = document.getElementById("burgCOA"+burg.i);
|
||||||
|
const svg = getSVG(el, burg.coa, 200);
|
||||||
|
return `<figure id="burg_${burg.i}"><figcaption>${burg.name}</figcaption>${svg}</figure>`;
|
||||||
|
}).join("") + "</div>" : "";
|
||||||
|
|
||||||
|
const FMG = `<a href="https://azgaar.github.io/Fantasy-Map-Generator" target="_blank">Azgaar's Fantasy Map Generator</a>`;
|
||||||
|
const license = `<a target="_blank" href="https://github.com/Azgaar/Armoria#license">the license</a>`;
|
||||||
|
const html = `<!DOCTYPE html><html><head><title>${mapName.value} Emblems Gallery</title></head>
|
||||||
|
<style type="text/css">
|
||||||
|
body { margin: 0; padding: 1em; font-family: serif; }
|
||||||
|
h1, h2 { font-family: 'Forum'; }
|
||||||
|
div { width: 100%; max-width: 1018px; margin: 0 auto; border-bottom: 1px solid #ddd; }
|
||||||
|
figure { margin: 0 0 2em; display: inline-block; transition: .2s; }
|
||||||
|
figure:hover { background-color: #f6f6f6; }
|
||||||
|
figcaption { text-align: center; margin: .4em 0; width: 200px; font-family: 'Overlock SC' }
|
||||||
|
figure > a { color: black; text-decoration: none; }
|
||||||
|
address { width: 100%; max-width: 1018px; margin: 0 auto; }
|
||||||
|
</style>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Forum&family=Overlock+SC" rel="stylesheet">
|
||||||
|
<body>
|
||||||
|
<div><h1>${mapName.value} Emblems Gallery</h1></div>
|
||||||
|
${stateSection}
|
||||||
|
${provinceSections}
|
||||||
|
${burgSections}
|
||||||
|
${neutralsSection}
|
||||||
|
<address>Generated by ${FMG}. The tool is free, but images may be copyrighted, see ${license}</address>
|
||||||
|
</body></html>`;
|
||||||
|
downloadFile(html, name + ".html", "text/plain");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function triggerCOALoad(states, provinces, burgs) {
|
||||||
|
states.forEach(state => COArenderer.trigger("stateCOA"+state.i, state.coa));
|
||||||
|
provinces.forEach(province => COArenderer.trigger("provinceCOA"+province.i, province.coa));
|
||||||
|
burgs.forEach(burg => COArenderer.trigger("burgCOA"+burg.i, burg.coa));
|
||||||
}
|
}
|
||||||
|
|
||||||
function dragEmblem() {
|
function dragEmblem() {
|
||||||
|
|
|
||||||
|
|
@ -330,7 +330,7 @@ document.querySelectorAll("[data-locked]").forEach(function(e) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
});
|
});
|
||||||
|
|
||||||
e.addEventListener("click", function(event) {
|
e.addEventListener("click", function() {
|
||||||
const id = (this.id).slice(5);
|
const id = (this.id).slice(5);
|
||||||
if (this.className === "icon-lock") unlock(id);
|
if (this.className === "icon-lock") unlock(id);
|
||||||
else lock(id);
|
else lock(id);
|
||||||
|
|
|
||||||
|
|
@ -87,8 +87,8 @@ function showSupporters() {
|
||||||
Jeppe Skov Jensen,María Martín López,Martin Seeger,Annie Rishor,Aram Sabatés,MadNomadMedia,Eric Foley,Vito Martono,James H. Anthony,Kevin Cossutta,
|
Jeppe Skov Jensen,María Martín López,Martin Seeger,Annie Rishor,Aram Sabatés,MadNomadMedia,Eric Foley,Vito Martono,James H. Anthony,Kevin Cossutta,
|
||||||
Thirty-OneR ,ThatGuyGW ,Dee Chiu,MontyBoosh ,Achillain ,Jaden ,SashaTK,Steve Johnson,Eric Foley,Vito Martono,James H. Anthony,Kevin Cossutta,Thirty-OneR,
|
Thirty-OneR ,ThatGuyGW ,Dee Chiu,MontyBoosh ,Achillain ,Jaden ,SashaTK,Steve Johnson,Eric Foley,Vito Martono,James H. Anthony,Kevin Cossutta,Thirty-OneR,
|
||||||
ThatGuyGW,Dee Chiu,MontyBoosh,Achillain,Jaden,SashaTK,Steve Johnson,Pierrick Bertrand,Jared Kennedy,Dylan Devenny,Kyle Robertson,Andrew Rostaing,Daniel Gill,
|
ThatGuyGW,Dee Chiu,MontyBoosh,Achillain,Jaden,SashaTK,Steve Johnson,Pierrick Bertrand,Jared Kennedy,Dylan Devenny,Kyle Robertson,Andrew Rostaing,Daniel Gill,
|
||||||
Char, Jack, Barna Csíkos, Ian Rousseau, Nicholas Grabstas, Tom Van Orden jr, Bryan Brake, Akylos, Riley Seaman,
|
Char,Jack,Barna Csíkos,Ian Rousseau,Nicholas Grabstas,Tom Van Orden jr,Bryan Brake,Akylos,Riley Seaman,MaxOliver,Evan-DiLeo,Alex Debus,Joshua Vaught,
|
||||||
MaxOliver, Evan-DiLeo, Alex Debus, Joshua Vaught, Kyle S, Eric Moore, Dean Dunakin, Uniquenameosaurus, WarWizardGames`;
|
Kyle S,Eric Moore,Dean Dunakin,Uniquenameosaurus,WarWizardGames,Chance Mena,Jan Ka`;
|
||||||
|
|
||||||
const array = supporters.replace(/(?:\r\n|\r|\n)/g, "").split(",").map(v => capitalize(v.trim())).sort();
|
const array = supporters.replace(/(?:\r\n|\r|\n)/g, "").split(",").map(v => capitalize(v.trim())).sort();
|
||||||
alertMessage.innerHTML = "<ul style='column-count: 5; column-gap: 2em'>" + array.map(n => `<li>${n}</li>`).join("") + "</ul>";
|
alertMessage.innerHTML = "<ul style='column-count: 5; column-gap: 2em'>" + array.map(n => `<li>${n}</li>`).join("") + "</ul>";
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue