egui_snarl/ui/
pin.rs

1use egui::{Color32, Painter, Rect, Shape, Stroke, Style, Vec2, epaint::PathShape, pos2, vec2};
2
3use crate::{InPinId, OutPinId};
4
5use super::{SnarlStyle, WireStyle};
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
8pub enum AnyPin {
9    Out(OutPinId),
10    In(InPinId),
11}
12
13/// In the current context, these are the I/O pins of the 'source' node that the newly
14/// created node's I/O pins will connect to.
15#[derive(Debug)]
16pub enum AnyPins<'a> {
17    /// Output pins.
18    Out(&'a [OutPinId]),
19    /// Input pins
20    In(&'a [InPinId]),
21}
22
23/// Contains information about a pin's wire.
24/// Used to draw the wire.
25/// When two pins are connected, the wire is drawn between them,
26/// using merged `PinWireInfo` from both pins.
27pub struct PinWireInfo {
28    /// Desired color of the wire.
29    pub color: Color32,
30
31    /// Desired style of the wire.
32    /// Zoomed with current scale.
33    pub style: WireStyle,
34}
35
36/// Uses `Painter` to draw a pin.
37pub trait SnarlPin {
38    /// Calculates pin Rect from the given parameters.
39    fn pin_rect(&self, x: f32, y0: f32, y1: f32, size: f32) -> Rect {
40        // Center vertically by default.
41        let y = (y0 + y1) * 0.5;
42        let pin_pos = pos2(x, y);
43        Rect::from_center_size(pin_pos, vec2(size, size))
44    }
45
46    /// Draws the pin.
47    ///
48    /// `rect` is the interaction rectangle of the pin.
49    /// Pin should fit in it.
50    /// `painter` is used to add pin's shapes to the UI.
51    ///
52    /// Returns the color
53    #[must_use]
54    fn draw(
55        self,
56        snarl_style: &SnarlStyle,
57        style: &Style,
58        rect: Rect,
59        painter: &Painter,
60    ) -> PinWireInfo;
61}
62
63/// Shape of a pin.
64#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
65#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
66#[cfg_attr(feature = "egui-probe", derive(egui_probe::EguiProbe))]
67pub enum PinShape {
68    /// Circle shape.
69    #[default]
70    Circle,
71
72    /// Triangle shape.
73    Triangle,
74
75    /// Square shape.
76    Square,
77
78    /// Star shape.
79    Star,
80}
81
82/// Information about a pin returned by `SnarlViewer::show_input` and `SnarlViewer::show_output`.
83///
84/// All fields are optional.
85/// If a field is `None`, the default value is used derived from the graph style.
86#[derive(Default)]
87pub struct PinInfo {
88    /// Shape of the pin.
89    pub shape: Option<PinShape>,
90
91    /// Fill color of the pin.
92    pub fill: Option<Color32>,
93
94    /// Outline stroke of the pin.
95    pub stroke: Option<Stroke>,
96
97    /// Color of the wire connected to the pin.
98    /// If `None`, the pin's fill color is used.
99    pub wire_color: Option<Color32>,
100
101    /// Style of the wire connected to the pin.
102    pub wire_style: Option<WireStyle>,
103
104    /// Custom vertical position of a pin
105    pub position: Option<f32>,
106}
107
108impl PinInfo {
109    /// Sets the shape of the pin.
110    #[must_use]
111    pub const fn with_shape(mut self, shape: PinShape) -> Self {
112        self.shape = Some(shape);
113        self
114    }
115
116    /// Sets the fill color of the pin.
117    #[must_use]
118    pub const fn with_fill(mut self, fill: Color32) -> Self {
119        self.fill = Some(fill);
120        self
121    }
122
123    /// Sets the outline stroke of the pin.
124    #[must_use]
125    pub const fn with_stroke(mut self, stroke: Stroke) -> Self {
126        self.stroke = Some(stroke);
127        self
128    }
129
130    /// Sets the style of the wire connected to the pin.
131    #[must_use]
132    pub const fn with_wire_style(mut self, wire_style: WireStyle) -> Self {
133        self.wire_style = Some(wire_style);
134        self
135    }
136
137    /// Sets the color of the wire connected to the pin.
138    #[must_use]
139    pub const fn with_wire_color(mut self, wire_color: Color32) -> Self {
140        self.wire_color = Some(wire_color);
141        self
142    }
143
144    /// Creates a circle pin.
145    #[must_use]
146    pub fn circle() -> Self {
147        PinInfo {
148            shape: Some(PinShape::Circle),
149            ..Default::default()
150        }
151    }
152
153    /// Creates a triangle pin.
154    #[must_use]
155    pub fn triangle() -> Self {
156        PinInfo {
157            shape: Some(PinShape::Triangle),
158            ..Default::default()
159        }
160    }
161
162    /// Creates a square pin.
163    #[must_use]
164    pub fn square() -> Self {
165        PinInfo {
166            shape: Some(PinShape::Square),
167            ..Default::default()
168        }
169    }
170
171    /// Creates a star pin.
172    #[must_use]
173    pub fn star() -> Self {
174        PinInfo {
175            shape: Some(PinShape::Star),
176            ..Default::default()
177        }
178    }
179
180    /// Returns the shape of the pin.
181    #[must_use]
182    pub fn get_shape(&self, snarl_style: &SnarlStyle) -> PinShape {
183        self.shape.unwrap_or_else(|| snarl_style.get_pin_shape())
184    }
185
186    /// Returns fill color of the pin.
187    #[must_use]
188    pub fn get_fill(&self, snarl_style: &SnarlStyle, style: &Style) -> Color32 {
189        self.fill.unwrap_or_else(|| snarl_style.get_pin_fill(style))
190    }
191
192    /// Returns outline stroke of the pin.
193    #[must_use]
194    pub fn get_stroke(&self, snarl_style: &SnarlStyle, style: &Style) -> Stroke {
195        self.stroke
196            .unwrap_or_else(|| snarl_style.get_pin_stroke(style))
197    }
198
199    /// Draws the pin and returns color.
200    ///
201    /// Wires are drawn with returned color by default.
202    #[must_use]
203    pub fn draw(
204        &self,
205        snarl_style: &SnarlStyle,
206        style: &Style,
207        rect: Rect,
208        painter: &Painter,
209    ) -> PinWireInfo {
210        let shape = self.get_shape(snarl_style);
211        let fill = self.get_fill(snarl_style, style);
212        let stroke = self.get_stroke(snarl_style, style);
213        draw_pin(painter, shape, fill, stroke, rect);
214
215        PinWireInfo {
216            color: self.wire_color.unwrap_or(fill),
217            style: self
218                .wire_style
219                .unwrap_or_else(|| snarl_style.get_wire_style()),
220        }
221    }
222}
223
224impl SnarlPin for PinInfo {
225    fn draw(
226        self,
227        snarl_style: &SnarlStyle,
228        style: &Style,
229        rect: Rect,
230        painter: &Painter,
231    ) -> PinWireInfo {
232        Self::draw(&self, snarl_style, style, rect, painter)
233    }
234}
235
236pub fn draw_pin(painter: &Painter, shape: PinShape, fill: Color32, stroke: Stroke, rect: Rect) {
237    let center = rect.center();
238    let size = f32::min(rect.width(), rect.height());
239
240    match shape {
241        PinShape::Circle => {
242            painter.circle(center, size / 2.0, fill, stroke);
243        }
244        PinShape::Triangle => {
245            const A: Vec2 = vec2(-0.649_519, 0.4875);
246            const B: Vec2 = vec2(0.649_519, 0.4875);
247            const C: Vec2 = vec2(0.0, -0.6375);
248
249            let points = vec![center + A * size, center + B * size, center + C * size];
250
251            painter.add(Shape::Path(PathShape {
252                points,
253                closed: true,
254                fill,
255                stroke: stroke.into(),
256            }));
257        }
258        PinShape::Square => {
259            let points = vec![
260                center + vec2(-0.5, -0.5) * size,
261                center + vec2(0.5, -0.5) * size,
262                center + vec2(0.5, 0.5) * size,
263                center + vec2(-0.5, 0.5) * size,
264            ];
265
266            painter.add(Shape::Path(PathShape {
267                points,
268                closed: true,
269                fill,
270                stroke: stroke.into(),
271            }));
272        }
273
274        PinShape::Star => {
275            let points = vec![
276                center + size * 0.700_000 * vec2(0.0, -1.0),
277                center + size * 0.267_376 * vec2(-0.587_785, -0.809_017),
278                center + size * 0.700_000 * vec2(-0.951_057, -0.309_017),
279                center + size * 0.267_376 * vec2(-0.951_057, 0.309_017),
280                center + size * 0.700_000 * vec2(-0.587_785, 0.809_017),
281                center + size * 0.267_376 * vec2(0.0, 1.0),
282                center + size * 0.700_000 * vec2(0.587_785, 0.809_017),
283                center + size * 0.267_376 * vec2(0.951_057, 0.309_017),
284                center + size * 0.700_000 * vec2(0.951_057, -0.309_017),
285                center + size * 0.267_376 * vec2(0.587_785, -0.809_017),
286            ];
287
288            painter.add(Shape::Path(PathShape {
289                points,
290                closed: true,
291                fill,
292                stroke: stroke.into(),
293            }));
294        }
295    }
296}