mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 17:51:24 +01:00
feat(charts): control buttons and legend base
This commit is contained in:
parent
9a9c8e700c
commit
4a50d89b41
3 changed files with 87 additions and 49 deletions
|
|
@ -42,7 +42,8 @@ export function open() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function appendStyleSheet() {
|
function appendStyleSheet() {
|
||||||
const styles = /* css */ `
|
const style = document.createElement("style");
|
||||||
|
style.textContent = /* css */ `
|
||||||
div.dialog > div.heightmap-selection {
|
div.dialog > div.heightmap-selection {
|
||||||
width: 70vw;
|
width: 70vw;
|
||||||
height: 70vh;
|
height: 70vh;
|
||||||
|
|
@ -145,8 +146,6 @@ function appendStyleSheet() {
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const style = document.createElement("style");
|
|
||||||
style.appendChild(document.createTextNode(styles));
|
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,8 @@ export function open(props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function appendStyleSheet() {
|
function appendStyleSheet() {
|
||||||
const styles = /* css */ `
|
const style = document.createElement("style");
|
||||||
|
style.textContent = /* css */ `
|
||||||
#hierarchyTree_selectedOrigins > button {
|
#hierarchyTree_selectedOrigins > button {
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
}
|
}
|
||||||
|
|
@ -122,8 +123,6 @@ function appendStyleSheet() {
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const style = document.createElement("style");
|
|
||||||
style.appendChild(document.createTextNode(styles));
|
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,6 @@ const quantizationMap = {
|
||||||
};
|
};
|
||||||
|
|
||||||
appendStyleSheet();
|
appendStyleSheet();
|
||||||
|
|
||||||
insertHtml();
|
insertHtml();
|
||||||
addListeners();
|
addListeners();
|
||||||
changeViewColumns();
|
changeViewColumns();
|
||||||
|
|
@ -204,12 +203,12 @@ changeViewColumns();
|
||||||
export function open() {
|
export function open() {
|
||||||
const charts = byId("chartsOverview__charts").childElementCount;
|
const charts = byId("chartsOverview__charts").childElementCount;
|
||||||
if (!charts) renderChart();
|
if (!charts) renderChart();
|
||||||
|
else $("#chartsOverview").dialog({title: "Data Charts"});
|
||||||
$("#chartsOverview").dialog({title: "Data Charts"});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function appendStyleSheet() {
|
function appendStyleSheet() {
|
||||||
const styles = /* css */ `
|
const style = document.createElement("style");
|
||||||
|
style.textContent = /* css */ `
|
||||||
#chartsOverview {
|
#chartsOverview {
|
||||||
max-width: 90vw !important;
|
max-width: 90vw !important;
|
||||||
max-height: 90vh !important;
|
max-height: 90vh !important;
|
||||||
|
|
@ -237,17 +236,12 @@ function appendStyleSheet() {
|
||||||
|
|
||||||
#chartsOverview__charts figcaption {
|
#chartsOverview__charts figcaption {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
margin-left: 4%;
|
margin: 0 1% 0 4%;
|
||||||
}
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
.chartsOverview__bars {
|
|
||||||
stroke: #666;
|
|
||||||
stroke-width: 0.5;
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const style = document.createElement("style");
|
|
||||||
style.appendChild(document.createTextNode(styles));
|
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -383,22 +377,7 @@ function renderChart(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// based on observablehq.com/@d3/stacked-horizontal-bar-chart
|
// based on observablehq.com/@d3/stacked-horizontal-bar-chart
|
||||||
function plot(
|
function plot(data, {sorting, colors, formatTicks, tooltip}) {
|
||||||
data,
|
|
||||||
{
|
|
||||||
marginTop = 30, // top margin, in pixels
|
|
||||||
marginRight = 10, // right margin, in pixels
|
|
||||||
marginBottom = 10, // bottom margin, in pixels
|
|
||||||
marginLeft = 80, // left margin, in pixels
|
|
||||||
width = 800, // outer width, in pixels
|
|
||||||
xRange = [marginLeft, width - marginRight], // [xmin, xmax]
|
|
||||||
yPadding = 0.2,
|
|
||||||
sorting,
|
|
||||||
colors,
|
|
||||||
formatTicks,
|
|
||||||
tooltip
|
|
||||||
} = {}
|
|
||||||
) {
|
|
||||||
const sortedData = sortData(data, sorting);
|
const sortedData = sortData(data, sorting);
|
||||||
|
|
||||||
const X = sortedData.map(d => d.value);
|
const X = sortedData.map(d => d.value);
|
||||||
|
|
@ -407,11 +386,10 @@ function plot(
|
||||||
|
|
||||||
const yDomain = new Set(Y);
|
const yDomain = new Set(Y);
|
||||||
const zDomain = new Set(Z);
|
const zDomain = new Set(Z);
|
||||||
|
|
||||||
const I = d3.range(X.length).filter(i => yDomain.has(Y[i]) && zDomain.has(Z[i]));
|
const I = d3.range(X.length).filter(i => yDomain.has(Y[i]) && zDomain.has(Z[i]));
|
||||||
|
|
||||||
const height = yDomain.size * 25 + marginTop + marginBottom;
|
const height = yDomain.size * 25 + MARGIN.top + MARGIN.bottom;
|
||||||
const yRange = [height - marginBottom, marginTop];
|
const yRange = [height - MARGIN.bottom, MARGIN.top];
|
||||||
|
|
||||||
const rolled = rollup(...[I, ([i]) => i, i => Y[i], i => Z[i]]);
|
const rolled = rollup(...[I, ([i]) => i, i => Y[i], i => Z[i]]);
|
||||||
|
|
||||||
|
|
@ -429,21 +407,21 @@ function plot(
|
||||||
const xDomain = d3.extent(series.map(d => d.data).flat(2));
|
const xDomain = d3.extent(series.map(d => d.data).flat(2));
|
||||||
|
|
||||||
const xScale = d3.scaleLinear(xDomain, xRange);
|
const xScale = d3.scaleLinear(xDomain, xRange);
|
||||||
const yScale = d3.scaleBand(Array.from(yDomain), yRange).paddingInner(yPadding);
|
const yScale = d3.scaleBand(Array.from(yDomain), yRange).paddingInner(Y_PADDING);
|
||||||
|
|
||||||
const xAxis = d3.axisTop(xScale).ticks(width / 80, null);
|
const xAxis = d3.axisTop(xScale).ticks(WIDTH / 80, null);
|
||||||
const yAxis = d3.axisLeft(yScale).tickSizeOuter(0);
|
const yAxis = d3.axisLeft(yScale).tickSizeOuter(0);
|
||||||
|
|
||||||
const svg = d3
|
const svg = d3
|
||||||
.create("svg")
|
.create("svg")
|
||||||
.attr("width", width)
|
.attr("version", "1.1")
|
||||||
.attr("height", height)
|
.attr("xmlns", "http://www.w3.org/2000/svg")
|
||||||
.attr("viewBox", [0, 0, width, height])
|
.attr("viewBox", [0, 0, WIDTH, height])
|
||||||
.attr("style", "max-width: 100%; height: auto; height: intrinsic;");
|
.attr("style", "max-width: 100%; height: auto; height: intrinsic;");
|
||||||
|
|
||||||
svg
|
svg
|
||||||
.append("g")
|
.append("g")
|
||||||
.attr("transform", `translate(0,${marginTop})`)
|
.attr("transform", `translate(0,${MARGIN.top})`)
|
||||||
.call(xAxis)
|
.call(xAxis)
|
||||||
.call(g => g.select(".domain").remove())
|
.call(g => g.select(".domain").remove())
|
||||||
.call(g => g.selectAll("text").text(d => formatTicks(d)))
|
.call(g => g.selectAll("text").text(d => formatTicks(d)))
|
||||||
|
|
@ -451,13 +429,14 @@ function plot(
|
||||||
g
|
g
|
||||||
.selectAll(".tick line")
|
.selectAll(".tick line")
|
||||||
.clone()
|
.clone()
|
||||||
.attr("y2", height - marginTop - marginBottom)
|
.attr("y2", height - MARGIN.top - MARGIN.bottom)
|
||||||
.attr("stroke-opacity", 0.1)
|
.attr("stroke-opacity", 0.1)
|
||||||
);
|
);
|
||||||
|
|
||||||
const bar = svg
|
const bar = svg
|
||||||
.append("g")
|
.append("g")
|
||||||
.attr("class", "chartsOverview__bars")
|
.attr("stroke", "#666")
|
||||||
|
.attr("stroke-width", 0.5)
|
||||||
.selectAll("g")
|
.selectAll("g")
|
||||||
.data(series)
|
.data(series)
|
||||||
.join("g")
|
.join("g")
|
||||||
|
|
@ -477,6 +456,37 @@ function plot(
|
||||||
.attr("transform", `translate(${xScale(0)},0)`)
|
.attr("transform", `translate(${xScale(0)},0)`)
|
||||||
.call(yAxis);
|
.call(yAxis);
|
||||||
|
|
||||||
|
const groups = Array.from(zDomain);
|
||||||
|
const minWidth = d3.max(groups.map(name => name.length)) * 8;
|
||||||
|
const maxInRow = Math.floor(WIDTH / minWidth);
|
||||||
|
const rows = Math.ceil(groups.length / maxInRow);
|
||||||
|
const rowElements = Math.floor(groups.length / rows);
|
||||||
|
|
||||||
|
const columnWidth = WIDTH / (rowElements + 0.5);
|
||||||
|
const rowHeight = 20;
|
||||||
|
|
||||||
|
const legend = svg
|
||||||
|
.append("g")
|
||||||
|
.attr("dominant-baseline", "central")
|
||||||
|
.attr("transform", `translate(${MARGIN.left},${height - MARGIN.bottom + 15})`);
|
||||||
|
|
||||||
|
legend
|
||||||
|
.selectAll("circle")
|
||||||
|
.data(groups)
|
||||||
|
.join("circle")
|
||||||
|
.attr("cx", (d, i) => (i % rowElements) * columnWidth)
|
||||||
|
.attr("cy", (d, i) => Math.floor(i / rowElements) * rowHeight)
|
||||||
|
.attr("r", 6)
|
||||||
|
.attr("fill", d => colors[d]);
|
||||||
|
|
||||||
|
legend
|
||||||
|
.selectAll("text")
|
||||||
|
.data(groups)
|
||||||
|
.join("text")
|
||||||
|
.attr("x", (d, i) => (i % rowElements) * columnWidth + 8)
|
||||||
|
.attr("y", (d, i) => Math.floor(i / rowElements) * rowHeight)
|
||||||
|
.text(d => d);
|
||||||
|
|
||||||
return svg.node();
|
return svg.node();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -487,12 +497,32 @@ function insertChart(chart, title) {
|
||||||
const $caption = document.createElement("figcaption");
|
const $caption = document.createElement("figcaption");
|
||||||
|
|
||||||
const figureNo = $chartContainer.childElementCount + 1;
|
const figureNo = $chartContainer.childElementCount + 1;
|
||||||
$caption.innerHTML = `<strong>Figure ${figureNo}</strong>. ${title}`;
|
$caption.innerHTML = /* html */ `
|
||||||
|
<div>
|
||||||
|
<strong>Figure ${figureNo}</strong>. ${title}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button class="icon-download"></button>
|
||||||
|
<button class="icon-trash"></button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
$figure.appendChild(chart);
|
$figure.appendChild(chart);
|
||||||
$figure.appendChild($caption);
|
$figure.appendChild($caption);
|
||||||
|
|
||||||
$chartContainer.appendChild($figure);
|
$chartContainer.appendChild($figure);
|
||||||
|
|
||||||
|
const downloadChart = () => {
|
||||||
|
const name = `${getFileName(title)}.svg`;
|
||||||
|
downloadFile(chart.outerHTML, name);
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeChart = () => {
|
||||||
|
$figure.remove();
|
||||||
|
updateDialog();
|
||||||
|
};
|
||||||
|
|
||||||
|
$figure.querySelector("button.icon-download").on("click", downloadChart);
|
||||||
|
$figure.querySelector("button.icon-trash").on("click", removeChart);
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeViewColumns() {
|
function changeViewColumns() {
|
||||||
|
|
@ -503,14 +533,24 @@ function changeViewColumns() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateDialog() {
|
function updateDialog() {
|
||||||
$("#chartsOverview").dialog({position: {my: "center", at: "center", of: window}});
|
$("#chartsOverview").dialog({position: {my: "center", at: "center", of: "svg"}});
|
||||||
}
|
}
|
||||||
|
|
||||||
// config
|
// config
|
||||||
const NEUTRAL_COLOR = "#ccc";
|
const NEUTRAL_COLOR = "#ccc";
|
||||||
const EMPTY_NAME = "no";
|
const EMPTY_NAME = "no";
|
||||||
|
|
||||||
// helper functions
|
const MARGIN = {
|
||||||
|
top: 30,
|
||||||
|
right: 10,
|
||||||
|
bottom: 50,
|
||||||
|
left: 80
|
||||||
|
};
|
||||||
|
|
||||||
|
const WIDTH = 800;
|
||||||
|
const xRange = [MARGIN.left, WIDTH - MARGIN.right];
|
||||||
|
const Y_PADDING = 0.2;
|
||||||
|
|
||||||
function nameGetter(entity) {
|
function nameGetter(entity) {
|
||||||
return i => pack[entity][i].name || EMPTY_NAME;
|
return i => pack[entity][i].name || EMPTY_NAME;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue