v1.5.04 - Emblems auto-hide and hightlighting

This commit is contained in:
Azgaar 2021-01-30 18:47:43 +03:00
parent 56b6eb2a13
commit 32c4566aa7
9 changed files with 125 additions and 54 deletions

View file

@ -3338,6 +3338,9 @@
<select id="emblemBurgs"></select>
</div>
</div>
<div id="emblemsBottom">
<button id="emblemsFocus" data-tip="Show emblem associated area or place" class="icon-target"></button>
</div>
</div>
<div id="unitsEditor" class="dialog stable" style="display: none">
@ -4399,7 +4402,7 @@
<script defer src="modules/ui/regiment-editor.js"></script>
<script defer src="modules/ui/battle-screen.js"></script>
<script defer src="modules/coa-renderer.js"></script>
<script defer src="modules/ui/coa-editor.js"></script>
<script defer src="modules/ui/emblems-editor.js"></script>
<script defer src="modules/ui/editors.js"></script>
<script defer src="modules/ui/3d.js"></script>
<script defer src="libs/rgbquant.js"></script>

35
main.js
View file

@ -61,7 +61,7 @@ let coastline = viewbox.append("g").attr("id", "coastline");
let ice = viewbox.append("g").attr("id", "ice").style("display", "none");
let prec = viewbox.append("g").attr("id", "prec").style("display", "none");
let population = viewbox.append("g").attr("id", "population");
let emblems = viewbox.append("g").attr("id", "emblems");
let emblems = viewbox.append("g").attr("id", "emblems").style("display", "none");
let labels = viewbox.append("g").attr("id", "labels");
let icons = viewbox.append("g").attr("id", "icons");
let burgIcons = icons.append("g").attr("id", "burgIcons");
@ -98,6 +98,11 @@ anchors.append("g").attr("id", "towns");
population.append("g").attr("id", "rural");
population.append("g").attr("id", "urban");
// emblem groups
emblems.append("g").attr("id", "burgEmblems");
emblems.append("g").attr("id", "provinceEmblems");
emblems.append("g").attr("id", "stateEmblems");
// fogging
fogging.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%");
fogging.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%").attr("fill", "#e8f0f6").attr("filter", "url(#splotch)");
@ -444,16 +449,12 @@ function invokeActiveZooming() {
// rescale emblems on zoom
if (emblems.style("display") !== "none") {
const fontSize = rn(1 / scale ** .1, 4);
emblems.attr("font-size", fontSize);
// const realSize = fontSize * scale;
// emblems.selectAll("use").each(function(d) {
// const hidden = realSize < 20 || realSize > 350;
// if (hidden) this.classList.add("hidden");
// else this.classList.remove("hidden");
// });
emblems.selectAll("g").each(function() {
const size = this.getAttribute("font-size") * scale;
const hidden = size < 25 || size > 300;
if (hidden) this.classList.add("hidden"); else this.classList.remove("hidden");
if (!hidden && this.children.length && !this.children[0].getAttribute("href")) renderGroupCOAs(this);
});
}
// turn off ocean pattern if scale is big (improves performance)
@ -484,6 +485,18 @@ function invokeActiveZooming() {
}
}
async function renderGroupCOAs(g) {
const [group, type] = g.id === "burgEmblems" ? [pack.burgs, "burg"] :
g.id === "provinceEmblems" ? [pack.provinces, "province"] :
[pack.states, "state"];
for (let use of g.children) {
const i = +use.dataset.i;
const id = type+"COA"+i;
COArenderer.trigger(id, group[i].coa);
use.setAttribute("href", "#"+id);
}
}
// add drag to upload logic, pull request from @evyatron
void function addDragToUpload() {
document.addEventListener("dragover", function(e) {

View file

@ -208,16 +208,16 @@
// fix download svg/png
// test in FF
const t1 = parent && P(.25) ? parent.t1 : getTincture("field");
const t1 = parent && P(.3) ? parent.t1 : getTincture("field");
const coa = {t1};
let charge = P(usedPattern ? .5 : .93) ? true : false; // 80% for charge
const linedOrdinary = charge && P(.3) || P(.5) ? parent?.ordinaries && P(.2) ? parent.ordinaries[0].ordinary : rw(ordinaries.lined) : null;
const linedOrdinary = charge && P(.3) || P(.5) ? parent?.ordinaries && P(.3) ? parent.ordinaries[0].ordinary : rw(ordinaries.lined) : null;
const ordinary = !charge && P(.65) || P(.3) ? linedOrdinary ? linedOrdinary : rw(ordinaries.straight) : null; // 36% for ordinary
const rareDivided = ["chief", "terrace", "chevron", "quarter", "flaunches"].includes(ordinary);
const divisioned = rareDivided ? P(.03) : charge && ordinary ? P(.03) : charge ? P(.3) : ordinary ? P(.7) : P(.995); // 33% for division
const division = divisioned ? parent?.division && P(.2) ? parent.division.division : rw(divisions.variants) : null;
if (charge) charge = parent?.charges && P(.3) ? parent.charges[0].charge : selectCharge();
if (charge) charge = parent?.charges && P(.2) ? parent.charges[0].charge : selectCharge();
if (division) {
const t = getTincture("division", usedTinctures, P(.98) ? coa.t1 : null);

View file

@ -683,7 +683,6 @@
const viewBox = shieldBox[coa.shield] || "0 0 200 200";
const coaDefs = document.getElementById("coaDefs");
const chargesGroup = coaDefs.querySelector("#charges");
let svg = `
<svg id="${id}" xmlns="http://www.w3.org/2000/svg" width=200 height=200 viewBox="${viewBox}">
@ -878,7 +877,7 @@
}
// async render coa if it does not exist
const trigger = function(id, coa) {
const trigger = async function(id, coa) {
if (!document.getElementById(id)) draw(id, coa);
}

View file

@ -21,7 +21,7 @@ function clicked() {
const p = d3.mouse(this);
const i = findCell(p[0], p[1]);
if (parent.id === "emblems") editEmblem();
if (grand.id === "emblems") editEmblem();
else if (parent.id === "rivers") editRiver();
else if (grand.id === "routes") editRoute();
else if (el.tagName === "tspan" && grand.parentNode.parentNode.id === "labels") editLabel();

View file

@ -3,10 +3,14 @@ function editEmblem(type, id, el) {
if (customization) return;
if (!id && d3.event) {
const data = d3.event.target.__data__;
type = data.type;
id = data.id;
el = data.el;
const parent = d3.event.target.parentNode;
const [g, t] = parent.id === "burgEmblems" ? [pack.burgs, "burg"] :
parent.id === "provinceEmblems" ? [pack.provinces, "province"] :
[pack.states, "state"];
const i = +d3.event.target.dataset.i;
type = t;
id = type+"COA"+i;
el = g[i];
}
emblems.selectAll(":scope > use").call(d3.drag().on("drag", dragEmblem)).classed("draggable", true);
@ -31,6 +35,7 @@ function editEmblem(type, id, el) {
emblemStates.addEventListener("input", selectState);
emblemProvinces.addEventListener("input", selectProvince);
emblemBurgs.addEventListener("input", selectBurg);
document.getElementById("emblemsFocus").addEventListener("click", showArea);
function updateElementSelectors(type, id, el) {
let state = 0, province = 0, burg = 0;
@ -132,6 +137,10 @@ function editEmblem(type, id, el) {
});
}
function showArea() {
highlightEmblemElement(type, el);
}
function closeEmblemEditor() {
emblems.selectAll(":scope > use").call(d3.drag().on("drag", null)).attr("class", null);
}

View file

@ -94,11 +94,18 @@ function showMapTooltip(point, e, i, g) {
tip(e.target.parentNode.dataset.name + ". Click to edit");
return;
}
if (group === "emblems" && e.target.__data__?.el) {
const el = d3.select(e.target);
el.raise();
const name = el.datum().el.fullName || el.datum().el.name || "";
const type = el.datum().type || "";
if (group === "emblems") {
const parent = e.target.parentNode;
const [g, type] = parent.id === "burgEmblems" ? [pack.burgs, "burg"] :
parent.id === "provinceEmblems" ? [pack.provinces, "province"] :
[pack.states, "state"];
const i = +e.target.dataset.i;
highlightEmblemElement(type, g[i]);
d3.select(e.target).raise();
d3.select(parent).raise();
const name = g[i].fullName || g[i].name;
tip(`${name} ${type} emblem. Click to edit`);
return;
}
@ -289,6 +296,32 @@ function getPopulationTip(i) {
return `Cell population: ${si(rural+urban)}; Rural: ${si(rural)}; Urban: ${si(urban)}`;
}
function highlightEmblemElement(type, el) {
if (emblems.selectAll("line, circle").size()) return;
const i = el.i, cells = pack.cells;
const animation = d3.transition().duration(1000).ease(d3.easeSinIn);
if (type === "burg") {
const {x, y} = el;
emblems.append("circle").attr("cx", x).attr("cy", y).attr("r", 0)
.attr("fill", "none").attr("stroke", "#d0240f").attr("stroke-width", 1).attr("opacity", 1)
.transition(animation).attr("r", 20).attr("opacity", .1).attr("stroke-width", 0).remove();
return;
}
const [x, y] = el.pole;
const obj = type === "state" ? cells.state : cells.province;
const borderCells = cells.i.filter(id => obj[id] === i && cells.c[id].some(n => obj[n] !== i));
const data = Array.from(borderCells).filter((c, i) => !(i%2)).map(i => cells.p[i]).map(i => [i[0], i[1], Math.hypot(i[0]-x, i[1]-y)]);
emblems.selectAll("line").data(data).enter().append("line")
.attr("x1", x).attr("y1", y).attr("x2", d => d[0]).attr("y2", d => d[1])
.attr("stroke", "#d0240f").attr("stroke-width", .5).attr("opacity", .2)
.attr("stroke-dashoffset", d => d[2]).attr("stroke-dasharray", d => d[2])
.transition(animation).attr("stroke-dashoffset", 0).attr("opacity", 1)
.transition(animation).delay(1000).attr("stroke-dashoffset", d => d[2]).attr("opacity", 0).remove();
}
// assign lock behavior
document.querySelectorAll("[data-locked]").forEach(function(e) {
e.addEventListener("mouseover", function(event) {

View file

@ -1230,7 +1230,7 @@ function toggleZones(event) {
function toggleEmblems(event) {
if (!layerIsOn("toggleEmblems")) {
turnButtonOn("toggleEmblems");
if (!emblems.selectAll("*").size()) drawEmblems();
if (!emblems.selectAll("use").size()) drawEmblems();
$('#emblems').fadeIn();
if (event && isCtrlClick(event)) editStyle("emblems");
} else {
@ -1242,19 +1242,14 @@ function toggleEmblems(event) {
function drawEmblems() {
TIME && console.time("drawEmblems");
const {states, provinces, burgs, cells} = pack;
// const sizeBurgs = +emblems.attr("size-burgs") || 15;
// burgs.filter(b => b.i && !b.removed && b.coa).forEach(burg => {
// const {x, y} = burg;
// COArenderer.trigger("burgCOA"+burg.i, burg.coa);
// svg += `<use x=${x-sizeBurgs/2} y=${y-sizeBurgs/2} width=${sizeBurgs} height=${sizeBurgs} href="#burgCOA${burg.i}"></use>`;
// });
const {states, provinces, burgs} = pack;
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);
const sizeMod = +emblems.attr("size-modifier") || 1;
const getStateEmblemsSize = () => {
const startSize = (graphHeight + graphWidth) / 40;
const statesMod = (1 + validStates.length / 100) - (15 - validStates.length) / 200; // states number modifier
@ -1265,36 +1260,47 @@ function drawEmblems() {
const getProvinceEmblemsSize = () => {
const startSize = (graphHeight + graphWidth) / 80;
const provincesMod = (1 + validProvinces.length / 1000) - (115 - validProvinces.length) / 1000; // states number modifier
const size = rn(startSize / provincesMod * sizeMod); // target size ~50px on 1536x754 map with 15 states
const size = rn(startSize / provincesMod * sizeMod); // target size ~26px on 1536x754 map with 115 provinces
return Math.min(Math.max(size, 5), 75);
}
const getBurgEmblemSize = () => {
const startSize = (graphHeight + graphWidth) / 150;
const burgsMod = (1 + validBurgs.length / 1000) - (450 - validBurgs.length) / 1000; // states number modifier
const size = rn(startSize / burgsMod * sizeMod); // target size ~10px on 1536x754 map with 450 burgs
return Math.min(Math.max(size, 5), 50);
}
const sizeBurgs = getBurgEmblemSize();
const burgCOAs = validBurgs.map(burg => {
const {x, y} = burg;
return {type: "burg", i: burg.i, x, y, size: sizeBurgs};
});
const sizeProvinces = getProvinceEmblemsSize();
const provinceCOAs = validProvinces.map(province => {
if (!province.pole) getProvincesVertices();
const [x, y] = province.pole;
COArenderer.trigger("provinceCOA"+province.i, province.coa);
return {type: "province", id: "provinceCOA"+province.i, x, y, size: sizeProvinces, el: province};
return {type: "province", i: province.i, x, y, size: sizeProvinces};
});
const sizeStates = getStateEmblemsSize();
const stateCOAs = validStates.map(state => {
const [x, y] = state.pole;
COArenderer.trigger("stateCOA"+state.i, state.coa);
return {type: "state", id: "stateCOA"+state.i, x, y, size: sizeStates, el: state};
return {type: "state", i: state.i, x, y, size: sizeStates};
});
const nodes = provinceCOAs.concat(stateCOAs);
const nodes = burgCOAs.concat(provinceCOAs).concat(stateCOAs);
const simulation = d3.forceSimulation(nodes)
.alphaMin(.6).alphaDecay(.2).velocityDecay(.6)
.force('collision', d3.forceCollide().radius(d => d.size/2))
.stop();
debug.attr("fill", "#fff").attr("stroke", "#000")
.selectAll("circle").data(nodes).join("circle")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", 2);
// debug.attr("fill", "#fff").attr("stroke", "#000")
// .selectAll("circle").data(nodes).join("circle")
// .attr("cx", d => d.x)
// .attr("cy", d => d.y)
// .attr("r", 2);
d3.timeout(function() {
const n = Math.ceil(Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay()));
@ -1302,12 +1308,20 @@ function drawEmblems() {
simulation.tick();
}
emblems.selectAll("use").data(nodes).join("use")
.attr("href", d => "#"+d.id)
.attr("x", d => d.x - d.size / 2)
.attr("y", d => d.y - d.size / 2)
.attr("width", d => d.size + "em")
.attr("height", d => d.size + "em");
emblems.select("#burgEmblems").attr("font-size", sizeBurgs)
.selectAll("use").data(nodes.filter(node => node.type === "burg")).join("use")
.attr("x", d => d.x - d.size / 2).attr("y", d => d.y - d.size / 2)
.attr("width", "1em").attr("height", "1em").attr("data-i", d => d.i);
emblems.select("#provinceEmblems").attr("font-size", sizeProvinces)
.selectAll("use").data(nodes.filter(node => node.type === "province")).join("use")
.attr("x", d => d.x - d.size / 2).attr("y", d => d.y - d.size / 2)
.attr("width", "1em").attr("height", "1em").attr("data-i", d => d.i);
emblems.select("#stateEmblems").attr("font-size", sizeStates)
.selectAll("use").data(nodes.filter(node => node.type === "state")).join("use")
.attr("x", d => d.x - d.size / 2).attr("y", d => d.y - d.size / 2)
.attr("width", "1em").attr("height", "1em").attr("data-i", d => d.i);
invokeActiveZooming();
});

View file

@ -795,7 +795,7 @@ function applyDefaultStyle() {
labels.select("#addedLabels").attr("fill", "#3e3e4b").attr("opacity", 1).attr("stroke", "#3a3a3a").attr("stroke-width", 0).attr("font-family", "Almendra SC").attr("data-font", "Almendra+SC").attr("font-size", 18).attr("data-size", 18).attr("filter", null);
fogging.attr("opacity", .98).attr("fill", "#30426f");
emblems.attr("opacity", .9).attr("font-size", 1).attr("size-modifier", 1).attr("filter", null)
emblems.attr("opacity", .9).attr("size-modifier", 1).attr("filter", null)
}
// apply style settings in JSON