Skip to main content

jellyflow_runtime/runtime/geometry/
edge_route.rs

1use jellyflow_core::core::{Edge, EdgeLabelAnchor, EdgeRouteKind};
2use serde::{Deserialize, Serialize};
3
4use super::{
5    BezierEdgeOptions, EdgeHitTestOptions, EdgePath, EdgePosition, SmoothStepEdgeOptions,
6    bezier_edge_path, edge_path_contains_point, smoothstep_edge_path, straight_edge_path,
7};
8
9/// Effective route kind used by runtime geometry projections.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[serde(rename_all = "snake_case")]
12pub enum ResolvedEdgeRouteKind {
13    Straight,
14    Orthogonal,
15    Bezier,
16    SmoothStep,
17}
18
19impl From<EdgeRouteKind> for ResolvedEdgeRouteKind {
20    fn from(value: EdgeRouteKind) -> Self {
21        match value {
22            EdgeRouteKind::Straight => Self::Straight,
23            EdgeRouteKind::Orthogonal => Self::Orthogonal,
24            EdgeRouteKind::Bezier => Self::Bezier,
25            EdgeRouteKind::SmoothStep => Self::SmoothStep,
26        }
27    }
28}
29
30impl From<ResolvedEdgeRouteKind> for EdgeRouteKind {
31    fn from(value: ResolvedEdgeRouteKind) -> Self {
32        match value {
33            ResolvedEdgeRouteKind::Straight => Self::Straight,
34            ResolvedEdgeRouteKind::Orthogonal => Self::Orthogonal,
35            ResolvedEdgeRouteKind::Bezier => Self::Bezier,
36            ResolvedEdgeRouteKind::SmoothStep => Self::SmoothStep,
37        }
38    }
39}
40
41/// Adapter-facing interaction facts for an edge.
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
43pub struct EdgeInteractionFacts {
44    pub selectable: bool,
45    pub selected: bool,
46    pub focusable: bool,
47    pub deletable: bool,
48    pub reconnect_source: bool,
49    pub reconnect_target: bool,
50}
51
52impl EdgeInteractionFacts {
53    pub fn can_reconnect(self) -> bool {
54        self.reconnect_source || self.reconnect_target
55    }
56}
57
58/// Renderer-neutral route and interaction facts for one edge.
59#[derive(Debug, Clone, PartialEq)]
60pub struct EdgeRouteFacts {
61    pub kind: ResolvedEdgeRouteKind,
62    pub endpoints: EdgePosition,
63    pub path: EdgePath,
64    pub label_anchor: EdgeLabelAnchor,
65    pub hit_test: EdgeHitTestOptions,
66    pub interaction: EdgeInteractionFacts,
67}
68
69impl EdgeRouteFacts {
70    pub fn with_hit_test(mut self, hit_test: EdgeHitTestOptions) -> Self {
71        self.hit_test = hit_test;
72        self
73    }
74
75    pub fn with_interaction(mut self, interaction: EdgeInteractionFacts) -> Self {
76        self.interaction = interaction;
77        self
78    }
79
80    pub fn contains_point(&self, point: jellyflow_core::core::CanvasPoint) -> bool {
81        edge_path_contains_point(&self.path, point, self.hit_test)
82    }
83}
84
85/// Resolves the route style and path for an edge from persisted view hints.
86pub fn resolve_edge_route_path(edge: &Edge, endpoints: EdgePosition) -> Option<EdgeRouteFacts> {
87    let kind = edge
88        .view
89        .route_kind
90        .map(ResolvedEdgeRouteKind::from)
91        .unwrap_or(ResolvedEdgeRouteKind::Bezier);
92    let path = match kind {
93        ResolvedEdgeRouteKind::Straight => straight_edge_path(endpoints.source, endpoints.target),
94        ResolvedEdgeRouteKind::Orthogonal | ResolvedEdgeRouteKind::SmoothStep => {
95            smoothstep_edge_path(
96                endpoints.source,
97                endpoints.target,
98                SmoothStepEdgeOptions::default(),
99            )
100        }
101        ResolvedEdgeRouteKind::Bezier => bezier_edge_path(
102            endpoints.source,
103            endpoints.target,
104            BezierEdgeOptions::default(),
105        ),
106    }?;
107
108    Some(EdgeRouteFacts {
109        kind,
110        endpoints,
111        path,
112        label_anchor: edge.view.label_anchor.unwrap_or(EdgeLabelAnchor::Center),
113        hit_test: EdgeHitTestOptions::default(),
114        interaction: EdgeInteractionFacts {
115            selectable: false,
116            selected: false,
117            focusable: false,
118            deletable: false,
119            reconnect_source: false,
120            reconnect_target: false,
121        },
122    })
123}