egui_treeize/ui/
pin.rs

1use egui::{Color32, Painter, Rect, Shape, Stroke, Style, Vec2, epaint::PathShape, pos2, vec2};
2
3use crate::{InPinId, OutPinId};
4
5use super::{TreeizeStyle, 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 TreeizePin {
38  /// Calculates pin Rect from the given parameters.
39  fn pin_rect(&self, x0: f32, x1: f32, y: f32, size: f32) -> Rect {
40    // Center vertically by default.
41    let x = (x0 + x1) * 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    treeize_style: &TreeizeStyle,
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 `TreeizeViewer::show_input` and `TreeizeViewer::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 { shape: Some(PinShape::Circle), ..Default::default() }
148  }
149
150  /// Creates a triangle pin.
151  #[must_use]
152  pub fn triangle() -> Self {
153    PinInfo { shape: Some(PinShape::Triangle), ..Default::default() }
154  }
155
156  /// Creates a square pin.
157  #[must_use]
158  pub fn square() -> Self {
159    PinInfo { shape: Some(PinShape::Square), ..Default::default() }
160  }
161
162  /// Creates a star pin.
163  #[must_use]
164  pub fn star() -> Self {
165    PinInfo { shape: Some(PinShape::Star), ..Default::default() }
166  }
167
168  /// Returns the shape of the pin.
169  #[must_use]
170  pub fn get_shape(&self, treeize_style: &TreeizeStyle) -> PinShape {
171    self.shape.unwrap_or_else(|| treeize_style.get_pin_shape())
172  }
173
174  /// Returns fill color of the pin.
175  #[must_use]
176  pub fn get_fill(&self, treeize_style: &TreeizeStyle, style: &Style) -> Color32 {
177    self.fill.unwrap_or_else(|| treeize_style.get_pin_fill(style))
178  }
179
180  /// Returns outline stroke of the pin.
181  #[must_use]
182  pub fn get_stroke(&self, treeize_style: &TreeizeStyle, style: &Style) -> Stroke {
183    self.stroke.unwrap_or_else(|| treeize_style.get_pin_stroke(style))
184  }
185
186  /// Draws the pin and returns color.
187  ///
188  /// Wires are drawn with returned color by default.
189  #[must_use]
190  pub fn draw(
191    &self,
192    treeize_style: &TreeizeStyle,
193    style: &Style,
194    rect: Rect,
195    painter: &Painter,
196  ) -> PinWireInfo {
197    let shape = self.get_shape(treeize_style);
198    let fill = self.get_fill(treeize_style, style);
199    let stroke = self.get_stroke(treeize_style, style);
200    draw_pin(painter, shape, fill, stroke, rect);
201
202    PinWireInfo {
203      color: self.wire_color.unwrap_or(fill),
204      style: self.wire_style.unwrap_or_else(|| treeize_style.get_wire_style()),
205    }
206  }
207}
208
209impl TreeizePin for PinInfo {
210  fn draw(
211    self,
212    treeize_style: &TreeizeStyle,
213    style: &Style,
214    rect: Rect,
215    painter: &Painter,
216  ) -> PinWireInfo {
217    Self::draw(&self, treeize_style, style, rect, painter)
218  }
219}
220
221pub fn draw_pin(painter: &Painter, shape: PinShape, fill: Color32, stroke: Stroke, rect: Rect) {
222  let center = rect.center();
223  let size = f32::min(rect.width(), rect.height());
224
225  match shape {
226    PinShape::Circle => {
227      painter.circle(center, size / 2.0, fill, stroke);
228    }
229    PinShape::Triangle => {
230      const A: Vec2 = vec2(-0.649_519, 0.4875);
231      const B: Vec2 = vec2(0.649_519, 0.4875);
232      const C: Vec2 = vec2(0.0, -0.6375);
233
234      let points = vec![center + A * size, center + B * size, center + C * size];
235
236      painter.add(Shape::Path(PathShape { points, closed: true, fill, stroke: stroke.into() }));
237    }
238    PinShape::Square => {
239      let points = vec![
240        center + vec2(-0.5, -0.5) * size,
241        center + vec2(0.5, -0.5) * size,
242        center + vec2(0.5, 0.5) * size,
243        center + vec2(-0.5, 0.5) * size,
244      ];
245
246      painter.add(Shape::Path(PathShape { points, closed: true, fill, stroke: stroke.into() }));
247    }
248
249    PinShape::Star => {
250      let points = vec![
251        center + size * 0.700_000 * vec2(0.0, -1.0),
252        center + size * 0.267_376 * vec2(-0.587_785, -0.809_017),
253        center + size * 0.700_000 * vec2(-0.951_057, -0.309_017),
254        center + size * 0.267_376 * vec2(-0.951_057, 0.309_017),
255        center + size * 0.700_000 * vec2(-0.587_785, 0.809_017),
256        center + size * 0.267_376 * vec2(0.0, 1.0),
257        center + size * 0.700_000 * vec2(0.587_785, 0.809_017),
258        center + size * 0.267_376 * vec2(0.951_057, 0.309_017),
259        center + size * 0.700_000 * vec2(0.951_057, -0.309_017),
260        center + size * 0.267_376 * vec2(0.587_785, -0.809_017),
261      ];
262
263      painter.add(Shape::Path(PathShape { points, closed: true, fill, stroke: stroke.into() }));
264    }
265  }
266}