chart = {
const width = 928;
const height = 600;
const color = d3.scaleOrdinal(d3.schemeCategory10);
const ROOT_EDGES = [
{ source: "quality-root", target: "efficient" },
{ source: "quality-root", target: "flexible" },
{ source: "quality-root", target: "reliable" },
{ source: "quality-root", target: "usable" },
{ source: "quality-root", target: "operable" },
{ source: "quality-root", target: "secure" },
{ source: "quality-root", target: "safe" },
{ source: "quality-root", target: "suitable" },
];
const ALL_TINY_EDGES = [
...ROOT_EDGES,
{ source: "availability", target: "reliability" },
{ source: "availability", target: "reliable" },
{ source: "availability", target: "usable" },
{ source: "capacity", target: "performanceEfficiency" },
{ source: "capacity", target: "efficient" },
{ source: "capacity", target: "usable" },
{ source: "coexistence", target: "portability" },
{ source: "coexistence", target: "flexible" },
{ source: "codeReadability", target: "flexibility" },
{ source: "codeReadability", target: "maintainability" },
{ source: "codeReadability", target: "efficient" },
{ source: "configurability", target: "flexibility" },
{ source: "configurability", target: "flexible" },
{ source: "easeOfUse", target: "usability" },
{ source: "easeOfUse", target: "usable" },
{ source: "faultTolerance", target: "reliability" },
{ source: "faultTolerance", target: "reliable" },
{ source: "faultTolerance", target: "usable" },
{ source: "faultlessness", target: "reliability" },
{ source: "faultlessness", target: "reliable" },
{ source: "flexibility", target: "usability" },
{ source: "flexibility", target: "configurability" },
{ source: "flexibility", target: "coexistence" },
{ source: "flexibility", target: "flexible" },
{ source: "flexibility", target: "usable" },
{ source: "gracefulDegradation", target: "reliability" },
{ source: "gracefulDegradation", target: "reliable" },
{ source: "i18n", target: "usable" },
{ source: "interoperability", target: "usable" },
{ source: "latency", target: "efficient" },
{ source: "maintainability", target: "flexible" },
{ source: "modifiability", target: "configurability" },
{ source: "modifiability", target: "flexibility" },
{ source: "modifiability", target: "maintainability" },
{ source: "modifiability", target: "flexible" },
{ source: "performanceEfficiency", target: "efficient" },
{ source: "portability", target: "flexible" },
{ source: "recoverability", target: "reliable" },
{ source: "reliability", target: "gracefulDegradation" },
{ source: "reliability", target: "reliable" },
{ source: "resourceEfficiency", target: "efficient" },
{ source: "scalability", target: "capacity" },
{ source: "scalability", target: "performanceEfficiency" },
{ source: "scalability", target: "efficient" },
{ source: "speed", target: "performanceEfficiency" },
{ source: "speed", target: "timeBehaviour" },
{ source: "speed", target: "efficient" },
{ source: "stability", target: "reliability" },
{ source: "stability", target: "recoverability" },
{ source: "stability", target: "reliable" },
{ source: "standardCompliance", target: "reliable" },
{ source: "testability", target: "reliability" },
{ source: "testability", target: "reliable" },
{ source: "timeBehaviour", target: "speed" },
{ source: "timeBehaviour", target: "performanceEfficiency" },
{ source: "timeBehaviour", target: "efficient" },
{ source: "understandability", target: "usability" },
{ source: "understandability", target: "usable" },
{ source: "usability", target: "usable" },
];
const PROPERTY_NODES = [
{
id: "quality-root",
label: "Quality",
size: 35,
color: "orange",
type: "property",
},
{
id: "efficient",
label: "Efficient",
size: 15,
color: "green",
type: "property",
page: "/tag-efficient",
},
{
id: "flexible",
label: "Flexible",
size: 15,
color: "green",
type: "property",
page: "/tag-flexible",
},
{
id: "reliable",
label: "Reliable",
size: 15,
color: "green",
type: "property",
page: "/tag-reliable",
},
{
id: "usable",
label: "Usable",
size: 15,
color: "green",
type: "property",
page: "/tag-usable",
},
{
id: "operable",
label: "Operable",
size: 15,
color: "green",
type: "property",
page: "/tag-operable",
},
{
id: "secure",
label: "Secure",
size: 15,
color: "green",
type: "property",
page: "/tag-secure",
},
{
id: "safe",
label: "Safe",
size: 15,
color: "green",
type: "property",
page: "/tag-safe",
},
{
id: "suitable",
label: "Suitable",
size: 15,
color: "green",
type: "property",
page: "/tag-suitable",
},
];
const QUALITY_NODES = [
{
id: "availability",
label: "Availability",
size: 15,
color: "blue",
type: "quality",
page: "/availability",
},
{
id: "capacity",
label: "Capacity",
size: 15,
color: "blue",
type: "quality",
page: "/capacity",
},
{
id: "coexistence",
label: "Co-existence",
size: 15,
color: "blue",
type: "quality",
page: "/coexistence",
},
{
id: "codeReadability",
label: "Code Readability",
size: 15,
color: "blue",
type: "quality",
page: "/code-readability",
},
{
id: "configurability",
label: "Configurability",
size: 15,
color: "blue",
type: "quality",
page: "/configurability",
},
{
id: "easeOfUse",
label: "Ease of Use",
size: 15,
color: "blue",
type: "quality",
page: "/ease-of-use",
},
{
id: "faultTolerance",
label: "Fault tolerance",
size: 15,
color: "blue",
type: "quality",
page: "/fault-tolerance",
},
{
id: "faultlessness",
label: "Faultlessness",
size: 15,
color: "blue",
type: "quality",
page: "/faultlessness",
},
{
id: "flexibility",
label: "Flexibility",
size: 15,
color: "blue",
type: "quality",
page: "/flexibility",
},
{
id: "gracefulDegradation",
label: "Graceful degradation",
size: 15,
color: "blue",
type: "quality",
page: "/graceful-degradation",
},
{
id: "i18n",
label: "i18n (Internationalization)",
size: 15,
color: "blue",
type: "quality",
page: "/i18n",
},
{
id: "interoperability",
label: "Interoperability",
size: 15,
color: "blue",
type: "quality",
page: "/interoperability",
},
{
id: "latency",
label: "Latency",
size: 15,
color: "blue",
type: "quality",
page: "/latency",
},
{
id: "maintainability",
label: "Maintainability",
size: 15,
color: "blue",
type: "quality",
page: "/maintainability",
},
{
id: "modifiability",
label: "Modifiability",
size: 15,
color: "blue",
type: "quality",
page: "/modifiability",
},
{
id: "performanceEfficiency",
label: "Performance efficiency",
size: 15,
color: "blue",
type: "quality",
page: "/performance",
},
{
id: "portability",
label: "Portability",
size: 15,
color: "blue",
type: "quality",
page: "/portability",
},
{
id: "recoverability",
label: "Recoverability",
size: 15,
color: "blue",
type: "quality",
page: "/recoverability",
},
{
id: "reliability",
label: "Reliability",
size: 15,
color: "blue",
type: "quality",
page: "/reliability",
},
{
id: "resourceEfficiency",
label: "Resource efficiency",
size: 15,
color: "blue",
type: "quality",
page: "/resource-efficiency",
},
{
id: "scalability",
label: "Scalability",
size: 15,
color: "blue",
type: "quality",
page: "/scalability",
},
{
id: "speed",
label: "Speed",
size: 15,
color: "blue",
type: "quality",
page: "/speed",
},
{
id: "stability",
label: "Stability",
size: 15,
color: "blue",
type: "quality",
page: "/stability",
},
{
id: "standardCompliance",
label: "Standard Compliance",
size: 15,
color: "blue",
type: "quality",
page: "/standard-compliance",
},
{
id: "testability",
label: "Testability",
size: 15,
color: "blue",
type: "quality",
page: "/testability",
},
{
id: "timeBehaviour",
label: "Time behaviour",
size: 15,
color: "blue",
type: "quality",
page: "/time-behaviour",
},
{
id: "understandability",
label: "Understandability",
size: 15,
color: "blue",
type: "quality",
page: "/understandability",
},
{
id: "usability",
label: "Usability",
size: 15,
color: "blue",
type: "quality",
page: "/usability",
},
];
const ALL_TINY_NODES = [...PROPERTY_NODES, ...QUALITY_NODES];
const links = ALL_TINY_EDGES.map(d => ({...d}));
const nodes = ALL_TINY_NODES.map(d => ({...d}));
// Create a simulation with several forces.
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2))
.on("tick", draw);
// Create the canvas.
const dpi = devicePixelRatio; // _e.g._, 2 for retina screens
const canvas = d3.create("canvas")
.attr("width", dpi * width)
.attr("height", dpi * height)
.attr("style", `width: ${width}px; max-width: 100%; height: auto;`)
.node();
const context = canvas.getContext("2d");
context.scale(dpi, dpi);
function draw() {
context.clearRect(0, 0, width, height);
context.save();
context.globalAlpha = 0.6;
context.strokeStyle = "#999";
context.beginPath();
links.forEach(drawLink);
context.stroke();
context.restore();
context.save();
context.strokeStyle = "#fff";
context.globalAlpha = 1;
nodes.forEach(node => {
context.beginPath();
drawNode(node)
context.fillStyle = color(node.group);
context.strokeStyle = "#fff";
context.fill();
context.stroke();
});
context.restore();
}
function drawLink(d) {
context.moveTo(d.source.x, d.source.y);
context.lineTo(d.target.x, d.target.y);
}
function drawNode(d) {
context.moveTo(d.x + 5, d.y);
context.arc(d.x, d.y, 5, 0, 2 * Math.PI);
}
// Add a drag behavior. The _subject_ identifies the closest node to the pointer,
// conditional on the distance being less than 20 pixels.
d3.select(canvas)
.call(d3.drag()
.subject(event => {
const [px, py] = d3.pointer(event, canvas);
return d3.least(nodes, ({x, y}) => {
const dist2 = (x - px) ** 2 + (y - py) ** 2;
if (dist2 < 400) return dist2;
});
})
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
// Reheat the simulation when drag starts, and fix the subject position.
function dragstarted(event) {
console.warn("Drag", event);
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
// Update the subject (dragged node) position during drag.
function dragged(event) {
event.subject.fx = event.x;
event.subject.fy = event.y;
}
// Restore the target alpha so the simulation cools after dragging ends.
// Unfix the subject position now that it’s no longer being dragged.
function dragended(event) {
if (!event.active) simulation.alphaTarget(0);
}
// When this cell is re-run, stop the previous simulation. (This doesn’t
// really matter since the target alpha is zero and the simulation will
// stop naturally, but it’s a good practice.)
invalidation.then(() => simulation.stop());
return canvas;
}