Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor/split graph helper logic #158

Merged
merged 5 commits into from
Dec 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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