Skip to content

Commit bce5474

Browse files
committed
refactor: Extend shared graph workspace for undirected/weighted reuse
- Move weightedAdjacency/edgeList into graph.js as core helpers (shortestPath.js re-exports them; its imports are unchanged). - floating-edge: draw the mid-edge travel arrow only when a direction is set (travelTo), so undirected uses get no arrow; add a 'reject' edge color. - graph-menu: add a hideDirected prop to omit the Directed toggle.
1 parent b0104fc commit bce5474

4 files changed

Lines changed: 37 additions & 30 deletions

File tree

src/components/graph/floating-edge.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const STROKE = {
1616
tree: '#f59e0b',
1717
path: '#10b981',
1818
negcycle: '#f43f5e',
19+
reject: '#cbd5e1',
1920
normal: '#64748b',
2021
};
2122

@@ -64,7 +65,9 @@ export default function FloatingEdge({ id, source, target, markerEnd, data }) {
6465
// doesn't grow during traversal; state is conveyed by color + the mid arrow.
6566
const strokeWidth = 2;
6667

67-
const showTravelArrow = state === 'tree' || state === 'path';
68+
// only show the direction arrow when a travel direction was set (BFS/DFS/SSSP);
69+
// undirected uses like MST leave travelTo null -> no arrow
70+
const showTravelArrow = (state === 'tree' || state === 'path') && travelTo != null;
6871
const mx = (sx + ex) / 2;
6972
const my = (sy + ey) / 2;
7073
const deg = (Math.atan2(uy, ux) * 180) / Math.PI;

src/components/graph/graph-menu.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export default function GraphMenu({
2222
algorithms,
2323
presets,
2424
weighted = false,
25+
hideDirected = false,
2526
disabled,
2627
onDirectedChange,
2728
onAlgorithmChange,
@@ -44,7 +45,9 @@ export default function GraphMenu({
4445
<span className="text-xs font-medium text-gray-500 uppercase tracking-wider">Config</span>
4546
<div className="h-px flex-1 bg-gray-300" />
4647
</div>
47-
<CustomToggle title="Directed" onCheckedChange={onDirectedChange} disabled={disabled} />
48+
{!hideDirected && (
49+
<CustomToggle title="Directed" onCheckedChange={onDirectedChange} disabled={disabled} />
50+
)}
4851
<CustomSelect
4952
title="Algorithm"
5053
options={algorithms}

src/lib/algorithms/graph.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,32 @@ export function adjacency(edges, directed) {
9090
return adj;
9191
}
9292

93+
// Weighted adjacency: id -> [{ node, edge, weight }] (used by shortest-path / MST).
94+
export function weightedAdjacency(edges, directed) {
95+
const adj = {};
96+
const add = (a, b, id, w) => { (adj[a] = adj[a] || []).push({ node: b, edge: id, weight: w }); };
97+
for (const e of edges) {
98+
const w = e.data?.weight ?? 1;
99+
add(e.source, e.target, e.id, w);
100+
if (!directed) add(e.target, e.source, e.id, w);
101+
}
102+
for (const k of Object.keys(adj)) {
103+
adj[k].sort((x, y) => (x.node < y.node ? -1 : x.node > y.node ? 1 : 0));
104+
}
105+
return adj;
106+
}
107+
108+
// Flat weighted edge list: [{ u, v, w, id }] — both directions when undirected.
109+
export function edgeList(edges, directed) {
110+
const list = [];
111+
for (const e of edges) {
112+
const w = e.data?.weight ?? 1;
113+
list.push({ u: e.source, v: e.target, w, id: e.id });
114+
if (!directed) list.push({ u: e.target, v: e.source, w, id: e.id });
115+
}
116+
return list;
117+
}
118+
93119
// Reconstruct start->finish path actions from parent maps.
94120
function pathActions(parent, parentEdge, startId, endId) {
95121
const nodes = [];

src/lib/algorithms/shortestPath.js

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
// { type: 'status', text } -> overlay status text
77
// plus edge states 'relax' | 'tree' | 'path' | 'negcycle'.
88

9-
import { toFlow } from './graph';
9+
import { toFlow, weightedAdjacency, edgeList } from './graph';
1010

11-
export { toFlow };
11+
// re-exported for back-compat with existing imports from this module
12+
export { toFlow, weightedAdjacency, edgeList };
1213

1314
export const SP_PRESETS = [
1415
{
@@ -50,32 +51,6 @@ export const SP_PRESETS = [
5051
},
5152
];
5253

53-
// id -> [{ node, edge, weight }]
54-
export function weightedAdjacency(edges, directed) {
55-
const adj = {};
56-
const add = (a, b, id, w) => { (adj[a] = adj[a] || []).push({ node: b, edge: id, weight: w }); };
57-
for (const e of edges) {
58-
const w = e.data?.weight ?? 1;
59-
add(e.source, e.target, e.id, w);
60-
if (!directed) add(e.target, e.source, e.id, w);
61-
}
62-
for (const k of Object.keys(adj)) {
63-
adj[k].sort((x, y) => (x.node < y.node ? -1 : x.node > y.node ? 1 : 0));
64-
}
65-
return adj;
66-
}
67-
68-
// [{ u, v, w, id }] — both directions when undirected (for Bellman-Ford)
69-
export function edgeList(edges, directed) {
70-
const list = [];
71-
for (const e of edges) {
72-
const w = e.data?.weight ?? 1;
73-
list.push({ u: e.source, v: e.target, w, id: e.id });
74-
if (!directed) list.push({ u: e.target, v: e.source, w, id: e.id });
75-
}
76-
return list;
77-
}
78-
7954
function pathActions(parent, parentEdge, startId, endId) {
8055
const nodes = [];
8156
const edgeSteps = [];

0 commit comments

Comments
 (0)