From 5319fb83c3d8f9d6158c95d38c7bd2b704d150c6 Mon Sep 17 00:00:00 2001 From: anandtiwary <52081890+anandtiwary@users.noreply.github.com> Date: Wed, 28 Jul 2021 09:03:05 -0700 Subject: [PATCH 1/3] feat: creating a more condensed left to right topology layout --- .../components/topology/d3/d3-topology.ts | 4 +-- .../topology/d3/layouts/custom-tree-layout.ts | 33 +++++++++++++++++++ .../topology/d3/layouts/tree-layout.ts | 10 +++--- .../curved/entity-edge-curve-renderer.scss | 2 +- .../entity-edge-curve-renderer.service.ts | 2 +- .../box/entity-node-box-renderer.service.ts | 10 +++--- 6 files changed, 47 insertions(+), 14 deletions(-) create mode 100644 projects/observability/src/shared/components/topology/d3/layouts/custom-tree-layout.ts diff --git a/projects/observability/src/shared/components/topology/d3/d3-topology.ts b/projects/observability/src/shared/components/topology/d3/d3-topology.ts index f6821ca61..e241f7f43 100644 --- a/projects/observability/src/shared/components/topology/d3/d3-topology.ts +++ b/projects/observability/src/shared/components/topology/d3/d3-topology.ts @@ -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'; @@ -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; diff --git a/projects/observability/src/shared/components/topology/d3/layouts/custom-tree-layout.ts b/projects/observability/src/shared/components/topology/d3/layouts/custom-tree-layout.ts new file mode 100644 index 000000000..a099d231b --- /dev/null +++ b/projects/observability/src/shared/components/topology/d3/layouts/custom-tree-layout.ts @@ -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): 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, + 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; + } +} diff --git a/projects/observability/src/shared/components/topology/d3/layouts/tree-layout.ts b/projects/observability/src/shared/components/topology/d3/layouts/tree-layout.ts index c14eb4dea..ed2d3a223 100644 --- a/projects/observability/src/shared/components/topology/d3/layouts/tree-layout.ts +++ b/projects/observability/src/shared/components/topology/d3/layouts/tree-layout.ts @@ -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 { @@ -66,7 +66,7 @@ export class TreeLayout implements TopologyLayout { return level0RootNode; } - private getNodeWidth(root: HierarchyNode): number { + protected getNodeWidth(root: HierarchyNode): number { const leaf = first(root.leaves()); let leafWidth = 240; @@ -81,7 +81,7 @@ export class TreeLayout implements TopologyLayout { return leafWidth + 160; } - private getNodeHeight(root: HierarchyNode): number { + protected getNodeHeight(root: HierarchyNode): number { return this.getRenderedNodeHeight(first(root.leaves())); } @@ -99,7 +99,7 @@ export class TreeLayout implements TopologyLayout { return defaultNodeHeight; } - private buildTopologyHierarchyNodeMap( + protected buildTopologyHierarchyNodeMap( nodes: RenderableTopologyNode[], startingLocation: TopologyCoordinates ): Map { @@ -163,7 +163,7 @@ export class TreeLayout implements TopologyLayout { } } -interface D3ProxyNode { +export interface D3ProxyNode { sourceNode: RenderableTopologyNode | undefined; hasIncomingEdges: boolean; children: D3ProxyNode[]; diff --git a/projects/observability/src/shared/dashboard/widgets/topology/edge/curved/entity-edge-curve-renderer.scss b/projects/observability/src/shared/dashboard/widgets/topology/edge/curved/entity-edge-curve-renderer.scss index 855f71e51..b12b23b5a 100644 --- a/projects/observability/src/shared/dashboard/widgets/topology/edge/curved/entity-edge-curve-renderer.scss +++ b/projects/observability/src/shared/dashboard/widgets/topology/edge/curved/entity-edge-curve-renderer.scss @@ -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; diff --git a/projects/observability/src/shared/dashboard/widgets/topology/edge/curved/entity-edge-curve-renderer.service.ts b/projects/observability/src/shared/dashboard/widgets/topology/edge/curved/entity-edge-curve-renderer.service.ts index da93fea4b..041d9fd04 100644 --- a/projects/observability/src/shared/dashboard/widgets/topology/edge/curved/entity-edge-curve-renderer.service.ts +++ b/projects/observability/src/shared/dashboard/widgets/topology/edge/curved/entity-edge-curve-renderer.service.ts @@ -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)) diff --git a/projects/observability/src/shared/dashboard/widgets/topology/node/box/entity-node-box-renderer.service.ts b/projects/observability/src/shared/dashboard/widgets/topology/node/box/entity-node-box-renderer.service.ts index 209392247..9670af928 100644 --- a/projects/observability/src/shared/dashboard/widgets/topology/node/box/entity-node-box-renderer.service.ts +++ b/projects/observability/src/shared/dashboard/widgets/topology/node/box/entity-node-box-renderer.service.ts @@ -77,15 +77,15 @@ export abstract class EntityNodeBoxRendererService implements TopologyNodeRender public getAttachmentPoint(angle: number): TopologyCoordinates { if (this.isAnglePerpendicularlyAbove(angle)) { return { - x: this.boxWidth() / 2, - y: 0 + x: 0, + y: this.boxHeight() / 2 }; } if (this.isAnglePerpendicularlyBelow(angle)) { return { - x: this.boxWidth() / 2, - y: this.boxHeight() + x: this.boxWidth(), + y: this.boxHeight() / 2 }; } @@ -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 { From 3611582535bfb369985c9b797ec720b1f957d6f0 Mon Sep 17 00:00:00 2001 From: anandtiwary <52081890+anandtiwary@users.noreply.github.com> Date: Wed, 28 Jul 2021 11:22:53 -0700 Subject: [PATCH 2/3] refactor: fixing lint --- .../edge/topology-edge-renderer.service.ts | 14 +++++++++---- .../node/topology-node-renderer.service.ts | 2 +- .../entity-edge-curve-renderer.service.ts | 20 +++++++++---------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/projects/observability/src/shared/components/topology/renderers/edge/topology-edge-renderer.service.ts b/projects/observability/src/shared/components/topology/renderers/edge/topology-edge-renderer.service.ts index 604755ac3..15d614c91 100644 --- a/projects/observability/src/shared/components/topology/renderers/edge/topology-edge-renderer.service.ts +++ b/projects/observability/src/shared/components/topology/renderers/edge/topology-edge-renderer.service.ts @@ -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'; @@ -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); @@ -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 }; } @@ -112,6 +116,8 @@ export interface TopologyEdgePositionInformation { x: number; y: number; }; + sourceRad: number; + targetRad: number; } export interface TopologyEdgeRenderDelegate { diff --git a/projects/observability/src/shared/components/topology/renderers/node/topology-node-renderer.service.ts b/projects/observability/src/shared/components/topology/renderers/node/topology-node-renderer.service.ts index 8dda37419..75daeaf84 100644 --- a/projects/observability/src/shared/components/topology/renderers/node/topology-node-renderer.service.ts +++ b/projects/observability/src/shared/components/topology/renderers/node/topology-node-renderer.service.ts @@ -135,7 +135,7 @@ export interface TopologyNodeRendererDelegate - linkHorizontal() - .x(datum => datum.x) - .y(datum => datum.y)(data) - ) - .classed('edge-path', true); + const lineGenerator: Link = 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( From 1867d260db17bc97bf9e09481703ac41785c55ae Mon Sep 17 00:00:00 2001 From: anandtiwary <52081890+anandtiwary@users.noreply.github.com> Date: Wed, 28 Jul 2021 11:33:29 -0700 Subject: [PATCH 3/3] refactor: self review --- .../renderers/node/topology-node-renderer.service.ts | 2 +- .../topology/node/box/entity-node-box-renderer.service.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/projects/observability/src/shared/components/topology/renderers/node/topology-node-renderer.service.ts b/projects/observability/src/shared/components/topology/renderers/node/topology-node-renderer.service.ts index 75daeaf84..8dda37419 100644 --- a/projects/observability/src/shared/components/topology/renderers/node/topology-node-renderer.service.ts +++ b/projects/observability/src/shared/components/topology/renderers/node/topology-node-renderer.service.ts @@ -135,7 +135,7 @@ export interface TopologyNodeRendererDelegate