1 | use crate::{rgb::Rgb, Color}; |
2 | |
3 | /// Linear color gradient between two color stops |
4 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
5 | pub struct Gradient { |
6 | /// Start Color of Gradient |
7 | pub start: Rgb, |
8 | |
9 | /// End Color of Gradient |
10 | pub end: Rgb, |
11 | } |
12 | |
13 | impl Gradient { |
14 | /// Creates a new [Gradient] with two [Rgb] colors, `start` and `end` |
15 | #[inline ] |
16 | pub const fn new(start: Rgb, end: Rgb) -> Self { |
17 | Self { start, end } |
18 | } |
19 | pub const fn from_color_rgb(start: Color, end: Color) -> Self { |
20 | let start_grad = match start { |
21 | Color::Rgb(r, g, b) => Rgb { r, g, b }, |
22 | _ => Rgb { r: 0, g: 0, b: 0 }, |
23 | }; |
24 | let end_grad = match end { |
25 | Color::Rgb(r, g, b) => Rgb { r, g, b }, |
26 | _ => Rgb { r: 0, g: 0, b: 0 }, |
27 | }; |
28 | |
29 | Self { |
30 | start: start_grad, |
31 | end: end_grad, |
32 | } |
33 | } |
34 | |
35 | /// Computes the [Rgb] color between `start` and `end` for `t` |
36 | pub fn at(&self, t: f32) -> Rgb { |
37 | self.start.lerp(self.end, t) |
38 | } |
39 | |
40 | /// Returns the reverse of `self` |
41 | #[inline ] |
42 | pub const fn reverse(&self) -> Self { |
43 | Self::new(self.end, self.start) |
44 | } |
45 | |
46 | #[allow (dead_code)] |
47 | pub fn build(&self, text: &str, target: TargetGround) -> String { |
48 | let delta = 1.0 / text.len() as f32; |
49 | let mut result = text.char_indices().fold(String::new(), |mut acc, (i, c)| { |
50 | let temp = format!( |
51 | " \x1B[{}m{}" , |
52 | self.at(i as f32 * delta).ansi_color_code(target), |
53 | c |
54 | ); |
55 | acc.push_str(&temp); |
56 | acc |
57 | }); |
58 | |
59 | result.push_str(" \x1B[0m" ); |
60 | result |
61 | } |
62 | } |
63 | |
64 | #[allow (dead_code)] |
65 | pub fn build_all_gradient_text(text: &str, foreground: Gradient, background: Gradient) -> String { |
66 | let delta = 1.0 / text.len() as f32; |
67 | let mut result = text.char_indices().fold(String::new(), |mut acc, (i, c)| { |
68 | let step = i as f32 * delta; |
69 | let temp = format!( |
70 | " \x1B[{};{}m{}" , |
71 | foreground |
72 | .at(step) |
73 | .ansi_color_code(TargetGround::Foreground), |
74 | background |
75 | .at(step) |
76 | .ansi_color_code(TargetGround::Background), |
77 | c |
78 | ); |
79 | acc.push_str(&temp); |
80 | acc |
81 | }); |
82 | |
83 | result.push_str(" \x1B[0m" ); |
84 | result |
85 | } |
86 | |
87 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
88 | pub enum TargetGround { |
89 | Foreground, |
90 | Background, |
91 | } |
92 | |
93 | impl TargetGround { |
94 | #[inline ] |
95 | pub const fn code(&self) -> u8 { |
96 | match self { |
97 | Self::Foreground => 30, |
98 | Self::Background => 40, |
99 | } |
100 | } |
101 | } |
102 | |
103 | pub trait ANSIColorCode { |
104 | fn ansi_color_code(&self, target: TargetGround) -> String; |
105 | } |
106 | |