-
Please select saving variant:
+
Please select saving method:
-
+
-
+
-
Generator uses pop-up window to download files. Please ensure your browser does not block popups
+
Keep noted that the only reliable project saving method is having .map file stored on your machine. There is no way to restore map if .map file is lost. We don't keep any data on our side.
+
Generator uses pop-up window to download files. Please ensure your browser does not block popups.
PNG / JPEG scale:
@@ -3700,6 +3715,569 @@
+
+
@@ -3759,6 +4337,8 @@
+
+
diff --git a/modules/coa-generator.js b/modules/coa-generator.js
index 9ede182a..869ad986 100644
--- a/modules/coa-generator.js
+++ b/modules/coa-generator.js
@@ -158,14 +158,14 @@
head: {e: 1},
headWreathed: {e: 1}
};
-
+
const lines = {
straight: 50, wavy: 8, engrailed: 4, invecked: 3, rayonne: 3, embattled: 1, raguly: 1, urdy: 1, dancetty: 1, indented: 2,
dentilly: 1, bevilled: 1, angled: 1, flechy: 1, barby: 1, enclavy: 1, escartely: 1, arched: 2, archedReversed: 1, nowy: 1, nowyReversed: 1,
embattledGhibellin: 1, embattledNotched: 1, embattledGrady: 1, dovetailedIndented: 1, dovetailed: 1,
potenty: 1, potentyDexter: 1, potentySinister: 1, nebuly: 2, seaWaves: 1, dragonTeeth: 1, firTrees: 1
};
-
+
const divisions = {
variants: { perPale: 5, perFess: 5, perBend: 2, perBendSinister: 1, perChevron: 1, perChevronReversed: 1, perCross: 5, perPile: 1, perSaltire: 1, gyronny: 1, chevronny: 1 },
perPale: lines,
@@ -177,7 +177,7 @@
perCross: { straight: 20, wavy: 5, engrailed: 4, invecked: 3, rayonne: 1, embattled: 1, raguly: 1, urdy: 1, indented: 2, dentilly: 1, bevilled: 1, angled: 1, embattledGhibellin: 1, embattledGrady: 1, dovetailedIndented: 1, dovetailed: 1, potenty: 1, potentyDexter: 1, potentySinister: 1, nebuly: 1 },
perPile: lines
};
-
+
const ordinaries = {
lined: {
pale: 7, fess: 5, bend: 3, bendSinister: 2, chief: 5, bar: 2, gemelle: 1, fessCotissed: 1, fessDoubleCotissed: 1,
@@ -194,6 +194,14 @@
TIME && console.time("generateCOA");
let usedPattern = null, usedTinctures = [];
+ // TODO
+ // seafaring
+ // stringify coa on save and load
+ // regenerateAll
+ // generate on new item creation
+ // shields for cultures
+ // old versions auti migration
+
const t1 = parent && P(.25) ? parent.t1 : getTincture("field");
const coa = {t1};
@@ -285,7 +293,7 @@
division === "perBend" ? ["l", "m"] :
["j", "o"]; // perBendSinister
coa.charges[0].p = p1;
-
+
const charge = selectCharge(charges.single);
const t = getTincture("charge", usedTinctures, coa.division.t);
coa.charges.push({charge, t, p: p2});
@@ -293,28 +301,28 @@
else if (["perCross", "perSaltire"].includes(division) && P(.5)) { // place 4 charges in division standard positions
const [p1, p2, p3, p4] = division === "perCross" ? ["j", "l", "m", "o"] : ["b", "d", "f", "h"];
coa.charges[0].p = p1;
-
+
const c2 = selectCharge(charges.single);
const t2 = getTincture("charge", [], coa.division.t);
-
+
const c3 = selectCharge(charges.single);
const t3 = getTincture("charge", [], coa.division.t);
-
+
const c4 = selectCharge(charges.single);
const t4 = getTincture("charge", [], coa.t1);
coa.charges.push({charge: c2, t: t2, p: p2}, {charge: c3, t: t3, p: p3}, {charge: c4, t: t4, p: p4});
}
else if (allowCounter && p.length > 1) coa.charges[0].divided = "counter"; // counterchanged, 40%
}
-
+
coa.charges.forEach(c => defineChargeAttributes(c));
function defineChargeAttributes(c) {
// define size
c.size = (c.size || 1) * getSize(c.p, ordinary, division);
-
+
// clean-up position
c.p = [...new Set(c.p)].join("");
-
+
// define orientation
if (P(.02) && charges.sinister.includes(c.charge)) c.sinister = 1;
if (P(.02) && charges.reversed.includes(c.charge)) c.reversed = 1;
@@ -329,21 +337,21 @@
// select tincture: element type (field, division, charge), used field tinctures, field type to follow RoT
function getTincture(element, fields = [], RoT) {
const base = RoT ? RoT.includes("-") ? RoT.split("-")[1] : RoT : null;
-
+
let type = rw(tinctures[element]); // metals, colours, stains, patterns
if (RoT && type !== "patterns") type = getType(base) === "metals" ? "colours" : "metals"; // follow RoT
if (type === "metals" && fields.includes("or") && fields.includes("argent")) type = "colours"; // exclude metals overuse
let tincture = rw(tinctures[type]);
-
+
while (tincture === base || fields.includes(tincture)) {tincture = rw(tinctures[type]);} // follow RoT
-
+
if (type !== "patterns" && element !== "charge") usedTinctures.push(tincture); // add field tincture
-
+
if (type === "patterns") {
usedPattern = tincture;
tincture = definePattern(tincture, element);
}
-
+
return tincture;
}
@@ -361,7 +369,7 @@
else if (P(.05)) size = "-smaller";
else if (P(.035)) size = "-big";
else if (P(.001)) size = "-smallest";
-
+
// apply standard tinctures
if (P(.5) && ["vair", "vairInPale", "vairEnPointe"].includes(pattern)) {t1 = "azure"; t2 = "argent";}
else if (P(.8) && pattern === "ermine") {t1 = "argent"; t2 = "sable";}
@@ -381,24 +389,24 @@
else if (P(.15)) {t1 = "gules"; t2 = "argent";}
}
else if (pattern === "semy") pattern += "_of_" + selectCharge(charges.semy);
-
-
+
+
if (!t1 || !t2) {
const startWithMetal = P(.7);
t1 = startWithMetal ? rw(tinctures.metals) : rw(tinctures.colours);
t2 = startWithMetal ? rw(tinctures.colours) : rw(tinctures.metals);
}
-
+
// division should not be the same tincture as base field
if (element === "division") {
if (usedTinctures.includes(t1)) t1 = replaceTincture(t1);
if (usedTinctures.includes(t2)) t2 = replaceTincture(t2);
}
-
+
usedTinctures.push(t1, t2);
return `${pattern}-${t1}-${t2}${size}`;
}
-
+
function replaceTincture(t, n) {
const type = getType(t);
while (!n || n === t) {n = rw(tinctures[type]);}
diff --git a/modules/coa-renderer.js b/modules/coa-renderer.js
new file mode 100644
index 00000000..db4db629
--- /dev/null
+++ b/modules/coa-renderer.js
@@ -0,0 +1,887 @@
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ (global.COArenderer = factory());
+}(this, (function () {'use strict';
+ const colors = {
+ argent: "#fafafa",
+ or: "#ffe066",
+ gules: "#d7374a",
+ sable: "#333333",
+ azure: "#377cd7",
+ vert: "#26c061",
+ purpure: "#522d5b",
+ murrey: "#85185b",
+ sanguine: "#b63a3a",
+ tenné: "#cc7f19"
+ }
+
+ const shieldPositions = {
+ // shield-specific position: [x, y] (relative to center)
+ heater: {
+ a: [-43.75, -50], b: [0, -50], c: [43.75, -50],
+ d: [-43.75, 0], e: [0, 0], f: [43.75, 0],
+ g: [-32.25, 37.5], h: [0, 50], i: [32.25, 37.5],
+ y: [-50, -50], z: [0, 62.5],
+ j: [-37.5, -37.5], k: [0, -37.5], l: [37.5, -37.5],
+ m: [-30, 30], n: [0, 42.5], o: [30, 30],
+ p: [-37.5, 0], q: [37.5, 0],
+ A: [-66.2, -66.6], B: [-22, -66.6], C: [22, -66.6], D: [66.2, -66.6],
+ K: [-66.2, -20], E: [66.2, -20],
+ J: [-55.5, 26], F: [55.5, 26],
+ I: [-33, 62], G: [33, 62],
+ H: [0, 89.5]
+ },
+ spanish: {
+ a: [-43.75, -50], b: [0, -50], c: [43.75, -50],
+ d: [-43.75, 0], e: [0, 0], f: [43.75, 0],
+ g: [-43.75, 50], h: [0, 50], i: [43.75, 50],
+ y: [-50, -50], z: [0, 50],
+ j: [-37.5, -37.5], k: [0, -37.5], l: [37.5, -37.5],
+ m: [-37.5, 37.5], n: [0, 37.5], o: [37.5, 37.5],
+ p: [-37.5, 0], q: [37.5, 0],
+ A: [-66.2, -66.6], B: [-22, -66.6], C: [22, -66.6], D: [66.2, -66.6],
+ K: [-66.4, -20], E: [66.4, -20],
+ J: [-66.4, 26], F: [66.4, 26],
+ I: [-49, 70], G: [49, 70],
+ H: [0, 92]
+ },
+ french: {
+ a: [-43.75, -50], b: [0, -50], c: [43.75, -50],
+ d: [-43.75, 0], e: [0, 0], f: [43.75, 0],
+ g: [-43.75, 50], h: [0, 50], i: [43.75, 50],
+ y: [-50, -50], z: [0, 65],
+ j: [-37.5, -37.5], k: [0, -37.5], l: [37.5, -37.5],
+ m: [-37.5, 37.5], n: [0, 37.5], o: [37.5, 37.5],
+ p: [-37.5, 0], q: [37.5, 0],
+ A: [-66.2, -66.6], B: [-22, -66.6], C: [22, -66.6], D: [66.2, -66.6],
+ K: [-66.4, -20], E: [66.4, -20],
+ J: [-66.4, 26], F: [66.4, 26],
+ I: [-65.4, 70], G: [65.4, 70],
+ H: [0, 89]
+ },
+ horsehead: {
+ a: [-43.75, -47.5], b: [0, -50], c: [43.75, -47.5],
+ d: [-35, 0], e: [0, 0], f: [35, 0],
+ h: [0, 50],
+ y: [-50, -50], z: [0, 55],
+ j: [-35, -35], k: [0, -40], l: [35, -35],
+ m: [-30, 30], n: [0, 40], o: [30, 30],
+ p: [-27.5, 0], q: [27.5, 0],
+ A: [-71, -52], B: [-24, -73], C: [24, -73], D: [71, -52],
+ K: [-62, -16], E: [62, -16],
+ J: [-39, 20], F: [39, 20],
+ I: [-33.5, 60], G: [33.5, 60],
+ H: [0, 91.5]
+ },
+ horsehead2: {
+ a: [-37.5, -47.5], b: [0, -50], c: [37.5, -47.5],
+ d: [-35, 0], e: [0, 0], f: [35, 0],
+ g: [-35, -47.5], h: [0, 50], i: [35, -47.5],
+ y: [-50, -50], z: [0, 55],
+ j: [-30, -30], k: [0, -40], l: [30, -30],
+ m: [-30, 30], n: [0, 40], o: [30, 30],
+ p: [-27.5, 0], q: [27.5, 0],
+ A: [-49, -39], B: [-22, -70], C: [22, -70], D: [49, -39],
+ K: [-51, -2], E: [51, -2],
+ J: [-38.5, 31], F: [38.5, 31],
+ I: [-35, 67], G: [35, 67],
+ H: [0, 85]
+ },
+ polish: {
+ a: [-35, -50], b: [0, -50], c: [35, -50],
+ d: [-40, 0], e: [0, 0], f: [40, 0],
+ g: [-37.5, 50], h: [0, 50], i: [37.5, 50],
+ y: [-50, -50], z: [0, 65],
+ j: [-27.5, -27.5], k: [0, -45], l: [27.5, -27.5],
+ m: [-27.5, 27.5], n: [0, 45], o: [27.5, 27.5],
+ p: [-32.5, 0], q: [32.5, 0],
+ A: [-48, -52], B: [-23, -80], C: [23, -80], D: [48, -52],
+ K: [-47, -10], E: [47, -10],
+ J: [-62, 32], F: [62, 32],
+ I: [-37, 68], G: [37, 68],
+ H: [0, 86]
+ },
+ hessen: {
+ a: [-43.75, -50], b: [0, -50], c: [43.75, -50],
+ d: [-43.75, 0], e: [0, 0], f: [43.75, 0],
+ g: [-43.75, 50], h: [0, 50], i: [43.75, 50],
+ y: [-50, -50], z: [0, 52.5],
+ j: [-40, -40], k: [0, -40], l: [40, -40],
+ m: [-40, 40], n: [0, 40], o: [40, 40],
+ p: [-40, 0], q: [40, 0],
+ A: [-69, -64], B: [-22, -76], C: [22, -76], D: [69, -64],
+ K: [-66.4, -20], E: [66.4, -20],
+ J: [-62, 26], F: [62, 26],
+ I: [-46, 70], G: [46, 70],
+ H: [0, 91.5]
+ },
+ swiss: {
+ a: [-43.75, -50], b: [0, -50], c: [43.75, -50],
+ d: [-43.75, 0], e: [0, 0], f: [43.75, 0],
+ g: [-32, 37.5], h: [0, 50], i: [32, 37.5],
+ y: [-50, -50], z: [0, 62.5],
+ j: [-37.5, -37.5], k: [0, -37.5], l: [37.5, -37.5],
+ m: [-32, 32.5], n: [0, 42.5], o: [32, 32.5],
+ p: [-37.5, 0], q: [37.5, 0],
+ A: [-66.2, -66.6], B: [-22, -66], C: [22, -66], D: [66.2, -66.6],
+ K: [-63, -20], E: [63, -20],
+ J: [-50, 26], F: [50, 26],
+ I: [-29, 62], G: [29, 62],
+ H: [0, 89.5]
+ },
+ boeotian: {
+ a: [-37.5, -47.5], b: [0, -47.5], c: [37.5, -47.5],
+ d: [-25, 0], e: [0, 0], f: [25, 0],
+ g: [-37.5, 47.5], h: [0, 47.5], i: [37.5, 47.5],
+ y: [-48, -48], z: [0, 60],
+ j: [-32.5, -37.5], k: [0, -45], l: [32.5, -37.5],
+ m: [-32.5, 37.5], n: [0, 45], o: [32.5, 37.5],
+ p: [-20, 0], q: [20, 0],
+ A: [-45, -55], B: [-20, -77], C: [20, -77], D: [45, -55],
+ K: [-59, -25], E: [59, -25],
+ J: [-58, 27], F: [58, 27],
+ I: [-39, 63], G: [39, 63],
+ H: [0, 81]
+ },
+ roman: {
+ a: [-40, -52.5], b: [0, -52.5], c: [40, -52.5],
+ d: [-40, 0], e: [0, 0], f: [40, 0],
+ g: [-40, 52.5], h: [0, 52.5], i: [40, 52.5],
+ y: [-42.5, -52.5], z: [0, 65],
+ j: [-30, -37.5], k: [0, -37.5], l: [30, -37.5],
+ m: [-30, 37.5], n: [0, 37.5], o: [30, 37.5],
+ p: [-30, 0], q: [30, 0],
+ A: [-51.5, -65], B: [-17, -75], C: [17, -75], D: [51.5, -65],
+ K: [-51.5, -21], E: [51.5, -21],
+ J: [-51.5, 21], F: [51.5, 21],
+ I: [-51.5, 65], G: [51.5, 65],
+ H: [-17, 75], L: [17, 75]
+ },
+ kite: {
+ b: [0, -65], e: [0, -15], h: [0, 35],
+ z: [0, 35], k: [0, -50], n: [0, 20],
+ p: [-20, -15], q: [20, -15],
+ A: [-38, -52], B: [-29, -78], C: [29, -78], D: [38, -52],
+ K: [-33, -20], E: [33, -20],
+ J: [-25, 11], F: [25, 11],
+ I: [-15, 42], G: [15, 42],
+ H: [0, 73], L: [0, -91]
+ },
+ oldFrench: {
+ a: [-43.75, -50], b: [0, -50], c: [43.75, -50],
+ d: [-43.75, 0], e: [0, 0], f: [43.75, 0],
+ g: [-37.5, 50], h: [0, 50], i: [37.5, 50],
+ y: [-50, -50], z: [0, 62.5],
+ j: [-37.5, -37.5], k: [0, -37.5], l: [37.5, -37.5],
+ m: [-37.5, 37.5], n: [0, 45], o: [37.5, 37.5],
+ p: [-37.5, 0], q: [37.5, 0],
+ A: [-66.2, -66.6], B: [-22, -66.6], C: [22, -66.6], D: [66.2, -66.6],
+ K: [-66.2, -20], E: [66.2, -20],
+ J: [-64, 26], F: [64, 26],
+ I: [-45, 62], G: [45, 62],
+ H: [0, 91],
+ },
+ renaissance: {
+ a: [-43.75, -50], b: [0, -50], c: [43.75, -50],
+ d: [-41.5, 0], e: [0, 0], f: [41.5, 0],
+ g: [-43.75, 50], h: [0, 50], i: [43.75, 50],
+ y: [-50, -50], z: [0, 62.5],
+ j: [-37.5, -37.5], k: [0, -37.5], l: [37.5, -37.5],
+ m: [-37.5, 37.5], n: [0, 37.5], o: [37.5, 37.5],
+ p: [-37.5, 0], q: [37.5, 0],
+ A: [-61, -55], B: [-23, -67], C: [23, -67], D: [61, -55],
+ K: [-55, -11], E: [55, -11],
+ J: [-65, 31], F: [65, 31],
+ I: [-45, 76], G: [45, 76],
+ H: [0, 87]
+ },
+ baroque: {
+ a: [-43.75, -45], b: [0, -45], c: [43.75, -45],
+ d: [-43.75, 0], e: [0, 0], f: [43.75, 0],
+ g: [-43.75, 50], h: [0, 50], i: [43.75, 50],
+ y: [-50, -50], z: [0, 60],
+ j: [-37.5, -37.5], k: [0, -37.5], l: [37.5, -37.5],
+ m: [-37.5, 37.5], n: [0, 37.5], o: [37.5, 37.5],
+ p: [-37.5, 0], q: [37.5, 0],
+ A: [-65, -54.5], B: [-22, -65], C: [22, -65], D: [65, -54.5],
+ K: [-58.5, -15], E: [58.5, -15],
+ J: [-65, 31], F: [66, 31],
+ I: [-35, 73], G: [35, 73],
+ H: [0, 89]
+ },
+ targe: {
+ a: [-43.75, -50], b: [0, -50], c: [43.75, -50],
+ d: [-43.75, 0], e: [0, 0], f: [43.75, 0],
+ g: [-43.75, 50], h: [0, 50], i: [43.75, 50],
+ y: [-50, -50], z: [0, 50],
+ j: [-40, -40], k: [0, -40], l: [40, -40],
+ m: [-40, 40], n: [0, 40], o: [40, 40],
+ p: [-32.5, 0], q: [32.5, 0],
+ A: [-66.2, -60], B: [-22, -77], C: [22, -86], D: [60, -66.6],
+ K: [-28, -20], E: [57, -20],
+ J: [-61, 26], F: [61, 26],
+ I: [-49, 63], G: [49, 59],
+ H: [0, 80]
+ },
+ targe2: {
+ a: [-43.75, -50], b: [0, -50], c: [43.75, -50],
+ d: [-40, 0], e: [0, 0], f: [40, 0],
+ g: [-43.75, 50], h: [0, 50], i: [43.75, 50],
+ y: [-50, -50], z: [0, 60],
+ j: [-37.5, -37.5], k: [0, -37.5], l: [37.5, -37.5],
+ m: [-37.5, 37.5], n: [0, 37.5], o: [37.5, 37.5],
+ p: [-32.5, 0], q: [32.5, 0],
+ A: [-55, -59], B: [-15, -59], C: [24, -79], D: [51, -58],
+ K: [-40, -14], E: [51, -14],
+ J: [-64, 26], F: [62, 26],
+ I: [-46, 66], G: [48, 67],
+ H: [0, 83]
+ },
+ pavise: {
+ a: [-40, -52.5], b: [0, -52.5], c: [40, -52.5],
+ d: [-40, 0], e: [0, 0], f: [40, 0],
+ g: [-40, 52.5], h: [0, 52.5], i: [40, 52.5],
+ y: [-42.5, -52.5], z: [0, 60],
+ j: [-30, -35], k: [0, -37.5], l: [30, -35],
+ m: [-30, 35], n: [0, 37.5], o: [30, 35],
+ p: [-30, 0], q: [30, 0],
+ A: [-57, -55], B: [-22, -74], C: [22, -74], D: [57, -55],
+ K: [-54, -11], E: [54, -11],
+ J: [-50, 36], F: [50, 36],
+ I: [-46, 81], G: [46, 81],
+ H: [0, 81]
+ },
+ wedged: {
+ a: [-43.75, -50], b: [0, -50], c: [43.75, -50],
+ d: [-43.75, 0], e: [0, 0], f: [43.75, 0],
+ g: [-32.25, 37.5], h: [0, 50], i: [32.25, 37.5],
+ y: [-50, -50], z: [0, 62.5],
+ j: [-37.5, -37.5], k: [0, -37.5], l: [37.5, -37.5],
+ m: [-32.5, 32.5], n: [0, 42.5], o: [32.5, 32.5],
+ p: [-37.5, 0], q: [37.5, 0],
+ A: [-66, -53], B: [-22, -72.5], C: [22, -72.5], D: [66, -53],
+ K: [-62.6, -13], E: [62.6, -13],
+ J: [-50, 26], F: [50, 26],
+ I: [-27, 62], G: [27, 62],
+ H: [0, 87]
+ },
+ flag: {
+ a: [-60, -40], b: [0, -40], c: [60, -40],
+ d: [-60, 0], e: [0, 0], f: [60, 0],
+ g: [-60, 40], h: [0, 40], i: [60, 40],
+ y: [-60, -42.5], z: [0, 40],
+ j: [-45, -30], k: [0, -30], l: [45, -30],
+ m: [-45, 30], n: [0, 30], o: [45, 30],
+ p: [-45, 0], q: [45, 0],
+ A: [-81, -51], B: [-27, -51], C: [27, -51], D: [81, -51],
+ K: [-81, -17], E: [81, -17],
+ J: [-81, 17], F: [81, 17],
+ I: [-81, 51], G: [81, 51],
+ H: [-27, 51], L: [27, 51]
+ },
+ pennon: {
+ a: [-75, -40],
+ d: [-75, 0], e: [-25, 0], f: [25, 0],
+ g: [-75, 40],
+ y: [-70, -42.5],
+ j: [-60, -30],
+ m: [-60, 30],
+ p: [-60, 0], q: [5, 0],
+ A: [-81, -48], B: [-43, -36], C: [-4.5, -24], D: [33, -12],
+ E: [72, 0],
+ F: [33, 12], G: [-4.5, 24], H: [-43, 36], I: [-81, 48],
+ J: [-81, 17], K: [-81, -17]
+ },
+ guidon: {
+ a: [-60, -40], b: [0, -40], c: [60, -40],
+ d: [-60, 0], e: [0, 0],
+ g: [-60, 40], h: [0, 40], i: [60, 40],
+ y: [-60, -42.5], z: [0, 40],
+ j: [-45, -30], k: [0, -30], l: [45, -30],
+ m: [-45, 30], n: [0, 30], o: [45, 30],
+ p: [-45, 0],
+ A: [-81, -51], B: [-27, -51], C: [27, -51], D: [78, -51],
+ K: [-81, -17], E: [40.5, -17],
+ J: [-81, 17], F: [40.5, 17],
+ I: [-81, 51], G: [78, 51],
+ H: [-27, 51], L: [27, 51]
+ },
+ banner: {
+ a: [-50, -50], b: [0, -50], c: [50, -50],
+ d: [-50, 0], e: [0, 0], f: [50, 0],
+ g: [-50, 40], h: [0, 40], i: [50, 40],
+ y: [-50, -50], z: [0, 40],
+ j: [-37.5, -37.5], k: [0, -37.5], l: [37.5, -37.5],
+ m: [-37.5, 27.5], n: [0, 27.5], o: [37.5, 27.5],
+ p: [-37.5, 0], q: [37.5, 0],
+ A: [-66.5, -66.5], B: [-22, -66.5], C: [22, -66.5], D: [66.5, -66.5],
+ K: [-66.5, -20], E: [66.5, -20],
+ J: [-66.5, 26], F: [66.5, 26],
+ I: [-66.5, 66.5], G: [66.5, 66.5],
+ H: [-25, 75], L: [25, 75]
+ },
+ dovetail: {
+ a: [-49.75, -50], b: [0, -50], c: [49.75, -50],
+ d: [-49.75, 0], e: [0, 0], f: [49.75, 0],
+ g: [-49.75, 50], i: [49.75, 50],
+ y: [-50, -50], z: [0, 40],
+ j: [-37.5, -37.5], k: [0, -37.5], l: [37.5, -37.5],
+ m: [-37.5, 37.5], n: [0, 32.5], o: [37.5, 37.5],
+ p: [-37.5, 0], q: [37.5, 0],
+ A: [-66.5, -66.5], B: [-22, -66.5], C: [22, -66.5], D: [66.5, -66.5],
+ K: [-66.5, -16.5], E: [66.5, -16.5],
+ J: [-66.5, 34.5], F: [66.5, 34.5],
+ I: [-66.5, 84.5], G: [66.5, 84.5],
+ H: [-25, 64], L: [25, 64]
+ },
+ gonfalon: {
+ a: [-49.75, -50], b: [0, -50], c: [49.75, -50],
+ d: [-49.75, 0], e: [0, 0], f: [49.75, 0],
+ g: [-49.75, 50], h: [0, 50], i: [49.75, 50],
+ y: [-50, -50], z: [0, 50],
+ j: [-37.5, -37.5], k: [0, -37.5], l: [37.5, -37.5],
+ m: [-37.5, 37.5], n: [0, 37.5], o: [37.5, 37.5],
+ p: [-37.5, 0], q: [37.5, 0],
+ A: [-66.5, -66.5], B: [-22, -66.5], C: [22, -66.5], D: [66.5, -66.5],
+ K: [-66.5, -20], E: [66.5, -20],
+ J: [-66.5, 26], F: [66.5, 26],
+ I: [-40, 63], G: [40, 63],
+ H: [0, 88]
+ },
+ pennant: {
+ a: [-45, -50], b: [0, -50], c: [45, -50],
+ e: [0, 0], h: [0, 50],
+ y: [-50, -50], z: [0, 50],
+ j: [-32.5, -37.5], k: [0, -37.5], l: [32.5, -37.5],
+ n: [0, 37.5],
+ A: [-60, -76], B: [-22, -76], C: [22, -76], D: [60, -76],
+ K: [-46, -38], E: [46, -38],
+ J: [-31, 0], F: [31, 0],
+ I: [-16, 38], G: [16, 38],
+ H: [0, 76]
+ },
+ round: {
+ a: [-40, -47.5], b: [0, -47.5], c: [40, -47.5],
+ d: [-40, 0], e: [0, 0], f: [40, 0],
+ g: [-32.5, 47.5], h: [0, 47.5], i: [32.5, 47.5],
+ y: [-48, -48], z: [0, 57.5],
+ j: [-35.5, -35.5], k: [0, -37.5], l: [35.5, -35.5],
+ m: [-35.5, 35.5], n: [0, 37.5], o: [35.5, 35.5],
+ p: [-36.5, 0], q: [36.5, 0],
+ A: [-59, -48], B: [-23, -73], C: [23, -73], D: [59, -48],
+ K: [-76, -10], E: [76, -10],
+ J: [-70, 31], F: [70, 31],
+ I: [-42, 64], G: [42, 64],
+ H: [0, 77]
+ },
+ oval: {
+ a: [-37.5, -50], b: [0, -50], c: [37.5, -50],
+ d: [-43, 0], e: [0, 0], f: [43, 0],
+ g: [-37.5, 50], h: [0, 50], i: [37.5, 50],
+ y: [-48, -48], z: [0, 60],
+ j: [-35.5, -37.5], k: [0, -37.5], l: [35.5, -37.5],
+ m: [-35.5, 37.5], n: [0, 50], o: [35.5, 37.5],
+ p: [-36.5, 0], q: [36.5, 0],
+ A: [-48, -48], B: [-23, -78], C: [23, -78], D: [48, -48],
+ K: [-59, -10], E: [59, -10],
+ J: [-55, 31], F: [55, 31],
+ I: [-36, 68], G: [36, 68],
+ H: [0, 85]
+ },
+ vesicaPiscis: {
+ a: [-32, -37], b: [0, -50], c: [32, -37],
+ d: [-32, 0], e: [0, 0], f: [32, 0],
+ g: [-32, 37], h: [0, 50], i: [32, 37],
+ y: [-50, -50], z: [0, 62],
+ j: [-27.5, -27.5], k: [0, -37], l: [27.5, -27.5],
+ m: [-27.5, 27.5], n: [0, 42], o: [27.5, 27.5],
+ p: [-27.5, 0], q: [27.5, 0],
+ A: [-45, -32], B: [-29, -63], C: [29, -63], D: [45, -32],
+ K: [-50, 0], E: [50, 0],
+ J: [-45, 32], F: [45, 32],
+ I: [-29, 63], G: [29, 63],
+ H: [0, 89], L: [0, -89]
+ },
+ square: {
+ a: [-49.75, -50], b: [0, -50], c: [49.75, -50],
+ d: [-49.75, 0], e: [0, 0], f: [49.75, 0],
+ g: [-49.75, 50], h: [0, 50], i: [49.75, 50],
+ y: [-50, -50], z: [0, 50],
+ j: [-37.5, -37.5], k: [0, -37.5], l: [37.5, -37.5],
+ m: [-37.5, 37.5], n: [0, 37.5], o: [37.5, 37.5],
+ p: [-37.5, 0], q: [37.5, 0],
+ A: [-66.5, -66.5], B: [-22, -66.5], C: [22, -66.5], D: [66.5, -66.5],
+ K: [-66.5, -20], E: [66.5, -20],
+ J: [-66.5, 26], F: [66.5, 26],
+ I: [-66.5, 66.5], G: [66.5, 66.5],
+ H: [-22, 66.5], L: [22, 66.5]
+ },
+ diamond: {
+ a: [-32, -37], b: [0, -50], c: [32, -37],
+ d: [-43, 0], e: [0, 0], f: [43, 0],
+ g: [-32, 37], h: [0, 50], i: [32, 37],
+ y: [-50, -50], z: [0, 62],
+ j: [-27.5, -27.5], k: [0, -37], l: [27.5, -27.5],
+ m: [-27.5, 27.5], n: [0, 42], o: [27.5, 27.5],
+ p: [-37, 0], q: [37, 0],
+ A: [-43, -28], B: [-22, -56], C: [22, -56], D: [43, -28],
+ K: [-63, 0], E: [63, 0],
+ J: [-42, 28], F: [42, 28],
+ I: [-22, 56], G: [22, 56],
+ H: [0, 83], L: [0, -82]
+ },
+ no: {
+ a: [-66.5, -66.5], b: [0, -66.5], c: [66.5, -66.5],
+ d: [-66.5, 0], e: [0, 0], f: [66.5, 0],
+ g: [-66.5, 66.5], h: [0, 66.5], i: [66.5, 66.5],
+ y: [-50, -50], z: [0, 75],
+ j: [-50, -50], k: [0, -50], l: [50, -50],
+ m: [-50, 50], n: [0, 50], o: [50, 50],
+ p: [-50, 0], q: [50, 0],
+ A: [-91.5, -91.5], B: [-30.5, -91.5], C: [30.5, -91.5], D: [91.5, -91.5],
+ K: [-91.5, -30.5], E: [91.5, -30.5],
+ J: [-91.5, 30.5], F: [91.5, 30.5],
+ I: [-91.5, 91.5], G: [91.5, 91.5],
+ H: [-30.5, 91.5], L: [30.5, 91.5]
+ },
+ fantasy1: {
+ a: [-45, -45], b: [0, -50], c: [45, -45],
+ d: [-40, 0], e: [0, 0], f: [40, 0],
+ g: [-36, 42.5], h: [0, 50], i: [36, 42.5],
+ y: [-50, -50], z: [0, 60],
+ j: [-37, -37], k: [0, -40], l: [37, -37],
+ m: [-32, 32], n: [0, 40], o: [32, 32],
+ p: [-28.5, 0], q: [28.5, 0],
+ A: [-66, -55], B: [-22, -67], C: [22, -67], D: [66, -55],
+ K: [-53, -20], E: [53, -20],
+ J: [-46, 26], F: [46, 26],
+ I: [-29, 62], G: [29, 62],
+ H: [0, 84]
+ },
+ fantasy2: {
+ a: [-45, -45], b: [0, -45], c: [45, -45],
+ d: [-35, 0], e: [0, 0], f: [35, 0],
+ g: [-36, 42.5], h: [0, 45], i: [36, 42.5],
+ y: [-50, -50], z: [0, 55],
+ j: [-32.5, -32.5], k: [0, -40], l: [32.5, -32.5],
+ m: [-30, 30], n: [0, 40], o: [30, 30],
+ p: [-27.5, 0], q: [27.5, 0],
+ A: [-58, -35], B: [-44, -67], C: [44, -67], D: [58, -35],
+ K: [-39, -5], E: [39, -5],
+ J: [-57, 26], F: [57, 26],
+ I: [-32, 58], G: [32, 58],
+ H: [0, 83], L: [0, -72]
+ },
+ fantasy3: {
+ a: [-40, -45], b: [0, -50], c: [40, -45],
+ d: [-35, 0], e: [0, 0], f: [35, 0],
+ g: [-36, 42.5], h: [0, 50], i: [36, 42.5],
+ y: [-50, -50], z: [0, 55],
+ j: [-32.5, -32.5], k: [0, -40], l: [32.5, -32.5],
+ m: [-30, 30], n: [0, 40], o: [30, 30],
+ p: [-27.5, 0], q: [27.5, 0],
+ A: [-56, -42], B: [-22, -72], C: [22, -72], D: [56, -42],
+ K: [-37, -11], E: [37, -11],
+ J: [-60, 20], F: [60, 20],
+ I: [-34, 56], G: [34, 56],
+ H: [0, 83]
+ },
+ fantasy4: {
+ a: [-50, -45], b: [0, -50], c: [50, -45],
+ d: [-45, 0], e: [0, 0], f: [45, 0],
+ g: [-40, 45], h: [0, 50], i: [40, 45],
+ y: [-50, -50], z: [0, 62.5],
+ j: [-37.5, -37.5], k: [0, -45], l: [37.5, -37.5],
+ m: [-37.5, 37.5], n: [0, 45], o: [37.5, 37.5],
+ p: [-35, 0], q: [35, 0],
+ A: [-75, -56], B: [-36, -61], C: [36, -61], D: [75, -56],
+ K: [-67, -12], E: [67, -12],
+ J: [-63, 32], F: [63, 32],
+ I: [-42, 75], G: [42, 75],
+ H: [0, 91.5], L: [0, -79]
+ },
+ fantasy5: {
+ a: [-45, -50], b: [0, -50], c: [45, -50],
+ d: [-40, 0], e: [0, 0], f: [40, 0],
+ g: [-30, 45], h: [0, 50], i: [30, 45],
+ y: [-50, -50], z: [0, 60],
+ j: [-37, -37], k: [0, -40], l: [37, -37],
+ m: [-32, 32], n: [0, 40], o: [32, 32],
+ p: [-28.5, 0], q: [28.5, 0],
+ A: [-61, -67], B: [-22, -76], C: [22, -76], D: [61, -67],
+ K: [-58, -25], E: [58, -25],
+ J: [-48, 20], F: [48, 20],
+ I: [-28.5, 60], G: [28.5, 60],
+ H: [0, 89]
+ },
+ noldor: {
+ b: [0, -65], e: [0, -15], h: [0, 35],
+ z: [0, 35], k: [0, -50], n: [0, 30],
+ p: [-20, -15], q: [20, -15],
+ A: [-34, -47], B: [-20, -68], C: [20, -68], D: [34, -47],
+ K: [-18, -20], E: [18, -20],
+ J: [-26, 11], F: [26, 11],
+ I: [-14, 43], G: [14, 43],
+ H: [0, 74], L: [0, -85]
+ },
+ gondor: {
+ a: [-32.5, -50], b: [0, -50], c: [32.5, -50],
+ d: [-32.5, 0], e: [0, 0], f: [32.5, 0],
+ g: [-32.5, 50], h: [0, 50], i: [32.5, 50],
+ y: [-42.5, -52.5], z: [0, 65],
+ j: [-25, -37.5], k: [0, -37.5], l: [25, -37.5],
+ m: [-25, 30], n: [0, 37.5], o: [25, 30],
+ p: [-25, 0], q: [25, 0],
+ A: [-42, -52], B: [-17, -75], C: [17, -75], D: [42, -52],
+ K: [-42, -15], E: [42, -15],
+ J: [-42, 22], F: [42, 22],
+ I: [-26, 60], G: [26, 60],
+ H: [0, 87]
+ },
+ easterling: {
+ a: [-40, -47.5], b: [0, -47.5], c: [40, -47.5],
+ d: [-40, 0], e: [0, 0], f: [40, 0],
+ g: [-40, 47.5], h: [0, 47.5], i: [40, 47.5],
+ y: [-42.5, -52.5], z: [0, 65],
+ j: [-30, -37.5], k: [0, -37.5], l: [30, -37.5],
+ m: [-30, 37.5], n: [0, 37.5], o: [30, 37.5],
+ p: [-30, 0], q: [30, 0],
+ A: [-52, -72], B: [0, -65], D: [52, -72],
+ K: [-52, -24], E: [52, -24],
+ J: [-52, 24], F: [52, 24],
+ I: [-52, 72], G: [52, 72],
+ H: [0, 65]
+ },
+ erebor: {
+ a: [-40, -40], b: [0, -55], c: [40, -40],
+ d: [-40, 0], e: [0, 0], f: [40, 0],
+ g: [-40, 40], h: [0, 55], i: [40, 40],
+ y: [-50, -50], z: [0, 50],
+ j: [-35, -35], k: [0, -45], l: [35, -35],
+ m: [-35, 35], n: [0, 45], o: [35, 35],
+ p: [-37.5, 0], q: [37.5, 0],
+ A: [-47, -46], B: [-22, -81], C: [22, -81], D: [47, -46],
+ K: [-66.5, 0], E: [66.5, 0],
+ J: [-47, 46], F: [47, 46],
+ I: [-22, 81], G: [22, 81]
+ },
+ ironHills: {
+ a: [-43.75, -50], b: [0, -50], c: [43.75, -50],
+ d: [-43.25, 0], e: [0, 0], f: [43.25, 0],
+ g: [-42.5, 42.5], h: [0, 50], i: [42.5, 42.5],
+ y: [-50, -50], z: [0, 62.5],
+ j: [-32.5, -32.5], k: [0, -40], l: [32.5, -32.5],
+ m: [-32.5, 32.5], n: [0, 40], o: [32.5, 32.5],
+ p: [-37.5, 0], q: [37.5, 0],
+ A: [-61, -67], B: [-22, -74], C: [22, -74], D: [61, -67],
+ K: [-59, -20], E: [59, -20],
+ J: [-57, 26], F: [57, 26],
+ I: [-33, 64], G: [33, 64],
+ H: [0, 88]
+ },
+ urukHai: {
+ a: [-40, -45], b: [0, -45], c: [40, -45],
+ d: [-36, 0], e: [0, 0], f: [36, 0],
+ g: [-32.25, 40], h: [0, 40], i: [32.25, 40],
+ y: [-50, -50], z: [0, 40],
+ j: [-32.5, -32.5], k: [0, -37.5], l: [32.5, -32.5],
+ m: [-27.5, 27.5], n: [0, 32.5], o: [27.5, 27.5],
+ p: [-37.5, 0], q: [37.5, 0],
+ A: [-31, -79], B: [-1, -90], C: [31, -74], D: [61, -57],
+ K: [-55, -19], E: [53, -19],
+ J: [-45, 19], F: [45, 19],
+ I: [-33, 57], G: [35, 57],
+ H: [0, 57], L: [-39, -50]
+ },
+ moriaOrc: {
+ a: [-37.5, -37.5], b: [0, -37.5], c: [37.5, -37.5],
+ d: [-37.5, 0], e: [0, 0], f: [37.5, 0],
+ g: [-37.5, 37.5], h: [0, 37.5], i: [37.5, 37.5],
+ y: [-50, -50], z: [0, 40],
+ j: [-30, -30], k: [0, -30], l: [30, -30],
+ m: [-30, 30], n: [0, 30], o: [30, 30],
+ p: [-30, 0], q: [30, 0],
+ A: [-48, -48], B: [-16, -50], C: [16, -46], D: [39, -61],
+ K: [-52, -19], E: [52, -26],
+ J: [-42, 9], F: [52, 9],
+ I: [-31, 40], G: [40, 43],
+ H: [4, 47]
+ }
+ };
+
+ const shieldSize = {
+ horsehead: .9, horsehead2: .9, polish: .85, swiss: .95,
+ boeotian: .75, roman: .95, kite: .65, targe2: .9, pavise: .9, wedged: .95,
+ flag: .7, pennon: .5, guidon: .65, banner: .8, dovetail: .8, pennant: .6,
+ oval: .95, vesicaPiscis: .8, diamond: .8, no: 1.2,
+ fantasy1: .8, fantasy2: .7, fantasy3: .7, fantasy5: .9,
+ noldor: .5, gondor: .75, easterling: .8, erebor: .9, urukHai: .8, moriaOrc: .7
+ }
+
+ const shieldBox = {
+ heater: "0 10 200 200",
+ spanish: "0 10 200 200",
+ french: "0 10 200 200",
+
+ horsehead: "0 10 200 200",
+ horsehead2: "0 10 200 200",
+ polish: "0 0 200 200",
+ hessen: "0 5 200 200",
+ swiss: "0 10 200 200",
+
+ boeotian: "0 0 200 200",
+ roman: "0 0 200 200",
+ kite: "0 0 200 200",
+ oldFrench: "0 10 200 200",
+ renaissance: "0 5 200 200",
+ baroque: "0 10 200 200",
+
+ targe: "0 0 200 200",
+ targe2: "0 0 200 200",
+ pavise: "0 0 200 200",
+ wedged: "0 10 200 200",
+
+ flag: "0 0 200 200",
+ pennon: "2.5 0 200 200",
+ guidon: "2.5 0 200 200",
+ banner: "0 10 200 200",
+ dovetail: "0 10 200 200",
+ gonfalon: "0 10 200 200",
+ pennant: "0 0 200 200",
+
+ round: "0 0 200 200",
+ oval: "0 0 200 200",
+ vesicaPiscis: "0 0 200 200",
+ square: "0 0 200 200",
+ diamond: "0 0 200 200",
+ no: "0 0 200 200",
+
+ fantasy1: "0 0 200 200",
+ fantasy2: "0 5 200 200",
+ fantasy3: "0 5 200 200",
+ fantasy4: "0 5 200 200",
+ fantasy5: "0 0 200 200",
+
+ noldor: "0 0 200 200",
+ gondor: "0 5 200 200",
+ easterling: "0 0 200 200",
+ erebor: "0 0 200 200",
+ ironHills: "0 5 200 200",
+ urukHai: "0 0 200 200",
+ moriaOrc: "0 0 200 200"
+ }
+
+ async function draw(id, coa) {
+ const {division, ordinaries = [], charges = []} = coa;
+ const ordinariesRegular = ordinaries.filter(o => !o.above);
+ const ordinariesAboveCharges = ordinaries.filter(o => o.above);
+ const shieldPath = document.getElementById(coa.shield).querySelector("path").getAttribute("d");
+ const tDiv = division ? division.t.includes("-") ? division.t.split("-")[1] : division.t : null;
+ const positions = shieldPositions[coa.shield];
+ const sizeModifier = shieldSize[coa.shield] || 1;
+ const viewBox = shieldBox[coa.shield] || "0 0 200 200";
+
+ const coaDefs = document.getElementById("coaDefs");
+ const chargesGroup = coaDefs.querySelector("#charges");
+
+ let svg = `
+
+ `;
+
+ // insert coa svg to coaDefs
+ coaDefs.querySelector("#coas").insertAdjacentHTML("beforeend", svg);
+
+ // fetch charges
+ if (charges.length) {
+ const defs = document.getElementById(id).querySelector("defs");
+ const uniqueCharges = [...new Set(charges.map(charge => charge.charge))];
+ uniqueCharges.forEach(charge => fetchCharge(charge, defs));
+ }
+
+ function templateDivision() {
+ if (!division) return "";
+ let svg = "";
+
+ // In field part
+ for (const ordinary of ordinariesRegular) {
+ if (ordinary.divided === "field") svg += templateOrdinary(ordinary, ordinary.t); else
+ if (ordinary.divided === "counter") svg += templateOrdinary(ordinary, tDiv);
+ }
+
+ for (const charge of charges) {
+ if (charge.divided === "field") svg += templateCharge(charge, charge.t); else
+ if (charge.divided === "counter") svg += templateCharge(charge, tDiv);
+ }
+
+ for (const ordinary of ordinariesAboveCharges) {
+ if (ordinary.divided === "field") svg += templateOrdinary(ordinary, ordinary.t); else
+ if (ordinary.divided === "counter") svg += templateOrdinary(ordinary, tDiv);
+ }
+
+ // In division part
+ svg += `
+
+
+ `;
+
+ for (const ordinary of ordinariesRegular) {
+ if (ordinary.divided === "division") svg += templateOrdinary(ordinary, ordinary.t); else
+ if (ordinary.divided === "counter") svg += templateOrdinary(ordinary, coa.t1);
+ }
+
+ for (const charge of charges) {
+ if (charge.divided === "division") svg += templateCharge(charge, charge.t); else
+ if (charge.divided === "counter") svg += templateCharge(charge, coa.t1);
+ }
+
+ for (const ordinary of ordinariesAboveCharges) {
+ if (ordinary.divided === "division") svg += templateOrdinary(ordinary, ordinary.t); else
+ if (ordinary.divided === "counter") svg += templateOrdinary(ordinary, coa.t1);
+ }
+
+ return svg += ``
+ }
+
+ function templateAboveAll() {
+ let svg = "";
+
+ ordinariesRegular.filter(o => !o.divided).forEach(ordinary => {
+ svg += templateOrdinary(ordinary, ordinary.t);
+ });
+
+ charges.filter(o => !o.divided || !division).forEach(charge => {
+ svg += templateCharge(charge, charge.t);
+ });
+
+ ordinariesAboveCharges.filter(o => !o.divided).forEach(ordinary => {
+ svg += templateOrdinary(ordinary, ordinary.t);
+ })
+
+ return svg;
+ }
+
+ function templateOrdinary(ordinary, tincture) {
+ const fill = clr(tincture);
+ let svg = `
`;
+ if (ordinary.ordinary === "bordure") svg += ``;
+ else if (ordinary.ordinary === "orle") svg += ``;
+ else svg += getTemplate(ordinary.ordinary, ordinary.line);
+ return svg + ``;
+ }
+
+ function templateCharge(charge, tincture) {
+ const fill = clr(tincture);
+ const chargePositions = [...new Set(charge.p)].filter(position => positions[position]);
+
+ let svg = "";
+ svg += `
`;
+ for (const p of chargePositions) {
+ const transform = getElTransform(charge, p);
+ svg += ``;
+ }
+ return svg + ``;
+
+ function getElTransform(c, p) {
+ const [x, y] = positions[p];
+ const s = (c.size || 1) * sizeModifier;
+ const scale = c.sinister || c.reversed ? `${c.sinister ? "-" : ""}${s}, ${c.reversed ? "-" : ""}${s}` : s;
+ return `translate(${x} ${y}) scale(${scale})`;
+ }
+ }
+
+ // get color or link to pattern
+ function clr(tincture) {
+ if (colors[tincture]) return colors[tincture];
+ const pattern = document.getElementById(tincture);
+ if (!pattern) renderPattern(tincture);
+ return "url(#"+tincture+")";
+ }
+
+ function renderPattern(tincture) {
+ const [pattern, t1, t2, size] = tincture.split("-");
+ const semy = pattern.slice(0, 4) === "semy";
+
+ const template = document.getElementById(semy ? "semy" : pattern);
+ let html = template.innerHTML.replace(/{id}/, tincture);
+
+ const width = template.querySelector("pattern").getAttribute("width");
+ const height = template.querySelector("pattern").getAttribute("height");
+
+ if (t1) html = html.replace(/{c1}/g, clr(t1));
+ if (t2) html = html.replace(/{c2}/g, clr(t2));
+
+ document.getElementById("patterns").insertAdjacentHTML("beforeend", html);
+
+ if (semy) {
+ const charge = pattern.split("_of_")[1];
+ const el = document.getElementById(tincture);
+
+ fetch("https://azgaar.github.io/Armoria/charges/"+charge+".svg").then(res => {
+ if (res.ok) return res.text();
+ else throw new Error('Cannot fetch charge');
+ }).then(text => {
+ const html = document.createElement("html");
+ html.innerHTML = text;
+ el.innerHTML = el.innerHTML.replace(/
/g, html.querySelector("g").outerHTML);
+ });
+ }
+
+ if (size) {
+ let mod = 1;
+ if (size === "small") mod = .5;
+ if (size === "smaller") mod = .25;
+ if (size === "smallest") mod = .125;
+ if (size === "big") mod = 2;
+
+ const el = document.getElementById(tincture);
+ el.setAttribute("width", width * mod);
+ el.setAttribute("height", height * mod);
+ }
+ }
+
+ function getTemplate(templateId, lineId) {
+ if (!lineId) return document.getElementById(templateId)?.innerHTML;
+ const template = document.getElementById(templateId);
+ const line = document.getElementById(lineId) ? document.getElementById(lineId) : document.getElementById("straight");
+ return template.innerHTML.replace(/{line}/g, line.getAttribute("d")).replace(/dpath/g, "d");
+ }
+
+ function fetchCharge(charge, defs) {
+ if (charge === "inescutcheon") {
+ const g = ``;
+ defs.insertAdjacentHTML("beforeend", g);
+ return;
+ }
+
+ fetch("https://azgaar.github.io/Armoria/charges/"+charge+".svg").then(res => {
+ if (res.ok) return res.text();
+ else throw new Error('Cannot fetch charge');
+ }).then(text => {
+ const el = document.createElement("html");
+ el.innerHTML = text;
+ const g = el.querySelector("g");
+ g.id = charge + "_" + id;
+ defs.insertAdjacentHTML("beforeend", g.outerHTML);
+ });
+ }
+ }
+
+ // async render coa if it does not exist
+ const trigger = function(id, coa) {
+ if (!document.getElementById(id)) draw(id, coa);
+ }
+
+ return {trigger};
+
+})));
\ No newline at end of file
diff --git a/modules/save-and-load.js b/modules/save-and-load.js
index 86d7e6db..e682002e 100644
--- a/modules/save-and-load.js
+++ b/modules/save-and-load.js
@@ -423,7 +423,7 @@ async function quickSave() {
if (customization) {tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error"); return;}
const blob = await getMapData();
if (blob) ldb.set("lastMap", blob); // auto-save map
- tip("Map is saved to browser memory", true, "success", 2000);
+ tip("Map is saved to browser memory. Please also save as .map file to secure progress", true, "success", 2000);
}
function quickLoad() {
diff --git a/modules/ui/coa-editor.js b/modules/ui/coa-editor.js
new file mode 100644
index 00000000..fd257c79
--- /dev/null
+++ b/modules/ui/coa-editor.js
@@ -0,0 +1,108 @@
+"use strict";
+function editEmblem(type, id, el) {
+ if (customization) return;
+
+ const emblemStates = document.getElementById("emblemStates");
+ const emblemProvinces = document.getElementById("emblemProvinces");
+ const emblemBurgs = document.getElementById("emblemBurgs");
+ const {states, provinces, burgs, cells} = pack;
+
+ updateElementSelectors(type, id, el);
+
+ $("#emblemEditor").dialog({
+ title: "Edit Emblem", resizable: true, width: "auto",
+ position: {my: "left top", at: "left+10 top+10", of: "svg", collision: "fit"}
+ });
+
+ if (modules.editEmblem) return;
+ modules.editEmblem = true;
+
+ // add listeners
+ emblemStates.addEventListener("input", selectState);
+ emblemProvinces.addEventListener("input", selectProvince);
+ emblemBurgs.addEventListener("input", selectBurg);
+
+ function updateElementSelectors(type, id, el) {
+ let state = 0, province = 0, burg = 0;
+
+ // set active type
+ emblemStates.parentElement.className = type === "state" ? "active" : "";
+ emblemProvinces.parentElement.className = type === "province" ? "active" : "";
+ emblemBurgs.parentElement.className = type === "burg" ? "active" : "";
+
+ // define selected values
+ if (type === "state") state = el.i;
+ else if (type === "province") {province = el.i; state = states[el.state].i;}
+ else {burg = el.i; province = provinces[cells.province[el.cell]].i; state = provinces[province].state;}
+
+ // update option list and select actual values
+ emblemStates.options.length = 0;
+ const neutralBurgs = burgs.filter(burg => burg.i && !burg.removed && !burg.state);
+ if (neutralBurgs.length) emblemProvinces.options.add(new Option(states[0].name, 0, false, !state));
+ const stateList = states.filter(state => state.i && !state.removed);
+ stateList.forEach(s => emblemStates.options.add(new Option(s.name, s.i, false, s.i === state)));
+
+ emblemProvinces.options.length = 0;
+ emblemProvinces.options.add(new Option("", 0, false, !province));
+ const provinceList = provinces.filter(province => !province.removed && province.state === state);
+ provinceList.forEach(p => emblemProvinces.options.add(new Option(p.name, p.i, false, p.i === province)));
+
+ emblemBurgs.options.length = 0;
+ emblemBurgs.options.add(new Option("", 0, false, !burg));
+ const burgList = burgs.filter(burg => !burg.removed && province ? cells.province[burg.cell] === province : burg.state === state);
+ burgList.forEach(b => emblemBurgs.options.add(new Option(b.name, b.i, false, b.i === burg)));
+ emblemBurgs.options[0].disabled = true;
+
+ COArenderer.trigger(id, el.coa);
+ updateEmblemData(type, id, el);
+ }
+
+ function updateEmblemData(type, id, el) {
+ if (!el.coa) return;
+ document.getElementById("emblemImage").setAttribute("href", "#" + id);
+ document.getElementById("emblemArmiger").innerText = el.fullName || el.name;
+ }
+
+ function selectState() {
+ const state = +this.value;
+ if (state) {
+ type = "state";
+ el = states[state];
+ id = "stateCOA"+ state;
+ } else {
+ // select neutral burg if state is changed to Neutrals
+ const neutralBurgs = burgs.filter(burg => burg.i && !burg.removed && !burg.state);
+ if (!neutralBurgs.length) return;
+ type = "burg";
+ el = neutralBurgs[0];
+ id = "burgCOA"+ neutralBurgs[0].i;
+ }
+ updateElementSelectors(type, id, el);
+ }
+
+ function selectProvince() {
+ const province = +this.value;
+
+ if (province) {
+ type = "province";
+ el = provinces[province];
+ id = "provinceCOA"+ province;
+ } else {
+ // select state if province is changed to null value
+ const state = +emblemStates.value;
+ type = "state";
+ el = states[state];
+ id = "stateCOA"+ state;
+ }
+
+ updateElementSelectors(type, id, el);
+ }
+
+ function selectBurg() {
+ const burg = +this.value;
+ type = "burg";
+ el = burgs[burg];
+ id = "burgCOA"+ burg;
+ updateElementSelectors(type, id, el);
+ }
+}
\ No newline at end of file
diff --git a/modules/ui/general.js b/modules/ui/general.js
index ca6ee9f7..3c1c7470 100644
--- a/modules/ui/general.js
+++ b/modules/ui/general.js
@@ -339,6 +339,7 @@ function showInfo() {
const Reddit = link("https://www.reddit.com/r/FantasyMapGenerator", "Reddit")
const Patreon = link("https://www.patreon.com/azgaar", "Patreon");
const Trello = link("https://trello.com/b/7x832DG4/fantasy-map-generator", "Trello");
+ const Armoria = link("https://azgaar.github.io/Armoria", "Armoria");
const QuickStart = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Quick-Start-Tutorial", "Quick start tutorial");
const QAA = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Q&A", "Q&A page");
@@ -353,9 +354,11 @@ function showInfo() {
The best way to get help is to contact the community on ${Discord} and ${Reddit}.
Before asking questions, please check out the ${QuickStart} and the ${QAA}.
- You can track the development process on ${Trello}.
+ Track the development process on ${Trello}.
- Links:
+ Check out our new project: ${Armoria}, heraldry generator and editor.
+
+ Links:
- ${link("https://github.com/Azgaar/Fantasy-Map-Generator", "GitHub repository")}
- ${link("https://github.com/Azgaar/Fantasy-Map-Generator/blob/master/LICENSE", "License")}
diff --git a/modules/ui/states-editor.js b/modules/ui/states-editor.js
index b9a78a9c..6ba44b8a 100644
--- a/modules/ui/states-editor.js
+++ b/modules/ui/states-editor.js
@@ -42,7 +42,7 @@ function editStates() {
const el = ev.target, cl = el.classList, line = el.parentNode, state = +line.dataset.id;
if (cl.contains("fillRect")) stateChangeFill(el); else
if (cl.contains("name")) editStateName(state); else
- if (cl.contains("emblemIcon")) editCOA(state); else
+ if (cl.contains("coaIcon")) editEmblem("state", "stateCOA"+state, pack.states[state]); else
if (cl.contains("icon-star-empty")) stateCapitalZoomIn(state); else
if (cl.contains("culturePopulation")) changePopulation(state); else
if (cl.contains("icon-pin")) toggleFog(state, cl); else
@@ -83,7 +83,6 @@ function editStates() {
totalPopulation += population;
totalBurgs += s.burgs;
const focused = defs.select("#fog #focusState"+s.i).size();
- const COAsize = Math.round(15 * +uiSizeInput.value);
if (!s.i) {
// Neutral line
@@ -91,7 +90,7 @@ function editStates() {
data-population=${population} data-burgs=${s.burgs} data-color="" data-form="" data-capital="" data-culture="" data-type="" data-expansionism="">
-
`;
continue;
}
+
const capital = pack.burgs[s.capital].name;
- const coaURL = `http://azgaar.github.io/Armoria/?view=1&size=${COAsize}&noedit&coa=${COA.toString(s.coa)}`;
+ COArenderer.trigger("stateCOA"+s.i, s.coa);
lines += `