From 8ab41844775d8803c602cefc2d4d477cdb28a2fb Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 9 Nov 2023 12:43:24 +0100 Subject: [PATCH] Color nodes by file --- .../src/visualization/visualization.css | 233 ++++++++++++++---- .../src/visualization/visualization.js | 45 +++- 2 files changed, 221 insertions(+), 57 deletions(-) diff --git a/stack-graphs/src/visualization/visualization.css b/stack-graphs/src/visualization/visualization.css index 360594182..c5cc80151 100644 --- a/stack-graphs/src/visualization/visualization.css +++ b/stack-graphs/src/visualization/visualization.css @@ -9,15 +9,33 @@ /* Paul Tol's Colorblind Friendly Color Scheme (vibrant) * Source: https://personal.sron.nl/~pault/ * + * In default order: + * + * orange #ee7733 * blue #0077bb * cyan #33bbee - * teal #009988 - * orange #ee7733 - * red #cc3311 * magenta #ee3377 + * red #cc3311 + * teal #009988 * grey #bbbbbb */ +/* Paul Tol's Colorblind Friendly Color Scheme (light) + * Source: https://personal.sron.nl/~pault/ + * + * In default order: + * + * light blue #77aadd + * orange #ee8866 + * light yellow #eedd88 + * pink #ffaabb + * light cyan #99ddff + * mint #44bb99 + * pear #bbcc33 + * olive #aaaa00 + * pale grey #dddddd + */ + .sg { width: 100%; height: 100%; @@ -50,52 +68,25 @@ /* --- drop scopes --- */ .sg .node.drop_scopes .background { - fill: #cc3311; r: 6px; } /* --- jump to scope --- */ .sg .node.jump_to_scope .background { - fill: #ee7733; r: 6px; - stroke: black; } /* --- pop symbol --- */ -.sg .node.pop_symbol .background, -.sg .node.pop_scoped_symbol .background { - fill: #009988; -} - -.sg .node.pop_symbol .arrow, -.sg .node.pop_scoped_symbol .arrow { - fill: #005b51; -} - .sg .node.pop_scoped_symbol .pop_scope { fill: #ee7733; r: 6px; stroke: black; } -.sg .node.definition .background { - stroke: black; -} - /* --- push symbol --- */ -.sg .node.push_symbol .background, -.sg .node.push_scoped_symbol .background { - fill: #33bbee; -} - -.sg .node.push_symbol .arrow, -.sg .node.push_scoped_symbol .arrow { - fill: #006e96; -} - .sg .node.push_scoped_symbol .push_scope { fill: #bbbbbb; r: 6px; @@ -111,16 +102,10 @@ fill: black; } -.sg .node.reference .background { - stroke: black; -} - /* --- root --- */ .sg .node.root .background { - fill: #0077bb; r: 6px; - stroke: black; } /* --- scope --- */ @@ -131,14 +116,9 @@ } .sg .node.scope .background { - fill: #bbbbbb; r: 6px; } -.sg .node.scope.exported .background { - stroke: black; -} - .sg .node.scope .focus-point { r: 3px; fill: none; @@ -156,7 +136,6 @@ } .sg .node.scope.plain_labeled_node .background { - fill: #bbbbbb; rx: 6px; } @@ -167,7 +146,6 @@ /* --- path highlight --- */ .sg .node.path-node .border { - stroke: #ee3377; stroke-width: 4px; stroke-dasharray: 5, 5; } @@ -185,30 +163,21 @@ } .sg .edge path { - stroke: black; stroke-width: 1px; fill: none; } .sg .edge text { font-size: 11pt; - stroke: black; stroke-width: 1px; dominant-baseline: central; } .sg .edge.path-edge path { - stroke: #ee3377; stroke-width: 3px; } -.sg .edge.path-edge text -{ - stroke: #ee3377; - fill: #ee3377; -} - /* ------------------------------------------------------------------------------------------------ * Jumps */ @@ -442,3 +411,163 @@ margin: 0px 6px; cursor: pointer; } + +/* ------------------------------------------------------------------------------------------------ + * Colors + */ + +.sg .node.global .background { + fill: #0077bb; /* blue */ +} +.sg .edge.global path, +.sg .edge.global text { + stroke: #0077bb; /* blue */ +} + +.sg .node.file-0 .background { + fill: #77aadd; /* light blue */ +} + +.sg .node.file-0 .arrow, +.sg .edge.file-0 text +{ + fill: #0e2236; /* light blue (darkened) */ +} +.sg .node.file-0.reference .background, +.sg .node.file-0.definition .background, +.sg .node.file-0.scope.exported .background, +.sg .edge.file-0 path, +.sg .edge.file-0 text +{ + stroke: #0e2236; /* light blue (darkened) */ +} + +.sg .node.file-1 .background, +.sg .edge.file-1 text +{ + fill: #ee8866; /* orange */ +} +.sg .node.file-1 .arrow { + fill: #3d1407; /* orange (darkened) */ +} +.sg .node.file-1.reference .background, +.sg .node.file-1.definition .background, +.sg .node.file-1.scope.exported .background, +.sg .edge.file-1 path, +.sg .edge.file-1 text +{ + stroke: #3d1407; /* orange (darkened) */ +} + +.sg .node.file-2 .background, +.sg .edge.file-2 text +{ + fill: #eedd88; /* light yellow */ +} +.sg .node.file-2 .arrow { + fill: #413809; /* light yellow (darkened) */ +} +.sg .node.file-2.reference .background, +.sg .node.file-2.definition .background, +.sg .node.file-2.scope.exported .background, +.sg .edge.file-2 path, +.sg .edge.file-2 text +{ + stroke: #413809; /* light yellow (darkened) */ +} + +.sg .node.file-3 .background, +.sg .edge.file-3 text +{ + fill: #ffaabb; /* pink */ +} +.sg .node.file-3 .arrow { + fill: #550011; /* pink (darkened) */ +} +.sg .node.file-3.reference .background, +.sg .node.file-3.definition .background, +.sg .node.file-3.scope.exported .background, +.sg .edge.file-3 path, +.sg .edge.file-3 text +{ + stroke: #550011; /* pink (darkened) */ +} + +.sg .node.file-4 .background, +.sg .edge.file-4 text +{ + fill: #99ddff; /* light cyan */ +} +.sg .node.file-4 .arrow { + fill: #003652; /* light cyan (darkened) */ +} +.sg .node.file-4.reference .background, +.sg .node.file-4.definition .background, +.sg .node.file-4.scope.exported .background, +.sg .edge.file-4 path, +.sg .edge.file-4 text +{ + stroke: #003652; /* light cyan (darkened) */ +} + +.sg .node.file-5 .background, +.sg .edge.file-5 text +{ + fill: #44bb99; /* mint */ +} +.sg .node.file-5 .arrow { + fill: #0e251f; /* mint (darkened) */ +} +.sg .node.file-5.reference .background, +.sg .node.file-5.definition .background, +.sg .node.file-5.scope.exported .background, +.sg .edge.file-5 path, +.sg .edge.file-5 text +{ + stroke: #0e251f; /* mint (darkened) */ +} + +.sg .node.file-6 .background, +.sg .edge.file-6 text +{ + fill: #bbcc33; /* pear */ +} +.sg .node.file-6 .arrow { + fill: #25290a; /* pear (darkened) */ +} +.sg .node.file-6.reference .background, +.sg .node.file-6.definition .background, +.sg .node.file-6.scope.exported .background, +.sg .edge.file-6 path, +.sg .edge.file-6 text +{ + stroke: #25290a; /* pear (darkened) */ +} + +.sg .node.file-7 .background, +.sg .edge.file-7 text +{ + fill: #aaaa00; /* olive */ +} +.sg .node.file-7 .arrow { + fill: #222200; /* olive (darkened) */ +} +.sg .node.file-7.reference .background, +.sg .node.file-7.definition .background, +.sg .node.file-7.scope.exported .background, +.sg .edge.file-7 path, +.sg .edge.file-7 text +{ + stroke: #222200; /* olive (darkened) */ +} + +.sg .node.path-node .border, +.sg .edge.path-edge path, +.sg .edge.path-edge text +{ + stroke: #ee7733; +} + +.sg .edge.path-edge text { + fill: #ee7733; +} diff --git a/stack-graphs/src/visualization/visualization.js b/stack-graphs/src/visualization/visualization.js index a1d9bbaac..7e5183973 100644 --- a/stack-graphs/src/visualization/visualization.js +++ b/stack-graphs/src/visualization/visualization.js @@ -17,6 +17,8 @@ class StackGraph { static arrow_head_w = 16; static arrow_head_h = 8; + static number_of_file_colors = 8; + constructor(container, graph, paths, metadata) { this.metadata = metadata; @@ -32,12 +34,22 @@ class StackGraph { } compute_data() { + this.F = {}; this.ID = {}; this.N = []; + this.compute_file_data(); this.compute_node_data(); this.compute_path_data(); } + compute_file_data() { + for (let i in graph.files) { + const file = graph.files[i]; + this.F[file] = i; + } + console.log(this.F); + } + compute_node_data() { for (let i in graph.nodes) { const node = graph.nodes[i]; @@ -225,7 +237,7 @@ class StackGraph { .data(dag.links()) .enter() .append("g") - .attr("class", (d) => d.data.is_jump ? "jump" : "edge") + .attr("class", (d) => `${d.data.is_jump ? "jump" : "edge"} ${this.edge_to_file_class(d.data)}`) .attr("id", (d) => this.edge_to_id_str(d.data)); edges.append("path") .attr("id", (d) => this.edge_to_id_str(d.data) + ":path") @@ -286,14 +298,14 @@ class StackGraph { render_node(node, g) { g.attr('id', this.node_to_id_str(node)); - g.attr('class', `node ${node.type}`); + g.attr('class', `node ${node.type} ${this.node_to_file_class(node)}`); switch (node.type) { case "drop_scopes": - this.render_scope(g); + this.render_symbol_node(g, "[drop]", null, ""); break; case "jump_to_scope": - this.render_scope(g); + this.render_symbol_node(g, "[jump]", null, ""); break; case "pop_symbol": this.render_symbol_node(g, node.symbol, null, "pop"); @@ -322,7 +334,7 @@ class StackGraph { } break; case "root": - this.render_scope(g); + this.render_symbol_node(g, "[root]", null, ""); break; case "scope": if (this.show_all_node_labels()) { @@ -347,6 +359,7 @@ class StackGraph { break; } } + render_symbol_node(g, text, scope, shape) { let content = g.append("g"); content.append('text').text(text); @@ -869,10 +882,24 @@ class StackGraph { return this.node_id_to_str(node.id); } + node_to_file_class(node) { + return this.node_id_to_file_class(node.id); + } + edge_to_id_str(edge) { return this.node_id_to_str(edge.source) + "->" + this.node_id_to_str(edge.sink); } + edge_to_file_class(edge) { + if (edge.source.hasOwnProperty('file')) { + return "file-" + (this.F[edge.source.file] % StackGraph.number_of_file_colors); + } else if (edge.sink.hasOwnProperty('file')) { + return "file-" + (this.F[edge.sink.file] % StackGraph.number_of_file_colors); + } else { + return "global"; + } + } + node_id_to_str(id) { if (id.hasOwnProperty('file')) { return id.file + "#" + id.local_id; @@ -881,6 +908,14 @@ class StackGraph { } } + node_id_to_file_class(id) { + if (id.hasOwnProperty('file')) { + return "file-" + (this.F[id.file] % StackGraph.number_of_file_colors); + } else { + return "global"; + } + } + id_selector(id) { const sel = "#" + id.replaceAll(/[^a-zA-Z0-9]/g, '\\$&'); return sel;