chart = {
const off = 80;
const offDNMN = 32;
let scale = 2.5;
const sizeDepth0 = 28;
const sizeDepth1 = 21;
const sizeDepth2 = 16;
const sizeHeight0 = 8.5;
const sizeHeight1 = 12;
const size = 12;
const bulletDepth0 = 2.5;
const bulletDepth1 = 2;
const bullet = 2;
const bulletHeight1 = 1.5;
const bulletHeight0 = 1.5;
let divisor = 1.035 + 0.008 - 0.004 - 0.007;
let angleDeg = -177.3 + 1.4 - 0.6 - 0.1;
let angleRad = (Math.PI / 180) * angleDeg;
let sepDeg = 1.2;
let sepRad = (Math.PI / 180) * sepDeg;
const root = tree(data);
let heightSN = 800;
root.each(function (d) {
d.y = d.y * scale;
if (d.data.name == "Devatāsaṁyutta") heightSN = d.y;
if (
d
.ancestors()
.find(
(x) =>
x.data.name == "Nidānasaṁyutta" ||
x.data.name == "Khandhasaṁyutta" ||
x.data.name == "Saḷāyatanasaṁyutta"
) &&
d.height == 1
) {
d.y = 890;
d.data.spec = "fontSN";
}
// if (d.height == 1 || d.height == 0) d.y = 600;
// if (d.height == 2) d.y = 490;
//if (d.height == 1) d.y = 800;
if (d.height == 2) d.y = heightSN;
// if (d.height == 3) d.y = 280;
if (d.depth == 1) d.y = 160 + 100 + 25 + 5;
if (d.depth == 2 && !d.data.acro?.includes("DN")) d.y = 400 + 100 + 30;
//if (d.depth == 1) d.data.name = d.data.name.replace(str, "");
if (d.depth == 0) d.x = (Math.PI / 2 - angleRad) * divisor;
//if (d.depth == 0) d.x = (2 * Math.PI) / 2;
if (d.depth == 0) d.y = -70;
if (d.ancestors().find((x) => x.data.name == "Majjhimanikāya"))
d.x = d.x + sepRad;
if (d.ancestors().find((x) => x.data.name == "Saṁyuttanikāya"))
d.x = d.x + 2 * sepRad;
if (d.ancestors().find((x) => x.data.name == "Aṅguttaranikāya"))
d.x = d.x + 3 * sepRad;
if (d.data.id == "test") {
d.y = 340 + 60 - 30 - 31; // could be calculated exaclty fo kn branch
d.x = (Math.PI - angleRad) * divisor;
}
if (d.data.id == "test2") {
d.y = 340 + 60 + 60; // could be calculated exaclty fo kn branch
// d.y = 340 + 60 - 30 + 200; // could be calculated exaclty fo kn branch
d.x = (Math.PI - angleRad) * divisor;
}
});
// testing
// console.log(root);
// fonction d'apartenance à DN ou MN (utile pour les acros)
const isDNMN = (d) =>
d.data.acro?.includes("DN") || d.data.acro?.includes("MN");
// fonction pour la taille de font
let fontSize = (d) => {
if (d.height == 0) {
return sizeHeight0;
} else if (d.data.spec == "fontSN") {
return sizeHeight0;
} else if (d.data.id.slice(0, 2) == "sn" && d.height == 1) {
return sizeHeight1 - 1;
} else if (d.depth == 1) {
return sizeDepth1;
} else if (d.height == 1 && !d.data.acro.includes("AN")) {
return sizeHeight1;
} else if (d.depth == 2) {
return sizeDepth2;
} else if (d.depth == 0) {
return sizeDepth0;
} else return size;
};
// fonction pour la taille des bullets
let bulletSize = (d) => {
if (d.depth == 0) {
return bulletDepth0;
} else if (
d.data.acro == "SN 12" ||
d.data.acro == "SN 22" ||
d.data.acro == "SN 35"
) {
return bulletHeight1;
} else if (d.data.id == "test" || d.data.id == "test2") {
return 0;
} else if (d.depth == 1) {
return bulletDepth1;
} else if (d.height == 0) {
return bulletHeight0;
} else if (d.height == 1 && !d.data.acro.includes("AN")) {
return bulletHeight1;
} else return bullet;
};
// Fonction qui retourne le nom de l'ancestre de depth 1
// utile pour la colorisation
let oneDepth = (d, source = false) => {
let ancestors = {};
source == true
? (ancestors = d.target.ancestors()) // source <=> target for a different result
: (ancestors = d.ancestors());
const oneDepth = ancestors.find((d) => d.depth == 1);
const result = oneDepth == null ? d.data.name : oneDepth.data.name;
// for some reason it dosen't start with sutta pitaka anymore
return result;
// return oneDepth?.data.name;
};
let color = d3.scaleOrdinal(domain);
// let colorFaded = d3.scaleOrdinal(domainFaded);
let isLeaf = (d) => {
let bool = false;
if (d.height == 0) bool = true;
return bool;
};
const fade = (color) => {
const c = d3.color(color);
c.l += 0.25;
return c + "";
};
// création du diagramme //////////////////////////////////////////////////////////////////////////////////////////////
const svg = d3.create("svg");
const g = svg.append("g").attr("cursor", "grab");
// paths
g.append("g")
.attr("fill", "none")
.attr("stroke", "#555")
.attr("stroke-opacity", 0.4)
.attr("stroke-width", 1.5)
.selectAll("path")
.data(root.links())
.join("path")
.attr(
"d",
d3
.linkRadial()
.angle((d) => d.x / divisor + angleRad)
.radius((d) => d.y)
)
.attr("stroke", (d) => color(oneDepth(d, true))); // colorisation
// bullet
g.append("g")
.selectAll("circle")
.data(root.descendants())
.join("circle")
.attr(
"transform",
(d) => `
rotate(${(d.x * 180) / divisor / Math.PI - 90 + angleDeg})
translate(${d.y},0)
`
)
.attr("r", (d) => bulletSize(d))
.attr("fill", (d) =>
isLeaf(d) ? fade(color(oneDepth(d))) : color(oneDepth(d))
); // colorisation
// text
g.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("stroke-linejoin", "round")
.attr("stroke-width", 3)
.selectAll("text")
.data(root.descendants())
.join("text")
.attr("font-size", (d) => fontSize(d))
.style("fill", (d) => color(oneDepth(d))) // colorisation
.attr(
"transform",
(d) => `
rotate(${(d.x * 180) / divisor / Math.PI - 90 + angleDeg})
translate(${d.y},0)
rotate(${
d.x > (Math.PI - angleRad) * divisor || d.x < -angleRad * divisor
? 180
: 0
})
`
)
.attr("dy", "0.31em")
.attr("x", (d) =>
(d.x > (Math.PI - angleRad) * divisor || d.x < -angleRad * divisor) ===
!d.children
? -6
: 6
)
.attr("text-anchor", (d) =>
(d.x > (Math.PI - angleRad) * divisor || d.x < -angleRad * divisor) ===
!d.children
? "end"
: "start"
)
.text((d) => {
if (d.depth == 0 || d.depth == 1) return d.data.name;
if (d.height != 0 && isDNMN(d)) return d.data.name;
if (d.data.acro == "") return d.data.name; // not usefull for KN
if (d.x > (Math.PI - angleRad) * divisor || d.x < -angleRad * divisor) {
return !d.children
? d.data.name + " : " + d.data.acro
: d.data.acro + " : " + d.data.name;
} else {
return !d.children
? d.data.acro + " : " + d.data.name
: d.data.name + " : " + d.data.acro;
}
})
.style("cursor", "pointer")
.on("click", function (d, i) {
// const link = "https://suttacentral.net/" + i.data.id + "/pli/ms";
let link = "https://suttacentral.net/" + i.data.id;
if (i.data.id == "suttaPitaka")
link = "https://suttacentral.net/pitaka/sutta";
window.open(link);
})
.clone(true)
.lower()
.attr("stroke", "white");
////////////////////////////////////////////////////// KN chart //////////////////////////////////////////////////////
/// add chart2 methode 1 ///
// const svg2 = chart2.node().innerHTML;
// g.append("g").attr("transform", "translate(0,1300)").html(svg2);
/// add chart2 methode 2 /// (to allow use of .on("click"))
scale = 3.5;
divisor = 1.5; // division of the full circle // 1.8
angleDeg = -30 + 90; // angle offset from midday position // -10 is interesting
angleRad = (Math.PI / 180) * angleDeg;
const translation = 1270;
data2.each(function (d) {
if (
d.ancestors().find(
(x) =>
x.data.id != "kn" &&
x.data.id != "kp" &&
x.data.id != "dhp" &&
x.data.id != "bv" &&
x.data.id != "mnd" &&
x.data.id != "mnd" &&
!x.data.id.slice(0, 3) == "cnd" &&
!x.data.id.slice(0, 2) == "cp" &&
!x.data.id.slice(0, 2) == "ps" &&
!x.data.id.slice(0, 2) == "ne" &&
// x.data.id != "ne" &&
x.data.id != "pe"
) &&
d.height == 1
)
d.children = null;
});
const root2 = tree(data2);
root2.each(function (d) {
d.y = d.y * scale;
if (d.height == 1 || d.height == 0) d.y = 600 + 230 + 100 + 40;
if (d.height == 2) d.y = 490 + 230 + 30 + 30;
if (d.depth == 1) d.y = 260 + 100 + 30;
if (d.depth == 0) d.x = Math.PI;
// if (d.depth == 0) d.x = (Math.PI / 2 - angleRad) * divisor;
if (d.depth == 0) d.y = -9 * 80 - 90 - 30 - 20;
if (d.data.name == "test") d.y = 800;
if (d.data.id.slice(0, 3) == "cnd" && d.height == 1)
d.y = 490 + 230 + 30 + 30;
if (d.data.id.slice(0, 3) == "cnd" && d.height == 1) d.data.acro = "";
// if (d.data.id.includes("cnd") && d.height == 2) d.y = 490 + 100;
if (d.data.id.slice(0, 2) == "cp" && d.height == 1)
d.y = 490 + 230 + 30 + 30;
if (d.data.id.slice(0, 2) == "cp" && d.height == 2) d.y = 490 + 100;
if (d.data.id.slice(0, 2) == "cp" && d.height == 2) d.data.acro = "";
if (d.data.id.slice(0, 2) == "ps" && d.height == 1)
d.y = 490 + 230 + 30 + 30;
if (d.data.id.slice(0, 2) == "ps" && d.height == 1) d.data.acro = "";
// if (d.data.id.includes("cp") && d.height == 3) d.y = 490;
if (d.data.id.slice(0, 2) == "ne" && d.height == 1)
d.y = 490 + 230 + 30 + 30;
if (d.data.id.slice(0, 2) == "ne" && d.height == 2) d.y = 490 + 100;
if (d.data.id.slice(0, 2) == "ne" && d.height == 1) d.data.acro = "";
// if (d.data.id.includes("ne") && d.height == 3) d.y = 490;
// if (data.id.slice(0, 3) == "cnd" && d.height == 1) d.y = 490 + 230 + 30;
});
// fonction pour la taille de font
fontSize = (d) => {
if (d.height == 0) {
return sizeHeight0;
} else if (d.data.id.slice(0, 2) == "ne" && d.height == 1) {
return sizeHeight1;
} else if (d.data.id.slice(0, 2) == "ps" && d.height == 1) {
return sizeHeight1;
} else if (d.data.id.slice(0, 3) == "cnd" && d.height == 1) {
return sizeHeight1;
} else if (d.data.id.slice(0, 2) == "cp" && d.height == 1) {
return sizeHeight1;
} else if (d.data.id.slice(0, 4) == "thag" && d.height == 2) {
return sizeHeight1 - 2;
} else if (d.data.id.slice(0, 2) == "ja" && d.height == 2) {
return sizeHeight1 - 1;
} else if (d.depth == 1) {
return sizeDepth2;
} else if (d.height == 1) {
return sizeHeight0;
} else if (d.depth == 2) {
return sizeHeight1;
} else if (d.depth == 0) {
return sizeDepth1;
} else return size;
};
// let bulletSize = (d) => {
// if (d.depth == 0) {
// return bulletDepth0;
// } else if (d.depth == 1) {
// return bulletDepth1;
// } else if (d.height == 0) {
// return bulletHeight0;
// } else if (d.height == 1 && !d.data.acro.includes("AN")) {
// return bulletHeight1;
// } else return bullet;
// };
oneDepth = (d, source = false) => {
let ancestors = {};
source == true
? (ancestors = d.target.ancestors()) // source <=> target for a different result
: (ancestors = d.ancestors());
const oneDepth = ancestors.find((d) => d.depth == 0);
const result = oneDepth == null ? d.data.name : oneDepth.data.name;
// for some reason it dosen't start with sutta pitaka anymore
return result;
// return oneDepth?.data.name;
};
isLeaf = (d) => {
let bool = false;
if (d.height == 0) bool = true;
if (
d.height == 1 &&
d.data.id.slice(0, 2) != "cp" &&
d.data.id.slice(0, 2) != "kp" &&
d.data.id.slice(0, 3) != "dhp" &&
d.data.id.slice(0, 3) != "cnd" &&
d.data.id.slice(0, 2) != "ne" &&
d.data.id.slice(0, 3) != "mnd" &&
d.data.id.slice(0, 2) != "ps" &&
d.data.id.slice(0, 2) != "bv" &&
d.data.id.slice(0, 2) != "pe"
)
bool = true;
// if (d.height )
return bool;
};
// fonction pour la taille des bullets
bulletSize = (d) => {
if (d.depth == 0) {
return bulletDepth1;
} else if (d.depth == 1) {
return bulletDepth1;
} else {
return bulletHeight0;
}
// } else if (d.data.id.slice(0, 2) == "ne" && d.height == 0) {
// return bulletHeight0;
// } else if (d.data.id.slice(0, 2) == "ps" && d.height == 0) {
// return bulletHeight0;
// } else if (d.data.id.slice(0, 3) == "cnd" && d.height == 0) {
// return bulletHeight0;
// } else if (d.data.id.slice(0, 2) == "cp" && d.height == 0) {
// return bulletHeight0;
// } else if (d.data.id.slice(0, 4) == "thag" && d.height == 2) {
// return sizeHeight1 - 2;
// } else if (d.data.id.slice(0, 2) == "ja" && d.height == 2) {
// return sizeHeight1 - 1;
// } else if (d.height == 0 || d.height == 1) {
// return bulletHeight0;
// } else return bulletHeight1;
};
// const color = () => col1;
color = d3.scaleOrdinal(domain);
color("1");
color("2");
color("3");
color("4");
color("5");
color("6");
//////////////////// diagrame KN ////////////////
const g2 = g
.append("g")
.attr("transform", "translate(0," + translation + ")");
// paths
g2.append("g")
.attr("fill", "none")
.attr("stroke", "#555")
.attr("stroke-opacity", 0.4)
.attr("stroke-width", 0.8)
.selectAll("path")
.data(root2.links())
.join("path")
.attr(
"d",
d3
.linkRadial()
.angle((d) => d.x / divisor + angleRad)
.radius((d) => d.y)
)
.attr("stroke", (d) => color(oneDepth(d, true))); // colorisation
// bullet
g2.append("g")
.selectAll("circle")
.data(root2.descendants())
.join("circle")
.attr(
"transform",
(d) => `
rotate(${(d.x * 180) / divisor / Math.PI - 90 + angleDeg})
translate(${d.y},0)
`
)
.attr("fill", (d) => (d.children ? "#555" : "#999"))
.attr("r", (d) => bulletSize(d))
.attr("fill", (d) => color(oneDepth(d))) // colorisation
// .attr("opacity", (d) => (isLeaf(d) ? 0.5 : 1)); // colorisation
.attr("fill", (d) =>
isLeaf(d) ? fade(color(oneDepth(d))) : color(oneDepth(d))
); // colorisation
// text
g2.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("stroke-linejoin", "round")
.attr("stroke-width", 3)
.selectAll("text")
.data(root2.descendants())
.join("text")
.attr("font-size", (d) => fontSize(d))
.style("fill", (d) => color(oneDepth(d))) // colorisation
.attr(
"transform",
(d) => `
rotate(${(d.x * 180) / divisor / Math.PI - 90 + angleDeg})
translate(${d.y},0)
rotate(${
d.x > (Math.PI - angleRad) * divisor || d.x < -angleRad * divisor
? 180
: 0
})
`
)
.attr("dy", "0.31em")
.attr("x", (d) =>
(d.x > (Math.PI - angleRad) * divisor || d.x < -angleRad * divisor) ===
!d.children
? -6
: 6
)
.attr("text-anchor", (d) =>
(d.x > (Math.PI - angleRad) * divisor || d.x < -angleRad * divisor) ===
!d.children
? "end"
: "start"
)
.text((d) => {
if (d.depth == 0 || d.depth == 1) return d.data.name;
if (d.height != 0 && isDNMN(d)) return d.data.name;
if (d.data.acro == "") return d.data.name; // not usefull for KN
if (d.x > (Math.PI - angleRad) * divisor || d.x < -angleRad * divisor) {
return !d.children
? d.data.name + " : " + d.data.acro
: d.data.acro + " : " + d.data.name;
} else {
return !d.children
? d.data.acro + " : " + d.data.name
: d.data.name + " : " + d.data.acro;
}
})
.style("cursor", "pointer")
.on("click", function (d, i) {
let link = "https://suttacentral.net/" + i.data.id;
if (i.data.id == "suttaPitaka")
link = "https://suttacentral.net/pitaka/sutta";
window.open(link);
})
.clone(true)
.lower()
.attr("stroke", "white");
////////////////////////////////////////////////////// End of KN chart ///////////////////////////////////////////////
////////////////////////////////////////////////////// Start of 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));
if (license) {
svg
.append("svg:image")
.attr("x", xLicense - 88)
.attr("y", yLicense - 65)
// .attr("width", 88)
// .attr("height", 31)
.attr(
"xlink:href",
// "https://mirrors.creativecommons.org/presskit/buttons/88x31/svg/cc-zero.svg"
await FileAttachment("cc0gris@3.svg").url()
);
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("SuttaCentral.net")
.attr("dy", "0.25em");
// .clone(true)
// .text("No Right reserved")
// .attr("dy", "1.6em");
}
if (licenseNoLogo) {
// svg
// .append("svg:image")
// .attr("x", xLicense - 88)
// .attr("y", yLicense - 65)
// // .attr("width", 88)
// // .attr("height", 31)
// .attr(
// "xlink:href",
// // "https://mirrors.creativecommons.org/presskit/buttons/88x31/svg/cc-zero.svg"
// await FileAttachment("cc0gris@3.svg").url()
// );
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("CC0 - Public Domain")
.attr("text-anchor", "end")
.attr("dy", "-2.45em")
.clone(true)
.text("DhammaCharts.org")
.attr("dy", "-1.1em")
.clone(true)
.text("SuttaCentral.net")
.attr("dy", "0.25em");
}
////////////////////////////////////////////////////// End of License ///////////////////////////////////////////////
// zoom
if (zoompan) {
g.call(
d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
);
// zooming + pan;
svg.call(
d3
.zoom()
.extent([
[0, 0],
[width, height]
])
.scaleExtent([1, 8])
.on("zoom", zoomed)
);
function dragstarted() {
d3.select(this).raise();
g.attr("cursor", "grabbing");
}
function dragged(event, d) {
d3.select(this)
.attr("cx", (d.x = event.x))
.attr("cy", (d.y = event.y));
}
function dragended() {
g.attr("cursor", "grab");
}
function zoomed({ transform }) {
g.attr("transform", transform);
}
}
return svg.attr("viewBox", autoBox).node();
}