Public
Edited
May 22, 2024
Paused
3 forks
Importers
1 star
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 = {
const root = tree(d3.hierarchy(data));
const offSet = 0.1;
const off = 38;
const svg = d3.create("svg");

const width = 1600,
height = 1600;

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

Object.defineProperty(Array.prototype, "first", {
value() {
return this.find(Boolean);
},
configurable: true
});

root.x = Math.PI / 2;
// root.y = -10;
root.children[3].y = 150;

// Path

svg
.append("g")
.attr("fill", "none")
.attr("stroke", "#555")
.attr("stroke-opacity", 0.4)
.attr("stroke-width", 1.5) // 1.5 for B&W 1.75 good too
.selectAll("path")
.data(root.links())
.join("path")
.attr("stroke", (d) => getBackgroundColor(d.target.data.class, seed))
.attr(
"d",
d3
.linkRadial()
.angle((d) => d.x)
.radius((d) => d.y)
);

// Bullet

svg
.append("g")
.selectAll("circle")
.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("fill", (d) =>
d.children
? getBackgroundColor(d.data.class, seed)
: getBackgroundColorFaded(d.data.class, seed)
)
.attr("r", (d) => {
if (d.depth == 0) {
return 2.25;
} // 11 or 12
else if (d.depth == 1) {
return 1.75;
} // 10 or 11
else if (d.height == 0) {
return 1.75;
} else if (d.depth == 2) {
return 1.75;
}
});

// Text

svg
.append("g")
.attr("font-family", "sans-serif")
// .attr("font-size", 9)
.attr("stroke-linejoin", "round")
.attr("stroke-width", 3)
.selectAll("text")
.data(root.descendants())
.join("text")
.attr("font-size", (d) => {
if (d.depth == 0) {
return 15;
} // 11 or 12
else if (d.depth == 1) {
return 11;
} // 10 or 11
else if (d.height == 0) {
return 10;
} else if (d.depth == 2) {
return 11;
}
})
.attr("fill", (d) => getBackgroundColor(d.data.class, seed))
.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 ? 9 : -9))

.attr("x", (d) => {
if (d.depth == 0) {
return d.x < Math.PI - offSet === !d.children ? 8 : -8;
} // 11 or 12
else if (d.depth == 1) {
return d.x < Math.PI - offSet === !d.children ? 8 : -8;
} // 10 or 11
else if (d.height == 0) {
return d.x < Math.PI - offSet === !d.children ? 9 : -9;
} else if (d.depth == 2) {
return d.x < Math.PI - offSet === !d.children ? 8 : -8;
}
})
.attr("text-anchor", (d) =>
d.x < Math.PI - offSet === !d.children ? "start" : "end"
)
.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;
} 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.num;
} else return d.data.name;
}
})
.clone(true)
.lower()
.attr("stroke", darkMode ? "black" : "white")
.clone(true)
.attr("stroke-width", 0)
.attr("x", (d) => (d.x < Math.PI - offSet === !d.children ? off : -off))
.text((d) => {
if (!d.children)
return d.x > Math.PI - offSet
? d.data.name + " : "
: " : " + d.data.name;
});

////////////////////////////////////////////////////// Start of License ///////////////////////////////////////////////

if (license) {
const fontName = "Poppins";

GFontToDataURI(
"https://fonts.googleapis.com/css2?family=Poppins&display=swap"
)
.then((cssRules) => {
let fontRules = cssRules.join("\n");
d3.select("svg")
.append("defs")
.append("style")
.attr("type", "text/css")
.text(fontRules);
console.log("Added Font");
})
.catch((reason) => console.log(reason));

// svg
// .append("svg:image")
// .attr("x", xLicense - 55)
// .attr("y", yLicense - 43)
// .attr("width", 55)
// // .attr("height", 31)
// .attr(
// "xlink:href",
// // "https://mirrors.creativecommons.org/presskit/buttons/88x31/svg/cc-zero.svg"
// "https://mirrors.creativecommons.org/presskit/buttons/88x31/svg/by-nc.svg"
// // await FileAttachment("88x31-1.png").url()
// )
// .attr("opacity", 0.75);

svg
.append("g")
.attr(
"transform",
` translate(${xLicense - 65}, ${yLicense - 46}) scale(0.55)`
)
//.attr("fill", "#828282")
.attr("opacity", 0.75)
.html(logo);

const text = svg
.append("text")
.attr("x", xLicense)
.attr("y", yLicense)
.style("font-weight", "bold")
.style("fill", "#828282")
.attr("font-family", fontName)
.attr("font-size", fLicense)
.text("DhammaCharts.org")
.attr("text-anchor", "end")
.attr("dy", "-1.1em")
.clone(true)
.text("For free distribution only")
.attr("dy", "0.25em");
// .clone(true)
// .text("No Right reserved")
// .attr("dy", "1.6em");
}

if (licenseNoLogo) {
const fontName = "Poppins";

GFontToDataURI(
"https://fonts.googleapis.com/css2?family=Poppins&display=swap"
)
.then((cssRules) => {
let fontRules = cssRules.join("\n");
d3.select("svg")
.append("defs")
.append("style")
.attr("type", "text/css")
.text(fontRules);
console.log("Added Font");
})
.catch((reason) => console.log(reason));

const text = svg
.append("text")
.attr("x", xLicense)
.attr("y", yLicense)
.style("font-weight", "bold")
.style("fill", "#828282")
.attr("font-family", fontName)
.attr("font-size", fLicense)
.text("CC-BY-NC")
.attr("text-anchor", "end")
.attr("dy", "-2.45em")
.clone(true)
.text("DhammaCharts.org")
.attr("dy", "-1.1em")
.clone(true)
.text("For free distribution only")
.attr("dy", "0.25em");
}

////////////////////////////////////////////////////// End of License ///////////////////////////////////////////////

return svg.attr("viewBox", autoBox).node();
}
Insert cell
yLicense = 765
Insert cell
xLicense = 765
Insert cell
Insert cell
fLicense = 10
Insert cell
lighten = (color, k = 1) => {
const { l, c, h } = d3.lch(color);
return d3.lch(l + 30 * k, c, h);
}
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
import { GFontToDataURI } from "@7722371e7ecac8bd/suttapitaka"
Insert cell
Insert cell
Insert cell
data = {
let root = { name: "Bhikkhu Pāṭimokkha", parentId: null, class: "root" };
const tree = [];
tree.push(root);
const classOff = [];
let chapter = [];
const num = [];
const content = [];
const chapterTest = [];

csvData.forEach((d) => {
d.content = toTitleCase(d.content);
d.gravity = +d.gravity;
// d.related = d3.csvParse(d.related); // this code is for another chart that include relation between rules
// d.related = d.related.columns;
// forEach dosen't work below
// for (var i = 0, len = d.related.length; i < len; i++) {
// d.related[i] = d.related[i].trim();
// }

// class
if (!classOff.includes(d.class)) classOff.push(d.class);
// chapter
d.chapter = d.chapter.replace("The", "");
d.chapter = d.chapter.replace(" Chapter", "");
if (!chapter.includes(d.chapter) && d.chapter != "") {
const pushed = [];
pushed[d.chapter] = d.class;
if (!chapterTest.includes(d.chapter)) {
chapterTest.push(d.chapter);
chapter.push(pushed);
}
}
// num + content
if (!num.includes(d.num) && d.chapter != "") {
const pushed = [];
pushed[d.content] = d.chapter;
pushed[d.content + "Num"] = d.num;
pushed[d.centent + "class"] = d.class;
num.push(pushed);
}
if (!num.includes(d.num) && d.chapter == "") {
const pushed = [];
pushed[d.content] = d.class;
pushed[d.content + "Num"] = d.num;
pushed[d.centent + "class"] = d.class;
num.push(pushed);
}
});

// pushing everything into the tree
// class
for (var i = 0, len = classOff.length; i < len; i++) {
const pushed = {};
pushed.name = classOff[i];
pushed.parentId = Object.values(root)[0];
pushed.class = classOff[i];
tree.push(pushed);
}
// chapter
for (var i = 0, len = chapter.length; i < len; i++) {
const pushed = {};
pushed.name = Object.keys(chapter[i])[0];
pushed.parentId = Object.values(chapter[i])[0];
pushed.class = Object.values(chapter[i])[0];
tree.push(pushed);
}
// num
for (var i = 0, len = num.length; i < len; i++) {
const pushed = {};
pushed.name = Object.keys(num[i])[0];
pushed.parentId = Object.values(num[i])[0];
pushed.num = Object.values(num[i])[1];
pushed.class = Object.values(num[i])[2];
tree.push(pushed);
}

// from https://typeofnan.dev/an-easy-way-to-build-a-tree-with-object-references/
const idMapping = tree.reduce((acc, el, i) => {
acc[el.name] = i;
return acc;
}, {});
tree.forEach((el) => {
if (el.parentId === null) {
root = el;
return;
}
// Use our mapping to locate the parent element in our data array
const parentEl = tree[idMapping[el.parentId]];
// Add our current el to its parent's `children` array
parentEl.children = [...(parentEl.children || []), el];
});
return tree[0];
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
radius = width / 2
Insert cell
tree = d3.cluster().size([2 * Math.PI, radius - 100])
Insert cell
d3 = require("d3@6")
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more