From 386b3a1855302d0d0e593fd2b95f5b13911a77c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mart=C3=ADnez=20Portela?= Date: Mon, 16 May 2022 23:28:43 +0200 Subject: [PATCH] Indicate name of graph relations. Most changes taken from: http://bl.ocks.org/fancellu/2c782394602a93921faff74e594d1bb1 --- scripts/generate.py | 18 ++++---- static/graph_explorer.html | 95 ++++++++++++++++++++++++++++++++------ 2 files changed, 92 insertions(+), 21 deletions(-) diff --git a/scripts/generate.py b/scripts/generate.py index 833d221..05e5951 100644 --- a/scripts/generate.py +++ b/scripts/generate.py @@ -135,7 +135,7 @@ def main(src_top, dest_top): if isinstance(headline.parent, org_rw.Headline): links.append({ "target": headline.parent.id, - "relation": "contained-in" + "relation": "in" }) graph[headline.id] = { "title": headline.title.strip(), @@ -161,6 +161,7 @@ def main(src_top, dest_top): def print_tree(tree, indentation=0): + return for element in tree: print(" " * indentation + "- " + str(type(element))) if "children" in dir(element): @@ -226,8 +227,8 @@ def render_text_tokens(tokens, acc): html.escape(link_target), html.escape(description), )) - else: - raise NotImplementedError('TextToken: {}'.format(chunk)) + # else: + # raise NotImplementedError('TextToken: {}'.format(chunk)) def render_tag(element, acc): @@ -247,11 +248,12 @@ def render_tree(tree, acc): render_tag(element, acc) -def render(headline, doc): - if headline.id != "ea48ec1d-f9d4-4fb7-b39a-faa7b6e2ba95": - return "" - print("\n===========") - dom = headline.as_dom() +def render(headline, doc, headlineLevel): + try: + dom = headline.as_dom() + except: + logging.error("Error generating DOM for {}".format(doc.path)) + raise print_tree(dom) content = [] diff --git a/static/graph_explorer.html b/static/graph_explorer.html index f903080..ade81c3 100644 --- a/static/graph_explorer.html +++ b/static/graph_explorer.html @@ -35,7 +35,8 @@ function ForceGraph({ colors = d3.schemeTableau10, // an array of color strings, for the node groups width = 640, // outer width, 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. const N = d3.map(nodes, nodeId).map(intern); @@ -75,15 +76,63 @@ function ForceGraph({ .attr("viewBox", [-width / 2, -height / 2, width, height]) .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-opacity", linkStrokeOpacity) .attr("stroke-width", typeof linkStrokeWidth !== "function" ? linkStrokeWidth : null) .attr("stroke-linecap", linkStrokeLinecap) + .attr('marker-end','url(#arrowhead)') .selectAll("line") .data(links) .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]); const node = svg.append("g") @@ -117,26 +166,45 @@ function ForceGraph({ node .attr("cx", d => d.x) .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) { if (!event.active) simulation.alphaTarget(0.3).restart(); event.subject.fx = event.subject.x; event.subject.fy = event.subject.y; } - + function dragged(event) { event.subject.fx = event.x; event.subject.fy = event.y; } - + function dragended(event) { if (!event.active) simulation.alphaTarget(0); event.subject.fx = null; event.subject.fy = null; } - + return d3.drag() .on("start", dragstarted) .on("drag", dragged) @@ -167,14 +235,14 @@ function ForceGraph({ for (var source of Object.keys(NODE_GRAPH)) { for (var target of NODE_GRAPH[source].links) { var node_id = target.target; - + if (node_id.startsWith('id:')) { node_id = node_id.substring(3); } if (NODE_GRAPH[node_id] === undefined) { continue; } - edges.push({source, target: node_id}); + edges.push({source, target: node_id, relation: target.relation}); } } var graph = { @@ -184,14 +252,15 @@ function ForceGraph({ var holder = document.getElementById('graph_holder'); var chart = ForceGraph(graph, { nodeId: d => d, - nodeGroup: d => Math.random() * 4 | 0, + nodeGroup: d => NODE_GRAPH[d].depth, 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, height: holder.clientHeight, - linkStrokeWidth: 0.5, - linkStrength: 1, - // invalidation // a promise to stop the simulation when the cell is re-run + linkStrokeWidth: 1, + // linkStrength: -5, + // 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);