Public
Edited
Mar 19, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
sets = Object.values(forest)
Insert cell
data
Insert cell
root = hierarchy({shift: 2})(wonen)
Insert cell
wonen = ({...data.arguments, children: data.arguments.children.slice(0, 3)})
Insert cell
flourish = hierarchy(argumentMapTreeDebugStyle)
Insert cell
flourish(data)
Insert cell
styles = Object.values(data.nest);
Insert cell
hierarchy =
({shift = 3}) =>
(data) =>
{
let start = 0;
let i = 0;
let yy = 0;
const gap = 0/2;
const middle = (children) => children ? (children[0].yy + children[children.length - 1].yy) / 2 : 0;
// const styles = Object.values(data.nest);
const tree = d3
.hierarchy(data)
;
tree
.eachBefore((node, k) => {
node.yy = yy;
yy += 1 + (node.height === 1 ? node.height : node.height / 2) ;
node.index = (i += node.height + gap);
node.color = node.data.color || node.parent?.color;
node.color = "red";
node.side = node.data?.side || node.parent?.side || +1;
node.count = node.copy().sum((node) => node.children ? 0 : 1).value;
})
.eachAfter(
(node, i) => {
if (node.children)
node.yy = middle(node.children);
node.distance = shift * node.side;
node.x = shift * node.side;
node.y = shift * 2 * node.yy;
}
)
.each(
(node) => {
node.altitude = node.yy - tree.yy;
}
)
return tree;
}
Insert cell
Insert cell
grow =
({
dx = 4,
dy = 144,
stroke = "#555", // stroke for links
strokeWidth = 1,
showText = true,
showDebug = true,
} = {}) => // style & configuration
(root) => // data
{
const isPrompt = (node) => node.height === 0;
const label = d => typeof d === "object" ? d.name : d;
const tree = d3.tree;
const padding = 1;
let height;
const r = 3; // radius of nodes
let link; // given a node d, its link (if any)
const fill = "white"; // fill for nodes
const linkTarget = "_blank"; // the target attribute for links (if any)
const title = (d, n) => `${n.ancestors().reverse().map(d => d.data.name).join(" » ")}`;
const halo = "#fff"; // color of label halo
const haloWidth = 3; // padding around the labels
;

// Compute labels and titles.
const descendants = root.descendants();
const L = label == null ? null : descendants.map(d => label(d.data));

// Compute the layout.
tree()
.nodeSize([dx, dy])
.separation((a, b) => (a.parent == b.parent ? 5:10))
(root);

// Center the tree.
let x0 = Infinity;
let x1 = -x0;
root.each(d => {
if (d.x > x1) x1 = d.x;
if (d.x < x0) x0 = d.x;
});

// Compute the default height.
if (height === undefined) height = x1 - x0 + dx * 2 + 10;

const svg = d3
.create("svg")
.attr("viewBox", [-dy * padding / 2 - 150, x0 - dx - 5, width, height])
.attr("width", width)
.attr("height", height)
.attr("style", "max-width: 100%; height: auto; height: intrinsic;")
.attr("font-family", "monospace")
.attr("font-size", 10)
;

svg
.append("g")
.attr("fill", "none")
.attr("stroke", stroke)
.attr("stroke-opacity", 1)
.attr("stroke-linecap", "round")
.attr("stroke-width", strokeWidth)
.selectAll("path")
.data(root.links())
.join("path")
.attr("d", d3.link(d3.curveBumpX).x(d => d.y).y(d => d.x))
;

const node = svg
.append("g")
.selectAll("a")
.data(root.descendants())
.join("a")
.attr("xlink:href", link == null ? null : d => link(d.data, d))
.attr("target", link == null ? null : linkTarget)
.attr("transform", d => `translate(${d.y},${d.x})`)
;

node
.append("circle")
.attr("fill", d => d.children ? stroke : d.color)
.attr("fill", d => styles[d.depth])
.attr("stroke", stroke)
.attr("stroke-width", strokeWidth)
.attr("r", r)
;

const firstLeaf = (d, i) => d.height === 0 && d.i === 0 ? "visible" : "hidden";
if (L) {
node
.append("rect")
.attr("x", 0)
.attr("y", -2*dx)
.attr("width", 500)
.attr("height", 20)
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("fill", "none")
.attr("rx", 5)
.attr("visibility", firstLeaf)
;
node
.append("text")
.attr("visibility", firstLeaf)
.attr("x", 9)
.attr("y", -dx * 1.5)
.style("font-weight", "bold")
.text(d => d.mother)
;
node
.append("text")
.attr("y", d => d.height === 1 ? 0 * dx : 0)
.attr("dy", "0.32em")
.attr("text-anchor", d => isPrompt(d) ? "start" : "end")
.attr("fill", "black")
.attr("stroke", "white")
.attr("paint-order", "stroke")
.attr("dx", d => isPrompt(d) ? 12 : -12)
.text(text({L, showText, showDebug}))
;
}

if (title != null)
node
.append("title")
.text(d => title(d.data, d))
;

return svg.node();
}
Insert cell
text = ({L, showText, showDebug}) => (d, i) =>
(showDebug ? `«${i}:${d.count}» [${d.depth}, ${d.height}] (${d.x.toFixed(0)}, ${d.y.toFixed(0)})` : "")
+ (showText ? L[i] : "")
Insert cell
argumentMapTreeDebugStyle = [
{
name: "root",
strokeWidth: 54,
connector: {
r: 186/2,
fill: "black",
color: "white", // text color
},
distance: 0,
},
{
name: "category",
strokeWidth: 24,
connector: {
r: 27/2,
fill: "white",
stroke: "black",
strokeWidth: 18,
},
distance: 2,
dx: 15,
dy: d => (d.altitude > 0 ? +18 : -10),
tall: 6 * 5,
wide: 6 * 12,
fontWeight: 900,
fontSize: 12,
},
{
name: "facet",
fontWeight: 900,
strokeWidth: 36,
connector: {
r: 10,
fill: "white",
stroke: "red",
strokeWidth: 48,
},
distance: 7,
dx: 0,
dy: d => -6 * (d.children.length + 1),
},
{
name: "prompt",
connector: {
r: 48,
fill: "white",
stroke: "black",
strokeWidth: 6,
},
distance: 3 * 8, // distance from root
dy: 2,
}
]
Insert cell
import {forest} from "@martien/forest-for-the-trees"
Insert cell
import {colorize} from "@martien/arguments-map"
Insert cell
import {Grid} from "@martien/gridder"
Insert cell
import {swatches} from "@martien/color"
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more