jellyflow_runtime/runtime/binding/
resolve.rs1use jellyflow_core::core::{
2 BindingEndpoint, CanvasPoint, CanvasRect, Graph, GraphLocalBindingTarget, NodeId,
3 PortDirection, PortId,
4};
5
6use crate::runtime::connection::ConnectionHandleRef;
7use crate::runtime::geometry::{
8 EdgeEndpointInput, HandlePosition, edge_position, handle_anchor_position,
9};
10use crate::runtime::lookups::NodeGraphLookups;
11use crate::runtime::measurement::resolve_handle_measurement;
12use crate::runtime::utils::get_node_rect;
13
14use super::query::{
15 BindingEndpointResolution, BindingQueryOptions, BindingQueryResult, ResolvedBinding,
16 ResolvedBindingEndpoint,
17};
18
19pub fn resolve_binding_query(
21 graph: &Graph,
22 lookups: &NodeGraphLookups,
23 revision: u64,
24 node_origin: (f32, f32),
25 options: BindingQueryOptions,
26) -> BindingQueryResult {
27 let bindings = graph
28 .bindings()
29 .iter()
30 .map(|(id, binding)| {
31 ResolvedBinding::new(
32 *id,
33 resolve_endpoint(graph, lookups, node_origin, options, &binding.subject),
34 resolve_endpoint(graph, lookups, node_origin, options, &binding.target),
35 binding.kind.clone(),
36 )
37 })
38 .collect();
39
40 BindingQueryResult::new(revision, bindings)
41}
42
43fn resolve_endpoint(
44 graph: &Graph,
45 lookups: &NodeGraphLookups,
46 node_origin: (f32, f32),
47 options: BindingQueryOptions,
48 endpoint: &BindingEndpoint,
49) -> ResolvedBindingEndpoint {
50 match endpoint {
51 BindingEndpoint::Source { .. } => ResolvedBindingEndpoint::source(endpoint.clone()),
52 BindingEndpoint::GraphLocal { target } => resolve_graph_local_target(
53 graph,
54 lookups,
55 node_origin,
56 options,
57 endpoint.clone(),
58 *target,
59 ),
60 }
61}
62
63fn resolve_graph_local_target(
64 graph: &Graph,
65 lookups: &NodeGraphLookups,
66 node_origin: (f32, f32),
67 options: BindingQueryOptions,
68 endpoint: BindingEndpoint,
69 target: GraphLocalBindingTarget,
70) -> ResolvedBindingEndpoint {
71 let resolution = match target {
72 GraphLocalBindingTarget::Graph => BindingEndpointResolution::Graph,
73 GraphLocalBindingTarget::Node { id } => {
74 resolve_node_target(graph, lookups, id, node_origin, options)
75 }
76 GraphLocalBindingTarget::Port { id } => {
77 resolve_port_target(graph, lookups, id, node_origin, options)
78 }
79 GraphLocalBindingTarget::Edge { id } => {
80 resolve_edge_target(graph, lookups, id, node_origin, options)
81 }
82 GraphLocalBindingTarget::Group { id } => graph
83 .groups()
84 .get(&id)
85 .map(|group| BindingEndpointResolution::GroupRect {
86 group: id,
87 rect: group.rect,
88 center: rect_center(group.rect),
89 })
90 .unwrap_or(BindingEndpointResolution::Unresolved),
91 GraphLocalBindingTarget::StickyNote { id } => graph
92 .sticky_notes()
93 .get(&id)
94 .map(|note| BindingEndpointResolution::StickyNoteRect {
95 note: id,
96 rect: note.rect,
97 center: rect_center(note.rect),
98 })
99 .unwrap_or(BindingEndpointResolution::Unresolved),
100 };
101
102 ResolvedBindingEndpoint::new(endpoint, resolution)
103}
104
105fn resolve_node_target(
106 graph: &Graph,
107 lookups: &NodeGraphLookups,
108 node: NodeId,
109 node_origin: (f32, f32),
110 options: BindingQueryOptions,
111) -> BindingEndpointResolution {
112 let Some(model) = graph.nodes().get(&node) else {
113 return BindingEndpointResolution::Unresolved;
114 };
115 if model.hidden && !options.include_hidden {
116 return BindingEndpointResolution::Hidden;
117 }
118 let Some(rect) = get_node_rect(lookups, node, node_origin, options.fallback_node_size) else {
119 return BindingEndpointResolution::Unresolved;
120 };
121 BindingEndpointResolution::NodeRect {
122 node,
123 rect,
124 center: rect_center(rect),
125 }
126}
127
128fn resolve_port_target(
129 graph: &Graph,
130 lookups: &NodeGraphLookups,
131 port: PortId,
132 node_origin: (f32, f32),
133 options: BindingQueryOptions,
134) -> BindingEndpointResolution {
135 let Some(model) = graph.ports().get(&port) else {
136 return BindingEndpointResolution::Unresolved;
137 };
138 let Some(node) = graph.nodes().get(&model.node) else {
139 return BindingEndpointResolution::Unresolved;
140 };
141 if node.hidden && !options.include_hidden {
142 return BindingEndpointResolution::Hidden;
143 }
144 let Some(node_rect) =
145 get_node_rect(lookups, model.node, node_origin, options.fallback_node_size)
146 else {
147 return BindingEndpointResolution::Unresolved;
148 };
149 let Some(point) = handle_anchor_position(
150 node_rect,
151 resolve_handle_measurement(
152 graph,
153 lookups,
154 ConnectionHandleRef::new(model.node, port, model.dir),
155 )
156 .bounds,
157 fallback_handle_position(model.dir),
158 )
159 .map(|endpoint| endpoint.point) else {
160 return BindingEndpointResolution::Unresolved;
161 };
162
163 BindingEndpointResolution::PortAnchor {
164 node: model.node,
165 point,
166 }
167}
168
169fn resolve_edge_target(
170 graph: &Graph,
171 lookups: &NodeGraphLookups,
172 edge: jellyflow_core::core::EdgeId,
173 node_origin: (f32, f32),
174 options: BindingQueryOptions,
175) -> BindingEndpointResolution {
176 let Some(edge_model) = graph.edges().get(&edge) else {
177 return BindingEndpointResolution::Unresolved;
178 };
179 if edge_model.hidden && !options.include_hidden {
180 return BindingEndpointResolution::Hidden;
181 }
182 let Some(from_port) = graph.ports().get(&edge_model.from) else {
183 return BindingEndpointResolution::Unresolved;
184 };
185 let Some(to_port) = graph.ports().get(&edge_model.to) else {
186 return BindingEndpointResolution::Unresolved;
187 };
188 let Some(source_node) = graph.nodes().get(&from_port.node) else {
189 return BindingEndpointResolution::Unresolved;
190 };
191 let Some(target_node) = graph.nodes().get(&to_port.node) else {
192 return BindingEndpointResolution::Unresolved;
193 };
194 if (source_node.hidden || target_node.hidden) && !options.include_hidden {
195 return BindingEndpointResolution::Hidden;
196 }
197
198 let Some(source_rect) = get_node_rect(
199 lookups,
200 from_port.node,
201 node_origin,
202 options.fallback_node_size,
203 ) else {
204 return BindingEndpointResolution::Unresolved;
205 };
206 let Some(target_rect) = get_node_rect(
207 lookups,
208 to_port.node,
209 node_origin,
210 options.fallback_node_size,
211 ) else {
212 return BindingEndpointResolution::Unresolved;
213 };
214 let Some(position) = edge_position(
215 EdgeEndpointInput {
216 node_rect: source_rect,
217 handle: resolve_handle_measurement(
218 graph,
219 lookups,
220 ConnectionHandleRef::new(from_port.node, edge_model.from, from_port.dir),
221 )
222 .bounds,
223 fallback_position: fallback_handle_position(from_port.dir),
224 },
225 EdgeEndpointInput {
226 node_rect: target_rect,
227 handle: resolve_handle_measurement(
228 graph,
229 lookups,
230 ConnectionHandleRef::new(to_port.node, edge_model.to, to_port.dir),
231 )
232 .bounds,
233 fallback_position: fallback_handle_position(to_port.dir),
234 },
235 ) else {
236 return BindingEndpointResolution::Unresolved;
237 };
238
239 BindingEndpointResolution::EdgePosition { edge, position }
240}
241
242fn fallback_handle_position(direction: PortDirection) -> HandlePosition {
243 HandlePosition::fallback_for_direction(direction)
244}
245
246fn rect_center(rect: CanvasRect) -> CanvasPoint {
247 CanvasPoint {
248 x: rect.origin.x + rect.size.width * 0.5,
249 y: rect.origin.y + rect.size.height * 0.5,
250 }
251}