use iced::{Element, Event, Length, Point, Rectangle, Size, Theme, Vector, keyboard};
use iced_wgpu::core::{
Clipboard, Layout, Shell, layout, mouse, overlay, renderer,
widget::{self, Tree, tree},
};
use web_time::Instant;
use super::{
Counts, DragInfo, GraphInfo, NodeGraph, OpTiming, RenderContext,
euclid::{IntoIced, WorldVector},
state::{Dragging, NodeGraphState, z_render_indices},
};
use super::{EdgeStyleFn, NodeStyleFn, PinStyleFn};
use crate::{
PinDirection, PinRef, PinSide,
ids::{EdgeId, NodeId, PinId},
node_graph::euclid::{IntoEuclid, ScreenPoint, WorldPoint},
node_pin::{NodePinState, PinEnd, PinInfo},
style::{
EdgeGeometry, EdgeStatus, EdgeStyle, GraphStyle, NodeStatus, NodeStyle, PinStatus,
PinStyle, TilingKind,
},
};
use iced_nodegraph_sdf::{Pattern, SdfPrimitive, Shape, Style, Tiling};
mod camera_overlay;
mod draw;
mod update;
use camera_overlay::CameraOverlay;
const BEZIER_SEGMENT_LENGTH: f32 = 80.0;
fn adaptive_bezier_length(start: [f32; 2], end: [f32; 2]) -> f32 {
let dx = end[0] - start[0];
let dy = end[1] - start[1];
let d = (dx * dx + dy * dy).sqrt();
BEZIER_SEGMENT_LENGTH.min(d * 0.5).max(1.0)
}
fn pin_side_direction(side: u32) -> [f32; 2] {
match side {
0 => [-1.0, 0.0], 1 => [1.0, 0.0], 2 => [0.0, -1.0], 3 => [0.0, 1.0], _ => [1.0, 0.0], }
}
impl<N, P, E, UI, Message, Renderer> iced_wgpu::core::Widget<Message, iced::Theme, Renderer>
for NodeGraph<'_, N, P, UI, Message, iced::Theme, Renderer, E>
where
N: NodeId + 'static,
P: PinId + 'static,
E: EdgeId + 'static,
UI: Clone + 'static,
Renderer: iced_wgpu::core::renderer::Renderer + iced_wgpu::primitive::Renderer,
{
fn tag(&self) -> tree::Tag {
tree::Tag::of::<NodeGraphState>()
}
fn state(&self) -> tree::State {
tree::State::new(NodeGraphState::default())
}
fn size(&self) -> Size<Length> {
self.size
}
fn layout(
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let limits = limits.width(self.size.width).height(self.size.height);
let size = limits.resolve(self.size.width, self.size.height, Size::ZERO);
let node_limits = layout::Limits::new(Size::ZERO, Size::INFINITE);
let nodes = self
.elements_iter_mut()
.zip(&mut tree.children)
.map(|((position, element), node_tree)| {
element
.as_widget_mut()
.layout(node_tree, renderer, &node_limits)
.move_to(position)
})
.collect();
layout::Node::with_children(size, nodes)
}
fn draw(
&self,
tree: &Tree,
renderer: &mut Renderer,
theme: &iced::Theme,
style: &renderer::Style,
layout: layout::Layout<'_>,
cursor: iced::mouse::Cursor,
viewport: &Rectangle,
) {
self.draw_impl(tree, renderer, theme, style, layout, cursor, viewport);
}
fn size_hint(&self) -> Size<Length> {
self.size()
}
fn children(&self) -> Vec<Tree> {
self.elements_iter()
.map(|(_, element)| Tree::new(element))
.collect()
}
fn diff(&self, tree: &mut Tree) {
let children: Vec<&Element<'_, Message, iced::Theme, Renderer>> =
self.elements_iter().map(|(_, e)| e).collect();
tree.diff_children(&children);
}
fn operate(
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn widget::Operation,
) {
for (((_, element), node_tree), node_layout) in self
.elements_iter_mut()
.zip(&mut tree.children)
.zip(layout.children())
{
element
.as_widget_mut()
.operate(node_tree, node_layout, renderer, operation);
}
}
fn overlay<'b>(
&'b mut self,
tree: &'b mut Tree,
layout: Layout<'b>,
renderer: &Renderer,
viewport: &Rectangle,
_translation: Vector,
) -> Option<overlay::Element<'b, Message, iced::Theme, Renderer>> {
let state = tree.state.downcast_ref::<NodeGraphState>();
let camera = state
.camera
.with_viewport_origin(layout.bounds().position().into_euclid().to_vector());
let children: Vec<overlay::Element<'b, Message, iced::Theme, Renderer>> = self
.nodes
.iter_mut()
.map(|(_, _, element, _, _)| element)
.zip(&mut tree.children)
.zip(layout.children())
.filter_map(|((element, node_tree), node_layout)| {
element.as_widget_mut().overlay(
node_tree,
node_layout,
renderer,
viewport,
Vector::ZERO,
)
})
.collect();
if children.is_empty() {
return None;
}
let content = overlay::Group::with_children(children).overlay();
Some(overlay::Element::new(Box::new(CameraOverlay {
content,
camera,
})))
}
fn update(
&mut self,
tree: &mut Tree,
event: &Event,
layout: Layout<'_>,
screen_cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
viewport: &Rectangle,
) {
self.update_impl(
tree,
event,
layout,
screen_cursor,
renderer,
clipboard,
shell,
viewport,
);
}
fn mouse_interaction(
&self,
_tree: &Tree,
_layout: Layout<'_>,
_cursor: mouse::Cursor,
_viewport: &Rectangle,
_renderer: &Renderer,
) -> mouse::Interaction {
mouse::Interaction::default()
}
}
impl<'a, N, P, E, UI, Message, Renderer>
From<NodeGraph<'a, N, P, UI, Message, iced::Theme, Renderer, E>>
for Element<'a, Message, iced::Theme, Renderer>
where
N: NodeId + 'static,
P: PinId + 'static,
E: EdgeId + 'static,
UI: Clone + 'static,
Renderer: iced_wgpu::core::renderer::Renderer + 'a + iced_wgpu::primitive::Renderer,
Message: 'static,
{
fn from(graph: NodeGraph<'a, N, P, UI, Message, iced::Theme, Renderer, E>) -> Self {
Element::new(graph)
}
}
pub fn node_graph<'a, Message, Theme, Renderer>()
-> NodeGraph<'a, usize, usize, (), Message, Theme, Renderer>
where
Renderer: iced_wgpu::core::renderer::Renderer,
{
NodeGraph::default()
}
fn find_pins<'a, P: 'static, UI: 'static>(
tree: &'a Tree,
layout: Layout<'a>,
) -> Vec<(usize, &'a NodePinState<P, UI>, (Point, Point))> {
let mut flat = Vec::new();
let mut pin_index = 0;
inner_find_pins::<P, UI>(&mut flat, &mut pin_index, layout, tree);
flat
}
fn inner_find_pins<'a, P: 'static, UI: 'static>(
flat: &mut Vec<(usize, &'a NodePinState<P, UI>, (Point, Point))>,
pin_index: &mut usize,
node_layout: Layout<'a>,
pin_tree: &'a Tree,
) {
if pin_tree.tag == tree::Tag::of::<NodePinState<P, UI>>() {
let pin_state = pin_tree.state.downcast_ref::<NodePinState<P, UI>>();
let node_bounds = node_layout.bounds();
let pin_positions = pin_positions(pin_state, node_bounds);
flat.push((*pin_index, pin_state, pin_positions));
*pin_index += 1;
}
for child_tree in &pin_tree.children {
inner_find_pins::<P, UI>(flat, pin_index, node_layout, child_tree);
}
}
fn orient_connection<N, P>(
from_dir: PinDirection,
to_dir: PinDirection,
from: PinRef<N, P>,
to: PinRef<N, P>,
) -> (PinRef<N, P>, PinRef<N, P>) {
let swap = !matches!(from_dir, PinDirection::Output) && matches!(to_dir, PinDirection::Output);
if swap { (to, from) } else { (from, to) }
}
fn pin_positions<P, UI>(state: &NodePinState<P, UI>, node_bounds: Rectangle) -> (Point, Point) {
if state.side == PinSide::Row {
(
pin_position(state.position, PinSide::Left, node_bounds),
pin_position(state.position, PinSide::Right, node_bounds),
)
} else {
let position = pin_position(state.position, state.side, node_bounds);
(position, position)
}
}
fn pin_position(position: Point, side: PinSide, node_bounds: Rectangle) -> Point {
match side {
PinSide::Row => panic!("Row pin is supposed to be handled separately"),
PinSide::Left => Point::new(node_bounds.x, position.y),
PinSide::Right => Point::new(node_bounds.x + node_bounds.width, position.y),
PinSide::Top => Point::new(position.x, node_bounds.y),
PinSide::Bottom => Point::new(position.x, node_bounds.y + node_bounds.height),
}
}
#[cfg(test)]
mod orient_tests {
use super::orient_connection;
use crate::PinRef;
use crate::node_pin::PinDirection;
#[test]
fn output_to_input_keeps_order() {
let out = PinRef::new(0usize, 0usize);
let inp = PinRef::new(1usize, 0usize);
let (from, to) = orient_connection(PinDirection::Output, PinDirection::Input, out, inp);
assert_eq!(from, PinRef::new(0, 0));
assert_eq!(to, PinRef::new(1, 0));
}
#[test]
fn input_to_output_is_flipped() {
let inp = PinRef::new(1usize, 0usize);
let out = PinRef::new(0usize, 0usize);
let (from, to) = orient_connection(PinDirection::Input, PinDirection::Output, inp, out);
assert_eq!(from, PinRef::new(0, 0));
assert_eq!(to, PinRef::new(1, 0));
}
#[test]
fn both_keeps_drag_order() {
let a = PinRef::new(0usize, 0usize);
let b = PinRef::new(1usize, 0usize);
let (from, to) = orient_connection(PinDirection::Both, PinDirection::Both, a, b);
assert_eq!(from, PinRef::new(0, 0));
assert_eq!(to, PinRef::new(1, 0));
}
}