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 | |
20 | pub const fn from_color_rgb(start: Color, end: Color) -> Self { |
21 | let start_grad = match start { |
22 | Color::Rgb(r, g, b) => Rgb { r, g, b }, |
23 | _ => Rgb { r: 0, g: 0, b: 0 }, |
24 | }; |
25 | let end_grad = match end { |
26 | Color::Rgb(r, g, b) => Rgb { r, g, b }, |
27 | _ => Rgb { r: 0, g: 0, b: 0 }, |
28 | }; |
29 | |
30 | Self { |
31 | start: start_grad, |
32 | end: end_grad, |
33 | } |
34 | } |
35 | |
36 | /// Computes the [Rgb] color between `start` and `end` for `t` |
37 | pub fn at(&self, t: f32) -> Rgb { |
38 | self.start.lerp(self.end, t) |
39 | } |
40 | |
41 | /// Returns the reverse of `self` |
42 | #[inline ] |
43 | pub const fn reverse(&self) -> Self { |
44 | Self::new(self.end, self.start) |
45 | } |
46 | |
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 | pub fn build_all_gradient_text(text: &str, foreground: Gradient, background: Gradient) -> String { |
65 | let delta: f32 = 1.0 / text.len() as f32; |
66 | let mut result: String = text.char_indices().fold(init:String::new(), |mut acc: String, (i: usize, c: char)| { |
67 | let step: f32 = i as f32 * delta; |
68 | let temp: String = format!( |
69 | " \x1B[ {}; {}m {}" , |
70 | foreground |
71 | .at(step) |
72 | .ansi_color_code(TargetGround::Foreground), |
73 | background |
74 | .at(step) |
75 | .ansi_color_code(TargetGround::Background), |
76 | c |
77 | ); |
78 | acc.push_str(&temp); |
79 | acc |
80 | }); |
81 | |
82 | result.push_str(string:" \x1B[0m" ); |
83 | result |
84 | } |
85 | |
86 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
87 | pub enum TargetGround { |
88 | Foreground, |
89 | Background, |
90 | } |
91 | |
92 | impl TargetGround { |
93 | #[inline ] |
94 | pub const fn code(&self) -> u8 { |
95 | match self { |
96 | Self::Foreground => 30, |
97 | Self::Background => 40, |
98 | } |
99 | } |
100 | } |
101 | |
102 | pub trait ANSIColorCode { |
103 | fn ansi_color_code(&self, target: TargetGround) -> String; |
104 | } |
105 | |