jellyflow_runtime/runtime/connection/
target.rs1use serde::{Deserialize, Serialize};
2
3use jellyflow_core::core::{CanvasPoint, CanvasRect, PortDirection};
4use jellyflow_core::interaction::NodeGraphConnectionMode;
5
6use crate::runtime::geometry::HandleBounds;
7
8use super::{
9 ClosestConnectionHandleInput, ConnectionHandleCandidate, ConnectionHandleRef,
10 ConnectionHandleValidity, closest_connection_handle, connection_handle_validity,
11};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
15pub struct ConnectionTargetHandle {
16 pub handle: ConnectionHandleRef,
17 pub connectable: bool,
18 pub connectable_end: bool,
19}
20
21impl ConnectionTargetHandle {
22 pub fn new(handle: ConnectionHandleRef, connectable: bool, connectable_end: bool) -> Self {
23 Self {
24 handle,
25 connectable,
26 connectable_end,
27 }
28 }
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
33pub struct ConnectionTargetCandidate {
34 pub target: ConnectionTargetHandle,
35 pub node_rect: CanvasRect,
36 pub bounds: HandleBounds,
37}
38
39impl ConnectionTargetCandidate {
40 pub fn new(
41 target: ConnectionTargetHandle,
42 node_rect: CanvasRect,
43 bounds: HandleBounds,
44 ) -> Self {
45 Self {
46 target,
47 node_rect,
48 bounds,
49 }
50 }
51
52 fn handle_candidate(self) -> ConnectionHandleCandidate {
53 ConnectionHandleCandidate::new(self.target.handle, self.node_rect, self.bounds)
54 }
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
59pub struct ConnectionHandleConnection {
60 pub source: ConnectionHandleRef,
61 pub target: ConnectionHandleRef,
62}
63
64impl ConnectionHandleConnection {
65 pub fn from_start_and_target(from: ConnectionHandleRef, target: ConnectionHandleRef) -> Self {
66 if from.direction == PortDirection::In {
67 Self {
68 source: target,
69 target: from,
70 }
71 } else {
72 Self {
73 source: from,
74 target,
75 }
76 }
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
82pub struct ConnectionTargetInput {
83 pub from: ConnectionHandleRef,
84 #[serde(default, skip_serializing_if = "Option::is_none")]
85 pub target: Option<ConnectionTargetHandle>,
86 pub mode: NodeGraphConnectionMode,
87 #[serde(default)]
88 pub is_inside_connection_radius: bool,
89 #[serde(default = "default_connection_validity")]
90 pub is_valid_connection: bool,
91}
92
93impl ConnectionTargetInput {
94 pub fn new(
95 from: ConnectionHandleRef,
96 target: Option<ConnectionTargetHandle>,
97 mode: NodeGraphConnectionMode,
98 is_inside_connection_radius: bool,
99 ) -> Self {
100 Self {
101 from,
102 target,
103 mode,
104 is_inside_connection_radius,
105 is_valid_connection: true,
106 }
107 }
108
109 pub fn with_connection_validity(mut self, is_valid_connection: bool) -> Self {
110 self.is_valid_connection = is_valid_connection;
111 self
112 }
113}
114
115#[derive(Debug, Clone, Copy, PartialEq)]
117pub struct ConnectionTargetFromHandlesInput<'a> {
118 pub pointer: CanvasPoint,
119 pub radius: f32,
120 pub from: ConnectionHandleRef,
121 pub candidates: &'a [ConnectionTargetCandidate],
122 pub mode: NodeGraphConnectionMode,
123 pub is_valid_connection: bool,
124}
125
126impl<'a> ConnectionTargetFromHandlesInput<'a> {
127 pub fn new(
128 pointer: CanvasPoint,
129 radius: f32,
130 from: ConnectionHandleRef,
131 candidates: &'a [ConnectionTargetCandidate],
132 mode: NodeGraphConnectionMode,
133 ) -> Self {
134 Self {
135 pointer,
136 radius,
137 from,
138 candidates,
139 mode,
140 is_valid_connection: true,
141 }
142 }
143
144 pub fn with_connection_validity(mut self, is_valid_connection: bool) -> Self {
145 self.is_valid_connection = is_valid_connection;
146 self
147 }
148}
149
150fn default_connection_validity() -> bool {
151 true
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
156pub struct ResolvedConnectionTarget {
157 pub target: Option<ConnectionTargetHandle>,
158 pub connection: Option<ConnectionHandleConnection>,
159 pub is_handle_valid: bool,
160 pub feedback: ConnectionHandleValidity,
161}
162
163pub fn resolve_connection_target(input: ConnectionTargetInput) -> ResolvedConnectionTarget {
168 let Some(target) = input.target else {
169 return ResolvedConnectionTarget {
170 target: None,
171 connection: None,
172 is_handle_valid: false,
173 feedback: connection_handle_validity(input.is_inside_connection_radius, false),
174 };
175 };
176
177 let connection = ConnectionHandleConnection::from_start_and_target(input.from, target.handle);
178 let mode_allows_target = match input.mode {
179 NodeGraphConnectionMode::Strict => input.from.direction != target.handle.direction,
180 NodeGraphConnectionMode::Loose => {
181 input.from.node != target.handle.node || input.from.port != target.handle.port
182 }
183 };
184 let is_handle_valid = target.connectable
185 && target.connectable_end
186 && mode_allows_target
187 && input.is_valid_connection;
188
189 ResolvedConnectionTarget {
190 target: Some(target),
191 connection: Some(connection),
192 is_handle_valid,
193 feedback: connection_handle_validity(input.is_inside_connection_radius, is_handle_valid),
194 }
195}
196
197pub fn resolve_connection_target_from_handles(
203 input: ConnectionTargetFromHandlesInput<'_>,
204) -> ResolvedConnectionTarget {
205 let handle_candidates = input
206 .candidates
207 .iter()
208 .copied()
209 .map(ConnectionTargetCandidate::handle_candidate)
210 .collect::<Vec<_>>();
211 let closest = closest_connection_handle(ClosestConnectionHandleInput::new(
212 input.pointer,
213 input.radius,
214 input.from,
215 &handle_candidates,
216 ));
217 let target = closest.and_then(|closest| {
218 input
219 .candidates
220 .iter()
221 .find(|candidate| candidate.target.handle == closest.handle)
222 .map(|candidate| candidate.target)
223 });
224
225 resolve_connection_target(
226 ConnectionTargetInput::new(input.from, target, input.mode, closest.is_some())
227 .with_connection_validity(input.is_valid_connection),
228 )
229}