Commit 62f037ad authored by James Long's avatar James Long Committed by Commit Bot

[Lorenz] Update documentation of D3 graph visualization

This change adds a bit of clarification via comments on the various
mechanisms of graph_view.js, plus a useful link at the beginning as a
primer to D3.

Bug: 1119942
Change-Id: I4e69ca8626d11d81ea41e0f7a516449866508711
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2367743
Commit-Queue: James Long <yjlong@google.com>
Reviewed-by: default avatarSamuel Huang <huangs@chromium.org>
Reviewed-by: default avatarHenrique Nakashima <hnakashima@chromium.org>
Cr-Commit-Position: refs/heads/master@{#800596}
parent d15f89d2
...@@ -2,6 +2,15 @@ ...@@ -2,6 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
/**
* @fileoverview Exports a class GraphView, which encapsulates all the D3 logic
* in the graph visualization.
*
* D3 uses the concept of "data joins" to bind data to the SVG, and might be
* difficult to read at first glance. This (https://bost.ocks.org/mike/join/) is
* a good resource if you are unfamiliar with data joins.
*/
import {DisplaySettingsData} from './display_settings_data.js'; import {DisplaySettingsData} from './display_settings_data.js';
import {GraphNode, D3GraphData} from './graph_model.js'; import {GraphNode, D3GraphData} from './graph_model.js';
import {GraphEdgeColor} from './display_settings_data.js'; import {GraphEdgeColor} from './display_settings_data.js';
...@@ -29,8 +38,8 @@ const DEFAULT_EDGE_COLOR = '#999'; ...@@ -29,8 +38,8 @@ const DEFAULT_EDGE_COLOR = '#999';
// A map from GraphEdgeColor to its start and end color codes. The property // A map from GraphEdgeColor to its start and end color codes. The property
// `targetDefId` will be used as the unique ID of the colored arrow in the SVG // `targetDefId` will be used as the unique ID of the colored arrow in the SVG
// defs (https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs), and the // defs, and the arrowhead will be referred to by `url(#targetDefId)` in the
// arrowhead will be referred to by `url(#targetDefId)` in the SVG. // SVG.
const EDGE_COLORS = { const EDGE_COLORS = {
[GraphEdgeColor.DEFAULT]: { [GraphEdgeColor.DEFAULT]: {
source: DEFAULT_EDGE_COLOR, source: DEFAULT_EDGE_COLOR,
...@@ -320,6 +329,8 @@ class GraphView { ...@@ -320,6 +329,8 @@ class GraphView {
/** @private {!HullColorManager} */ /** @private {!HullColorManager} */
this.hullColorManager_ = new HullColorManager(); this.hullColorManager_ = new HullColorManager();
// Event handler callbacks, to be registered externally and called when the
// relevant event is triggered.
/** @private @type {?GetNodeGroupCallback} */ /** @private @type {?GetNodeGroupCallback} */
this.getNodeGroup_ = null; this.getNodeGroup_ = null;
/** @private @type {?OnNodeClickedCallback} */ /** @private @type {?OnNodeClickedCallback} */
...@@ -328,7 +339,10 @@ class GraphView { ...@@ -328,7 +339,10 @@ class GraphView {
this.onNodeDoubleClicked_ = null; this.onNodeDoubleClicked_ = null;
const svg = d3.select('#graph-svg'); const svg = d3.select('#graph-svg');
const graphGroup = svg.append('g'); // Contains entire graph (for zoom/pan). // An SVG group containing the entire graph as its children (for zoom/pan).
const graphGroup = svg.append('g');
// The defs element for the entire SVG
// (https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs).
this.svgDefs_ = svg.append('defs'); this.svgDefs_ = svg.append('defs');
// Add an arrowhead def for every possible edge target color. // Add an arrowhead def for every possible edge target color.
...@@ -344,26 +358,43 @@ class GraphView { ...@@ -344,26 +358,43 @@ class GraphView {
)) ))
.on('dblclick.zoom', null); .on('dblclick.zoom', null);
// Each group here will have a collection of SVG elements as their children.
// The order of these groups decide the SVG paint order (since we append // The order of these groups decide the SVG paint order (since we append
// sequentially), we want hulls below edges below nodes below labels. // sequentially), earlier groups will be painted below later ones.
/** @private {*} */ /**
* @private {*} The convex hulls displayed around node groupings. Contains
* <path/> elements as children, each hull is one <path/>.
*/
this.hullGroup_ = graphGroup.append('g') this.hullGroup_ = graphGroup.append('g')
.classed('graph-hull', true) .classed('graph-hull', true)
.attr('stroke-width', 1) .attr('stroke-width', 1)
.attr('fill-opacity', 0.1); .attr('fill-opacity', 0.1);
/** @private {*} */ /**
* @private {*} The edges of the graph. Contains <path/> elements as
* children, each edge is one <path/>.
*/
this.edgeGroup_ = graphGroup.append('g') this.edgeGroup_ = graphGroup.append('g')
.classed('graph-edges', true) .classed('graph-edges', true)
.attr('stroke-width', 1) .attr('stroke-width', 1)
.attr('fill', 'transparent'); .attr('fill', 'transparent');
/** @private {*} */ /**
* @private {*} The nodes of the graph. Contains <circle/> elements as
* children, each node is one <circle/>.
*/
this.nodeGroup_ = graphGroup.append('g') this.nodeGroup_ = graphGroup.append('g')
.classed('graph-nodes', true); .classed('graph-nodes', true);
/** @private {*} */ /**
* @private {*} The labels for the convex hulls contained in
* `this.hullGroup_`. Contains <text/> elements as children, each hull label
* is one <text/>.
*/
this.hullLabelGroup_ = graphGroup.append('g') this.hullLabelGroup_ = graphGroup.append('g')
.classed('graph-hull-labels', true) .classed('graph-hull-labels', true)
.attr('pointer-events', 'none'); .attr('pointer-events', 'none');
/** @private {*} */ /**
* @private {*} The labels for the nodes in `this.nodeGroup_`. Contains
* <text/> elements as children, each node label is one <text/>.
*/
this.labelGroup_ = graphGroup.append('g') this.labelGroup_ = graphGroup.append('g')
.classed('graph-labels', true) .classed('graph-labels', true)
.attr('pointer-events', 'none'); .attr('pointer-events', 'none');
...@@ -376,6 +407,8 @@ class GraphView { ...@@ -376,6 +407,8 @@ class GraphView {
const width = parseInt(svg.style('width'), 10); const width = parseInt(svg.style('width'), 10);
const height = parseInt(svg.style('height'), 10); const height = parseInt(svg.style('height'), 10);
// Initializes forces in the force-directed simulation, which will update
// position variables on the input data as the simulation runs.
const centeringStrengthY = 0.07; const centeringStrengthY = 0.07;
const centeringStrengthX = centeringStrengthY * (height / width); const centeringStrengthX = centeringStrengthY * (height / width);
/** @private {*} */ /** @private {*} */
...@@ -680,8 +713,16 @@ class GraphView { ...@@ -680,8 +713,16 @@ class GraphView {
reheatSimulation(shouldEase) { reheatSimulation(shouldEase) {
let tickNum = 0; let tickNum = 0;
// The simulation updates position variables in the data every tick, it's up /**
// to us to update the visualization to match. * Executed every time the simulation updates.
*
* Every tick of the simulation, we need to manually sync the visualization
* with the updated position variables. For more info on the position
* variables, see (https://github.com/d3/d3-force#simulation_nodes).
*
* Any part of the visualization that need to update on tick (e.g., node
* positions) should have their updates be performed in here.
*/
const tickActions = () => { const tickActions = () => {
this.syncEdgePaths(); this.syncEdgePaths();
this.syncEdgeColors(); this.syncEdgeColors();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment