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

Reorganizing the computation of arrows and links for circle nodes #271

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
23 changes: 18 additions & 5 deletions src/components/graph/graph.builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import CONST from "./graph.const";

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

/**
* Get the correct node opacity in order to properly make decisions based on context such as currently highlighted node.
Expand Down Expand Up @@ -53,12 +54,16 @@ function _getNodeOpacity(node, highlightedNode, highlightedLink, config) {
*/
function buildLinkProps(link, nodes, links, config, linkCallbacks, highlightedNode, highlightedLink, transform) {
const { source, target } = link;
const x1 = nodes?.[source]?.x || 0;
const y1 = nodes?.[source]?.y || 0;
const x2 = nodes?.[target]?.x || 0;
const y2 = nodes?.[target]?.y || 0;

let x1 = nodes?.[source]?.x || 0;

let y1 = nodes?.[source]?.y || 0;

let x2 = nodes?.[target]?.x || 0;

let y2 = nodes?.[target]?.y || 0;

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

let mainNodeParticipates = false;

Expand Down Expand Up @@ -121,6 +126,14 @@ function buildLinkProps(link, nodes, links, config, linkCallbacks, highlightedNo
fontWeight = highlight ? config.link.highlightFontWeight : config.link.fontWeight;
}

const normalizedNodeCoordinates = getNormalizedNodeCoordinates(
{ source: { x: x1, y: y1 }, target: { x: x2, y: y2 } },
nodes,
config,
strokeWidth
);
const d = buildLinkPathDefinition(normalizedNodeCoordinates, type);

return {
className: CONST.LINK_CLASS_NAME,
d,
Expand Down
60 changes: 60 additions & 0 deletions src/components/graph/graph.helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -451,11 +451,71 @@ function updateNodeHighlightedValue(nodes, links, config, id, value = false) {
};
}

/**
* Computes the normalized vector from a vector
*
antoninklopp marked this conversation as resolved.
Show resolved Hide resolved
* @param {Object} vector a 2D vector with x and y components
* @param {number} vector.x x coordinate
* @param {number} vector.y y coordinate
* @returns {Object} normalized vector
*/
function normalize(vector) {
let norm = Math.sqrt(Math.pow(vector.x, 2) + Math.pow(vector.y, 2));

return { x: vector.x / norm, y: vector.y / norm };
}

/**
* Computes new node coordinates to make arrowheads point at nodes
*
* @param {Object} node - the couple of nodes we need to compute new coordinates
* @param {Object} node.source - node source
* @param {Object} node.target - node target
* @param {Object.<string, Object>} nodes - same as {@link #graphrenderer|nodes in renderGraph}.
* @param {Object} config - same as {@link #graphrenderer|config in renderGraph}.
* @param {number} strokeWidth width of the link stroke
* @returns {Object} new nodes coordinates
*
*/
function getNormalizedNodeCoordinates({ source = {}, target = {} }, nodes, config, strokeWidth) {
let { x: x1, y: y1 } = source;

let { x: x2, y: y2 } = target;

if (config.node) {
// Arrow configuration is only available for circles for now
switch (config.node.symbolType) {
case CONST.SYMBOLS.CIRCLE: {
let directionVector = normalize({ x: x2 - x1, y: y2 - y1 });

let strokeSize = strokeWidth * Math.min(config.link.markerWidth, config.link.markerHeight);

let nodeSize = nodes?.[source]?.size || config.node.size;

// cause this is a circle and A = pi * r^2
// We multiply by 0.95, because if we don't the link is not melting properly
nodeSize = Math.sqrt(nodeSize / Math.PI) * 0.95;

// Points from the source, we move them not to begin in the circle but outside
x1 += nodeSize * directionVector.x;
y1 += nodeSize * directionVector.y;
// Points from the target, we move the by the size of the radius of the circle + the size of the arrow
x2 -= (nodeSize + (config.directed ? strokeSize : 0)) * directionVector.x;
y2 -= (nodeSize + (config.directed ? strokeSize : 0)) * directionVector.y;
break;
}
}
}

return { source: { x: x1, y: y1 }, target: { x: x2, y: y2 } };
}

export {
checkForGraphConfigChanges,
checkForGraphElementsChanges,
getCenterAndZoomTransformation,
getId,
initializeGraphState,
updateNodeHighlightedValue,
getNormalizedNodeCoordinates,
};
8 changes: 3 additions & 5 deletions src/components/graph/graph.renderer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
import React from "react";

import CONST from "./graph.const";
import { MARKERS, MARKER_SMALL_SIZE, MARKER_MEDIUM_OFFSET, MARKER_LARGE_OFFSET } from "../marker/marker.const";
import { MARKERS } from "../marker/marker.const";

import Link from "../link/Link";
import Node from "../node/Node";
import Marker from "../marker/Marker";
import { buildLinkProps, buildNodeProps } from "./graph.builder";
import { getId } from "../graph/graph.helper";
import { isNodeVisible } from "./collapse.helper";
import { getMarkerSize } from "../marker/marker.helper";

/**
* Build Link components given a list of links.
Expand Down Expand Up @@ -105,10 +106,7 @@ function _renderDefs() {
return cachedDefs;
}

const small = MARKER_SMALL_SIZE;
const medium = small + (MARKER_MEDIUM_OFFSET * config.maxZoom) / 3;
const large = small + (MARKER_LARGE_OFFSET * config.maxZoom) / 3;

const { small, medium, large } = getMarkerSize(config);
const markerProps = {
markerWidth: config.link.markerWidth,
markerHeight: config.link.markerHeight,
Expand Down
39 changes: 37 additions & 2 deletions src/components/marker/marker.helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@
* @description
* Offers a series of methods to compute proper markers within a given context.
*/
import { MARKERS, SIZES, HIGHLIGHTED } from "./marker.const";
import {
MARKERS,
SIZES,
HIGHLIGHTED,
MARKER_SMALL_SIZE,
MARKER_MEDIUM_OFFSET,
MARKER_LARGE_OFFSET,
} from "./marker.const";
import CONST from "../graph/graph.const";

/**
* This function is a key template builder to access MARKERS structure.
Expand Down Expand Up @@ -93,4 +101,31 @@ function _memoizedComputeMarkerId() {
*/
const getMarkerId = _memoizedComputeMarkerId();

export { getMarkerId };
/**
* Computes the three marker sizes
*
antoninklopp marked this conversation as resolved.
Show resolved Hide resolved
* For supported shapes in {@link Graph/helper/getNormalizedNodeCoordinates}, the function should return 0,
* to be able to control more accurately nodes and arrows sizes and positions in directional graphs.
*
* @param {Object} config - the graph config object.
* @returns {Object} size of markers
*/
function getMarkerSize(config) {
let small = MARKER_SMALL_SIZE;

let medium = small + (MARKER_MEDIUM_OFFSET * config.maxZoom) / 3;

let large = small + (MARKER_LARGE_OFFSET * config.maxZoom) / 3;

switch (config.node.symbolType) {
case CONST.SYMBOLS.CIRCLE:
small = 0;
medium = 0;
large = 0;
break;
}

return { small: small, medium: medium, large: large };
}

export { getMarkerId, getMarkerSize };