Skip to content

Commit

Permalink
Refactor/split graph helper logic (#158)
Browse files Browse the repository at this point in the history
* Move buildLinks and buildNodes to graph.builder.js

* Add Graph/builder to documentation.yml

* Fix several docs internal references to Graph/renderer

* Some reording in graph.config.js

* Removed unused const FORCE_IDEAL_STRENGTH
  • Loading branch information
danielcaldas authored Dec 24, 2018
1 parent a6126a7 commit c555e3f
Show file tree
Hide file tree
Showing 9 changed files with 470 additions and 452 deletions.
3 changes: 2 additions & 1 deletion documentation.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
toc:
- graph.config
- Graph
- Graph/helper
- Graph/renderer
- Graph/builder
- Graph/helper
- Graph/collapse-helper
- Node
- Node/helper
Expand Down
4 changes: 2 additions & 2 deletions src/components/graph/collapse.helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ function computeNodeDegree(nodeId, linksMatrix = {}) {
* @param {string} rootNodeId - node who's leafs we want to calculate.
* @param {Object.<string, Object>} linksMatrix - an object containing a matrix of connections of the graph, for each nodeId,
* there is an object that maps adjacent nodes ids (string) and their values (number).
* @param {Object} config - same as {@link #renderGraph|config in renderGraph}.
* @param {Object} config - same as {@link #graphrenderer|config in renderGraph}.
* @param {boolean} config.directed - tells whether linksMatrix represents a directed graph or not.
* @returns {Array.<Object.<string, string>>} a list of leaf connections.
* What is a leaf connection? A leaf connection is a link between some node A and other node B
Expand Down Expand Up @@ -172,7 +172,7 @@ function toggleLinksConnections(d3Links, connectionMatrix) {
* @param {Object.<string, Object>} linksMatrix - an object containing a matrix of connections of the graph, for each nodeId,
* there is an object that maps adjacent nodes ids (string) and their values (number).
* @param {Array.<Object.<string, string>>} connections - connections to toggle on matrix.
* @param {Object} config - same as {@link #renderGraph|config in renderGraph}.
* @param {Object} config - same as {@link #graphrenderer|config in renderGraph}.
* @param {boolean} config.directed - tells whether linksMatrix represents a directed graph or not.
* @returns {Object.<string, Object>} updated linksMatrix
* @memberof Graph/collapse-helper
Expand Down
195 changes: 195 additions & 0 deletions src/components/graph/graph.builder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/**
* @module Graph/builder
* @description
* Offers a series of methods that isolate the way graph elements are built (nodes and links mainly).
*/
import CONST from "./graph.const";

import { buildLinkPathDefinition } from "../link/link.helper";
import { getMarkerId } from "../marker/marker.helper";

/**
* Get the correct node opacity in order to properly make decisions based on context such as currently highlighted node.
* @param {Object} node - the node object for whom we will generate properties.
* @param {string} highlightedNode - same as {@link #graphrenderer|highlightedNode in renderGraph}.
* @param {Object} highlightedLink - same as {@link #graphrenderer|highlightedLink in renderGraph}.
* @param {Object} config - same as {@link #graphrenderer|config in renderGraph}.
* @returns {number} the opacity value for the given node.
* @memberof Graph/builder
*/
function _getNodeOpacity(node, highlightedNode, highlightedLink, config) {
const highlight =
node.highlighted ||
node.id === (highlightedLink && highlightedLink.source) ||
node.id === (highlightedLink && highlightedLink.target);
const someNodeHighlighted = !!(
highlightedNode ||
(highlightedLink && highlightedLink.source && highlightedLink.target)
);
let opacity;

if (someNodeHighlighted && config.highlightDegree === 0) {
opacity = highlight ? config.node.opacity : config.highlightOpacity;
} else if (someNodeHighlighted) {
opacity = highlight ? config.node.opacity : config.highlightOpacity;
} else {
opacity = config.node.opacity;
}

return opacity;
}

/**
* Build some Link properties based on given parameters.
* @param {Object} link - the link object for which we will generate properties.
* @param {Object.<string, Object>} nodes - same as {@link #graphrenderer|nodes in renderGraph}.
* @param {Object.<string, Object>} links - same as {@link #graphrenderer|links in renderGraph}.
* @param {Object} config - same as {@link #graphrenderer|config in renderGraph}.
* @param {Function[]} linkCallbacks - same as {@link #graphrenderer|linkCallbacks in renderGraph}.
* @param {string} highlightedNode - same as {@link #graphrenderer|highlightedNode in renderGraph}.
* @param {Object} highlightedLink - same as {@link #graphrenderer|highlightedLink in renderGraph}.
* @param {number} transform - value that indicates the amount of zoom transformation.
* @returns {Object} returns an object that aggregates all props for creating respective Link component instance.
* @memberof Graph/builder
*/
function buildLinkProps(link, nodes, links, config, linkCallbacks, highlightedNode, highlightedLink, transform) {
const { source, target } = link;
const x1 = (nodes[source] && nodes[source].x) || 0;
const y1 = (nodes[source] && nodes[source].y) || 0;
const x2 = (nodes[target] && nodes[target].x) || 0;
const y2 = (nodes[target] && nodes[target].y) || 0;

const d = buildLinkPathDefinition({ source: { x: x1, y: y1 }, target: { x: x2, y: y2 } }, config.link.type);

let mainNodeParticipates = false;

switch (config.highlightDegree) {
case 0:
break;
case 2:
mainNodeParticipates = true;
break;
default:
// 1st degree is the fallback behavior
mainNodeParticipates = source === highlightedNode || target === highlightedNode;
break;
}

const reasonNode = mainNodeParticipates && nodes[source].highlighted && nodes[target].highlighted;
const reasonLink =
source === (highlightedLink && highlightedLink.source) &&
target === (highlightedLink && highlightedLink.target);
const highlight = reasonNode || reasonLink;

let opacity = config.link.opacity;

if (highlightedNode || (highlightedLink && highlightedLink.source)) {
opacity = highlight ? config.link.opacity : config.highlightOpacity;
}

let stroke = link.color || config.link.color;

if (highlight) {
stroke = config.link.highlightColor === CONST.KEYWORDS.SAME ? config.link.color : config.link.highlightColor;
}

let strokeWidth = config.link.strokeWidth * (1 / transform);

if (config.link.semanticStrokeWidth) {
const linkValue = links[source][target] || links[target][source] || 1;

strokeWidth += (linkValue * strokeWidth) / 10;
}

const markerId = config.directed ? getMarkerId(highlight, transform, config) : null;

return {
markerId,
d,
source,
target,
strokeWidth,
stroke,
mouseCursor: config.link.mouseCursor,
className: CONST.LINK_CLASS_NAME,
opacity,
onClickLink: linkCallbacks.onClickLink,
onRightClickLink: linkCallbacks.onRightClickLink,
onMouseOverLink: linkCallbacks.onMouseOverLink,
onMouseOutLink: linkCallbacks.onMouseOutLink,
};
}

/**
* Build some Node properties based on given parameters.
* @param {Object} node - the node object for whom we will generate properties.
* @param {Object} config - same as {@link #graphrenderer|config in renderGraph}.
* @param {Function[]} nodeCallbacks - same as {@link #graphrenderer|nodeCallbacks in renderGraph}.
* @param {string} highlightedNode - same as {@link #graphrenderer|highlightedNode in renderGraph}.
* @param {Object} highlightedLink - same as {@link #graphrenderer|highlightedLink in renderGraph}.
* @param {number} transform - value that indicates the amount of zoom transformation.
* @returns {Object} returns object that contain Link props ready to be feeded to the Link component.
* @memberof Graph/builder
*/
function buildNodeProps(node, config, nodeCallbacks = {}, highlightedNode, highlightedLink, transform) {
const highlight =
node.highlighted ||
(node.id === (highlightedLink && highlightedLink.source) ||
node.id === (highlightedLink && highlightedLink.target));
const opacity = _getNodeOpacity(node, highlightedNode, highlightedLink, config);
let fill = node.color || config.node.color;

if (highlight && config.node.highlightColor !== CONST.KEYWORDS.SAME) {
fill = config.node.highlightColor;
}

let stroke = node.strokeColor || config.node.strokeColor;

if (highlight && config.node.highlightStrokeColor !== CONST.KEYWORDS.SAME) {
stroke = config.node.highlightStrokeColor;
}

let label = node[config.node.labelProperty] || node.id;

if (typeof config.node.labelProperty === "function") {
label = config.node.labelProperty(node);
}

const t = 1 / transform;
const nodeSize = node.size || config.node.size;
const fontSize = highlight ? config.node.highlightFontSize : config.node.fontSize;
const dx = fontSize * t + nodeSize / 100 + 1.5;
const strokeWidth = highlight ? config.node.highlightStrokeWidth : config.node.strokeWidth;
const svg = node.svg || config.node.svg;
const fontColor = node.fontColor || config.node.fontColor;

return {
...node,
className: CONST.NODE_CLASS_NAME,
cursor: config.node.mouseCursor,
cx: (node && node.x) || "0",
cy: (node && node.y) || "0",
fill,
fontColor,
fontSize: fontSize * t,
dx,
fontWeight: highlight ? config.node.highlightFontWeight : config.node.fontWeight,
id: node.id,
label,
onClickNode: nodeCallbacks.onClickNode,
onRightClickNode: nodeCallbacks.onRightClickNode,
onMouseOverNode: nodeCallbacks.onMouseOverNode,
onMouseOut: nodeCallbacks.onMouseOut,
opacity,
renderLabel: config.node.renderLabel,
size: nodeSize * t,
stroke,
strokeWidth: strokeWidth * t,
svg,
type: node.symbolType || config.node.symbolType,
viewGenerator: node.viewGenerator || config.node.viewGenerator,
overrideGlobalViewGenerator: !node.viewGenerator && node.svg,
};
}

export { buildLinkProps, buildNodeProps };
30 changes: 15 additions & 15 deletions src/components/graph/graph.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,6 @@
* out of the box provide the look and feel of a directed graph and add directional semantic to links. You can see a sample in the image below.
* <br/>
* <img src="https://github.com/danielcaldas/react-d3-graph/blob/master/docs/rd3g-directed.gif?raw=true" width="820" height="480"/>
* @param {number} [height=400] - the height of the (svg) area where the graph will be rendered.
* @param {boolean} [nodeHighlightBehavior=false] - 🚅🚅🚅 when user mouse hovers a node that node and adjacent common
* connections will be highlighted (depending on the *highlightDegree* value). All the remaining nodes and links assume opacity value equal to **highlightOpacity**.
* @param {boolean} [linkHighlightBehavior=false] - 🚅🚅🚅 when the user mouse hovers some link that link and the correspondent nodes will be highlighted, this is a similar behavior
* to *nodeHighlightBehavior* but for links <small>(just for historical reference this property was introduced in **v1.0.0**)</small>.
* @param {number} [highlightDegree=1] - **Possible values: 0, 1 or 2**. This value represents the range of the
* highlight behavior when some node is highlighted. If the value is set to **0** only the selected node will be
* highlighted. If the value is set to **1** the selected node and his 1st degree connections will be highlighted. If
* the value is set to **2** the selected node will be highlighted as well as the 1st and 2nd common degree connections.
* @param {number} [highlightOpacity=1] - this value is used to highlight nodes in the network. The lower
* the value the more the less highlighted nodes will be visible (related to *nodeHighlightBehavior*).
* @param {number} [maxZoom=8] - max zoom that can be performed against the graph.
* @param {number} [minZoom=0.1] - min zoom that can be performed against the graph.
* @param {number} [focusZoom=1] - zoom that will be applied when the graph view is focused in a node. Its value must be between
* *minZoom* and *maxZoom*. If the specified *focusZoom* is out of this range, *minZoom* or *maxZoom* will be applied instead.
* **NOTE:** This animation is not trigger by default. In order to trigger it you need to pass down to `react-d3-graph` the
Expand All @@ -75,6 +62,19 @@
* <img src="https://github.com/danielcaldas/react-d3-graph/blob/master/docs/rd3g-zoom-animation.gif?raw=true" width="820" height="480"/>
*
* @param {number} [focusAnimationDuration=0.75] - duration (in seconds) for the animation that takes place when focusing the graph on a node.
* @param {number} [height=400] - the height of the (svg) area where the graph will be rendered.
* @param {boolean} [nodeHighlightBehavior=false] - 🚅🚅🚅 when user mouse hovers a node that node and adjacent common
* connections will be highlighted (depending on the *highlightDegree* value). All the remaining nodes and links assume opacity value equal to **highlightOpacity**.
* @param {boolean} [linkHighlightBehavior=false] - 🚅🚅🚅 when the user mouse hovers some link that link and the correspondent nodes will be highlighted, this is a similar behavior
* to *nodeHighlightBehavior* but for links <small>(just for historical reference this property was introduced in **v1.0.0**)</small>.
* @param {number} [highlightDegree=1] - **Possible values: 0, 1 or 2**. This value represents the range of the
* highlight behavior when some node is highlighted. If the value is set to **0** only the selected node will be
* highlighted. If the value is set to **1** the selected node and his 1st degree connections will be highlighted. If
* the value is set to **2** the selected node will be highlighted as well as the 1st and 2nd common degree connections.
* @param {number} [highlightOpacity=1] - this value is used to highlight nodes in the network. The lower
* the value the more the less highlighted nodes will be visible (related to *nodeHighlightBehavior*).
* @param {number} [maxZoom=8] - max zoom that can be performed against the graph.
* @param {number} [minZoom=0.1] - min zoom that can be performed against the graph.
* @param {boolean} [panAndZoom=false] - 🚅🚅🚅 pan and zoom effect when performing zoom in the graph,
* a similar functionality may be consulted {@link https://bl.ocks.org/mbostock/2a39a768b1d4bc00a09650edef75ad39|here}.
* @param {boolean} [staticGraph=false] - when setting this value to true the graph will be completely static, thus
Expand Down Expand Up @@ -192,14 +192,14 @@ export default {
automaticRearrangeAfterDropNode: false,
collapsible: false,
directed: false,
focusAnimationDuration: 0.75,
focusZoom: 1,
height: 400,
highlightDegree: 1,
highlightOpacity: 1,
linkHighlightBehavior: false,
maxZoom: 8,
minZoom: 0.1,
focusZoom: 1,
focusAnimationDuration: 0.75,
nodeHighlightBehavior: false,
panAndZoom: false,
staticGraph: false,
Expand Down
1 change: 0 additions & 1 deletion src/components/graph/graph.const.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import CONST from "../../const";

export default {
COORDS_SEPARATOR: ",",
FORCE_IDEAL_STRENGTH: -100, // TODO: Expose as configurable,
FORCE_X: 0.06,
FORCE_Y: 0.06,
GRAPH_CONTAINER_ID: "graph-container-zoomable",
Expand Down
Loading

0 comments on commit c555e3f

Please sign in to comment.