tachyonfx/pattern/
diagonal.rs1#[cfg(feature = "dsl")]
2use compact_str::{format_compact, CompactString, ToCompactString};
3use ratatui::layout::{Position, Rect};
4
5#[cfg(feature = "dsl")]
6use crate::dsl::DslFormat;
7use crate::{
8 math,
9 pattern::{InstancedPattern, Pattern, PreparedPattern, TransitionProgress},
10};
11
12#[derive(Clone, Debug, Copy, PartialEq)]
13pub struct DiagonalPattern {
14 direction: DiagonalDirection,
15 transition_width: f32,
16}
17
18#[derive(Clone, Debug, Copy, PartialEq)]
20pub enum DiagonalDirection {
21 TopLeftToBottomRight,
23 TopRightToBottomLeft,
25 BottomLeftToTopRight,
27 BottomRightToTopLeft,
29}
30
31impl DiagonalPattern {
32 pub fn top_left_to_bottom_right() -> Self {
33 Self {
34 direction: DiagonalDirection::TopLeftToBottomRight,
35 transition_width: 2.0, }
37 }
38
39 pub fn top_right_to_bottom_left() -> Self {
40 Self {
41 direction: DiagonalDirection::TopRightToBottomLeft,
42 transition_width: 2.0, }
44 }
45
46 pub fn bottom_left_to_top_right() -> Self {
47 Self {
48 direction: DiagonalDirection::BottomLeftToTopRight,
49 transition_width: 2.0, }
51 }
52
53 pub fn bottom_right_to_top_left() -> Self {
54 Self {
55 direction: DiagonalDirection::BottomRightToTopLeft,
56 transition_width: 2.0, }
58 }
59
60 pub fn new(direction: DiagonalDirection, transition_width: f32) -> Self {
67 Self {
68 direction,
69 transition_width: transition_width.max(0.1),
70 }
71 }
72
73 pub fn with_transition_width(mut self, width: f32) -> Self {
78 self.transition_width = width.max(0.1);
79 self
80 }
81}
82
83impl Pattern for DiagonalPattern {
84 type Context = (f32, Rect);
85
86 fn for_frame(self, alpha: f32, area: Rect) -> PreparedPattern<Self::Context, Self>
87 where
88 Self: Sized,
89 {
90 PreparedPattern { pattern: self, context: (alpha, area) }
91 }
92}
93
94impl InstancedPattern for PreparedPattern<(f32, Rect), DiagonalPattern> {
95 fn map_alpha(&mut self, pos: Position) -> f32 {
96 let pattern = &self.pattern;
97 let (global_alpha, area) = self.context;
98
99 let norm_x = (pos.x - area.x) as f32 / area.width as f32;
101 let norm_y = (pos.y - area.y) as f32 / area.height as f32;
102
103 use DiagonalDirection::*;
105 let diagonal_progress = match pattern.direction {
106 TopLeftToBottomRight => (norm_x + norm_y) / 2.0,
107 TopRightToBottomLeft => ((1.0 - norm_x) + norm_y) / 2.0,
108 BottomLeftToTopRight => (norm_x + (1.0 - norm_y)) / 2.0,
109 BottomRightToTopLeft => ((1.0 - norm_x) + (1.0 - norm_y)) / 2.0,
110 };
111
112 let diagonal_length =
115 math::sqrt(math::powi(area.width as f32, 2) + math::powi(area.height as f32, 2));
116 let normalized_transition_width = pattern.transition_width / diagonal_length;
117 TransitionProgress::from(normalized_transition_width).map_spatial(
118 global_alpha,
119 diagonal_progress,
120 1.0,
121 )
122 }
123}
124
125#[cfg(feature = "dsl")]
126impl DslFormat for DiagonalDirection {
127 fn dsl_format(&self) -> CompactString {
128 match self {
129 DiagonalDirection::TopLeftToBottomRight => {
130 "DiagonalDirection::TopLeftToBottomRight".to_compact_string()
131 },
132 DiagonalDirection::TopRightToBottomLeft => {
133 "DiagonalDirection::TopRightToBottomLeft".to_compact_string()
134 },
135 DiagonalDirection::BottomLeftToTopRight => {
136 "DiagonalDirection::BottomLeftToTopRight".to_compact_string()
137 },
138 DiagonalDirection::BottomRightToTopLeft => {
139 "DiagonalDirection::BottomRightToTopLeft".to_compact_string()
140 },
141 }
142 }
143}
144
145#[cfg(feature = "dsl")]
146impl DslFormat for DiagonalPattern {
147 fn dsl_format(&self) -> CompactString {
148 if (self.transition_width - 2.0).abs() < f32::EPSILON {
149 match self.direction {
151 DiagonalDirection::TopLeftToBottomRight => {
152 "DiagonalPattern::top_left_to_bottom_right()".to_compact_string()
153 },
154 DiagonalDirection::TopRightToBottomLeft => {
155 "DiagonalPattern::top_right_to_bottom_left()".to_compact_string()
156 },
157 DiagonalDirection::BottomLeftToTopRight => {
158 "DiagonalPattern::bottom_left_to_top_right()".to_compact_string()
159 },
160 DiagonalDirection::BottomRightToTopLeft => {
161 "DiagonalPattern::bottom_right_to_top_left()".to_compact_string()
162 },
163 }
164 } else {
165 let base = match self.direction {
167 DiagonalDirection::TopLeftToBottomRight => {
168 "DiagonalPattern::top_left_to_bottom_right()"
169 },
170 DiagonalDirection::TopRightToBottomLeft => {
171 "DiagonalPattern::top_right_to_bottom_left()"
172 },
173 DiagonalDirection::BottomLeftToTopRight => {
174 "DiagonalPattern::bottom_left_to_top_right()"
175 },
176 DiagonalDirection::BottomRightToTopLeft => {
177 "DiagonalPattern::bottom_right_to_top_left()"
178 },
179 };
180 if self.transition_width.fract() == 0.0 {
181 format_compact!(
182 "{}.with_transition_width({})",
183 base,
184 self.transition_width as u32
185 )
186 } else {
187 format_compact!("{}.with_transition_width({})", base, self.transition_width)
188 }
189 }
190 }
191}