egui_treeize/ui/
background_pattern.rs

1use egui::{Painter, Rect, Style, Vec2, emath::Rot2, vec2};
2
3use super::TreeizeStyle;
4
5///Grid background pattern.
6///Use `TreeizeStyle::background_pattern_stroke` for change stroke options
7#[derive(Clone, Copy, Debug, PartialEq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9#[cfg_attr(feature = "egui-probe", derive(egui_probe::EguiProbe))]
10pub struct Grid {
11  /// Spacing between grid lines.
12  pub spacing: Vec2,
13
14  /// Angle of the grid.
15  #[cfg_attr(feature = "egui-probe", egui_probe(as egui_probe::angle))]
16  pub angle: f32,
17}
18
19const DEFAULT_GRID_SPACING: Vec2 = vec2(50.0, 50.0);
20macro_rules! default_grid_spacing {
21  () => {
22    stringify!(vec2(50.0, 50.0))
23  };
24}
25
26const DEFAULT_GRID_ANGLE: f32 = 1.0;
27macro_rules! default_grid_angle {
28  () => {
29    stringify!(1.0)
30  };
31}
32
33impl Default for Grid {
34  fn default() -> Self {
35    Self { spacing: DEFAULT_GRID_SPACING, angle: DEFAULT_GRID_ANGLE }
36  }
37}
38
39impl Grid {
40  /// Create new grid with given spacing and angle.
41  #[must_use]
42  pub const fn new(spacing: Vec2, angle: f32) -> Self {
43    Self { spacing, angle }
44  }
45
46  fn draw(&self, viewport: &Rect, treeize_style: &TreeizeStyle, style: &Style, painter: &Painter) {
47    let bg_stroke = treeize_style.get_bg_pattern_stroke(style);
48
49    let spacing = vec2(self.spacing.x.max(1.0), self.spacing.y.max(1.0));
50
51    let rot = Rot2::from_angle(self.angle);
52    let rot_inv = rot.inverse();
53
54    let pattern_bounds = viewport.rotate_bb(rot_inv);
55
56    let min_x = (pattern_bounds.min.x / spacing.x).ceil();
57    let max_x = (pattern_bounds.max.x / spacing.x).floor();
58
59    #[allow(clippy::cast_possible_truncation)]
60    for x in 0..=f32::ceil(max_x - min_x) as i64 {
61      #[allow(clippy::cast_precision_loss)]
62      let x = (x as f32 + min_x) * spacing.x;
63
64      let top = (rot * vec2(x, pattern_bounds.min.y)).to_pos2();
65      let bottom = (rot * vec2(x, pattern_bounds.max.y)).to_pos2();
66
67      painter.line_segment([top, bottom], bg_stroke);
68    }
69
70    let min_y = (pattern_bounds.min.y / spacing.y).ceil();
71    let max_y = (pattern_bounds.max.y / spacing.y).floor();
72
73    #[allow(clippy::cast_possible_truncation)]
74    for y in 0..=f32::ceil(max_y - min_y) as i64 {
75      #[allow(clippy::cast_precision_loss)]
76      let y = (y as f32 + min_y) * spacing.y;
77
78      let top = (rot * vec2(pattern_bounds.min.x, y)).to_pos2();
79      let bottom = (rot * vec2(pattern_bounds.max.x, y)).to_pos2();
80
81      painter.line_segment([top, bottom], bg_stroke);
82    }
83  }
84}
85
86/// Background pattern show beneath nodes and wires.
87#[derive(Clone, Copy, Debug, PartialEq)]
88#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
89#[cfg_attr(feature = "egui-probe", derive(egui_probe::EguiProbe))]
90pub enum BackgroundPattern {
91  /// No pattern.
92  NoPattern,
93
94  /// Linear grid.
95  #[cfg_attr(feature = "egui-probe", egui_probe(transparent))]
96  Grid(Grid),
97}
98
99impl Default for BackgroundPattern {
100  fn default() -> Self {
101    BackgroundPattern::new()
102  }
103}
104
105impl BackgroundPattern {
106  /// Create new background pattern with default values.
107  ///
108  /// Default patter is `Grid` with spacing - `
109  #[doc = default_grid_spacing!()]
110  /// ` and angle - `
111  #[doc = default_grid_angle!()]
112  /// ` radian.
113  #[must_use]
114  pub const fn new() -> Self {
115    Self::Grid(Grid::new(DEFAULT_GRID_SPACING, DEFAULT_GRID_ANGLE))
116  }
117
118  /// Create new grid background pattern with given spacing and angle.
119  #[must_use]
120  pub const fn grid(spacing: Vec2, angle: f32) -> Self {
121    Self::Grid(Grid::new(spacing, angle))
122  }
123
124  /// Draws background pattern.
125  pub fn draw(
126    &self,
127    viewport: &Rect,
128    treeize_style: &TreeizeStyle,
129    style: &Style,
130    painter: &Painter,
131  ) {
132    match self {
133      BackgroundPattern::Grid(g) => g.draw(viewport, treeize_style, style, painter),
134      BackgroundPattern::NoPattern => {}
135    }
136  }
137}