FTXUI 6.1.9
C++ functional terminal UI.
Chargement...
Recherche...
Aucune correspondance
src/ftxui/dom/linear_gradient.cpp
Aller à la documentation de ce fichier.
1// Copyright 2023 Arthur Sonzogni. Tous droits réservés.
2// L'utilisation de ce code source est régie par la licence MIT qui peut être trouvée dans
3// le fichier LICENSE.
4#include <algorithm> // for max, min, sort, copy
5#include <cmath> // for fmod, cos, sin
6#include <cstddef> // for size_t
7#include <ftxui/dom/linear_gradient.hpp> // for LinearGradient::Stop, LinearGradient
8#include <memory> // for allocator_traits<>::value_type, make_shared
9#include <optional> // for optional, operator!=, operator<
10#include <utility> // for move
11#include <vector> // for vector
12
13#include "ftxui/dom/elements.hpp" // for Element, Decorator, bgcolor, color
14#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
15#include "ftxui/screen/box.hpp" // for Box
16#include "ftxui/screen/color.hpp" // for Color, Color::Default, Color::Blue
17#include "ftxui/screen/screen.hpp" // for Pixel, Screen
18
19namespace ftxui {
20namespace {
21
22struct LinearGradientNormalized {
23 float angle = 0.F;
24 std::vector<Color> colors;
25 std::vector<float> positions; // Sorted.
26};
27
28// Convertit un LinearGradient en une version normalisée.
29LinearGradientNormalized Normalize(LinearGradient gradient) {
30 // Gère les dégradés de taille 0.
31 if (gradient.stops.empty()) {
32 return LinearGradientNormalized{
33 0.F,
35 {0.F, 1.F},
36 };
37 }
38
39 // Remplit les deux étendues, si non fournies.
40 if (!gradient.stops.front().position) {
41 gradient.stops.front().position = 0.F;
42 }
43 if (!gradient.stops.back().position) {
44 gradient.stops.back().position = 1.F;
45 }
46
47 // Remplit les blancs, en interpolant les positions.
48 size_t last_checkpoint = 0;
49 for (size_t i = 1; i < gradient.stops.size(); ++i) {
50 if (!gradient.stops[i].position) {
51 continue;
52 }
53
54 if (i - last_checkpoint >= 2) {
55 const float min = gradient.stops[i].position.value(); // NOLINT
56 const float max =
57 gradient.stops[last_checkpoint].position.value(); // NOLINT
58 for (size_t j = last_checkpoint + 1; j < i; ++j) {
59 gradient.stops[j].position = min + (max - min) *
60 float(j - last_checkpoint) /
61 float(i - last_checkpoint);
62 }
63 }
64
65 last_checkpoint = i;
66 }
67
68 // Trie les arrêts par position.
69 std::sort(
70 gradient.stops.begin(), gradient.stops.end(),
71 [](const auto& a, const auto& b) { return a.position < b.position; });
72
73 // Si on ne commence pas à zéro, ajoute un arrêt à zéro.
74 if (gradient.stops.front().position != 0) {
75 gradient.stops.insert(gradient.stops.begin(),
76 {gradient.stops.front().color, 0.F});
77 }
78 // Si on ne termine pas à un, ajoute un arrêt à un.
79 if (gradient.stops.back().position != 1) {
80 gradient.stops.push_back({gradient.stops.back().color, 1.F});
81 }
82
83 // Normalise l'angle.
84 LinearGradientNormalized normalized;
85 const float modulo = 360.F;
86 normalized.angle =
87 std::fmod(std::fmod(gradient.angle, modulo) + modulo, modulo);
88 for (auto& stop : gradient.stops) {
89 normalized.colors.push_back(stop.color);
90 // NOLINTNEXTLINE
91 normalized.positions.push_back(stop.position.value());
92 }
93 return normalized;
94}
95
96Color Interpolate(const LinearGradientNormalized& gradient, float t) {
97 // Trouve la bonne couleur dans les arrêts du dégradé.
98 size_t i = 1;
99 while (true) {
100 // Notez que `t` peut être légèrement supérieur à 1.0 en raison de la précision des nombres flottants.
101 // C'est pourquoi nous devons gérer le cas où `t` est supérieur à la position du dernier arrêt.
102 // Voir https://github.com/ArthurSonzogni/FTXUI/issues/998 if (i >= gradient.positions.size()) {
103 const float half = 0.5F;
104 return Color::Interpolate(half, gradient.colors.back(),
105 gradient.colors.back());
106 }
107 if (t <= gradient.positions[i]) {
108 break;
109 }
110 ++i;
111 }
112
113 const float t0 = gradient.positions[i - 1];
114 const float t1 = gradient.positions[i - 0];
115 const float tt = (t - t0) / (t1 - t0);
116
117 const Color& c0 = gradient.colors[i - 1];
118 const Color& c1 = gradient.colors[i - 0];
119 const Color& cc = Color::Interpolate(tt, c0, c1);
120
121 return cc;
122}
123
124class LinearGradientColor : public NodeDecorator {
125 public:
126 explicit LinearGradientColor(Element child,
127 const LinearGradient& gradient,
128 bool background_color)
129 : NodeDecorator(std::move(child)),
130 gradient_(Normalize(gradient)),
131 background_color_{background_color} {}
132
133 private:
134 void Render(Screen& screen) override {
135 const float degtorad = 0.01745329251F;
136 const float dx = std::cos(gradient_.angle * degtorad);
137 const float dy = std::sin(gradient_.angle * degtorad);
138
139 // Projeter chaque coin pour obtenir l'étendue du dégradé.
140 const float p1 = float(box_.x_min) * dx + float(box_.y_min) * dy;
141 const float p2 = float(box_.x_min) * dx + float(box_.y_max) * dy;
142 const float p3 = float(box_.x_max) * dx + float(box_.y_min) * dy;
143 const float p4 = float(box_.x_max) * dx + float(box_.y_max) * dy;
144 const float min = std::min({p1, p2, p3, p4});
145 const float max = std::max({p1, p2, p3, p4});
146
147 // Renormaliser la projection à [0, 1] en utilisant l'étendue et la géométrie projective. const float dX = dx / (max - min);
148 const float dY = dy / (max - min);
149 const float dZ = -min / (max - min);
150
151 // Projeter chaque pixel pour obtenir la couleur.
152 if (background_color_) {
153 for (int y = box_.y_min; y <= box_.y_max; ++y) {
154 for (int x = box_.x_min; x <= box_.x_max; ++x) {
155 const float t = float(x) * dX + float(y) * dY + dZ;
156 screen.PixelAt(x, y).background_color = Interpolate(gradient_, t);
157 }
158 }
159 } else {
160 for (int y = box_.y_min; y <= box_.y_max; ++y) {
161 for (int x = box_.x_min; x <= box_.x_max; ++x) {
162 const float t = float(x) * dX + float(y) * dY + dZ;
163 screen.PixelAt(x, y).foreground_color = Interpolate(gradient_, t);
164 }
165 }
166 }
167
169 }
170
171 LinearGradientNormalized gradient_;
172 bool background_color_;
173};
174
175} // namespace
176
177/// @brief Construit le dégradé "vide". Ceci est souvent suivi d'appels à
178/// LinearGradient::Angle() et LinearGradient::Stop().
179/// Exemple:
180/// ```cpp
181/// auto gradient =
182/// LinearGradient()
183/// .Angle(45)
184/// .Stop(Color::Red, 0.0)
185/// .Stop(Color::Green, 0.5)
186/// .Stop(Color::Blue, 1.0);;
187/// ```
188LinearGradient::LinearGradient() = default;
189
190/// @brief Construit un dégradé avec deux couleurs.
191/// @param begin La couleur au début du dégradé.
192/// @param end La couleur à la fin du dégradé.
193LinearGradient::LinearGradient(Color begin, Color end)
194 : LinearGradient(0, begin, end) {}
195
196/// @brief Construit un dégradé avec deux couleurs et un angle.
197/// @param a L'angle du dégradé.
198/// @param begin La couleur au début du dégradé.
199/// @param end La couleur à la fin du dégradé.
200LinearGradient::LinearGradient(float a, Color begin, Color end) : angle(a) {
201 stops.push_back({begin, {}});
202 stops.push_back({end, {}});
203}
204
205/// @brief Définit l'angle du dégradé.
206/// @param a L'angle du dégradé.
207/// @return Le dégradé.
209 angle = a;
210 return *this;
211}
212
213/// @brief Ajoute un arrêt de couleur au dégradé.
214/// @param c La couleur de l'arrêt.
215/// @param p La position de l'arrêt.
217 stops.push_back({c, p});
218 return *this;
219}
220
221/// @brief Ajoute un arrêt de couleur au dégradé.
222/// @param c La couleur de l'arrêt.
223/// @return Le dégradé.
224/// @note La position de l'arrêt est interpolée à partir des arrêts voisins.
225LinearGradient& LinearGradient::Stop(Color c) {
226 stops.push_back({c, {}});
227 return *this;
228}
229
230/// @brief Définit la couleur de premier plan d'un élément avec un effet de dégradé linéaire.
231/// @param gradient L'effet de dégradé à appliquer sur l'élément de sortie.
232/// @param child L'élément d'entrée.
233/// @return L'élément de sortie coloré.
234/// @ingroup dom
235///
236/// ### Exemple
237///
238/// ```cpp
239/// color(LinearGradient{0, {Color::Red, Color::Blue}}, text("Hello"))
240/// ```
241Element color(const LinearGradient& gradient, Element child) {
242 return std::make_shared<LinearGradientColor>(std::move(child), gradient,
243 /*background_color*/ false);
244}
245
246/// @brief Définit la couleur de fond d'un élément avec un effet de dégradé linéaire.
247/// @param gradient L'effet de dégradé à appliquer sur l'élément de sortie.
248/// @param child L'élément d'entrée.
249/// @return L'élément de sortie coloré.
250/// @ingroup dom
251///
252/// ### Exemple
253///
254/// ```cpp
255/// bgcolor(LinearGradient{0, {Color::Red, Color::Blue}}, text("Hello"))
256/// ```
257Element bgcolor(const LinearGradient& gradient, Element child) {
258 return std::make_shared<LinearGradientColor>(std::move(child), gradient,
259 /*background_color*/ true);
260}
261
262/// @brief Décore en utilisant un effet de dégradé linéaire sur la couleur de premier plan.
263/// @param gradient L'effet de dégradé à appliquer sur l'élément de sortie.
264/// @return Le Decorator appliquant la couleur.
265/// @ingroup dom
266///
267/// ### Exemple
268///
269/// ```cpp
270/// text("Hello") | color(LinearGradient{0, {Color::Red, Color::Blue}})
271/// ```
272Decorator color(const LinearGradient& gradient) {
273 return
274 [gradient](Element child) { return color(gradient, std::move(child)); };
275}
276
277/// @brief Décore en utilisant un effet de dégradé linéaire sur la couleur de fond.
278/// @param gradient L'effet de dégradé à appliquer sur l'élément de sortie.
279/// @return Le Decorator appliquant la couleur.
280/// @ingroup dom
281///
282/// ### Exemple
283///
284/// ```cpp
285/// text("Hello") | color(LinearGradient{0, {Color::Red, Color::Blue}})
286/// ```
287Decorator bgcolor(const LinearGradient& gradient) {
288 return
289 [gradient](Element child) { return bgcolor(gradient, std::move(child)); };
290}
291
292} // namespace ftxui
NodeDecorator(Element child)
auto screen
LinearGradient & Stop(Color color, float position)
LinearGradient & Angle(float angle)
Définit l'angle du dégradé.
LinearGradient()
Construit le dégradé "vide". Ceci est souvent suivi d'appels à LinearGradient::Angle() et LinearGradi...
std::vector< Stop > stops
Box box_
Definition node.hpp:80
friend void Render(Screen &screen, Node *node, Selection &selection)
Decorator bgcolor(Color)
Décore en utilisant une couleur d'arrière-plan.
Decorator color(Color)
Décore en utilisant une couleur de premier plan.
Une classe représentant les paramètres de l'effet de couleur en dégradé linéaire.
int x_max
Definition box.hpp:18
int y_min
Definition box.hpp:19
int y_max
Definition box.hpp:20
int x_min
Definition box.hpp:17
static Color Interpolate(float t, const Color &a, const Color &b)
Color est une classe qui représente une couleur dans l'interface utilisateur du terminal.
Definition color.hpp:21
Color
Color est une énumération qui représente le support des couleurs du terminal.
Definition terminal.hpp:23
L'espace de noms FTXUI ftxui::
Definition animation.hpp:10
std::function< Element(Element)> Decorator
Definition elements.hpp:24
std::shared_ptr< Node > Element
Definition elements.hpp:22
std::vector< Color > colors
std::vector< float > positions