mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 09:41:24 +01:00
feat(hierarchy tree): UI buttons to remove origin, zoom
This commit is contained in:
parent
182c6d558d
commit
127eafb656
1 changed files with 77 additions and 31 deletions
|
|
@ -261,7 +261,7 @@ function getTypeOptions(type) {
|
||||||
function religionHighlightOn(event) {
|
function religionHighlightOn(event) {
|
||||||
const religionId = Number(event.id || event.target.dataset.id);
|
const religionId = Number(event.id || event.target.dataset.id);
|
||||||
const $info = byId("religionInfo");
|
const $info = byId("religionInfo");
|
||||||
if ($info) {
|
if ($info && religionId) {
|
||||||
d3.select("#hierarchy").select(`g[data-id='${religionId}']`).classed("selected", 1);
|
d3.select("#hierarchy").select(`g[data-id='${religionId}']`).classed("selected", 1);
|
||||||
const {name, type, form, rural, urban} = pack.religions[religionId];
|
const {name, type, form, rural, urban} = pack.religions[religionId];
|
||||||
|
|
||||||
|
|
@ -563,39 +563,55 @@ function showHierarchy() {
|
||||||
const validReligions = pack.religions.filter(r => !r.removed);
|
const validReligions = pack.religions.filter(r => !r.removed);
|
||||||
if (validReligions.length < 3) return tip("Not enough religions to show hierarchy", false, "error");
|
if (validReligions.length < 3) return tip("Not enough religions to show hierarchy", false, "error");
|
||||||
|
|
||||||
const root = d3
|
const getRoot = () =>
|
||||||
|
d3
|
||||||
.stratify()
|
.stratify()
|
||||||
.id(d => d.i)
|
.id(d => d.i)
|
||||||
.parentId(d => d.origins[0])(validReligions);
|
.parentId(d => d.origins[0])(validReligions);
|
||||||
const treeWidth = root.leaves().length;
|
|
||||||
const treeHeight = root.height;
|
const root = getRoot();
|
||||||
const width = Math.max(treeWidth * 40, 300);
|
const treeWidth = root.leaves().length * 50;
|
||||||
const height = treeHeight * 60;
|
const treeHeight = root.height * 50;
|
||||||
|
|
||||||
const margin = {top: 10, right: 10, bottom: -5, left: 10};
|
const margin = {top: 10, right: 10, bottom: -5, left: 10};
|
||||||
const w = width - margin.left - margin.right;
|
const w = treeWidth - margin.left - margin.right;
|
||||||
const h = height + 30 - margin.top - margin.bottom;
|
const h = treeHeight + 30 - margin.top - margin.bottom;
|
||||||
const treeLayout = d3.tree().size([w, h]);
|
const treeLayout = d3.tree().size([w, h]);
|
||||||
|
|
||||||
alertMessage.innerHTML = /* html */ `<div id="religionChartDetails" class='chartInfo'>
|
const width = minmax(treeWidth, 300, innerWidth * 0.75);
|
||||||
|
const height = minmax(treeHeight, 200, innerHeight * 0.75);
|
||||||
|
|
||||||
|
alertMessage.innerHTML = /* html */ `<div id="religionChart" style="overflow: hidden; width: ${width}px">
|
||||||
|
<div id="religionChartDetails" class='chartInfo'>
|
||||||
<div id='religionInfo' style="display: block">‍</div>
|
<div id='religionInfo' style="display: block">‍</div>
|
||||||
<div id='religionSelected' style="display: none">
|
<div id='religionSelected' style="display: none">
|
||||||
<span><span id='religionSelectedName'></span>. </span>
|
<span><span id='religionSelectedName'></span>. </span>
|
||||||
<span data-name="Type religion short name (abbreviation)">Abbreviation: <input id='religionSelectedCode' type='text' maxlength='3' size='3' /></span>
|
<span data-name="Type religion short name (abbreviation)">Abbreviation: <input id='religionSelectedCode' type='text' maxlength='3' size='3' /></span>
|
||||||
<button data-tip='Clear origin, religion will be linked to top level' id='religionSelectedClear'>Clear</button>
|
<span id='religionSelectedOrigins'></span>
|
||||||
<button data-tip='Close edit mode' id='religionSelectedClose'>Close</button>
|
<button data-tip='Close edit mode' id='religionSelectedClose'>Close</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
// prepare svg
|
// prepare svg
|
||||||
|
const zoom = d3
|
||||||
|
.zoom()
|
||||||
|
.extent([Array(2).fill(0), [width, height]])
|
||||||
|
.scaleExtent([0.2, 1.5])
|
||||||
|
.on("zoom", () => {
|
||||||
|
viewbox.attr("transform", d3.event.transform);
|
||||||
|
});
|
||||||
|
|
||||||
const svg = d3
|
const svg = d3
|
||||||
.select("#alertMessage")
|
.select("#religionChart")
|
||||||
.insert("svg", "#religionChartDetails")
|
.insert("svg", "#religionChartDetails")
|
||||||
.attr("id", "hierarchy")
|
.attr("id", "hierarchy")
|
||||||
.attr("width", width)
|
.attr("viewBox", [0, 0, width, height])
|
||||||
.attr("height", height)
|
.style("text-anchor", "middle")
|
||||||
.style("text-anchor", "middle");
|
.call(zoom);
|
||||||
const graph = svg.append("g").attr("transform", `translate(10, -45)`);
|
|
||||||
|
const viewbox = svg.append("g");
|
||||||
|
const graph = viewbox.append("g").attr("transform", `translate(10, -45)`);
|
||||||
const links = graph.append("g").attr("fill", "none").attr("stroke", "#aaaaaa");
|
const links = graph.append("g").attr("fill", "none").attr("stroke", "#aaaaaa");
|
||||||
const primaryLinks = links.append("g");
|
const primaryLinks = links.append("g");
|
||||||
const secondaryLinks = links.append("g").attr("stroke-dasharray", 1);
|
const secondaryLinks = links.append("g").attr("stroke-dasharray", 1);
|
||||||
|
|
@ -637,8 +653,9 @@ function showHierarchy() {
|
||||||
|
|
||||||
const getNodePath = d => nodePathMap[d.data.type];
|
const getNodePath = d => nodePathMap[d.data.type];
|
||||||
|
|
||||||
renderTree();
|
renderTree(root, treeLayout);
|
||||||
function renderTree() {
|
|
||||||
|
function renderTree(root, treeLayout) {
|
||||||
treeLayout(root);
|
treeLayout(root);
|
||||||
|
|
||||||
primaryLinks.selectAll("path").data(root.links()).enter().append("path").attr("d", getLinkPath);
|
primaryLinks.selectAll("path").data(root.links()).enter().append("path").attr("d", getLinkPath);
|
||||||
|
|
@ -669,10 +686,24 @@ function showHierarchy() {
|
||||||
.text(d => d.data.code || "");
|
.text(d => d.data.code || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function rerenderTree() {
|
||||||
|
nodes.selectAll("*").remove();
|
||||||
|
primaryLinks.selectAll("*").remove();
|
||||||
|
secondaryLinks.selectAll("*").remove();
|
||||||
|
|
||||||
|
const root = getRoot();
|
||||||
|
const treeWidth = root.leaves().length * 50;
|
||||||
|
const treeHeight = root.height * 50;
|
||||||
|
|
||||||
|
const w = treeWidth - margin.left - margin.right;
|
||||||
|
const h = treeHeight + 30 - margin.top - margin.bottom;
|
||||||
|
const treeLayout = d3.tree().size([w, h]);
|
||||||
|
|
||||||
|
renderTree(root, treeLayout);
|
||||||
|
}
|
||||||
|
|
||||||
$("#alert").dialog({
|
$("#alert").dialog({
|
||||||
title: "Religions tree",
|
title: "Religions tree",
|
||||||
width: fitContent(),
|
|
||||||
resizable: false,
|
|
||||||
position: {my: "left center", at: "left+10 center", of: "svg"},
|
position: {my: "left center", at: "left+10 center", of: "svg"},
|
||||||
buttons: {},
|
buttons: {},
|
||||||
close: () => {
|
close: () => {
|
||||||
|
|
@ -699,9 +730,24 @@ function showHierarchy() {
|
||||||
religion.code = this.value;
|
religion.code = this.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
byId("religionSelectedClear").onclick = () => {
|
byId("religionSelectedOrigins").innerHTML = religion.origins
|
||||||
religion.origins = [0];
|
.filter(origin => origin)
|
||||||
showHierarchy();
|
.map((origin, index) => {
|
||||||
|
const {name, code} = validReligions.find(r => r.i === origin) || {};
|
||||||
|
const type = index ? "Secondary" : "Primary";
|
||||||
|
const tip = `${type} origin: ${name}. Click to remove link to that origin`;
|
||||||
|
return `<button data-id="${origin}" class='religionSelectedOrigin' data-tip="${tip}">${code}</button>`;
|
||||||
|
})
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
byId("religionSelectedOrigins").onclick = function (event) {
|
||||||
|
const target = event.target;
|
||||||
|
if (target.tagName !== "BUTTON") return;
|
||||||
|
const origin = Number(target.dataset.id);
|
||||||
|
const filtered = religion.origins.filter(religionOrigin => religionOrigin !== origin);
|
||||||
|
religion.origins = filtered.length ? filtered : [0];
|
||||||
|
target.remove();
|
||||||
|
rerenderTree();
|
||||||
};
|
};
|
||||||
|
|
||||||
byId("religionSelectedClose").onclick = () => {
|
byId("religionSelectedClose").onclick = () => {
|
||||||
|
|
@ -733,7 +779,7 @@ function showHierarchy() {
|
||||||
if (religion.origins[0] === 0) religion.origins = [];
|
if (religion.origins[0] === 0) religion.origins = [];
|
||||||
religion.origins.push(newOrigin);
|
religion.origins.push(newOrigin);
|
||||||
|
|
||||||
showHierarchy();
|
rerenderTree();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue