chart = {
const scale = 1.03;
const scaleWord = 1.2;
const sw = 0.4;
const so = 0.95;
const k = 2;
const myColor = blackWhite
? (d) => (darkMode ? "white" : "black")
: colorFunc(colorNumber, colorRotation, colorSeed, darkenFactor);
const width = 1500;
const height = width;
const cx = width * 0.5;
const cy = height * 0.5;
const radius = Math.min(width, height) / 2 - 135;
const tree = d3
.cluster()
.size([2 * Math.PI, radius])
.separation((a, b) => {
const depth2Ancestor = a
.ancestors()
.find((ancestor) => ancestor.depth === 2);
const depth3Ancestor = a
.ancestors()
.find((ancestor) => ancestor.depth === 3);
const bDepth2Ancestor = b
.ancestors()
.find((ancestor) => ancestor.depth === 2);
const bDepth3Ancestor = b
.ancestors()
.find((ancestor) => ancestor.depth === 3);
// If both have a common ancestor at depth 3
if (depth3Ancestor && depth3Ancestor === bDepth3Ancestor) {
return 0.6; // Moderate separation for shared depth 3 ancestor
}
// If both have a common ancestor at depth 2
if (depth2Ancestor && depth2Ancestor === bDepth2Ancestor) {
return 0.7; // Larger separation for shared depth 2 ancestor
}
// Default separation for other cases
return 1;
});
// Sort the tree and apply the layout.
const root = tree(hierarchie);
root.leaves().forEach((leaf) => {
leaf.outgoing = [];
leaf.incoming = [];
const link = links.find((node) => node.file === leaf.data.name);
if (link) {
link.links.forEach((targetLink) => {
const targetNode = root.find((d) => d.data.name === targetLink.file);
if (targetNode) {
leaf.outgoing.push([leaf, targetNode]);
console.log(targetNode);
}
});
}
links.forEach((node) => {
node.links.forEach((link) => {
if (link.file === leaf.data.name) leaf.incoming.push([node, leaf]);
});
});
leaf.degree = leaf.incoming.length + leaf.outgoing.length;
});
console.log(root);
// Creates the SVG container.
const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-cx * scale, -cy * scale, width * scale, height * scale])
.attr("style", "width: 100%; height: auto; font: 10px sans-serif;");
if (darkMode)
svg
.append("rect")
.attr("x", -width / 2)
.attr("y", -height / 2)
.attr("width", width)
.attr("height", height)
.attr("fill", "black");
// Append links.
if (structure)
svg
.append("g")
.attr("fill", "none")
.attr("stroke", "#555")
.attr("stroke-opacity", 0.4)
.attr("stroke-width", 1.5)
.selectAll()
.data(root.links())
.join("path")
.attr(
"d",
d3
.linkRadial()
.angle((d) => d.x)
.radius((d) => d.y)
)
.attr("stroke", (d) =>
myColor(
d.target.ancestors().find((ancestor) => ancestor.depth == colorDepth)
)
);
// Append nodes.
svg
.append("g")
.selectAll()
.data(root.leaves())
.join("circle")
.attr(
"transform",
(d) => `rotate(${(d.x * 180) / Math.PI - 90}) translate(${d.y},0)`
)
.attr("fill", (d) => (d.children ? "#555" : "#999"))
.attr("r", (d) => 1 + 0.05 * d.degree)
.attr("fill", (d) => {
const col = myColor(
d.ancestors().find((ancestor) => ancestor.depth == colorDepth)
);
return d.children ? col : darkMode ? darken(col, 0.8) : lighten(col, 0);
// return d.children ? col : darkMode ? darken(col, 0.8) : lighten(col, 0.8);
});
// Append labels.
svg
.append("g")
.attr("stroke-linejoin", "round")
.attr("stroke-width", 3)
.selectAll()
.data(root.leaves())
.join("text")
.attr(
"transform",
(d) =>
`rotate(${(d.x * 180) / Math.PI - 90}) translate(${d.y},0) rotate(${
d.x >= Math.PI ? 180 : 0
})`
)
.attr("dy", "0.31em")
.attr("x", (d) =>
d.x < Math.PI === !d.children ? 6 * scaleWord : -6 * scaleWord
)
.attr("text-anchor", (d) =>
d.x < Math.PI === !d.children ? "start" : "end"
)
.attr("font-size", (d) => 9.5 + 0.05 * d.degree)
.attr("paint-order", "stroke")
.attr("stroke", darkMode ? "black" : "white")
.attr("fill", "currentColor")
.attr("fill", (d) => {
const col = myColor(
d.ancestors().find((ancestor) => ancestor.depth == colorDepth)
);
return darkMode ? lighten(col, 0.4) : col;
})
.text((d) =>
d.height == 0
? links.find((node) => node.file == d.data.name).title
: d.depth
);
if (edge)
svg
.append("g")
.attr("fill", "none")
.attr("stroke-width", sw)
.attr("stroke-opacity", so)
.selectAll("path")
.data(root.leaves().flatMap((leaf) => leaf.outgoing))
.join("path")
.style("mix-blend-mode", darkMode ? "normal" : "multiply")
.style("opacity", so)
.attr("d", ([i, o]) => line(i.path(o)))
.each(function (d) {
d.path = this;
})
.attr("stroke", (d) =>
lighten(
myColor(
d[1].ancestors().find((ancestor) => ancestor.depth == colorDepth)
),
LighenFactor
)
);
const g = svg.append("g");
const drawRingAround = (
level = 2,
radiusCat = 1000,
catRadius = 50,
fontSize = 10,
offAngle = 0,
withText = false,
withEdges = true,
cornerRadius = 5,
arrayName
) => {
let leaves = root.leaves();
let currentCategory = leaves[0]
.ancestors()
.find((ancestor) => ancestor.depth == level).data.name;
console.log(currentCategory);
let currentColor = myColor(
leaves[0].ancestors().find((ancestor) => ancestor.depth == colorDepth)
);
let currentFirstX = leaves[0].x;
let currentLastX = leaves[0].x;
let catArray = [];
for (let i = 1; i < leaves.length; i++) {
if (
leaves[i].ancestors().find((ancestor) => ancestor.depth == level).data
.name !== currentCategory
) {
catArray.push({
color: currentColor,
name: currentCategory,
start: currentFirstX,
end: currentLastX
});
currentColor = myColor(
leaves[i].ancestors().find((ancestor) => ancestor.depth == colorDepth)
);
currentCategory = leaves[i]
.ancestors()
.find((ancestor) => ancestor.depth == level).data.name;
currentFirstX = leaves[i].x;
}
currentLastX = leaves[i].x;
}
catArray.push({
color: currentColor,
name: currentCategory,
start: currentFirstX,
end: currentLastX
});
const catData = catArray.map((d, i) => {
return {
color: d.color,
text: withText ? arrayName[i] : "", //d.name,
startAngle: (d.start / Math.PI) * 180,
endAngle: (d.end / Math.PI) * 180
};
});
drawRing(g, {
fontSize: fontSize,
r: radiusCat,
innerR: catRadius,
opacity: 0,
padAngle: 0,
endsPadAngle: -offAngle,
opaque: false,
//radial: true,
cornerRadius: cornerRadius,
data: catData,
strokeWidth: withEdges ? undefined : 0
});
};
drawRingAround(2, 618, 150, 10, 0.8, false, true, undefined);
drawRingAround(2, 743, 30, 12, 0.8, true, false, undefined, paliCategories);
drawRingAround(3, 621, 125, 10, 0.5, false, true, 3);
drawRingAround(3, 725, 30, 8, 0.5, true, false, 3, categories);
return svg.node();
}