use serde::{Deserialize, Serialize};
use crate::runtime::connection::{
ConnectionHandleRef, ConnectionTargetCandidate, ResolvedConnectionTarget,
};
use crate::runtime::geometry::{EdgePosition, HandleBounds};
use crate::runtime::rendering::RenderingQueryResult;
use crate::runtime::store::NodeGraphStore;
use jellyflow_core::core::{CanvasPoint, CanvasSize, EdgeId, NodeId};
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct MeasuredHandle {
pub handle: ConnectionHandleRef,
pub bounds: HandleBounds,
}
impl MeasuredHandle {
pub fn new(handle: ConnectionHandleRef, bounds: HandleBounds) -> Self {
Self { handle, bounds }
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct NodeMeasurement {
pub node: NodeId,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub size: Option<CanvasSize>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub handles: Vec<MeasuredHandle>,
}
impl NodeMeasurement {
pub fn new(node: NodeId) -> Self {
Self {
node,
size: None,
handles: Vec::new(),
}
}
pub fn with_size(mut self, size: Option<CanvasSize>) -> Self {
self.size = size;
self
}
pub fn with_handles(mut self, handles: impl IntoIterator<Item = MeasuredHandle>) -> Self {
self.handles = handles.into_iter().collect();
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NodeMeasurementOutcome {
Changed,
Unchanged,
}
impl NodeMeasurementOutcome {
pub fn changed(self) -> bool {
matches!(self, Self::Changed)
}
}
#[derive(Debug, thiserror::Error)]
pub enum NodeMeasurementError {
#[error("measurement target node does not exist: {0:?}")]
MissingNode(NodeId),
#[error("measurement size is not positive and finite for node {node:?}: {size:?}")]
InvalidSize { node: NodeId, size: CanvasSize },
#[error("measurement handle does not belong to node {node:?}: {handle:?}")]
InvalidHandle {
node: NodeId,
handle: ConnectionHandleRef,
},
#[error("measurement handle bounds are not positive and finite for node {node:?}: {handle:?}")]
InvalidHandleBounds {
node: NodeId,
handle: ConnectionHandleRef,
},
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LayoutEdgePosition {
pub edge: EdgeId,
pub position: EdgePosition,
}
impl LayoutEdgePosition {
pub fn new(edge: EdgeId, position: EdgePosition) -> Self {
Self { edge, position }
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct LayoutFactsQueryResult {
pub revision: u64,
pub rendering: RenderingQueryResult,
pub visible_edge_positions: Vec<LayoutEdgePosition>,
pub connection_target_candidates: Vec<ConnectionTargetCandidate>,
}
impl LayoutFactsQueryResult {
pub fn new(
revision: u64,
rendering: RenderingQueryResult,
visible_edge_positions: Vec<LayoutEdgePosition>,
connection_target_candidates: Vec<ConnectionTargetCandidate>,
) -> Self {
Self {
revision,
rendering,
visible_edge_positions,
connection_target_candidates,
}
}
pub fn visible_edge_position(&self, edge: EdgeId) -> Option<EdgePosition> {
self.visible_edge_positions
.iter()
.find(|position| position.edge == edge)
.map(|position| position.position)
}
}
impl NodeGraphStore {
pub fn report_node_measurement(
&mut self,
measurement: NodeMeasurement,
) -> Result<NodeMeasurementOutcome, NodeMeasurementError> {
let measurement = self.validate_node_measurement(measurement)?;
let Some(entry) = self.lookups_mut().node_lookup.get_mut(&measurement.node) else {
return Err(NodeMeasurementError::MissingNode(measurement.node));
};
if entry.apply_measurement(&measurement) {
self.publish_layout_facts_changed();
Ok(NodeMeasurementOutcome::Changed)
} else {
Ok(NodeMeasurementOutcome::Unchanged)
}
}
pub fn clear_node_measurement(&mut self, node: NodeId) -> NodeMeasurementOutcome {
let Some(entry) = self.lookups_mut().node_lookup.get_mut(&node) else {
return NodeMeasurementOutcome::Unchanged;
};
if entry.clear_measurement() {
self.publish_layout_facts_changed();
NodeMeasurementOutcome::Changed
} else {
NodeMeasurementOutcome::Unchanged
}
}
pub fn node_measurement(&self, node: NodeId) -> Option<NodeMeasurement> {
self.lookups()
.node_lookup
.get(&node)
.and_then(|entry| entry.measurement(node))
}
pub fn layout_facts_query(&self, viewport_size: CanvasSize) -> LayoutFactsQueryResult {
crate::runtime::query::layout_facts_query(self, viewport_size)
}
pub fn connection_target_candidates_from_layout_facts(&self) -> Vec<ConnectionTargetCandidate> {
crate::runtime::query::connection_target_candidates_from_layout_facts(self)
}
pub fn resolve_connection_target_from_layout_facts(
&self,
pointer: CanvasPoint,
from: ConnectionHandleRef,
) -> ResolvedConnectionTarget {
crate::runtime::query::resolve_connection_target_from_layout_facts(self, pointer, from)
}
pub fn edge_position_from_layout_facts(&self, edge: EdgeId) -> Option<EdgePosition> {
crate::runtime::query::edge_position_from_layout_facts(self, edge)
}
fn validate_node_measurement(
&self,
measurement: NodeMeasurement,
) -> Result<NodeMeasurement, NodeMeasurementError> {
if !self.graph().nodes.contains_key(&measurement.node) {
return Err(NodeMeasurementError::MissingNode(measurement.node));
}
if let Some(size) = measurement.size
&& !size.is_positive_finite()
{
return Err(NodeMeasurementError::InvalidSize {
node: measurement.node,
size,
});
}
for measured in &measurement.handles {
if measured.handle.node != measurement.node {
return Err(NodeMeasurementError::InvalidHandle {
node: measurement.node,
handle: measured.handle,
});
}
if !measured.bounds.rect.is_positive_finite() {
return Err(NodeMeasurementError::InvalidHandleBounds {
node: measurement.node,
handle: measured.handle,
});
}
let Some(port) = self.graph().ports.get(&measured.handle.port) else {
return Err(NodeMeasurementError::InvalidHandle {
node: measurement.node,
handle: measured.handle,
});
};
if port.node != measurement.node || port.dir != measured.handle.direction {
return Err(NodeMeasurementError::InvalidHandle {
node: measurement.node,
handle: measured.handle,
});
}
}
Ok(measurement)
}
}