Skip to content

feat: creating a more condensed left to right topology layout #1036

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

Merged
merged 5 commits into from
Jul 28, 2021
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
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import {
TOPOLOGY_INTERACTION_CONTROL_DATA
} from './interactions/topology-interaction-control.component';
import { TopologyZoom } from './interactions/zoom/topology-zoom';
import { TreeLayout } from './layouts/tree-layout';
import { CustomTreeLayout } from './layouts/custom-tree-layout';

export class D3Topology implements Topology {
private static readonly CONTAINER_CLASS: string = 'topology-internal-container';
Expand All @@ -57,7 +57,7 @@ export class D3Topology implements Topology {
protected readonly dataClearCallbacks: (() => void)[] = [];
protected container?: HTMLDivElement;
protected tooltip?: TopologyTooltip;
protected layout: TopologyLayout = new TreeLayout(); // TODO: Make this configurable with Node and edge renderers
protected layout: TopologyLayout = new CustomTreeLayout(); // TODO: Make this configurable with Node and edge renderers

protected readonly userNodes: TopologyNode[];
protected readonly nodeRenderer: TopologyNodeRenderer;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { hierarchy, HierarchyNode } from 'd3-hierarchy';
import { RenderableTopology, TopologyEdge, TopologyNode } from '../../topology';
import { D3ProxyNode, TreeLayout } from './tree-layout';

export class CustomTreeLayout extends TreeLayout {
public layout(topology: RenderableTopology<TopologyNode, TopologyEdge>): void {
const rootHierarchyNode = hierarchy(this.buildHierarchyProxyNodes(topology.nodes, { x: 0, y: 0 }));

const nodeWidth = this.getNodeWidth(rootHierarchyNode);
const nodeHeight = this.getNodeHeight(rootHierarchyNode);

this.updateLayout(rootHierarchyNode, 0, -1, nodeWidth * 1, nodeHeight);
}

private updateLayout(
hierarchyNode: HierarchyNode<D3ProxyNode>,
nodeRowIndex: number,
nodeColumnIndex: number,
cellWidth: number,
cellHeight: number
): number {
if (hierarchyNode.data.sourceNode !== undefined) {
hierarchyNode.data.sourceNode.x = nodeColumnIndex * cellWidth;
hierarchyNode.data.sourceNode.y = nodeRowIndex * cellHeight;
}

hierarchyNode.children?.forEach((node, index) => {
this.updateLayout(node, nodeRowIndex + index, nodeColumnIndex + 1, cellWidth, cellHeight);
});

return (hierarchyNode.children?.length ?? 0) + 1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class TreeLayout implements TopologyLayout {
return Math.min(hierarchyNode.x, ...(hierarchyNode.children ?? []).map(node => this.getMinYPosition(node)));
}

private buildHierarchyProxyNodes(
protected buildHierarchyProxyNodes(
nodes: RenderableTopologyNode[],
startingLocation: TopologyCoordinates
): D3ProxyNode {
Expand All @@ -66,7 +66,7 @@ export class TreeLayout implements TopologyLayout {
return level0RootNode;
}

private getNodeWidth(root: HierarchyNode<D3ProxyNode>): number {
protected getNodeWidth(root: HierarchyNode<D3ProxyNode>): number {
const leaf = first(root.leaves());
let leafWidth = 240;

Expand All @@ -81,7 +81,7 @@ export class TreeLayout implements TopologyLayout {
return leafWidth + 160;
}

private getNodeHeight(root: HierarchyNode<D3ProxyNode>): number {
protected getNodeHeight(root: HierarchyNode<D3ProxyNode>): number {
return this.getRenderedNodeHeight(first(root.leaves()));
}

Expand All @@ -99,7 +99,7 @@ export class TreeLayout implements TopologyLayout {
return defaultNodeHeight;
}

private buildTopologyHierarchyNodeMap(
protected buildTopologyHierarchyNodeMap(
nodes: RenderableTopologyNode[],
startingLocation: TopologyCoordinates
): Map<RenderableTopologyNode, D3ProxyNode> {
Expand Down Expand Up @@ -163,7 +163,7 @@ export class TreeLayout implements TopologyLayout {
}
}

interface D3ProxyNode {
export interface D3ProxyNode {
sourceNode: RenderableTopologyNode | undefined;
hasIncomingEdges: boolean;
children: D3ProxyNode[];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Injectable, Renderer2 } from '@angular/core';
import { distanceBetweenPoints, getVectorAngleRad } from '@hypertrace/common';
import { distanceBetweenPoints, getVectorAngleRad, normalizeAngleRadians } from '@hypertrace/common';
import { D3UtilService } from '../../../utils/d3/d3-util.service';
import { RenderableTopologyEdge, TopologyEdge, TopologyEdgeRenderer, TopologyEdgeState } from '../../topology';

Expand Down Expand Up @@ -79,7 +79,7 @@ export class TopologyEdgeRendererService implements TopologyEdgeRenderer {
}

const sourceRad = getVectorAngleRad(edge.source, edge.target);
const targetRad = sourceRad + Math.PI;
const targetRad = normalizeAngleRadians(sourceRad + Math.PI);

const sourceAttachPoint = sourceNodeRenderedData.getAttachmentPoint(sourceRad);
const targetAttachPoint = targetNodeRenderedData.getAttachmentPoint(targetRad);
Expand All @@ -88,13 +88,17 @@ export class TopologyEdgeRendererService implements TopologyEdgeRenderer {
if (distanceBetweenPoints(edge.source, sourceAttachPoint) > distanceBetweenPoints(edge.source, targetAttachPoint)) {
return {
source: targetAttachPoint,
target: sourceAttachPoint
sourceRad: targetRad,
target: sourceAttachPoint,
targetRad: sourceRad
};
}

return {
source: sourceAttachPoint,
target: targetAttachPoint
sourceRad: sourceRad,
target: targetAttachPoint,
targetRad: targetRad
};
}

Expand All @@ -112,6 +116,8 @@ export interface TopologyEdgePositionInformation {
x: number;
y: number;
};
sourceRad: number;
targetRad: number;
}

export interface TopologyEdgeRenderDelegate<T extends TopologyEdge = TopologyEdge> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
stroke: $gray-3;
stroke: $gray-2;
fill: none;

@include chart-small-regular;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Injectable, Renderer2 } from '@angular/core';
import { Color, DomElementMeasurerService, NumericFormatter, selector } from '@hypertrace/common';
import { MetricAggregation } from '@hypertrace/distributed-tracing';
import { select, Selection } from 'd3-selection';
import { linkHorizontal } from 'd3-shape';
import { Link, linkHorizontal } from 'd3-shape';
import {
TopologyEdgePositionInformation,
TopologyEdgeRenderDelegate
Expand Down Expand Up @@ -114,7 +114,7 @@ export class EntityEdgeCurveRendererService implements TopologyEdgeRenderDelegat
selection
.select(selector(this.edgeLineClass))
.select('.edge-path')
.attr('stroke', edgeFocusedCategory?.strokeColor ?? Color.Gray3);
.attr('stroke', edgeFocusedCategory?.strokeColor ?? Color.Gray2);

selection
.select(selector(this.edgeMetricBubbleClass))
Expand Down Expand Up @@ -200,16 +200,14 @@ export class EntityEdgeCurveRendererService implements TopologyEdgeRenderDelegat

pathSelections.exit().remove();

pathSelections
.enter()
.append('path')
.merge(pathSelections)
.attr('d', data =>
linkHorizontal<TopologyEdgePositionInformation, Position>()
.x(datum => datum.x)
.y(datum => datum.y)(data)
)
.classed('edge-path', true);
const lineGenerator: Link<unknown, TopologyEdgePositionInformation, Position> = linkHorizontal<
TopologyEdgePositionInformation,
Position
>()
.x(datum => datum.x)
.y(datum => datum.y);

pathSelections.enter().append('path').merge(pathSelections).attr('d', lineGenerator).classed('edge-path', true);
}

private updateLabelPosition(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ export abstract class EntityNodeBoxRendererService implements TopologyNodeRender
}

private isAngleInIQuadrant(angle: number): boolean {
return angle > 0 && angle < Math.PI / 2;
return angle >= 0 && angle < Math.PI / 2;
}

private isAnglePerpendicularlyAbove(angle: number): boolean {
Expand Down