Initial attempt to implement Render Graph
This commit is contained in:
		
							parent
							
								
									173cfe2acf
								
							
						
					
					
						commit
						3732fc5e3d
					
				
					 33 changed files with 8851 additions and 938 deletions
				
			
		
							
								
								
									
										259
									
								
								vendor/egui-snarl/src/ui/background_pattern.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								vendor/egui-snarl/src/ui/background_pattern.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,259 @@
 | 
			
		|||
use std::fmt;
 | 
			
		||||
 | 
			
		||||
use egui::{emath::Rot2, vec2, Pos2, Rect, Stroke, Ui, Vec2};
 | 
			
		||||
 | 
			
		||||
use super::SnarlStyle;
 | 
			
		||||
 | 
			
		||||
/// Viewport is a rectangle in graph space that is visible on screen.
 | 
			
		||||
pub struct Viewport {
 | 
			
		||||
    /// Screen-space rectangle.
 | 
			
		||||
    pub rect: Rect,
 | 
			
		||||
 | 
			
		||||
    /// Scale of the viewport.
 | 
			
		||||
    pub scale: f32,
 | 
			
		||||
 | 
			
		||||
    /// Offset of the viewport.
 | 
			
		||||
    pub offset: Vec2,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Viewport {
 | 
			
		||||
    /// Converts screen-space position to graph-space position.
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn screen_pos_to_graph(&self, pos: Pos2) -> Pos2 {
 | 
			
		||||
        (pos + self.offset - self.rect.center().to_vec2()) / self.scale
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Converts graph-space position to screen-space position.
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn graph_pos_to_screen(&self, pos: Pos2) -> Pos2 {
 | 
			
		||||
        pos * self.scale - self.offset + self.rect.center().to_vec2()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Converts screen-space vector to graph-space vector.
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn graph_vec_to_screen(&self, size: Vec2) -> Vec2 {
 | 
			
		||||
        size * self.scale
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Converts graph-space vector to screen-space vector.
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn screen_vec_to_graph(&self, size: Vec2) -> Vec2 {
 | 
			
		||||
        size / self.scale
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Converts screen-space size to graph-space size.
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn graph_size_to_screen(&self, size: f32) -> f32 {
 | 
			
		||||
        size * self.scale
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Converts graph-space size to screen-space size.
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn screen_size_to_graph(&self, size: f32) -> f32 {
 | 
			
		||||
        size / self.scale
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
///Grid background pattern.
 | 
			
		||||
///Use `SnarlStyle::background_pattern_stroke` for change stroke options
 | 
			
		||||
#[derive(Clone, Copy, Debug, PartialEq)]
 | 
			
		||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
			
		||||
#[cfg_attr(feature = "egui-probe", derive(egui_probe::EguiProbe))]
 | 
			
		||||
pub struct Grid {
 | 
			
		||||
    /// Spacing between grid lines.
 | 
			
		||||
    pub spacing: Vec2,
 | 
			
		||||
 | 
			
		||||
    /// Angle of the grid.
 | 
			
		||||
    #[cfg_attr(feature = "egui-probe", egui_probe(as egui_probe::angle))]
 | 
			
		||||
    pub angle: f32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const DEFAULT_GRID_SPACING: Vec2 = vec2(5.0, 5.0);
 | 
			
		||||
macro_rules! default_grid_spacing {
 | 
			
		||||
    () => {
 | 
			
		||||
        stringify!(vec2(5.0, 5.0))
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const DEFAULT_GRID_ANGLE: f32 = 1.0;
 | 
			
		||||
macro_rules! default_grid_angle {
 | 
			
		||||
    () => {
 | 
			
		||||
        stringify!(1.0)
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for Grid {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            spacing: DEFAULT_GRID_SPACING,
 | 
			
		||||
            angle: DEFAULT_GRID_ANGLE,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Grid {
 | 
			
		||||
    /// Create new grid with given spacing and angle.
 | 
			
		||||
    pub const fn new(spacing: Vec2, angle: f32) -> Self {
 | 
			
		||||
        Self { spacing, angle }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn draw(&self, style: &SnarlStyle, viewport: &Viewport, ui: &mut Ui) {
 | 
			
		||||
        let bg_stroke = style
 | 
			
		||||
            .background_pattern_stroke
 | 
			
		||||
            .unwrap_or_else(|| ui.visuals().widgets.noninteractive.bg_stroke);
 | 
			
		||||
 | 
			
		||||
        let stroke = Stroke::new(
 | 
			
		||||
            bg_stroke.width * viewport.scale.max(1.0),
 | 
			
		||||
            bg_stroke.color.gamma_multiply(viewport.scale.min(1.0)),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let spacing =
 | 
			
		||||
            ui.spacing().icon_width * vec2(self.spacing.x.max(1.0), self.spacing.y.max(1.0));
 | 
			
		||||
 | 
			
		||||
        let rot = Rot2::from_angle(self.angle);
 | 
			
		||||
        let rot_inv = rot.inverse();
 | 
			
		||||
 | 
			
		||||
        let graph_viewport = Rect::from_min_max(
 | 
			
		||||
            viewport.screen_pos_to_graph(viewport.rect.min),
 | 
			
		||||
            viewport.screen_pos_to_graph(viewport.rect.max),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let pattern_bounds = graph_viewport.rotate_bb(rot_inv);
 | 
			
		||||
 | 
			
		||||
        let min_x = (pattern_bounds.min.x / spacing.x).ceil();
 | 
			
		||||
        let max_x = (pattern_bounds.max.x / spacing.x).floor();
 | 
			
		||||
 | 
			
		||||
        for x in 0..=(max_x - min_x) as i64 {
 | 
			
		||||
            #[allow(clippy::cast_possible_truncation)]
 | 
			
		||||
            let x = (x as f32 + min_x) * spacing.x;
 | 
			
		||||
 | 
			
		||||
            let top = (rot * vec2(x, pattern_bounds.min.y)).to_pos2();
 | 
			
		||||
            let bottom = (rot * vec2(x, pattern_bounds.max.y)).to_pos2();
 | 
			
		||||
 | 
			
		||||
            let top = viewport.graph_pos_to_screen(top);
 | 
			
		||||
            let bottom = viewport.graph_pos_to_screen(bottom);
 | 
			
		||||
 | 
			
		||||
            ui.painter().line_segment([top, bottom], stroke);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let min_y = (pattern_bounds.min.y / spacing.y).ceil();
 | 
			
		||||
        let max_y = (pattern_bounds.max.y / spacing.y).floor();
 | 
			
		||||
 | 
			
		||||
        for y in 0..=(max_y - min_y) as i64 {
 | 
			
		||||
            #[allow(clippy::cast_possible_truncation)]
 | 
			
		||||
            let y = (y as f32 + min_y) * spacing.y;
 | 
			
		||||
 | 
			
		||||
            let top = (rot * vec2(pattern_bounds.min.x, y)).to_pos2();
 | 
			
		||||
            let bottom = (rot * vec2(pattern_bounds.max.x, y)).to_pos2();
 | 
			
		||||
 | 
			
		||||
            let top = viewport.graph_pos_to_screen(top);
 | 
			
		||||
            let bottom = viewport.graph_pos_to_screen(bottom);
 | 
			
		||||
 | 
			
		||||
            ui.painter().line_segment([top, bottom], stroke);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
tiny_fn::tiny_fn! {
 | 
			
		||||
    /// Custom background pattern function with signature
 | 
			
		||||
    /// `Fn(style: &SnarlStyle, viewport: &Viewport, ui: &mut Ui)`
 | 
			
		||||
    pub struct CustomBackground = Fn(style: &SnarlStyle, viewport: &Viewport, ui: &mut Ui);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<const INLINE_SIZE: usize> Default for CustomBackground<'_, INLINE_SIZE> {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::new(|_, _, _| {})
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "egui-probe")]
 | 
			
		||||
impl<const INLINE_SIZE: usize> egui_probe::EguiProbe for CustomBackground<'_, INLINE_SIZE> {
 | 
			
		||||
    fn probe(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        ui: &mut egui_probe::egui::Ui,
 | 
			
		||||
        _style: &egui_probe::Style,
 | 
			
		||||
    ) -> egui_probe::egui::Response {
 | 
			
		||||
        ui.weak("Custom")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Background pattern show beneath nodes and wires.
 | 
			
		||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
			
		||||
#[cfg_attr(feature = "egui-probe", derive(egui_probe::EguiProbe))]
 | 
			
		||||
pub enum BackgroundPattern {
 | 
			
		||||
    /// No pattern.
 | 
			
		||||
    NoPattern,
 | 
			
		||||
 | 
			
		||||
    /// Linear grid.
 | 
			
		||||
    #[cfg_attr(feature = "egui-probe", egui_probe(transparent))]
 | 
			
		||||
    Grid(Grid),
 | 
			
		||||
 | 
			
		||||
    /// Custom pattern.
 | 
			
		||||
    /// Contains function with signature
 | 
			
		||||
    /// `Fn(style: &SnarlStyle, viewport: &Viewport, ui: &mut Ui)`
 | 
			
		||||
    #[cfg_attr(feature = "egui-probe", egui_probe(transparent))]
 | 
			
		||||
    Custom(#[cfg_attr(feature = "serde", serde(skip))] CustomBackground<'static>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PartialEq for BackgroundPattern {
 | 
			
		||||
    fn eq(&self, other: &Self) -> bool {
 | 
			
		||||
        match (self, other) {
 | 
			
		||||
            (BackgroundPattern::Grid(l), BackgroundPattern::Grid(r)) => *l == *r,
 | 
			
		||||
            _ => false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Debug for BackgroundPattern {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            BackgroundPattern::Grid(grid) => f
 | 
			
		||||
                .debug_tuple("BackgroundPattern::Grid")
 | 
			
		||||
                .field(grid)
 | 
			
		||||
                .finish(),
 | 
			
		||||
            BackgroundPattern::Custom(_) => f.write_str("BackgroundPattern::Custom"),
 | 
			
		||||
            BackgroundPattern::NoPattern => f.write_str("BackgroundPattern::NoPattern"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for BackgroundPattern {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::Grid(Default::default())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BackgroundPattern {
 | 
			
		||||
    /// Create new background pattern with default values.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Default patter is `Grid` with spacing - `
 | 
			
		||||
    #[doc = default_grid_spacing!()]
 | 
			
		||||
    /// ` and angle - `
 | 
			
		||||
    #[doc = default_grid_angle!()]
 | 
			
		||||
    /// ` radian.
 | 
			
		||||
    pub const fn new() -> Self {
 | 
			
		||||
        Self::Grid(Grid::new(DEFAULT_GRID_SPACING, DEFAULT_GRID_ANGLE))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create new grid background pattern with given spacing and angle.
 | 
			
		||||
    pub const fn grid(spacing: Vec2, angle: f32) -> Self {
 | 
			
		||||
        Self::Grid(Grid::new(spacing, angle))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create new custom background pattern.
 | 
			
		||||
    pub fn custom<F>(f: F) -> Self
 | 
			
		||||
    where
 | 
			
		||||
        F: Fn(&SnarlStyle, &Viewport, &mut Ui) + 'static,
 | 
			
		||||
    {
 | 
			
		||||
        Self::Custom(CustomBackground::new(f))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Draws background pattern.
 | 
			
		||||
    pub(super) fn draw(&self, style: &SnarlStyle, viewport: &Viewport, ui: &mut Ui) {
 | 
			
		||||
        match self {
 | 
			
		||||
            BackgroundPattern::Grid(g) => g.draw(style, viewport, ui),
 | 
			
		||||
            BackgroundPattern::Custom(c) => c.call(style, viewport, ui),
 | 
			
		||||
            BackgroundPattern::NoPattern => {}
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										160
									
								
								vendor/egui-snarl/src/ui/effect.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								vendor/egui-snarl/src/ui/effect.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,160 @@
 | 
			
		|||
use std::cell::RefCell;
 | 
			
		||||
 | 
			
		||||
use egui::Pos2;
 | 
			
		||||
 | 
			
		||||
use crate::{wire_pins, InPinId, Node, OutPinId, Snarl};
 | 
			
		||||
 | 
			
		||||
pub enum Effect<T> {
 | 
			
		||||
    /// Adds a new node to the Snarl.
 | 
			
		||||
    InsertNode { pos: Pos2, node: T },
 | 
			
		||||
 | 
			
		||||
    /// Removes a node from snarl.
 | 
			
		||||
    RemoveNode { node: NodeId },
 | 
			
		||||
 | 
			
		||||
    /// Opens/closes a node.
 | 
			
		||||
    OpenNode { node: NodeId, open: bool },
 | 
			
		||||
 | 
			
		||||
    /// Adds connection between two nodes.
 | 
			
		||||
    Connect { from: OutPinId, to: InPinId },
 | 
			
		||||
 | 
			
		||||
    /// Removes connection between two nodes.
 | 
			
		||||
    Disconnect { from: OutPinId, to: InPinId },
 | 
			
		||||
 | 
			
		||||
    /// Removes all connections from the output pin.
 | 
			
		||||
    DropOutputs { pin: OutPinId },
 | 
			
		||||
 | 
			
		||||
    /// Removes all connections to the input pin.
 | 
			
		||||
    DropInputs { pin: InPinId },
 | 
			
		||||
 | 
			
		||||
    /// Executes a closure with mutable reference to the Snarl.
 | 
			
		||||
    Closure(Box<dyn FnOnce(&mut Snarl<T>)>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Contained for deferred execution of effects.
 | 
			
		||||
/// It is populated by [`SnarlViewer`] methods and then applied to the Snarl.
 | 
			
		||||
pub struct Effects<T> {
 | 
			
		||||
    effects: Vec<Effect<T>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> Default for Effects<T> {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Effects {
 | 
			
		||||
            effects: Default::default(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> Effects<T> {
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    #[doc(hidden)]
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        Effects {
 | 
			
		||||
            effects: Vec::new(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if there are no effects.
 | 
			
		||||
    /// Returns `false` otherwise.
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn is_empty(&self) -> bool {
 | 
			
		||||
        self.effects.is_empty()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Inserts a new node to the Snarl.
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn insert_node(&mut self, pos: Pos2, node: T) {
 | 
			
		||||
        self.effects.push(Effect::InsertNode { node, pos });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Removes a node from the Snarl.
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn remove_node(&mut self, node: NodeId) {
 | 
			
		||||
        self.effects.push(Effect::RemoveNode { node });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Opens/closes a node.
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn open_node(&mut self, node: NodeId, open: bool) {
 | 
			
		||||
        self.effects.push(Effect::OpenNode { node, open });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Connects two nodes.
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn connect(&mut self, from: OutPinId, to: InPinId) {
 | 
			
		||||
        self.effects.push(Effect::Connect { from, to });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Disconnects two nodes.
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn disconnect(&mut self, from: OutPinId, to: InPinId) {
 | 
			
		||||
        self.effects.push(Effect::Disconnect { from, to });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Removes all connections from the output pin.
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn drop_inputs(&mut self, pin: InPinId) {
 | 
			
		||||
        self.effects.push(Effect::DropInputs { pin });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Removes all connections to the input pin.
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn drop_outputs(&mut self, pin: OutPinId) {
 | 
			
		||||
        self.effects.push(Effect::DropOutputs { pin });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> Snarl<T> {
 | 
			
		||||
    pub fn apply_effects(&mut self, effects: Effects<T>) {
 | 
			
		||||
        if effects.effects.is_empty() {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        for effect in effects.effects {
 | 
			
		||||
            self.apply_effect(effect);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn apply_effect(&mut self, effect: Effect<T>) {
 | 
			
		||||
        match effect {
 | 
			
		||||
            Effect::InsertNode { node, pos } => {
 | 
			
		||||
                let idx = self.nodes.insert(Node {
 | 
			
		||||
                    value: RefCell::new(node),
 | 
			
		||||
                    pos,
 | 
			
		||||
                    open: true,
 | 
			
		||||
                });
 | 
			
		||||
                self.draw_order.push(idx);
 | 
			
		||||
            }
 | 
			
		||||
            Effect::RemoveNode { node } => {
 | 
			
		||||
                if self.nodes.contains(node) {
 | 
			
		||||
                    self.remove_node(node);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Effect::OpenNode { node, open } => {
 | 
			
		||||
                if self.nodes.contains(node) {
 | 
			
		||||
                    self.nodes[node].open = open;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Effect::Connect { from, to } => {
 | 
			
		||||
                if self.nodes.contains(from.node) && self.nodes.contains(to.node) {
 | 
			
		||||
                    self.wires.insert(wire_pins(from, to));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Effect::Disconnect { from, to } => {
 | 
			
		||||
                if self.nodes.contains(from.node) && self.nodes.contains(to.node) {
 | 
			
		||||
                    self.wires.remove(&wire_pins(from, to));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Effect::DropOutputs { pin } => {
 | 
			
		||||
                if self.nodes.contains(pin.node) {
 | 
			
		||||
                    self.wires.drop_outputs(pin);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Effect::DropInputs { pin } => {
 | 
			
		||||
                if self.nodes.contains(pin.node) {
 | 
			
		||||
                    self.wires.drop_inputs(pin);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Effect::Closure(f) => f(self),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										171
									
								
								vendor/egui-snarl/src/ui/pin.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								vendor/egui-snarl/src/ui/pin.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,171 @@
 | 
			
		|||
use egui::{epaint::PathShape, vec2, Color32, Painter, Pos2, Rect, Shape, Stroke, Vec2};
 | 
			
		||||
 | 
			
		||||
use crate::{InPinId, OutPinId};
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 | 
			
		||||
pub enum AnyPin {
 | 
			
		||||
    Out(OutPinId),
 | 
			
		||||
    In(InPinId),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// In the current context, these are the I/O pins of the 'source' node that the newly
 | 
			
		||||
/// created node's I/O pins will connect to.
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub enum AnyPins<'a> {
 | 
			
		||||
    /// Output pins.
 | 
			
		||||
    Out(&'a [OutPinId]),
 | 
			
		||||
    /// Input pins
 | 
			
		||||
    In(&'a [InPinId]),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
tiny_fn::tiny_fn! {
 | 
			
		||||
    /// Custom pin shape drawing function with signature
 | 
			
		||||
    /// `Fn(painter: &Painter, rect: Rect, fill: Color32, stroke: Stroke)`
 | 
			
		||||
    pub struct CustomPinShape = Fn(painter: &Painter, rect: Rect, fill: Color32, stroke: Stroke);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Shape of a pin.
 | 
			
		||||
pub enum PinShape {
 | 
			
		||||
    /// Circle shape.
 | 
			
		||||
    Circle,
 | 
			
		||||
 | 
			
		||||
    /// Triangle shape.
 | 
			
		||||
    Triangle,
 | 
			
		||||
 | 
			
		||||
    /// Square shape.
 | 
			
		||||
    Square,
 | 
			
		||||
 | 
			
		||||
    /// Custom shape.
 | 
			
		||||
    Custom(CustomPinShape<'static>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Information about a pin returned by `SnarlViewer::show_input` and `SnarlViewer::show_output`.
 | 
			
		||||
pub struct PinInfo {
 | 
			
		||||
    /// Shape of the pin.
 | 
			
		||||
    pub shape: PinShape,
 | 
			
		||||
 | 
			
		||||
    /// Size of the pin.
 | 
			
		||||
    pub size: f32,
 | 
			
		||||
 | 
			
		||||
    /// Fill color of the pin.
 | 
			
		||||
    pub fill: Color32,
 | 
			
		||||
 | 
			
		||||
    /// Outline stroke of the pin.
 | 
			
		||||
    pub stroke: Stroke,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for PinInfo {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        PinInfo {
 | 
			
		||||
            shape: PinShape::Circle,
 | 
			
		||||
            size: 1.0,
 | 
			
		||||
            fill: Color32::GRAY,
 | 
			
		||||
            stroke: Stroke::new(1.0, Color32::BLACK),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PinInfo {
 | 
			
		||||
    /// Sets the shape of the pin.
 | 
			
		||||
    pub fn with_shape(mut self, shape: PinShape) -> Self {
 | 
			
		||||
        self.shape = shape;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the size of the pin.
 | 
			
		||||
    pub fn with_size(mut self, size: f32) -> Self {
 | 
			
		||||
        self.size = size;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the fill color of the pin.
 | 
			
		||||
    pub fn with_fill(mut self, fill: Color32) -> Self {
 | 
			
		||||
        self.fill = fill;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the outline stroke of the pin.
 | 
			
		||||
    pub fn with_stroke(mut self, stroke: Stroke) -> Self {
 | 
			
		||||
        self.stroke = stroke;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Creates a circle pin.
 | 
			
		||||
    pub fn circle() -> Self {
 | 
			
		||||
        PinInfo {
 | 
			
		||||
            shape: PinShape::Circle,
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Creates a triangle pin.
 | 
			
		||||
    pub fn triangle() -> Self {
 | 
			
		||||
        PinInfo {
 | 
			
		||||
            shape: PinShape::Triangle,
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Creates a square pin.
 | 
			
		||||
    pub fn square() -> Self {
 | 
			
		||||
        PinInfo {
 | 
			
		||||
            shape: PinShape::Square,
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Creates a square pin.
 | 
			
		||||
    pub fn custom<F>(f: F) -> Self
 | 
			
		||||
    where
 | 
			
		||||
        F: Fn(&Painter, Rect, Color32, Stroke) + 'static,
 | 
			
		||||
    {
 | 
			
		||||
        PinInfo {
 | 
			
		||||
            shape: PinShape::Custom(CustomPinShape::new(f)),
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn draw_pin(painter: &Painter, pin: PinInfo, pos: Pos2, base_size: f32) {
 | 
			
		||||
    let size = base_size * pin.size;
 | 
			
		||||
    match pin.shape {
 | 
			
		||||
        PinShape::Circle => {
 | 
			
		||||
            painter.circle(pos, size * 2.0 / std::f32::consts::PI, pin.fill, pin.stroke);
 | 
			
		||||
        }
 | 
			
		||||
        PinShape::Triangle => {
 | 
			
		||||
            const A: Vec2 = vec2(-0.649_519, 0.4875);
 | 
			
		||||
            const B: Vec2 = vec2(0.649_519, 0.4875);
 | 
			
		||||
            const C: Vec2 = vec2(0.0, -0.6375);
 | 
			
		||||
 | 
			
		||||
            let points = vec![pos + A * size, pos + B * size, pos + C * size];
 | 
			
		||||
 | 
			
		||||
            painter.add(Shape::Path(PathShape {
 | 
			
		||||
                points,
 | 
			
		||||
                closed: true,
 | 
			
		||||
                fill: pin.fill,
 | 
			
		||||
                stroke: pin.stroke,
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
        PinShape::Square => {
 | 
			
		||||
            let points = vec![
 | 
			
		||||
                pos + vec2(-0.5, -0.5) * size,
 | 
			
		||||
                pos + vec2(0.5, -0.5) * size,
 | 
			
		||||
                pos + vec2(0.5, 0.5) * size,
 | 
			
		||||
                pos + vec2(-0.5, 0.5) * size,
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
            painter.add(Shape::Path(PathShape {
 | 
			
		||||
                points,
 | 
			
		||||
                closed: true,
 | 
			
		||||
                fill: pin.fill,
 | 
			
		||||
                stroke: pin.stroke,
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
        PinShape::Custom(f) => f.call(
 | 
			
		||||
            painter,
 | 
			
		||||
            Rect::from_center_size(pos, vec2(size, size)),
 | 
			
		||||
            pin.fill,
 | 
			
		||||
            pin.stroke,
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										404
									
								
								vendor/egui-snarl/src/ui/state.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										404
									
								
								vendor/egui-snarl/src/ui/state.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,404 @@
 | 
			
		|||
use egui::{style::Spacing, Align, Context, Id, Pos2, Rect, Vec2};
 | 
			
		||||
 | 
			
		||||
use crate::{InPinId, OutPinId, Snarl};
 | 
			
		||||
 | 
			
		||||
use super::SnarlStyle;
 | 
			
		||||
 | 
			
		||||
/// Node UI state.
 | 
			
		||||
 | 
			
		||||
pub struct NodeState {
 | 
			
		||||
    /// Node size for this frame.
 | 
			
		||||
    /// It is updated to fit content.
 | 
			
		||||
    size: Vec2,
 | 
			
		||||
    header_height: f32,
 | 
			
		||||
    body_width: f32,
 | 
			
		||||
    footer_width: f32,
 | 
			
		||||
 | 
			
		||||
    id: Id,
 | 
			
		||||
    scale: f32,
 | 
			
		||||
    dirty: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, PartialEq)]
 | 
			
		||||
struct NodeData {
 | 
			
		||||
    unscaled_size: Vec2,
 | 
			
		||||
    unscaled_header_height: f32,
 | 
			
		||||
    unscaled_body_width: f32,
 | 
			
		||||
    unsacled_footer_width: f32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl NodeState {
 | 
			
		||||
    pub fn load(cx: &Context, id: Id, spacing: &Spacing, scale: f32) -> Self {
 | 
			
		||||
        match cx.data_mut(|d| d.get_temp::<NodeData>(id)) {
 | 
			
		||||
            Some(data) => NodeState {
 | 
			
		||||
                size: data.unscaled_size * scale,
 | 
			
		||||
                header_height: data.unscaled_header_height * scale,
 | 
			
		||||
                body_width: data.unscaled_body_width * scale,
 | 
			
		||||
                footer_width: data.unsacled_footer_width * scale,
 | 
			
		||||
                id,
 | 
			
		||||
                scale,
 | 
			
		||||
                dirty: false,
 | 
			
		||||
            },
 | 
			
		||||
            None => Self::initial(id, spacing, scale),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn clear(self, cx: &Context) {
 | 
			
		||||
        cx.data_mut(|d| d.remove::<Self>(self.id));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn store(&self, cx: &Context) {
 | 
			
		||||
        if self.dirty {
 | 
			
		||||
            cx.data_mut(|d| {
 | 
			
		||||
                d.insert_temp(
 | 
			
		||||
                    self.id,
 | 
			
		||||
                    NodeData {
 | 
			
		||||
                        unscaled_size: self.size / self.scale,
 | 
			
		||||
                        unscaled_header_height: self.header_height / self.scale,
 | 
			
		||||
                        unscaled_body_width: self.body_width / self.scale,
 | 
			
		||||
                        unsacled_footer_width: self.footer_width / self.scale,
 | 
			
		||||
                    },
 | 
			
		||||
                )
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Finds node rect at specific position (excluding node frame margin).
 | 
			
		||||
    pub fn node_rect(&self, pos: Pos2, openness: f32) -> Rect {
 | 
			
		||||
        Rect::from_min_size(
 | 
			
		||||
            pos,
 | 
			
		||||
            egui::vec2(
 | 
			
		||||
                self.size.x,
 | 
			
		||||
                f32::max(self.header_height, self.size.y * openness),
 | 
			
		||||
            ),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn payload_offset(&self, openness: f32) -> f32 {
 | 
			
		||||
        (self.size.y) * (1.0 - openness)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn align_body(&mut self, rect: Rect) -> Rect {
 | 
			
		||||
        let x_range = Align::Center.align_size_within_range(self.body_width, rect.x_range());
 | 
			
		||||
        Rect::from_x_y_ranges(x_range, rect.y_range())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn align_footer(&mut self, rect: Rect) -> Rect {
 | 
			
		||||
        let x_range = Align::Center.align_size_within_range(self.footer_width, rect.x_range());
 | 
			
		||||
        Rect::from_x_y_ranges(x_range, rect.y_range())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn set_size(&mut self, size: Vec2) {
 | 
			
		||||
        if self.size != size {
 | 
			
		||||
            self.size = size;
 | 
			
		||||
            self.dirty = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn set_header_height(&mut self, height: f32) {
 | 
			
		||||
        if self.header_height != height {
 | 
			
		||||
            self.header_height = height;
 | 
			
		||||
            self.dirty = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn set_body_width(&mut self, width: f32) {
 | 
			
		||||
        if self.body_width != width {
 | 
			
		||||
            self.body_width = width;
 | 
			
		||||
            self.dirty = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn set_footer_width(&mut self, width: f32) {
 | 
			
		||||
        if self.footer_width != width {
 | 
			
		||||
            self.footer_width = width;
 | 
			
		||||
            self.dirty = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn initial(id: Id, spacing: &Spacing, scale: f32) -> Self {
 | 
			
		||||
        NodeState {
 | 
			
		||||
            size: spacing.interact_size,
 | 
			
		||||
            header_height: spacing.interact_size.y,
 | 
			
		||||
            body_width: 0.0,
 | 
			
		||||
            footer_width: 0.0,
 | 
			
		||||
            id,
 | 
			
		||||
            dirty: true,
 | 
			
		||||
            scale,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub enum NewWires {
 | 
			
		||||
    In(Vec<InPinId>),
 | 
			
		||||
    Out(Vec<OutPinId>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct SnarlState {
 | 
			
		||||
    /// Where viewport's center in graph's space.
 | 
			
		||||
    offset: Vec2,
 | 
			
		||||
 | 
			
		||||
    /// Scale of the viewport.
 | 
			
		||||
    scale: f32,
 | 
			
		||||
 | 
			
		||||
    target_scale: f32,
 | 
			
		||||
 | 
			
		||||
    new_wires: Option<NewWires>,
 | 
			
		||||
 | 
			
		||||
    id: Id,
 | 
			
		||||
 | 
			
		||||
    /// Flag indicating that the graph state is dirty must be saved.
 | 
			
		||||
    dirty: bool,
 | 
			
		||||
 | 
			
		||||
    /// Flag indicating that the link menu is open.
 | 
			
		||||
    is_link_menu_open: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
struct SnarlStateData {
 | 
			
		||||
    offset: Vec2,
 | 
			
		||||
    scale: f32,
 | 
			
		||||
    target_scale: f32,
 | 
			
		||||
    new_wires: Option<NewWires>,
 | 
			
		||||
    is_link_menu_open: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SnarlState {
 | 
			
		||||
    pub fn load<T>(
 | 
			
		||||
        cx: &Context,
 | 
			
		||||
        id: Id,
 | 
			
		||||
        pivot: Pos2,
 | 
			
		||||
        viewport: Rect,
 | 
			
		||||
        snarl: &Snarl<T>,
 | 
			
		||||
        style: &SnarlStyle,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let Some(mut data) = cx.data_mut(|d| d.get_temp::<SnarlStateData>(id)) else {
 | 
			
		||||
            return Self::initial(id, viewport, snarl, style);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let new_scale = cx.animate_value_with_time(id.with("zoom-scale"), data.target_scale, 0.1);
 | 
			
		||||
 | 
			
		||||
        let mut dirty = false;
 | 
			
		||||
        if new_scale != data.scale {
 | 
			
		||||
            let a = pivot + data.offset - viewport.center().to_vec2();
 | 
			
		||||
 | 
			
		||||
            data.offset += a * new_scale / data.scale - a;
 | 
			
		||||
            data.scale = new_scale;
 | 
			
		||||
            dirty = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SnarlState {
 | 
			
		||||
            offset: data.offset,
 | 
			
		||||
            scale: data.scale,
 | 
			
		||||
            target_scale: data.target_scale,
 | 
			
		||||
            new_wires: data.new_wires,
 | 
			
		||||
            is_link_menu_open: data.is_link_menu_open,
 | 
			
		||||
            id,
 | 
			
		||||
            dirty,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn initial<T>(id: Id, viewport: Rect, snarl: &Snarl<T>, style: &SnarlStyle) -> Self {
 | 
			
		||||
        let mut bb = Rect::NOTHING;
 | 
			
		||||
 | 
			
		||||
        for (_, node) in snarl.nodes.iter() {
 | 
			
		||||
            bb.extend_with(node.pos);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if !bb.is_positive() {
 | 
			
		||||
            let scale = 1.0f32.clamp(style.min_scale, style.max_scale);
 | 
			
		||||
 | 
			
		||||
            return SnarlState {
 | 
			
		||||
                offset: Vec2::ZERO,
 | 
			
		||||
                scale,
 | 
			
		||||
                target_scale: scale,
 | 
			
		||||
                new_wires: None,
 | 
			
		||||
                is_link_menu_open: false,
 | 
			
		||||
                id,
 | 
			
		||||
                dirty: true,
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bb = bb.expand(100.0);
 | 
			
		||||
 | 
			
		||||
        let bb_size = bb.size();
 | 
			
		||||
        let viewport_size = viewport.size();
 | 
			
		||||
 | 
			
		||||
        let scale = (viewport_size.x / bb_size.x)
 | 
			
		||||
            .min(1.0)
 | 
			
		||||
            .min(viewport_size.y / bb_size.y)
 | 
			
		||||
            .min(style.max_scale)
 | 
			
		||||
            .max(style.min_scale);
 | 
			
		||||
 | 
			
		||||
        let offset = bb.center().to_vec2() * scale;
 | 
			
		||||
 | 
			
		||||
        SnarlState {
 | 
			
		||||
            offset,
 | 
			
		||||
            scale,
 | 
			
		||||
            target_scale: scale,
 | 
			
		||||
            new_wires: None,
 | 
			
		||||
            is_link_menu_open: false,
 | 
			
		||||
            id,
 | 
			
		||||
            dirty: true,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn store(self, cx: &Context) {
 | 
			
		||||
        if self.dirty {
 | 
			
		||||
            cx.data_mut(|d| {
 | 
			
		||||
                d.insert_temp(
 | 
			
		||||
                    self.id,
 | 
			
		||||
                    SnarlStateData {
 | 
			
		||||
                        offset: self.offset,
 | 
			
		||||
                        scale: self.scale,
 | 
			
		||||
                        target_scale: self.target_scale,
 | 
			
		||||
                        new_wires: self.new_wires,
 | 
			
		||||
                        is_link_menu_open: self.is_link_menu_open,
 | 
			
		||||
                    },
 | 
			
		||||
                )
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn pan(&mut self, delta: Vec2) {
 | 
			
		||||
        self.offset += delta;
 | 
			
		||||
        self.dirty = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn scale(&self) -> f32 {
 | 
			
		||||
        self.scale
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn offset(&self) -> Vec2 {
 | 
			
		||||
        self.offset
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn set_scale(&mut self, scale: f32) {
 | 
			
		||||
        self.target_scale = scale;
 | 
			
		||||
        self.dirty = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn screen_pos_to_graph(&self, pos: Pos2, viewport: Rect) -> Pos2 {
 | 
			
		||||
        (pos + self.offset - viewport.center().to_vec2()) / self.scale
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn graph_pos_to_screen(&self, pos: Pos2, viewport: Rect) -> Pos2 {
 | 
			
		||||
        pos * self.scale - self.offset + viewport.center().to_vec2()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // #[inline(always)]
 | 
			
		||||
    // pub fn graph_vec_to_screen(&self, size: Vec2) -> Vec2 {
 | 
			
		||||
    //     size * self.scale
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    pub fn screen_vec_to_graph(&self, size: Vec2) -> Vec2 {
 | 
			
		||||
        size / self.scale
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // #[inline(always)]
 | 
			
		||||
    // pub fn graph_value_to_screen(&self, value: f32) -> f32 {
 | 
			
		||||
    //     value * self.scale
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    // #[inline(always)]
 | 
			
		||||
    // pub fn screen_value_to_graph(&self, value: f32) -> f32 {
 | 
			
		||||
    //     value / self.scale
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    pub fn start_new_wire_in(&mut self, pin: InPinId) {
 | 
			
		||||
        self.new_wires = Some(NewWires::In(vec![pin]));
 | 
			
		||||
        self.dirty = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn start_new_wire_out(&mut self, pin: OutPinId) {
 | 
			
		||||
        self.new_wires = Some(NewWires::Out(vec![pin]));
 | 
			
		||||
        self.dirty = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn start_new_wires_in(&mut self, pins: &[InPinId]) {
 | 
			
		||||
        self.new_wires = Some(NewWires::In(pins.to_vec()));
 | 
			
		||||
        self.dirty = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn start_new_wires_out(&mut self, pins: &[OutPinId]) {
 | 
			
		||||
        self.new_wires = Some(NewWires::Out(pins.to_vec()));
 | 
			
		||||
        self.dirty = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn add_new_wire_in(&mut self, pin: InPinId) {
 | 
			
		||||
        if let Some(NewWires::In(pins)) = &mut self.new_wires {
 | 
			
		||||
            if !pins.contains(&pin) {
 | 
			
		||||
                pins.push(pin);
 | 
			
		||||
                self.dirty = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn add_new_wire_out(&mut self, pin: OutPinId) {
 | 
			
		||||
        if let Some(NewWires::Out(pins)) = &mut self.new_wires {
 | 
			
		||||
            if !pins.contains(&pin) {
 | 
			
		||||
                pins.push(pin);
 | 
			
		||||
                self.dirty = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn remove_new_wire_in(&mut self, pin: InPinId) {
 | 
			
		||||
        if let Some(NewWires::In(pins)) = &mut self.new_wires {
 | 
			
		||||
            if let Some(idx) = pins.iter().position(|p| *p == pin) {
 | 
			
		||||
                pins.swap_remove(idx);
 | 
			
		||||
                self.dirty = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn remove_new_wire_out(&mut self, pin: OutPinId) {
 | 
			
		||||
        if let Some(NewWires::Out(pins)) = &mut self.new_wires {
 | 
			
		||||
            if let Some(idx) = pins.iter().position(|p| *p == pin) {
 | 
			
		||||
                pins.swap_remove(idx);
 | 
			
		||||
                self.dirty = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn has_new_wires(&self) -> bool {
 | 
			
		||||
        self.new_wires.is_some()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn new_wires(&self) -> Option<&NewWires> {
 | 
			
		||||
        self.new_wires.as_ref()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn take_wires(&mut self) -> Option<NewWires> {
 | 
			
		||||
        self.dirty |= self.new_wires.is_some();
 | 
			
		||||
        self.new_wires.take()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn revert_take_wires(&mut self, wires: NewWires) {
 | 
			
		||||
        self.new_wires = Some(wires);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn open_link_menu(&mut self) {
 | 
			
		||||
        self.is_link_menu_open = true;
 | 
			
		||||
        self.dirty = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn close_link_menu(&mut self) {
 | 
			
		||||
        self.new_wires = None;
 | 
			
		||||
        self.is_link_menu_open = false;
 | 
			
		||||
        self.dirty = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn is_link_menu_open(&self) -> bool {
 | 
			
		||||
        self.is_link_menu_open
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										186
									
								
								vendor/egui-snarl/src/ui/viewer.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								vendor/egui-snarl/src/ui/viewer.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,186 @@
 | 
			
		|||
use egui::{Color32, Pos2, Style, Ui};
 | 
			
		||||
 | 
			
		||||
use crate::{InPin, NodeId, OutPin, Snarl};
 | 
			
		||||
 | 
			
		||||
use super::pin::{AnyPins, PinInfo};
 | 
			
		||||
 | 
			
		||||
/// SnarlViewer is a trait for viewing a Snarl.
 | 
			
		||||
///
 | 
			
		||||
/// It can extract necessary data from the nodes and controls their
 | 
			
		||||
/// response to certain events.
 | 
			
		||||
pub trait SnarlViewer<T> {
 | 
			
		||||
    /// Returns title of the node.
 | 
			
		||||
    fn title(&mut self, node: &T) -> String;
 | 
			
		||||
 | 
			
		||||
    /// Checks if node has something to show in body - between input and output pins.
 | 
			
		||||
    fn has_body(&mut self, node: &T) -> bool {
 | 
			
		||||
        let _ = node;
 | 
			
		||||
        false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Checks if node has something to show in footer - below pins and body.
 | 
			
		||||
    fn has_footer(&mut self, node: &T) -> bool {
 | 
			
		||||
        let _ = node;
 | 
			
		||||
        false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Checks if node has something to show in on-hover popup.
 | 
			
		||||
    fn has_on_hover_popup(&mut self, node: &T) -> bool {
 | 
			
		||||
        let _ = node;
 | 
			
		||||
        false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Renders the node's header.
 | 
			
		||||
    fn show_header(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        node: NodeId,
 | 
			
		||||
        inputs: &[InPin],
 | 
			
		||||
        outputs: &[OutPin],
 | 
			
		||||
        ui: &mut Ui,
 | 
			
		||||
        scale: f32,
 | 
			
		||||
        snarl: &mut Snarl<T>,
 | 
			
		||||
    ) {
 | 
			
		||||
        let _ = (inputs, outputs, scale);
 | 
			
		||||
        ui.label(self.title(&snarl[node]));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns number of output pins of the node.
 | 
			
		||||
    fn outputs(&mut self, node: &T) -> usize;
 | 
			
		||||
 | 
			
		||||
    /// Returns number of input pins of the node.
 | 
			
		||||
    fn inputs(&mut self, node: &T) -> usize;
 | 
			
		||||
 | 
			
		||||
    /// Renders the node's input pin.
 | 
			
		||||
    fn show_input(&mut self, pin: &InPin, ui: &mut Ui, scale: f32, snarl: &mut Snarl<T>)
 | 
			
		||||
        -> PinInfo;
 | 
			
		||||
 | 
			
		||||
    /// Renders the node's output pin.
 | 
			
		||||
    fn show_output(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        pin: &OutPin,
 | 
			
		||||
        ui: &mut Ui,
 | 
			
		||||
        scale: f32,
 | 
			
		||||
        snarl: &mut Snarl<T>,
 | 
			
		||||
    ) -> PinInfo;
 | 
			
		||||
 | 
			
		||||
    /// Renders the node's body.
 | 
			
		||||
    fn show_body(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        node: NodeId,
 | 
			
		||||
        inputs: &[InPin],
 | 
			
		||||
        outputs: &[OutPin],
 | 
			
		||||
        ui: &mut Ui,
 | 
			
		||||
        scale: f32,
 | 
			
		||||
        snarl: &mut Snarl<T>,
 | 
			
		||||
    ) {
 | 
			
		||||
        let _ = (node, inputs, outputs, ui, scale, snarl);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Renders the node's footer.
 | 
			
		||||
    fn show_footer(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        node: NodeId,
 | 
			
		||||
        inputs: &[InPin],
 | 
			
		||||
        outputs: &[OutPin],
 | 
			
		||||
        ui: &mut Ui,
 | 
			
		||||
        scale: f32,
 | 
			
		||||
        snarl: &mut Snarl<T>,
 | 
			
		||||
    ) {
 | 
			
		||||
        let _ = (node, inputs, outputs, ui, scale, snarl);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Renders the node's on-hover popup.
 | 
			
		||||
    fn show_on_hover_popup(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        node: NodeId,
 | 
			
		||||
        inputs: &[InPin],
 | 
			
		||||
        outputs: &[OutPin],
 | 
			
		||||
        ui: &mut Ui,
 | 
			
		||||
        scale: f32,
 | 
			
		||||
        snarl: &mut Snarl<T>,
 | 
			
		||||
    ) {
 | 
			
		||||
        let _ = (node, inputs, outputs, ui, scale, snarl);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns color of the node's input pin.
 | 
			
		||||
    /// Called when pin in not visible.
 | 
			
		||||
    fn input_color(&mut self, pin: &InPin, style: &Style, snarl: &mut Snarl<T>) -> Color32;
 | 
			
		||||
 | 
			
		||||
    /// Returns color of the node's output pin.
 | 
			
		||||
    /// Called when pin in not visible.
 | 
			
		||||
    fn output_color(&mut self, pin: &OutPin, style: &Style, snarl: &mut Snarl<T>) -> Color32;
 | 
			
		||||
 | 
			
		||||
    /// Show context menu for the snarl.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This can be used to implement menu for adding new nodes.
 | 
			
		||||
    fn graph_menu(&mut self, pos: Pos2, ui: &mut Ui, scale: f32, snarl: &mut Snarl<T>) {
 | 
			
		||||
        let _ = (pos, ui, scale, snarl);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Show context menu for the snarl. This menu is opened when releasing a pin to empty
 | 
			
		||||
    /// space. It can be used to implement menu for adding new node, and directly
 | 
			
		||||
    /// connecting it to the released wire.
 | 
			
		||||
    ///
 | 
			
		||||
    ///
 | 
			
		||||
    fn graph_menu_for_dropped_wire(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        pos: Pos2,
 | 
			
		||||
        ui: &mut Ui,
 | 
			
		||||
        scale: f32,
 | 
			
		||||
        src_pins: AnyPins,
 | 
			
		||||
        snarl: &mut Snarl<T>,
 | 
			
		||||
    ) {
 | 
			
		||||
        let _ = (pos, scale, src_pins, snarl);
 | 
			
		||||
 | 
			
		||||
        // Default implementation simply doesn't utilize wire drop.
 | 
			
		||||
        ui.close_menu();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Show context menu for the snarl.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This can be used to implement menu for adding new nodes.
 | 
			
		||||
    fn node_menu(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        node: NodeId,
 | 
			
		||||
        inputs: &[InPin],
 | 
			
		||||
        outputs: &[OutPin],
 | 
			
		||||
        ui: &mut Ui,
 | 
			
		||||
        scale: f32,
 | 
			
		||||
        snarl: &mut Snarl<T>,
 | 
			
		||||
    ) {
 | 
			
		||||
        let _ = (node, inputs, outputs, ui, scale, snarl);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Asks the viewer to connect two pins.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is usually happens when user drags a wire from one node's output pin to another node's input pin or vice versa.
 | 
			
		||||
    /// By default this method connects the pins and returns `Ok(())`.
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn connect(&mut self, from: &OutPin, to: &InPin, snarl: &mut Snarl<T>) {
 | 
			
		||||
        snarl.connect(from.id, to.id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Asks the viewer to disconnect two pins.
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn disconnect(&mut self, from: &OutPin, to: &InPin, snarl: &mut Snarl<T>) {
 | 
			
		||||
        snarl.disconnect(from.id, to.id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Asks the viewer to disconnect all wires from the output pin.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is usually happens when right-clicking on an output pin.
 | 
			
		||||
    /// By default this method disconnects the pins and returns `Ok(())`.
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn drop_outputs(&mut self, pin: &OutPin, snarl: &mut Snarl<T>) {
 | 
			
		||||
        snarl.drop_outputs(pin.id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Asks the viewer to disconnect all wires from the input pin.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is usually happens when right-clicking on an input pin.
 | 
			
		||||
    /// By default this method disconnects the pins and returns `Ok(())`.
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn drop_inputs(&mut self, pin: &InPin, snarl: &mut Snarl<T>) {
 | 
			
		||||
        snarl.drop_inputs(pin.id);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										323
									
								
								vendor/egui-snarl/src/ui/wire.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										323
									
								
								vendor/egui-snarl/src/ui/wire.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,323 @@
 | 
			
		|||
use egui::{epaint::PathShape, pos2, Color32, Pos2, Rect, Shape, Stroke, Ui};
 | 
			
		||||
 | 
			
		||||
/// Layer where wires are rendered.
 | 
			
		||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 | 
			
		||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
			
		||||
#[cfg_attr(feature = "egui-probe", derive(egui_probe::EguiProbe))]
 | 
			
		||||
pub enum WireLayer {
 | 
			
		||||
    /// Wires are rendered behind nodes.
 | 
			
		||||
    /// This is default.
 | 
			
		||||
    BehindNodes,
 | 
			
		||||
 | 
			
		||||
    /// Wires are rendered above nodes.
 | 
			
		||||
    AboveNodes,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns 6th degree bezier curve for the wire
 | 
			
		||||
fn wire_bezier(
 | 
			
		||||
    mut frame_size: f32,
 | 
			
		||||
    upscale: bool,
 | 
			
		||||
    downscale: bool,
 | 
			
		||||
    from: Pos2,
 | 
			
		||||
    to: Pos2,
 | 
			
		||||
) -> [Pos2; 6] {
 | 
			
		||||
    if upscale {
 | 
			
		||||
        frame_size = frame_size.max((from - to).length() / 4.0);
 | 
			
		||||
    }
 | 
			
		||||
    if downscale {
 | 
			
		||||
        frame_size = frame_size.min((from - to).length() / 4.0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let from_norm_x = frame_size;
 | 
			
		||||
    let from_2 = pos2(from.x + from_norm_x, from.y);
 | 
			
		||||
    let to_norm_x = -from_norm_x;
 | 
			
		||||
    let to_2 = pos2(to.x + to_norm_x, to.y);
 | 
			
		||||
 | 
			
		||||
    let between = (from_2 - to_2).length();
 | 
			
		||||
 | 
			
		||||
    if from_2.x <= to_2.x && between >= frame_size * 2.0 {
 | 
			
		||||
        let middle_1 = from_2 + (to_2 - from_2).normalized() * frame_size;
 | 
			
		||||
        let middle_2 = to_2 + (from_2 - to_2).normalized() * frame_size;
 | 
			
		||||
 | 
			
		||||
        [from, from_2, middle_1, middle_2, to_2, to]
 | 
			
		||||
    } else if from_2.x <= to_2.x {
 | 
			
		||||
        let t =
 | 
			
		||||
            (between - (to_2.y - from_2.y).abs()) / (frame_size * 2.0 - (to_2.y - from_2.y).abs());
 | 
			
		||||
 | 
			
		||||
        let mut middle_1 = from_2 + (to_2 - from_2).normalized() * frame_size;
 | 
			
		||||
        let mut middle_2 = to_2 + (from_2 - to_2).normalized() * frame_size;
 | 
			
		||||
 | 
			
		||||
        if from_2.y >= to_2.y + frame_size {
 | 
			
		||||
            let u = (from_2.y - to_2.y - frame_size) / frame_size;
 | 
			
		||||
 | 
			
		||||
            let t0_middle_1 = pos2(from_2.x + (1.0 - u) * frame_size, from_2.y - frame_size * u);
 | 
			
		||||
            let t0_middle_2 = pos2(to_2.x, to_2.y + frame_size);
 | 
			
		||||
 | 
			
		||||
            middle_1 = t0_middle_1.lerp(middle_1, t);
 | 
			
		||||
            middle_2 = t0_middle_2.lerp(middle_2, t);
 | 
			
		||||
        } else if from_2.y >= to_2.y {
 | 
			
		||||
            let u = (from_2.y - to_2.y) / frame_size;
 | 
			
		||||
 | 
			
		||||
            let t0_middle_1 = pos2(from_2.x + u * frame_size, from_2.y + frame_size * (1.0 - u));
 | 
			
		||||
            let t0_middle_2 = pos2(to_2.x, to_2.y + frame_size);
 | 
			
		||||
 | 
			
		||||
            middle_1 = t0_middle_1.lerp(middle_1, t);
 | 
			
		||||
            middle_2 = t0_middle_2.lerp(middle_2, t);
 | 
			
		||||
        } else if to_2.y >= from_2.y + frame_size {
 | 
			
		||||
            let u = (to_2.y - from_2.y - frame_size) / frame_size;
 | 
			
		||||
 | 
			
		||||
            let t0_middle_1 = pos2(from_2.x, from_2.y + frame_size);
 | 
			
		||||
            let t0_middle_2 = pos2(to_2.x - (1.0 - u) * frame_size, to_2.y - frame_size * u);
 | 
			
		||||
 | 
			
		||||
            middle_1 = t0_middle_1.lerp(middle_1, t);
 | 
			
		||||
            middle_2 = t0_middle_2.lerp(middle_2, t);
 | 
			
		||||
        } else if to_2.y >= from_2.y {
 | 
			
		||||
            let u = (to_2.y - from_2.y) / frame_size;
 | 
			
		||||
 | 
			
		||||
            let t0_middle_1 = pos2(from_2.x, from_2.y + frame_size);
 | 
			
		||||
            let t0_middle_2 = pos2(to_2.x - u * frame_size, to_2.y + frame_size * (1.0 - u));
 | 
			
		||||
 | 
			
		||||
            middle_1 = t0_middle_1.lerp(middle_1, t);
 | 
			
		||||
            middle_2 = t0_middle_2.lerp(middle_2, t);
 | 
			
		||||
        } else {
 | 
			
		||||
            unreachable!();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [from, from_2, middle_1, middle_2, to_2, to]
 | 
			
		||||
    } else if from_2.y >= to_2.y + frame_size * 2.0 {
 | 
			
		||||
        let middle_1 = pos2(from_2.x, from_2.y - frame_size);
 | 
			
		||||
        let middle_2 = pos2(to_2.x, to_2.y + frame_size);
 | 
			
		||||
 | 
			
		||||
        [from, from_2, middle_1, middle_2, to_2, to]
 | 
			
		||||
    } else if from_2.y >= to_2.y + frame_size {
 | 
			
		||||
        let t = (from_2.y - to_2.y - frame_size) / frame_size;
 | 
			
		||||
 | 
			
		||||
        let middle_1 = pos2(from_2.x + (1.0 - t) * frame_size, from_2.y - frame_size * t);
 | 
			
		||||
        let middle_2 = pos2(to_2.x, to_2.y + frame_size);
 | 
			
		||||
 | 
			
		||||
        [from, from_2, middle_1, middle_2, to_2, to]
 | 
			
		||||
    } else if from_2.y >= to_2.y {
 | 
			
		||||
        let t = (from_2.y - to_2.y) / frame_size;
 | 
			
		||||
 | 
			
		||||
        let middle_1 = pos2(from_2.x + t * frame_size, from_2.y + frame_size * (1.0 - t));
 | 
			
		||||
        let middle_2 = pos2(to_2.x, to_2.y + frame_size);
 | 
			
		||||
 | 
			
		||||
        [from, from_2, middle_1, middle_2, to_2, to]
 | 
			
		||||
    } else if to_2.y >= from_2.y + frame_size * 2.0 {
 | 
			
		||||
        let middle_1 = pos2(from_2.x, from_2.y + frame_size);
 | 
			
		||||
        let middle_2 = pos2(to_2.x, to_2.y - frame_size);
 | 
			
		||||
 | 
			
		||||
        [from, from_2, middle_1, middle_2, to_2, to]
 | 
			
		||||
    } else if to_2.y >= from_2.y + frame_size {
 | 
			
		||||
        let t = (to_2.y - from_2.y - frame_size) / frame_size;
 | 
			
		||||
 | 
			
		||||
        let middle_1 = pos2(from_2.x, from_2.y + frame_size);
 | 
			
		||||
        let middle_2 = pos2(to_2.x - (1.0 - t) * frame_size, to_2.y - frame_size * t);
 | 
			
		||||
 | 
			
		||||
        [from, from_2, middle_1, middle_2, to_2, to]
 | 
			
		||||
    } else if to_2.y >= from_2.y {
 | 
			
		||||
        let t = (to_2.y - from_2.y) / frame_size;
 | 
			
		||||
 | 
			
		||||
        let middle_1 = pos2(from_2.x, from_2.y + frame_size);
 | 
			
		||||
        let middle_2 = pos2(to_2.x - t * frame_size, to_2.y + frame_size * (1.0 - t));
 | 
			
		||||
 | 
			
		||||
        [from, from_2, middle_1, middle_2, to_2, to]
 | 
			
		||||
    } else {
 | 
			
		||||
        unreachable!();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(clippy::too_many_arguments)]
 | 
			
		||||
pub fn draw_wire(
 | 
			
		||||
    ui: &mut Ui,
 | 
			
		||||
    shapes: &mut Vec<Shape>,
 | 
			
		||||
    frame_size: f32,
 | 
			
		||||
    upscale: bool,
 | 
			
		||||
    downscale: bool,
 | 
			
		||||
    from: Pos2,
 | 
			
		||||
    to: Pos2,
 | 
			
		||||
    stroke: Stroke,
 | 
			
		||||
) {
 | 
			
		||||
    let points = wire_bezier(frame_size, upscale, downscale, from, to);
 | 
			
		||||
 | 
			
		||||
    let bb = Rect::from_points(&points);
 | 
			
		||||
    if ui.is_rect_visible(bb) {
 | 
			
		||||
        draw_bezier(shapes, &points, stroke);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn hit_wire(
 | 
			
		||||
    pos: Pos2,
 | 
			
		||||
    frame_size: f32,
 | 
			
		||||
    upscale: bool,
 | 
			
		||||
    downscale: bool,
 | 
			
		||||
    from: Pos2,
 | 
			
		||||
    to: Pos2,
 | 
			
		||||
    threshold: f32,
 | 
			
		||||
) -> bool {
 | 
			
		||||
    let points = wire_bezier(frame_size, upscale, downscale, from, to);
 | 
			
		||||
    hit_bezier(pos, &points, threshold)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn bezier_reference_size(points: &[Pos2; 6]) -> f32 {
 | 
			
		||||
    let [p0, p1, p2, p3, p4, p5] = *points;
 | 
			
		||||
 | 
			
		||||
    (p1 - p0).length()
 | 
			
		||||
        + (p2 - p1).length()
 | 
			
		||||
        + (p3 - p2).length()
 | 
			
		||||
        + (p4 - p3).length()
 | 
			
		||||
        + (p5 - p4).length()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const MAX_BEZIER_SAMPLES: usize = 100;
 | 
			
		||||
 | 
			
		||||
fn bezier_samples_number(points: &[Pos2; 6], threshold: f32) -> usize {
 | 
			
		||||
    let reference_size = bezier_reference_size(points);
 | 
			
		||||
 | 
			
		||||
    #[allow(clippy::cast_sign_loss)]
 | 
			
		||||
    #[allow(clippy::cast_possible_truncation)]
 | 
			
		||||
    ((reference_size / threshold).ceil().max(0.0) as usize).min(MAX_BEZIER_SAMPLES)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn draw_bezier(shapes: &mut Vec<Shape>, points: &[Pos2; 6], mut stroke: Stroke) {
 | 
			
		||||
    if stroke.width < 1.0 {
 | 
			
		||||
        stroke.color = stroke.color.gamma_multiply(stroke.width);
 | 
			
		||||
        stroke.width = 1.0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let samples = bezier_samples_number(points, stroke.width);
 | 
			
		||||
 | 
			
		||||
    let mut path = Vec::new();
 | 
			
		||||
 | 
			
		||||
    for i in 0..samples {
 | 
			
		||||
        #[allow(clippy::cast_precision_loss)]
 | 
			
		||||
        let t = i as f32 / (samples - 1) as f32;
 | 
			
		||||
        path.push(sample_bezier(points, t));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let shape = Shape::Path(PathShape {
 | 
			
		||||
        points: path,
 | 
			
		||||
        closed: false,
 | 
			
		||||
        fill: Color32::TRANSPARENT,
 | 
			
		||||
        stroke,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    shapes.push(shape);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(clippy::let_and_return)]
 | 
			
		||||
fn sample_bezier(points: &[Pos2; 6], t: f32) -> Pos2 {
 | 
			
		||||
    let [p0, p1, p2, p3, p4, p5] = *points;
 | 
			
		||||
 | 
			
		||||
    let p0_0 = p0;
 | 
			
		||||
    let p1_0 = p1;
 | 
			
		||||
    let p2_0 = p2;
 | 
			
		||||
    let p3_0 = p3;
 | 
			
		||||
    let p4_0 = p4;
 | 
			
		||||
    let p5_0 = p5;
 | 
			
		||||
 | 
			
		||||
    let p0_1 = p0_0.lerp(p1_0, t);
 | 
			
		||||
    let p1_1 = p1_0.lerp(p2_0, t);
 | 
			
		||||
    let p2_1 = p2_0.lerp(p3_0, t);
 | 
			
		||||
    let p3_1 = p3_0.lerp(p4_0, t);
 | 
			
		||||
    let p4_1 = p4_0.lerp(p5_0, t);
 | 
			
		||||
 | 
			
		||||
    let p0_2 = p0_1.lerp(p1_1, t);
 | 
			
		||||
    let p1_2 = p1_1.lerp(p2_1, t);
 | 
			
		||||
    let p2_2 = p2_1.lerp(p3_1, t);
 | 
			
		||||
    let p3_2 = p3_1.lerp(p4_1, t);
 | 
			
		||||
 | 
			
		||||
    let p0_3 = p0_2.lerp(p1_2, t);
 | 
			
		||||
    let p1_3 = p1_2.lerp(p2_2, t);
 | 
			
		||||
    let p2_3 = p2_2.lerp(p3_2, t);
 | 
			
		||||
 | 
			
		||||
    let p0_4 = p0_3.lerp(p1_3, t);
 | 
			
		||||
    let p1_4 = p1_3.lerp(p2_3, t);
 | 
			
		||||
 | 
			
		||||
    let p0_5 = p0_4.lerp(p1_4, t);
 | 
			
		||||
 | 
			
		||||
    p0_5
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn split_bezier(points: &[Pos2; 6], t: f32) -> [[Pos2; 6]; 2] {
 | 
			
		||||
    let [p0, p1, p2, p3, p4, p5] = *points;
 | 
			
		||||
 | 
			
		||||
    let p0_0 = p0;
 | 
			
		||||
    let p1_0 = p1;
 | 
			
		||||
    let p2_0 = p2;
 | 
			
		||||
    let p3_0 = p3;
 | 
			
		||||
    let p4_0 = p4;
 | 
			
		||||
    let p5_0 = p5;
 | 
			
		||||
 | 
			
		||||
    let p0_1 = p0_0.lerp(p1_0, t);
 | 
			
		||||
    let p1_1 = p1_0.lerp(p2_0, t);
 | 
			
		||||
    let p2_1 = p2_0.lerp(p3_0, t);
 | 
			
		||||
    let p3_1 = p3_0.lerp(p4_0, t);
 | 
			
		||||
    let p4_1 = p4_0.lerp(p5_0, t);
 | 
			
		||||
 | 
			
		||||
    let p0_2 = p0_1.lerp(p1_1, t);
 | 
			
		||||
    let p1_2 = p1_1.lerp(p2_1, t);
 | 
			
		||||
    let p2_2 = p2_1.lerp(p3_1, t);
 | 
			
		||||
    let p3_2 = p3_1.lerp(p4_1, t);
 | 
			
		||||
 | 
			
		||||
    let p0_3 = p0_2.lerp(p1_2, t);
 | 
			
		||||
    let p1_3 = p1_2.lerp(p2_2, t);
 | 
			
		||||
    let p2_3 = p2_2.lerp(p3_2, t);
 | 
			
		||||
 | 
			
		||||
    let p0_4 = p0_3.lerp(p1_3, t);
 | 
			
		||||
    let p1_4 = p1_3.lerp(p2_3, t);
 | 
			
		||||
 | 
			
		||||
    let p0_5 = p0_4.lerp(p1_4, t);
 | 
			
		||||
 | 
			
		||||
    [
 | 
			
		||||
        [p0_0, p0_1, p0_2, p0_3, p0_4, p0_5],
 | 
			
		||||
        [p0_5, p1_4, p2_3, p3_2, p4_1, p5_0],
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn hit_bezier(pos: Pos2, points: &[Pos2; 6], threshold: f32) -> bool {
 | 
			
		||||
    let aabb = Rect::from_points(points);
 | 
			
		||||
 | 
			
		||||
    if pos.x + threshold < aabb.left() {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    if pos.x - threshold > aabb.right() {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    if pos.y + threshold < aabb.top() {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    if pos.y - threshold > aabb.bottom() {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let samples = bezier_samples_number(points, threshold);
 | 
			
		||||
    if samples > 16 {
 | 
			
		||||
        let [points1, points2] = split_bezier(points, 0.5);
 | 
			
		||||
 | 
			
		||||
        return hit_bezier(pos, &points1, threshold) || hit_bezier(pos, &points2, threshold);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for i in 0..samples {
 | 
			
		||||
        #[allow(clippy::cast_precision_loss)]
 | 
			
		||||
        let t = i as f32 / (samples - 1) as f32;
 | 
			
		||||
        let p = sample_bezier(points, t);
 | 
			
		||||
        if (p - pos).length() < threshold {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn mix_colors(a: Color32, b: Color32) -> Color32 {
 | 
			
		||||
    let [or, og, ob, oa] = a.to_array();
 | 
			
		||||
    let [ir, ig, ib, ia] = b.to_array();
 | 
			
		||||
 | 
			
		||||
    Color32::from_rgba_premultiplied(
 | 
			
		||||
        or / 2 + ir / 2,
 | 
			
		||||
        og / 2 + ig / 2,
 | 
			
		||||
        ob / 2 + ib / 2,
 | 
			
		||||
        oa / 2 + ia / 2,
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										192
									
								
								vendor/egui-snarl/src/ui/zoom.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								vendor/egui-snarl/src/ui/zoom.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,192 @@
 | 
			
		|||
use egui::{
 | 
			
		||||
    epaint::Shadow,
 | 
			
		||||
    style::{Interaction, ScrollStyle, Spacing, WidgetVisuals, Widgets},
 | 
			
		||||
    FontId, Frame, Margin, Rounding, Stroke, Style, Vec2, Visuals,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub trait Zoom {
 | 
			
		||||
    fn zoomed(mut self, zoom: f32) -> Self
 | 
			
		||||
    where
 | 
			
		||||
        Self: Copy,
 | 
			
		||||
    {
 | 
			
		||||
        self.zoom(zoom);
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn zoom(&mut self, zoom: f32);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Zoom for f32 {
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn zoom(&mut self, zoom: f32) {
 | 
			
		||||
        *self *= zoom;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Zoom for Vec2 {
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn zoom(&mut self, zoom: f32) {
 | 
			
		||||
        *self *= zoom;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Zoom for Rounding {
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn zoom(&mut self, zoom: f32) {
 | 
			
		||||
        self.nw.zoom(zoom);
 | 
			
		||||
        self.ne.zoom(zoom);
 | 
			
		||||
        self.se.zoom(zoom);
 | 
			
		||||
        self.sw.zoom(zoom);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Zoom for Margin {
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn zoom(&mut self, zoom: f32) {
 | 
			
		||||
        self.left.zoom(zoom);
 | 
			
		||||
        self.right.zoom(zoom);
 | 
			
		||||
        self.top.zoom(zoom);
 | 
			
		||||
        self.bottom.zoom(zoom);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Zoom for Shadow {
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn zoom(&mut self, zoom: f32) {
 | 
			
		||||
        self.spread = zoom;
 | 
			
		||||
        // self.extrusion.zoom(zoom);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Zoom for Stroke {
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn zoom(&mut self, zoom: f32) {
 | 
			
		||||
        self.width *= zoom;
 | 
			
		||||
        if self.width < 1.0 {
 | 
			
		||||
            self.color.gamma_multiply(self.width);
 | 
			
		||||
            self.width = 1.0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Zoom for WidgetVisuals {
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn zoom(&mut self, zoom: f32) {
 | 
			
		||||
        self.bg_stroke.zoom(zoom);
 | 
			
		||||
        self.rounding.zoom(zoom);
 | 
			
		||||
        self.fg_stroke.zoom(zoom);
 | 
			
		||||
        self.expansion.zoom(zoom);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Zoom for Interaction {
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn zoom(&mut self, zoom: f32) {
 | 
			
		||||
        self.resize_grab_radius_corner.zoom(zoom);
 | 
			
		||||
        self.resize_grab_radius_side.zoom(zoom);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Zoom for Widgets {
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn zoom(&mut self, zoom: f32) {
 | 
			
		||||
        self.noninteractive.zoom(zoom);
 | 
			
		||||
        self.inactive.zoom(zoom);
 | 
			
		||||
        self.hovered.zoom(zoom);
 | 
			
		||||
        self.active.zoom(zoom);
 | 
			
		||||
        self.open.zoom(zoom);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Zoom for Visuals {
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn zoom(&mut self, zoom: f32) {
 | 
			
		||||
        self.clip_rect_margin.zoom(zoom);
 | 
			
		||||
        self.menu_rounding.zoom(zoom);
 | 
			
		||||
        self.popup_shadow.zoom(zoom);
 | 
			
		||||
        self.resize_corner_size.zoom(zoom);
 | 
			
		||||
        self.selection.stroke.zoom(zoom);
 | 
			
		||||
        self.text_cursor.zoom(zoom);
 | 
			
		||||
        self.widgets.zoom(zoom);
 | 
			
		||||
        self.window_rounding.zoom(zoom);
 | 
			
		||||
        self.window_shadow.zoom(zoom);
 | 
			
		||||
        self.window_stroke.zoom(zoom);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Zoom for ScrollStyle {
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn zoom(&mut self, zoom: f32) {
 | 
			
		||||
        self.bar_inner_margin.zoom(zoom);
 | 
			
		||||
        self.bar_outer_margin.zoom(zoom);
 | 
			
		||||
        self.bar_width.zoom(zoom);
 | 
			
		||||
        self.floating_allocated_width.zoom(zoom);
 | 
			
		||||
        self.floating_width.zoom(zoom);
 | 
			
		||||
        self.handle_min_length.zoom(zoom);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Zoom for Spacing {
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn zoom(&mut self, zoom: f32) {
 | 
			
		||||
        self.button_padding.zoom(zoom);
 | 
			
		||||
        self.combo_height.zoom(zoom);
 | 
			
		||||
        self.combo_width.zoom(zoom);
 | 
			
		||||
        self.icon_spacing.zoom(zoom);
 | 
			
		||||
        self.icon_width.zoom(zoom);
 | 
			
		||||
        self.icon_width_inner.zoom(zoom);
 | 
			
		||||
        self.indent.zoom(zoom);
 | 
			
		||||
        self.interact_size.zoom(zoom);
 | 
			
		||||
        self.item_spacing.zoom(zoom);
 | 
			
		||||
        self.menu_margin.zoom(zoom);
 | 
			
		||||
        self.scroll.zoom(zoom);
 | 
			
		||||
        self.slider_width.zoom(zoom);
 | 
			
		||||
        self.text_edit_width.zoom(zoom);
 | 
			
		||||
        self.tooltip_width.zoom(zoom);
 | 
			
		||||
        self.window_margin.zoom(zoom);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Zoom for FontId {
 | 
			
		||||
    fn zoom(&mut self, zoom: f32) {
 | 
			
		||||
        self.size.zoom(zoom);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Zoom for Style {
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn zoom(&mut self, zoom: f32) {
 | 
			
		||||
        if let Some(font_id) = &mut self.override_font_id {
 | 
			
		||||
            font_id.zoom(zoom);
 | 
			
		||||
        }
 | 
			
		||||
        for font_id in self.text_styles.values_mut() {
 | 
			
		||||
            font_id.zoom(zoom);
 | 
			
		||||
        }
 | 
			
		||||
        self.interaction.zoom(zoom);
 | 
			
		||||
        self.spacing.zoom(zoom);
 | 
			
		||||
        self.visuals.zoom(zoom);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> Zoom for Option<T>
 | 
			
		||||
where
 | 
			
		||||
    T: Zoom,
 | 
			
		||||
{
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn zoom(&mut self, zoom: f32) {
 | 
			
		||||
        if let Some(value) = self {
 | 
			
		||||
            value.zoom(zoom)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Zoom for Frame {
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn zoom(&mut self, zoom: f32) {
 | 
			
		||||
        self.inner_margin.zoom(zoom);
 | 
			
		||||
        self.outer_margin.zoom(zoom);
 | 
			
		||||
        self.rounding.zoom(zoom);
 | 
			
		||||
        self.shadow.zoom(zoom);
 | 
			
		||||
        self.stroke.zoom(zoom);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue