Indicate name of graph relations.

Most changes taken from: http://bl.ocks.org/fancellu/2c782394602a93921faff74e594d1bb1
This commit is contained in:
Sergio Martínez Portela 2022-05-16 23:28:43 +02:00
parent 819948e969
commit 386b3a1855
2 changed files with 92 additions and 21 deletions

View File

@ -135,7 +135,7 @@ def main(src_top, dest_top):
if isinstance(headline.parent, org_rw.Headline): if isinstance(headline.parent, org_rw.Headline):
links.append({ links.append({
"target": headline.parent.id, "target": headline.parent.id,
"relation": "contained-in" "relation": "in"
}) })
graph[headline.id] = { graph[headline.id] = {
"title": headline.title.strip(), "title": headline.title.strip(),
@ -161,6 +161,7 @@ def main(src_top, dest_top):
def print_tree(tree, indentation=0): def print_tree(tree, indentation=0):
return
for element in tree: for element in tree:
print(" " * indentation + "- " + str(type(element))) print(" " * indentation + "- " + str(type(element)))
if "children" in dir(element): if "children" in dir(element):
@ -226,8 +227,8 @@ def render_text_tokens(tokens, acc):
html.escape(link_target), html.escape(link_target),
html.escape(description), html.escape(description),
)) ))
else: # else:
raise NotImplementedError('TextToken: {}'.format(chunk)) # raise NotImplementedError('TextToken: {}'.format(chunk))
def render_tag(element, acc): def render_tag(element, acc):
@ -247,11 +248,12 @@ def render_tree(tree, acc):
render_tag(element, acc) render_tag(element, acc)
def render(headline, doc): def render(headline, doc, headlineLevel):
if headline.id != "ea48ec1d-f9d4-4fb7-b39a-faa7b6e2ba95": try:
return "" dom = headline.as_dom()
print("\n===========") except:
dom = headline.as_dom() logging.error("Error generating DOM for {}".format(doc.path))
raise
print_tree(dom) print_tree(dom)
content = [] content = []

View File

@ -35,7 +35,8 @@ function ForceGraph({
colors = d3.schemeTableau10, // an array of color strings, for the node groups colors = d3.schemeTableau10, // an array of color strings, for the node groups
width = 640, // outer width, in pixels width = 640, // outer width, in pixels
height = 400, // outer height, in pixels height = 400, // outer height, in pixels
invalidation // when this promise resolves, stop the simulation invalidation, // when this promise resolves, stop the simulation
linkLabel = null,
} = {}) { } = {}) {
// Compute values. // Compute values.
const N = d3.map(nodes, nodeId).map(intern); const N = d3.map(nodes, nodeId).map(intern);
@ -75,15 +76,63 @@ function ForceGraph({
.attr("viewBox", [-width / 2, -height / 2, width, height]) .attr("viewBox", [-width / 2, -height / 2, width, height])
.attr("style", "max-width: 100%; height: auto; height: intrinsic;"); .attr("style", "max-width: 100%; height: auto; height: intrinsic;");
const link = svg.append("g")
// Add arrowheads
svg.append('defs').append('marker')
.attr('id', 'arrowhead')
.attr('viewBox', '-0 -5 10 10')
.attr('refX', 13)
.attr('refY', 0)
.attr('orient', 'auto')
.attr('markerWidth', 13)
.attr('markerHeight', 13)
.attr('xoverflow', 'visible')
.append('svg:path')
.attr('d', 'M 0,-5 L 10 ,0 L 0,5')
.attr('fill', '#999')
.style('stroke','none');
const link = svg.append("g")
.attr("stroke", linkStroke) .attr("stroke", linkStroke)
.attr("stroke-opacity", linkStrokeOpacity) .attr("stroke-opacity", linkStrokeOpacity)
.attr("stroke-width", typeof linkStrokeWidth !== "function" ? linkStrokeWidth : null) .attr("stroke-width", typeof linkStrokeWidth !== "function" ? linkStrokeWidth : null)
.attr("stroke-linecap", linkStrokeLinecap) .attr("stroke-linecap", linkStrokeLinecap)
.attr('marker-end','url(#arrowhead)')
.selectAll("line") .selectAll("line")
.data(links) .data(links)
.join("line"); .join("line");
let edgelabels = null;
let edgepaths = null;
if (linkLabel) {
edgepaths = svg.selectAll(".edgepath")
.data(links)
.enter()
.append('path')
.attr('class', 'edgepath')
.attr('fill-opacity', 0)
.attr('stroke-opacity', 0)
.attr('id', function (d, i) {return 'edgepath' + i})
.style("pointer-events", "none");
edgelabels = svg.selectAll(".edgelabel")
.data(links)
.enter()
.append('text')
.style("pointer-events", "none")
.attr('class', 'edgelabel')
.attr('id', function (d, i) {return 'edgelabel' + i})
.attr('font-size', 10)
.attr('fill', '#666');
edgelabels.append('textPath')
.attr('xlink:href', function (d, i) {return '#edgepath' + i})
.style("text-anchor", "middle")
.style("pointer-events", "none")
.attr("startOffset", "50%")
.text(linkLabel);
}
if (W) link.attr("stroke-width", ({index: i}) => W[i]); if (W) link.attr("stroke-width", ({index: i}) => W[i]);
const node = svg.append("g") const node = svg.append("g")
@ -117,26 +166,45 @@ function ForceGraph({
node node
.attr("cx", d => d.x) .attr("cx", d => d.x)
.attr("cy", d => d.y); .attr("cy", d => d.y);
if (linkLabel) {
edgepaths.attr('d', function (d) {
return 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y;
});
edgelabels.attr('transform', function (d) {
if (d.target.x < d.source.x) {
var bbox = this.getBBox();
rx = bbox.x + bbox.width / 2;
ry = bbox.y + bbox.height / 2;
return 'rotate(180 ' + rx + ' ' + ry + ')';
}
else {
return 'rotate(0)';
}
});
}
} }
function drag(simulation) { function drag(simulation) {
function dragstarted(event) { function dragstarted(event) {
if (!event.active) simulation.alphaTarget(0.3).restart(); if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x; event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y; event.subject.fy = event.subject.y;
} }
function dragged(event) { function dragged(event) {
event.subject.fx = event.x; event.subject.fx = event.x;
event.subject.fy = event.y; event.subject.fy = event.y;
} }
function dragended(event) { function dragended(event) {
if (!event.active) simulation.alphaTarget(0); if (!event.active) simulation.alphaTarget(0);
event.subject.fx = null; event.subject.fx = null;
event.subject.fy = null; event.subject.fy = null;
} }
return d3.drag() return d3.drag()
.on("start", dragstarted) .on("start", dragstarted)
.on("drag", dragged) .on("drag", dragged)
@ -167,14 +235,14 @@ function ForceGraph({
for (var source of Object.keys(NODE_GRAPH)) { for (var source of Object.keys(NODE_GRAPH)) {
for (var target of NODE_GRAPH[source].links) { for (var target of NODE_GRAPH[source].links) {
var node_id = target.target; var node_id = target.target;
if (node_id.startsWith('id:')) { if (node_id.startsWith('id:')) {
node_id = node_id.substring(3); node_id = node_id.substring(3);
} }
if (NODE_GRAPH[node_id] === undefined) { if (NODE_GRAPH[node_id] === undefined) {
continue; continue;
} }
edges.push({source, target: node_id}); edges.push({source, target: node_id, relation: target.relation});
} }
} }
var graph = { var graph = {
@ -184,14 +252,15 @@ function ForceGraph({
var holder = document.getElementById('graph_holder'); var holder = document.getElementById('graph_holder');
var chart = ForceGraph(graph, { var chart = ForceGraph(graph, {
nodeId: d => d, nodeId: d => d,
nodeGroup: d => Math.random() * 4 | 0, nodeGroup: d => NODE_GRAPH[d].depth,
nodeTitle: d => `${NODE_GRAPH[d].title}`, nodeTitle: d => `${NODE_GRAPH[d].title}`,
nodeRadius: d => Math.max(1, 4 - NODE_GRAPH[d.id].depth) * 3, nodeRadius: d => 3 + Math.max(1, 4 - NODE_GRAPH[d.id].depth) * 2,
width: holder.clientWidth, width: holder.clientWidth,
height: holder.clientHeight, height: holder.clientHeight,
linkStrokeWidth: 0.5, linkStrokeWidth: 1,
linkStrength: 1, // linkStrength: -5,
// invalidation // a promise to stop the simulation when the cell is re-run // invalidation, // a promise to stop the simulation when the cell is re-run
linkLabel: (d) => { const e = edges[d.index]; if (e.relation) { return e.relation; } else { return ''; } },
}); });
holder.appendChild(chart); holder.appendChild(chart);
</script> </script>