Public
Edited
Oct 4, 2024
2 forks
Insert cell
Insert cell
Insert cell
chart = {
const root = rootWithIngoing;
const offSet = 0;
const off = 38;
const strokeWidth = 2;

const svg = d3
.create("svg")
.attr("viewBox", [-width / 2, -width / 2, width, width]);

const node = svg
.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.selectAll("g")
.data(root.leaves())
.join("g")
.attr(
"transform",
(d) => `rotate(${(d.x * 180) / Math.PI - 90}) translate(${d.y},0)`
)
.append("text")
.attr("dy", "0.31em")
.attr("x", (d) => (d.x < Math.PI ? 6 : -6))
.attr("text-anchor", (d) => (d.x < Math.PI ? "start" : "end"))
.attr("transform", (d) => (d.x >= Math.PI ? "rotate(180)" : null))
.text((d) => {
if (d.x < Math.PI - offSet) {
if (typeof d.data.num !== "undefined") {
if (d.data.num.length <= 4) {
var str = d.data.num;
var nameOnly = str.substring(0, str.length - 1);
var numOnly = str.substring(str.length - 1, str.length);
d.data.num = nameOnly + "\xa0\xa0" + numOnly;
}
return d.data.num + " : " + d.data.name;
} else return d.data.name;
} else {
if (typeof d.data.num !== "undefined") {
if (d.data.num.length <= 4) {
var str = d.data.num;
var nameOnly = str.substring(0, str.length - 1);
var numOnly = str.substring(str.length - 1, str.length);
d.data.num = nameOnly + "\xa0\xa0" + numOnly;
}
return d.data.name + " : " + d.data.num;
} else return d.data.name;
}
})
.each(function (d) {
d.text = this;
})
.on("mouseover", overed)
.on("mouseover", overed2)
.on("mouseout", outed)
.on("mouseout", outed2);
// .call((text) =>
// text.append("title").text(
// (d) => `${id(d)}
// ${d.outgoing.length} outgoing
// ${d.incoming.length} incoming`
// )
// );

const link = svg
.append("g")
.attr("stroke", colornone)
.attr("fill", "none")
.selectAll("path")
.data(root.leaves().flatMap((leaf) => leaf.outgoing))
.join("path")
.style("mix-blend-mode", "multiply")
.style("opacity", 0.5)
.attr("d", ([i, o]) => line(i.path(o)))
.each(function (d) {
d.path = this;
});

const link2 = svg
.append("g")
.attr("stroke", null)
.attr("fill", "none")
.selectAll("path")
.data(root.leaves().flatMap((leaf) => leaf.incoming))
.join("path")
.style("mix-blend-mode", "multiply")
.style("opacity", 0.5)
.attr("d", ([i, o]) => line(i.path(o)))
.each(function (d) {
d.path = this;
});

function overed(event, d) {
link.style("mix-blend-mode", null);
d3.select(this).attr("font-weight", "bold");
d3.selectAll(d.incoming.map((d) => d.path))
.attr("stroke", colorin)
.style("stroke-width", strokeWidth)
.raise();
d3.selectAll(d.incoming.map(([d]) => d.text))
.attr("fill", colorin)
.style("stroke-width", strokeWidth)

.attr("font-weight", "bold");
d3.selectAll(d.outgoing.map((d) => d.path))
.attr("stroke", colorout)
.style("stroke-width", strokeWidth)

.raise();
d3.selectAll(d.outgoing.map(([, d]) => d.text))
.attr("fill", colorout)
.style("stroke-width", strokeWidth)

.attr("font-weight", "bold");
}

function overed2(event, d) {
link.style("mix-blend-mode", null);
d3.select(this).attr("font-weight", "bold");
d3.selectAll(d.incoming.map((d) => d.path))
.attr("stroke", colorin)
.style("stroke-width", strokeWidth)

.raise();
d3.selectAll(d.incoming.map(([d]) => d.text))
.attr("fill", colorin)
.style("stroke-width", strokeWidth)

.attr("font-weight", "bold");
d3.selectAll(d.outgoing.map((d) => d.path))
.attr("stroke", colorout)
.style("stroke-width", strokeWidth)

.raise();
d3.selectAll(d.outgoing.map(([, d]) => d.text))
.attr("fill", colorout)
.style("stroke-width", strokeWidth)

.attr("font-weight", "bold");
}

function outed(event, d) {
link.style("mix-blend-mode", "multiply");
d3.select(this).attr("font-weight", null);
d3.selectAll(d.incoming.map((d) => d.path)).attr("stroke", null);
d3.selectAll(d.incoming.map(([d]) => d.text))
.attr("fill", null)
.attr("font-weight", null);
d3.selectAll(d.outgoing.map((d) => d.path)).attr("stroke", null);
d3.selectAll(d.outgoing.map(([, d]) => d.text))
.attr("fill", null)
.attr("font-weight", null);
}

function outed2(event, d) {
link2.style("mix-blend-mode", "multiply");
d3.select(this).attr("font-weight", null);
d3.selectAll(d.incoming.map((d) => d.path)).attr("stroke", null);
d3.selectAll(d.incoming.map(([d]) => d.text))
.attr("fill", null)
.attr("font-weight", null);
d3.selectAll(d.outgoing.map((d) => d.path)).attr("stroke", null);
d3.selectAll(d.outgoing.map(([, d]) => d.text))
.attr("fill", null)
.attr("font-weight", null);
}

return svg.node();
}
Insert cell
Insert cell
Insert cell
import { rootWithIngoing } from "e2dc343574f8b3c5";
Insert cell
data = hierarchy(await FileAttachment("flare.json").json())
Insert cell
function hierarchy(data, delimiter = ".") {
let root;
const map = new Map;
data.forEach(function find(data) {
const {name} = data;
if (map.has(name)) return map.get(name);
const i = name.lastIndexOf(delimiter);
map.set(name, data);
if (i >= 0) {
find({name: name.substring(0, i), children: []}).children.push(data);
data.name = name.substring(i + 1);
} else {
root = data;
}
return data;
});
return root;
}
Insert cell
Insert cell
function id(node) {
return `${node.parent ? id(node.parent) + "." : ""}${node.data.name}`;
}
Insert cell
colorin = "#00f"
Insert cell
colorout = "#f00"
Insert cell
colornone = "#ccc"
Insert cell
width = 954 * 1.3
Insert cell
radius = width / 2
Insert cell
line = d3.lineRadial()
.curve(d3.curveBundle.beta(0.85))
.radius(d => d.y)
.angle(d => d.x)
Insert cell
tree = d3.cluster()
.size([2 * Math.PI, radius - 100])
Insert cell
d3 = require("d3@6")
Insert cell
# Creative Commons License Deed
## Attribution-NonCommercial 4.0 International (CC BY-NC 4.0)
This is a human-readable summary of (and not a substitute for) the [license](https://creativecommons.org/licenses/by-nc/4.0/legalcode).
### You are free to:
* **Share** — copy and redistribute the material in any medium or format
* **Adapt** — remix, transform, and build upon the material

The licensor cannot revoke these freedoms as long as you follow the license terms.
### Under the following terms:
* **Attribution** — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
* **NonCommercial** — You may not use the material for commercial purposes.
* **No additional restrictions** — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.

### Notices:

You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation.

No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material.
FileAttachment("88x31.png").image()
Insert cell
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