Public
Edited
Nov 25, 2023
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
// Specify the chart’s dimensions.
// const width = 1100;
const height = width;
const cx = width * 0.5; // adjust as needed to fit
const cy = height * 0.5; // adjust as needed to fit
const radius = Math.min(width, height) / 2 + radiusSapce;
const myColor = blackWhite
? (d) => (darkMode ? "white" : "#000")
: colorFunc(colorNumber, colorRotation, colorSeed, darkenFactor);
const offset = 0.1; // for reversing the node at the bottom
// Create a radial cluster layout. The layout’s first dimension (x)
// is the angle, while the second (y) is the radius.
const tree = d3.cluster().size([2 * Math.PI, radius]);

if (leavesGrouping)
tree.separation((a, b) => (a.parent == b.parent ? 1 : 2) / a.depth);

const root = tree(d3.hierarchy(data));

// const childrens2 = root.children;

// const temp2 = childrens2[4];
// childrens2[4] = childrens2[3];
// childrens2[3] = temp2;

// const childrens = root.children[0].children;

// const temp = childrens[6];
// childrens[6] = childrens[4];
// childrens[4] = temp;

// tree(root);

root.x = Math.PI / 2;
root.y = -30;

// Creates the SVG container.

const svgOrigin = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-cx / zoom, -cy / zoom, width / zoom, height / zoom])
.attr("style", "width: 100%; height: auto; font: 10px sans-serif;")
.attr("cursor", "grab");

const svg = svgOrigin.append("g");

if (darkMode)
svg
.append("rect")
.attr("x", -width / 2 / zoom)
.attr("y", -height / 2 / zoom)
.attr("width", width / zoom)
.attr("height", height / zoom)
.attr("fill", "black");

// Append links.
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 == 1))
);

// Append nodes.
svg
.append("g")
.selectAll()
.data(root.descendants())
.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", 2.5)
.attr("fill", (d) => {
const col = myColor(
d.ancestors().find((ancestor) => ancestor.depth == 1)
);
return d.children
? blackWhite
? lighten(col, 1)
: col
: darkMode
? darken(col, 0.8)
: lighten(col, blackWhite ? 2 : 0.8);
});

// multiline https://observablehq.com/@saneef/svg-multiline-text-line-height

// Append labels.
const label = svg
.append("g")
.attr("stroke-linejoin", "round")
.attr("stroke-width", 3)
.selectAll()
.data(root.descendants())
.join("text")
.attr(
"transform",
(d) =>
`rotate(${(d.x * 180) / Math.PI - 90}) translate(${d.y},0) rotate(${
d.x >= Math.PI + offset ? 180 : 0
})`
)
.attr("dy", "0.31em")
.attr("x", (d) => (d.x < Math.PI + offset === !d.children ? 6 : -6))
.attr("text-anchor", (d) =>
d.x < Math.PI + offset === !d.children ? "start" : "end"
)
.attr("paint-order", "stroke")
.attr("stroke", darkMode ? "black" : "white")
.attr("fill", "currentColor")
.attr("fill", (d) =>
myColor(d.ancestors().find((ancestor) => ancestor.depth == 1))
)
.attr("font-size", (d) => (d.depth == 0 ? 12 : d.depth == 1 ? 10 : 10))
.text((d) => d.data.id)
.append("tspan")
.attr("dy", "1em")
.attr("font-size", (d) => (d.depth == 0 ? 9 : d.depth == 1 ? 8 : 8))
.attr("x", (d) => (d.x < Math.PI + offset === !d.children ? 6 : -6))
.text((d) => d.data.data.name);

if (emailPhone)
label
.append("tspan")
.attr("dy", "1em")
.attr("font-size", (d) => (d.depth == 0 ? 8 : d.depth == 1 ? 8 : 8))
.attr("x", (d) => (d.x < Math.PI + offset === !d.children ? 6 : -6))
.text((d) => d.data.data.email)
.append("tspan")
.attr("dy", "1.2em")
.attr("font-size", (d) => (d.depth == 0 ? 8 : d.depth == 1 ? 8 : 8))
.attr("x", (d) => (d.x < Math.PI + offset === !d.children ? 6 : -6))
.text((d) => d.data.data.phone);

if (titleOnTop) drawRing(svg, title);

// zoom

svgOrigin.call(
d3
.zoom()
.scaleExtent([scrollZoom ? 0.1 : 1, scrollZoom ? 8 : 1])
.on("zoom", zoomed)
);

function zoomed({ transform }) {
svg.attr("transform", transform);
}

return svgOrigin.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof p = columns({
colorNumber: Inputs.range([0, 15], {
label: "colorNumber",
step: 1,
value: 10
}),
leavesGrouping: Inputs.toggle({ label: "leavesGrouping", value: false })
})
Insert cell
columns = (args) => {
const form = html`<form></form>`;
form.value = {};

let cols = 0;
for (const key in args) {
form.appendChild(args[key]);
cols++;
}

form.style = `display: grid; grid-gap: 10px 15px; grid-template-columns: repeat(${cols}, auto); grid-auto-flow: row;`;

form.oninput = () => {
form.value = Object.keys(args).reduce(
(result, key) => {
result[key] = args[key].value;
return result;
},
Array.isArray(args) ? [] : {}
);
};
form.oninput();

return form;
}
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