Skip to content

Commit 5adff9e

Browse files
committed
fix(topicgraph): replace cytoscape-dagre with breadthfirst layout
Remove the deprecated dagre dependency chain so npm embeds no longer require dagre in the host app. Add panels.urdfDebug.defaultTitle i18n keys for UrdfDebug tab labels (v1.2.1).
1 parent bcd0a6a commit 5adff9e

6 files changed

Lines changed: 78 additions & 106 deletions

File tree

package-lock.json

Lines changed: 2 additions & 55 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ioai/rosview",
3-
"version": "1.2.0",
3+
"version": "1.2.1",
44
"description": "High-performance robotics data visualization for MCAP, ROS bag, ROS2 db3, HDF5 and BVH — embeddable React component and standalone SPA",
55
"keywords": [
66
"ros",
@@ -108,7 +108,6 @@
108108
"@react-three/drei": "^10.0.0",
109109
"@react-three/fiber": "^9.0.0",
110110
"@types/cytoscape": "^3.21.9",
111-
"@types/cytoscape-dagre": "^2.3.4",
112111
"@types/node": "^25.6.0",
113112
"@types/react": "^19.2.15",
114113
"@types/react-dom": "^19.2.3",
@@ -120,7 +119,6 @@
120119
"cmdk": "^1.1.1",
121120
"comlink": "^4.4.1",
122121
"cytoscape": "^3.33.2",
123-
"cytoscape-dagre": "^2.5.0",
124122
"dockview": "^6.0.7",
125123
"dockview-core": "^6.0.7",
126124
"esbuild": "^0.28.0",
Lines changed: 72 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,114 @@
1-
import React, { useEffect, useRef } from 'react';
2-
import cytoscape from "cytoscape";
3-
import CytoscapeDagre from "cytoscape-dagre";
4-
5-
cytoscape['use'](CytoscapeDagre);
1+
import React, { useCallback, useEffect, useRef } from 'react';
2+
import cytoscape from 'cytoscape';
63

74
export interface GraphMutation {
85
fit: () => void;
96
}
107

118
interface GraphProps {
129
elements: cytoscape.ElementDefinition[];
13-
rankDir: "TB" | "LR";
10+
rankDir: 'TB' | 'LR';
1411
graphRef?: React.RefObject<GraphMutation | null>;
1512
}
1613

17-
const DAG_LAYOUT = {
18-
name: "dagre",
19-
fit: true,
20-
nodeSep: 50,
21-
rankSep: 100,
22-
};
23-
24-
const STYLESHEET: NonNullable<cytoscape.CytoscapeOptions["style"]> = [
14+
const STYLESHEET: NonNullable<cytoscape.CytoscapeOptions['style']> = [
2515
{
26-
selector: "edge",
16+
selector: 'edge',
2717
style: {
28-
"target-arrow-shape": "triangle",
29-
"line-color": "#666",
30-
"target-arrow-color": "#666",
31-
"curve-style": "bezier",
32-
"width": 1.5,
18+
'target-arrow-shape': 'triangle',
19+
'line-color': '#666',
20+
'target-arrow-color': '#666',
21+
'curve-style': 'bezier',
22+
width: 1.5,
3323
},
3424
},
3525
{
3626
selector: 'node[type="node"]',
3727
style: {
38-
content: "data(label)",
39-
shape: "round-rectangle",
40-
"background-color": "#1e293b",
41-
"border-color": "#3b82f6",
42-
"border-width": 1.5,
43-
padding: "8px",
44-
"font-size": "12px",
45-
"color": "#3b82f6",
46-
"text-valign": "center",
47-
"text-halign": "center",
28+
content: 'data(label)',
29+
shape: 'round-rectangle',
30+
'background-color': '#1e293b',
31+
'border-color': '#3b82f6',
32+
'border-width': 1.5,
33+
padding: '8px',
34+
'font-size': '12px',
35+
color: '#3b82f6',
36+
'text-valign': 'center',
37+
'text-halign': 'center',
4838
},
4939
},
5040
{
5141
selector: 'node[type="topic"]',
5242
style: {
53-
content: "data(label)",
54-
shape: "diamond",
55-
"background-color": "#4c1d95",
56-
"border-color": "#8b5cf6",
57-
"border-width": 1,
58-
"font-size": "11px",
59-
"color": "#fff",
60-
"text-valign": "center",
61-
"text-halign": "center",
62-
padding: "10px",
43+
content: 'data(label)',
44+
shape: 'diamond',
45+
'background-color': '#4c1d95',
46+
'border-color': '#8b5cf6',
47+
'border-width': 1,
48+
'font-size': '11px',
49+
color: '#fff',
50+
'text-valign': 'center',
51+
'text-halign': 'center',
52+
padding: '10px',
6353
},
6454
},
6555
];
6656

57+
function applyLeftRightOrientation(cy: cytoscape.Core): void {
58+
cy.nodes().positions((node) => {
59+
const pos = node.position();
60+
return { x: pos.y, y: pos.x };
61+
});
62+
cy.fit(undefined, 30);
63+
}
64+
65+
function createBreadthfirstLayout(
66+
cy: cytoscape.Core,
67+
rankDir: 'TB' | 'LR',
68+
): cytoscape.LayoutOptions {
69+
const publisherRoots = cy.nodes('[type="node"]');
70+
return {
71+
name: 'breadthfirst',
72+
fit: rankDir === 'TB',
73+
directed: true,
74+
padding: 30,
75+
spacingFactor: 1.2,
76+
grid: true,
77+
avoidOverlap: true,
78+
...(publisherRoots.length > 0 ? { roots: publisherRoots } : {}),
79+
stop: () => {
80+
if (rankDir === 'LR') {
81+
applyLeftRightOrientation(cy);
82+
}
83+
},
84+
};
85+
}
86+
6787
export const Graph: React.FC<GraphProps> = ({ elements, rankDir, graphRef }) => {
6888
const containerRef = useRef<HTMLDivElement>(null);
6989
const cyRef = useRef<cytoscape.Core | null>(null);
7090

91+
const runLayout = useCallback((cy: cytoscape.Core, direction: 'TB' | 'LR') => {
92+
cy.layout(createBreadthfirstLayout(cy, direction)).run();
93+
}, []);
94+
7195
useEffect(() => {
7296
if (!containerRef.current) return;
7397

74-
const layout = { ...DAG_LAYOUT, rankDir } as cytoscape.LayoutOptions;
7598
const cy = cytoscape({
7699
container: containerRef.current,
77-
elements: elements,
100+
elements,
78101
style: STYLESHEET,
79-
layout,
80102
userZoomingEnabled: true,
81103
userPanningEnabled: true,
82104
});
83105

84106
cyRef.current = cy;
107+
runLayout(cy, rankDir);
85108

86109
if (graphRef) {
87110
graphRef.current = {
88-
fit: () => cy.fit(),
111+
fit: () => cy.fit(undefined, 30),
89112
};
90113
}
91114

@@ -97,14 +120,15 @@ export const Graph: React.FC<GraphProps> = ({ elements, rankDir, graphRef }) =>
97120

98121
useEffect(() => {
99122
if (!cyRef.current) return;
100-
123+
101124
cyRef.current.batch(() => {
102125
cyRef.current?.elements().remove();
103126
cyRef.current?.add(elements);
104-
const layout = { ...DAG_LAYOUT, rankDir } as cytoscape.LayoutOptions;
105-
cyRef.current?.layout(layout).run();
127+
if (cyRef.current) {
128+
runLayout(cyRef.current, rankDir);
129+
}
106130
});
107-
}, [elements, rankDir]);
131+
}, [elements, rankDir, runLayout]);
108132

109133
return <div ref={containerRef} className="w-full h-full bg-background" />;
110134
};

src/shared/intl/messages/en/panels.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"panels.topicGraph.defaultTitle": "Topic Graph",
1010
"panels.align.defaultTitle": "Align",
1111
"panels.unavailable.defaultTitle": "Unavailable",
12+
"panels.urdfDebug.defaultTitle": "URDF Debug",
1213
"panels.jointStatePlot.fallback.missingHeaderStamp": "header.stamp missing; fell back to receiveTime",
1314
"panels.jointStatePlot.fallback.missingCustomPath": "Custom timestamp path invalid; fell back to receiveTime",
1415
"panels.jointStatePlot.panorama.waitJointState": "Waiting for JointState messages to determine joint count…",

src/shared/intl/messages/ja/panels.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"panels.topicGraph.defaultTitle": "トピックグラフ",
1010
"panels.align.defaultTitle": "整列",
1111
"panels.unavailable.defaultTitle": "利用不可",
12+
"panels.urdfDebug.defaultTitle": "URDF デバッグ",
1213
"panels.jointStatePlot.fallback.missingHeaderStamp": "header.stamp がありません。receiveTime にフォールバックしました",
1314
"panels.jointStatePlot.fallback.missingCustomPath": "カスタムタイムスタンプパスが無効です。receiveTime にフォールバックしました",
1415
"panels.jointStatePlot.panorama.waitJointState": "JointState メッセージを待って関節数を決定しています…",

src/shared/intl/messages/zh/panels.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"panels.topicGraph.defaultTitle": "话题图",
1010
"panels.align.defaultTitle": "对齐",
1111
"panels.unavailable.defaultTitle": "不可用",
12+
"panels.urdfDebug.defaultTitle": "URDF 调试",
1213
"panels.jointStatePlot.fallback.missingHeaderStamp": "header.stamp 缺失,已回退到 receiveTime",
1314
"panels.jointStatePlot.fallback.missingCustomPath": "custom timestamp path 无效,已回退到 receiveTime",
1415
"panels.jointStatePlot.panorama.waitJointState": "等待 JointState 消息以确定关节数量…",

0 commit comments

Comments
 (0)