Skip to content

Commit

Permalink
fix(graph-viz): optimize node and edge styles (#954)
Browse files Browse the repository at this point in the history
  • Loading branch information
Xwatson authored Jul 15, 2024
1 parent d66c77e commit 4093d3d
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 24 deletions.
9 changes: 6 additions & 3 deletions packages/graph-viz/src/force-atlas/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const DEFAULT_NODE_LABEL_MARGIN_TOP = 4;

export const DEFAULT_NODE_LABEL_FONT_SIZE = 12;

export const DEFAULT_NODE_LABEL_WIDTH = 150;
export const DEFAULT_NODE_LABEL_WIDTH = 72;

export const DEFAULT_NODE_LABEL_HEIGHT = 22;

Expand Down Expand Up @@ -55,6 +55,9 @@ export const DEFAULT_LINE_STYLES = {
color: {
[EdgeDirection.IN]: '#73D897',
[EdgeDirection.OUT]: '#6698FF',
[EdgeDirection.NONE]: `#ddd`
}
[EdgeDirection.NONE]: `#eee`
},
opacity: 0.4
};

export const DEFAULT_EDGE_PARTICLE_SIZE = 4;
12 changes: 7 additions & 5 deletions packages/graph-viz/src/force-atlas/node.flavour.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import Graph from 'graphology';
import { ForceAtlasElement, ForceAtlasNodeElement } from '../interfaces';
import { ForceAtlasNodeGenerator } from './generators/node.generator';
import { getEdgeGenerator, getEdgeGeneratorData, getEdgesInSourceOrTarget } from './utils/edge';
import { getAssociatedNodesById, getNodeGenerator, isFirstDepthNode } from './utils/node';
import { getNodeGenerator, getNodes, isFirstDepthNode } from './utils/node';
import { SECOND_DEPTH_NODE_ALPHA } from './constants';

export class ForceAtlasNodeFlavour extends CommonElementFlavour<ForceAtlasNodeElement, PlaitBoard>
implements OnContextChanged<ForceAtlasNodeElement, PlaitBoard> {
Expand All @@ -36,7 +37,7 @@ export class ForceAtlasNodeFlavour extends CommonElementFlavour<ForceAtlasNodeEl
const isActive = activeNodeId === this.element.id;
this.nodeGenerator.processDrawing(this.element, isActive ? PlaitBoard.getElementActiveHost(this.board) : this.getElementG(), {
isActive,
isFirstDepth: isFirstDepthNode(this.element.id, activeNodeId, parent)
opacity: isFirstDepthNode(this.element.id, activeNodeId, parent) ? 1 : SECOND_DEPTH_NODE_ALPHA
});
}

Expand All @@ -50,13 +51,14 @@ export class ForceAtlasNodeFlavour extends CommonElementFlavour<ForceAtlasNodeEl
cacheSelectedElements(this.board, [value.element]);
}
const selectElements = getSelectedElements(this.board);
const associatedNodes = getAssociatedNodesById(value.element.id, parent);
associatedNodes.forEach(node => {
const nodes = getNodes(parent);
nodes.forEach(node => {
const nodeGenerator = getNodeGenerator(node);
nodeGenerator.destroy();
const isFirstDepth = selectElements.length > 0 && isFirstDepthNode(node.id, selectElements[0].id, parent);
nodeGenerator.processDrawing(node, this.getElementG(), {
isActive: selectElements?.[0]?.id === node.id,
isFirstDepth: selectElements.length > 0 && isFirstDepthNode(node.id, selectElements[0].id, parent)
opacity: selectElements.length === 0 ? 1 : isFirstDepth ? 1 : SECOND_DEPTH_NODE_ALPHA
});
});

Expand Down
2 changes: 1 addition & 1 deletion packages/graph-viz/src/force-atlas/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export interface EdgeGeneratorData extends GeneratorExtraData {

export interface NodeGeneratorData extends GeneratorExtraData {
isActive: boolean;
isFirstDepth: boolean;
opacity: number;
}

export interface NodeIconItem {
Expand Down
30 changes: 17 additions & 13 deletions packages/graph-viz/src/force-atlas/utils/draw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ACTIVE_BACKGROUND_NODE_ALPHA,
DEFAULT_ACTIVE_NODE_SIZE_MULTIPLIER,
DEFAULT_ACTIVE_WAVE_NODE_SIZE_MULTIPLIER,
DEFAULT_EDGE_PARTICLE_SIZE,
DEFAULT_LINE_STYLES,
DEFAULT_NODE_LABEL_HEIGHT,
DEFAULT_NODE_LABEL_STYLE,
Expand All @@ -21,7 +22,7 @@ export function drawNode(
board: PlaitBoard,
node: ForceAtlasNodeElement,
point: Point,
options: { iconG?: SVGGElement; isActive: boolean; isFirstDepth: boolean }
options: { iconG?: SVGGElement; isActive: boolean; opacity?: number }
) {
const roughSVG = PlaitBoard.getRoughSVG(board);
const nodeStyles: NodeStyles = {
Expand All @@ -34,9 +35,6 @@ export function drawNode(
diameter = diameter * DEFAULT_ACTIVE_NODE_SIZE_MULTIPLIER;
}
const nodeG = drawCircle(roughSVG, [x, y], diameter, nodeStyles);
if (options.iconG) {
nodeG.append(options.iconG);
}
const labelWidth = node.styles?.labelWidth ?? DEFAULT_NODE_LABEL_WIDTH;
const labelHeight = node.styles?.labelHeight ?? DEFAULT_NODE_LABEL_HEIGHT;
const textForeignObject = createForeignObject(x - labelWidth / 2, y, labelWidth, labelHeight);
Expand All @@ -54,10 +52,11 @@ export function drawNode(
nodeG.append(waveCircle);
textForeignObject.setAttribute('y', `${y + waveDiameter / 2}`);
} else {
if (!options.isFirstDepth) {
nodeG.setAttribute('opacity', SECOND_DEPTH_NODE_ALPHA.toString());
}
textForeignObject.setAttribute('y', `${y + diameter / 2}`);
nodeG.setAttribute('opacity', (options.opacity ?? 1).toString());
}
if (options.iconG) {
nodeG.append(options.iconG);
}
nodeG.append(textForeignObject);
return nodeG;
Expand All @@ -77,19 +76,24 @@ export function drawEdge(startPoint: Point, endPoint: Point, direction: EdgeDire
if (!isTargetSelf) {
path.setAttribute('d', `M${sx},${sy} Q${cx},${cy} ${ex},${ey}`);
} else {
const x = startPoint[0];
const y = startPoint[1];
const besselX = 40;
const besselY = 75;
const angle = 55;
const angleRad = (angle * Math.PI) / 180;
const x = nodeRadius * Math.cos(angleRad);
const y = nodeRadius * Math.sin(angleRad);
const offsetX = nodeRadius * Math.cos(angleRad);
const offsetY = nodeRadius * Math.sin(angleRad);
path.setAttribute(
'd',
`M -${x},-${y}
C -45,-85, 45 -85
${x},-${y}`
`M ${x - offsetX},${y - offsetY}
C ${x - besselX},${y - besselY}, ${x + besselX} ${y - besselY}
${x + offsetX},${y - offsetY}`
);
}
path.setAttribute('fill', 'none');
path.setAttribute('stroke', DEFAULT_LINE_STYLES.color[direction]);
path.setAttribute('opacity', DEFAULT_LINE_STYLES.opacity.toString());
g.append(path);
return {
g,
Expand All @@ -99,7 +103,7 @@ export function drawEdge(startPoint: Point, endPoint: Point, direction: EdgeDire

export function drawParticle(board: PlaitBoard, startPoint: Point, direction: EdgeDirection) {
const roughSVG = PlaitBoard.getRoughSVG(board);
const pointG = drawCircle(roughSVG, [0, 0], 5, {
const pointG = drawCircle(roughSVG, [0, 0], DEFAULT_EDGE_PARTICLE_SIZE, {
...DEFAULT_STYLES,
strokeWidth: 0,
fill: DEFAULT_LINE_STYLES.color[direction]
Expand Down
1 change: 1 addition & 0 deletions src/app/graph-viz/graph-viz.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
align-items: center;
justify-content: center;
height: 100%;
cursor: pointer;
}
}
4 changes: 2 additions & 2 deletions src/app/graph-viz/mock-force-atlas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ export const mockForceAtlasData: ForceAtlasElement[] = [
},
{
id: '1-6',
source: '1',
target: '1'
source: '2',
target: '2'
}
]
}
Expand Down

0 comments on commit 4093d3d

Please sign in to comment.