Skip to content
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
25 changes: 24 additions & 1 deletion docs/how-to/define-nodes-edges.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,34 @@ edges = [
| `source` | yes | ID of the source node. |
| `target` | yes | ID of the target node. |
| `label` | no | Text rendered on the edge. |
| `type` | no | Edge type name (for styling / editors). |
| `type` | no | Edge type (see built-in types below, or a custom type name). |
| `data` | no | Arbitrary dict of payload data. |
| `sourceHandle` | no | Specific output handle on the source node. |
| `targetHandle` | no | Specific input handle on the target node. |

### Built-in edge types

| Type | Description |
|------------------|-------------|
| `"bezier"` | Smooth bezier curve (default). |
| `"straight"` | Straight line between nodes. |
| `"step"` | Orthogonal path with right angles. |
| `"smoothstep"` | Step path with rounded corners. |
| `"smart_bezier"` | Bezier curve that automatically routes around nodes. |
| `"smart_straight"`| Straight segments that automatically route around nodes. |
| `"smart_step"` | Step path that automatically routes around nodes. |

Smart edge types use pathfinding to avoid overlapping with other nodes in
the graph. They are useful when edges would otherwise pass through
intermediate nodes.

```python
edges = [
{"id": "e1", "source": "n1", "target": "n2", "type": "smoothstep"},
{"id": "e2", "source": "n1", "target": "n3", "type": "smart_bezier"},
]
```

---

## Define edges as classes
Expand Down
102 changes: 102 additions & 0 deletions examples/edge_types_comparison.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"""
Comparison of standard edges vs smart edges.

This example demonstrates the difference between standard React Flow edges
and smart edges that automatically route around obstacles.
"""

import panel as pn
from panel_reactflow import EdgeSpec, NodeSpec, ReactFlow

pn.extension()

# Create nodes arranged to show edge routing
nodes = [
# Left column - sources
NodeSpec(id="s1", position={"x": 0, "y": 0}, label="Source 1").to_dict(),
NodeSpec(id="s2", position={"x": 0, "y": 100}, label="Source 2").to_dict(),
NodeSpec(id="s3", position={"x": 0, "y": 200}, label="Source 3").to_dict(),
NodeSpec(id="s4", position={"x": 0, "y": 300}, label="Source 4").to_dict(),
# Middle obstacles
NodeSpec(id="obs1", position={"x": 150, "y": 50}, label="Obstacle 1").to_dict(),
NodeSpec(id="obs2", position={"x": 150, "y": 150}, label="Obstacle 2").to_dict(),
NodeSpec(id="obs3", position={"x": 150, "y": 250}, label="Obstacle 3").to_dict(),
# Right column - targets
NodeSpec(id="t1", position={"x": 300, "y": 0}, label="Target 1").to_dict(),
NodeSpec(id="t2", position={"x": 300, "y": 100}, label="Target 2").to_dict(),
NodeSpec(id="t3", position={"x": 300, "y": 200}, label="Target 3").to_dict(),
NodeSpec(id="t4", position={"x": 300, "y": 300}, label="Target 4").to_dict(),
]

# Create edges with different types
edges = [
# Default edge (goes through obstacle)
EdgeSpec(
id="e1",
source="s1",
target="t2",
label="default",
type=None,
style={"stroke": "#999"},
).to_dict(),
# Smart bezier (routes around obstacle)
EdgeSpec(
id="e2",
source="s2",
target="t3",
label="smart_bezier",
type="smart_bezier",
style={"stroke": "#3b82f6"},
).to_dict(),
# Smart straight (routes around obstacle)
EdgeSpec(
id="e3",
source="s3",
target="t1",
label="smart_straight",
type="smart_straight",
style={"stroke": "#10b981"},
).to_dict(),
# Smart step (routes around obstacle)
EdgeSpec(
id="e4",
source="s4",
target="t4",
label="smart_step",
type="smart_step",
style={"stroke": "#f59e0b"},
).to_dict(),
]

# Create the flow
flow = ReactFlow(
nodes=nodes,
edges=edges,
height=600,
width="100%",
)

# Layout
pn.template.FastListTemplate(
title="Edge Types Comparison",
sidebar=[
pn.pane.Markdown(
"""
## Edge Types

This example compares different edge types:

- **Gray (default)**: Standard edge that goes straight through obstacles
- **Blue (smart_bezier)**: Curved edge that routes around obstacles
- **Green (smart_straight)**: Straight segments that avoid obstacles
- **Orange (smart_step)**: Step-style edge that avoids obstacles

### Try it:
- Drag the obstacle nodes around
- Watch how smart edges automatically reroute
- Notice the default edge doesn't avoid obstacles
""",
),
],
main=[flow],
).servable()
99 changes: 99 additions & 0 deletions examples/smart_edges_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"""
Example demonstrating smart edges that automatically route around nodes.

Smart edges automatically find paths around nodes to avoid overlaps.
Available edge types:
- 'bezier' (default): Smooth bezier curve
- 'straight': Straight line between nodes
- 'step': Orthogonal step path (right angles)
- 'smoothstep': Step path with rounded corners
- 'smart_bezier': Smart bezier curve that routes around nodes
- 'smart_straight': Smart straight segments that route around nodes
- 'smart_step': Smart step path that routes around nodes
"""

import panel as pn
import panel_material_ui as pmui
from panel_reactflow import EdgeSpec, NodeSpec, ReactFlow

pn.extension()

# Create a simple graph with nodes that would normally cause edge overlaps
nodes = [
NodeSpec(id="1", position={"x": 0, "y": 100}, label="Start").to_dict(),
NodeSpec(id="2", position={"x": 200, "y": 0}, label="Top").to_dict(),
NodeSpec(id="3", position={"x": 200, "y": 200}, label="Bottom").to_dict(),
NodeSpec(id="4", position={"x": 400, "y": 100}, label="End").to_dict(),
NodeSpec(id="5", position={"x": 200, "y": 100}, label="Middle Obstacle").to_dict(),
]

# Create edges with different types
edges = [
EdgeSpec(id="e1", source="1", target="4", label="Regular", type=None).to_dict(),
EdgeSpec(id="e2", source="2", target="3", label="Smart Bezier", type="smart_bezier").to_dict(),
EdgeSpec(id="e3", source="1", target="2", label="Smart Straight", type="smart_straight").to_dict(),
EdgeSpec(id="e4", source="1", target="3", label="Smart Step", type="smart_step").to_dict(),
]

# Create the flow with smart edges
flow = ReactFlow(
nodes=nodes,
edges=edges,
sizing_mode="stretch_both"
)

# Create edge type selector
edge_type_selector = pmui.Select(
label="Edge Type for e1",
options={
"Bezier (default)": "bezier",
"Straight": "straight",
"Step": "step",
"Smooth Step": "smoothstep",
"Smart Bezier": "smart_bezier",
"Smart Straight": "smart_straight",
"Smart Step": "smart_step",
},
value=None,
)


def update_edge_type(event):
"""Update the edge type when selector changes."""
for edge in flow.edges:
edge["type"] = event.new
flow.edges = flow.edges # Trigger update

edge_type_selector.param.watch(update_edge_type, "value")

# Layout
pmui.Page(
title="Smart Edges Example",
sidebar=[
pn.pane.Markdown(
"""
## Smart Edges Demo

Smart edges automatically route around nodes to avoid overlaps.

**Standard Edge Types:**
- **Bezier**: Smooth bezier curve (default)
- **Straight**: Direct straight line
- **Step**: Orthogonal path (right angles)
- **Smooth Step**: Step path with rounded corners

**Smart Edge Types:**
- **Smart Bezier**: Curved path that avoids nodes
- **Smart Straight**: Straight segments that route around nodes
- **Smart Step**: Step-style path that avoids nodes

**Try it:**
1. Change the edge type for the "Start → End" connection
2. Drag nodes around to see smart edges automatically reroute
3. Notice how smart edges avoid the "Middle Obstacle" node
""",
),
edge_type_selector,
],
main=[flow],
).servable()
19 changes: 17 additions & 2 deletions src/panel_reactflow/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,17 @@ class EdgeSpec:
label : str, optional
Display label shown on the edge. If ``None``, no label is displayed.
type : str, optional
Edge type identifier. Reference a custom type defined in
Edge type identifier. Built-in types are:

- ``'bezier'`` (default): Smooth bezier curve
- ``'straight'``: Straight line between nodes
- ``'step'``: Orthogonal step path (right angles)
- ``'smoothstep'``: Step path with rounded corners
- ``'smart_bezier'``: Bezier curve that routes around nodes
- ``'smart_straight'``: Straight segments that route around nodes
- ``'smart_step'``: Step path that routes around nodes

You can also reference a custom type defined in
``ReactFlow.edge_types`` for schema validation and custom rendering.
selected : bool, default False
Whether the edge is currently selected in the UI.
Expand Down Expand Up @@ -1411,7 +1421,12 @@ class ReactFlow(ReactComponent):

_bundle = DIST_PATH / "panel-reactflow.bundle.js"
_esm = Path(__file__).parent / "models" / "reactflow.jsx"
_importmap = {"imports": {"@xyflow/react": "https://esm.sh/@xyflow/react@12.8.3"}}
_importmap = {
"imports": {
"@xyflow/react": "https://esm.sh/@xyflow/react@12.8.3",
"@tisoap/react-flow-smart-edge": "https://esm.sh/@tisoap/react-flow-smart-edge@4.0.3",
}
}
_stylesheets = [DIST_PATH / "panel-reactflow.bundle.css", DIST_PATH / "css" / "reactflow.css"]
_render_policy = "manual"

Expand Down
Loading
Loading