FTXUI 6.1.9
C++ functional terminal UI.
Chargement...
Recherche...
Aucune correspondance
src/ftxui/dom/canvas.cpp
Aller à la documentation de ce fichier.
1// Copyright 2021 Arthur Sonzogni. Tous droits réservés.
2// L'utilisation de ce code source est régie par la licence MIT qui se trouve
3// dans le fichier LICENSE.
5
6#include <algorithm> // for max, min
7#include <cmath> // for abs
8#include <cstdint> // for uint8_t
9#include <cstdlib> // for abs
10#include <ftxui/screen/color.hpp> // for Color
11#include <functional> // for function
12#include <map> // for map
13#include <memory> // for make_shared
14#include <utility> // for move, pair
15#include <vector> // for vector
16
17#include "ftxui/dom/elements.hpp" // for Element, canvas
18#include "ftxui/dom/node.hpp" // for Node
19#include "ftxui/dom/requirement.hpp" // for Requirement
20#include "ftxui/screen/box.hpp" // for Box
21#include "ftxui/screen/image.hpp" // for Image
22#include "ftxui/screen/pixel.hpp" // for Pixel
23#include "ftxui/screen/screen.hpp" // for Pixel, Screen
24#include "ftxui/screen/string.hpp" // for Utf8ToGlyphs
25#include "ftxui/util/ref.hpp" // for ConstRef
26
27namespace ftxui {
28
29namespace {
30
31// Base UTF8 pattern:
32// 11100010 10100000 10000000 // empty
33
34// Pattern for the individual dots:
35// ┌──────┬───────┐
36// │dot1 │ dot4 │
37// ├──────┼───────┤
38// │dot2 │ dot5 │
39// ├──────┼───────┤
40// │dot3 │ dot6 │
41// ├──────┼───────┤
42// │dot0-1│ dot0-2│
43// └──────┴───────┘
44// 11100010 10100000 10000001 // dot1
45// 11100010 10100000 10000010 // dot2
46// 11100010 10100000 10000100 // dot3
47// 11100010 10100001 10000000 // dot0-1
48// 11100010 10100000 10001000 // dot4
49// 11100010 10100000 10010000 // dot5
50// 11100010 10100000 10100000 // dot6
51// 11100010 10100010 10000000 // dot0-2
52
53// NOLINTNEXTLINE
54uint8_t g_map_braille[2][4][2] = {
55 {
56 {0b00000000, 0b00000001}, // NOLINT | dot1
57 {0b00000000, 0b00000010}, // NOLINT | dot2
58 {0b00000000, 0b00000100}, // NOLINT | dot3
59 {0b00000001, 0b00000000}, // NOLINT | dot0-1
60 },
61 {
62 {0b00000000, 0b00001000}, // NOLINT | dot4
63 {0b00000000, 0b00010000}, // NOLINT | dot5
64 {0b00000000, 0b00100000}, // NOLINT | dot6
65 {0b00000010, 0b00000000}, // NOLINT | dot0-2
66 },
67};
68
69// NOLINTNEXTLINE
70std::vector<std::string> g_map_block = {
71 " ", "▘", "▖", "▌", "▝", "▀", "▞", "▛",
72 "▗", "▚", "▄", "▙", "▐", "▜", "▟", "█",
73};
74
75// NOLINTNEXTLINE
76const std::map<std::string, uint8_t> g_map_block_inversed = {
77 {" ", 0b0000}, {"▘", 0b0001}, {"▖", 0b0010}, {"▌", 0b0011},
78 {"▝", 0b0100}, {"▀", 0b0101}, {"▞", 0b0110}, {"▛", 0b0111},
79 {"▗", 0b1000}, {"▚", 0b1001}, {"▄", 0b1010}, {"▙", 0b1011},
80 {"▐", 0b1100}, {"▜", 0b1101}, {"▟", 0b1110}, {"█", 0b1111},
81};
82
83constexpr auto nostyle = [](Pixel& /*pixel*/) {};
84
85} // namespace
86
87/// @brief Constructeur.
88/// @param width la largeur du canevas. Une cellule est un point braille 2x4.
89/// @param height la hauteur du canevas. Une cellule est un point braille 2x4.
90Canvas::Canvas(int width, int height)
91 : width_(width),
92 height_(height),
93 storage_(width_ * height_ / 8 /* NOLINT */) {}
94
95/// @brief Récupère le contenu d'une cellule.
96/// @param x la coordonnée x de la cellule.
97/// @param y la coordonnée y de la cellule.
98Pixel Canvas::GetPixel(int x, int y) const {
99 auto it = storage_.find(XY{x, y});
100 return (it == storage_.end()) ? Pixel() : it->second.content;
101}
102
103/// @brief Dessine un point braille.
104/// @param x la coordonnée x du point.
105/// @param y la coordonnée y du point.
106/// @param value indique si le point est rempli ou non.
107void Canvas::DrawPoint(int x, int y, bool value) {
108 DrawPoint(x, y, value, [](Pixel& /*pixel*/) {});
109}
110
111/// @brief Dessine un point braille.
112/// @param x la coordonnée x du point.
113/// @param y la coordonnée y du point.
114/// @param value indique si le point est rempli ou non.
115/// @param color la couleur du point.
116void Canvas::DrawPoint(int x, int y, bool value, const Color& color) {
117 DrawPoint(x, y, value, [color](Pixel& p) { p.foreground_color = color; });
118}
119
120/// @brief Dessine un point braille.
121/// @param x la coordonnée x du point.
122/// @param y la coordonnée y du point.
123/// @param value indique si le point est rempli ou non.
124/// @param style le style de la cellule.
125void Canvas::DrawPoint(int x, int y, bool value, const Stylizer& style) {
126 Style(x, y, style);
127 if (value) {
128 DrawPointOn(x, y);
129 } else {
130 DrawPointOff(x, y);
131 }
132}
133
134/// @brief Dessine un point braille.
135/// @param x la coordonnée x du point.
136/// @param y la coordonnée y du point.
137void Canvas::DrawPointOn(int x, int y) {
138 if (!IsIn(x, y)) {
139 return;
140 }
141 Cell& cell = storage_[XY{x / 2, y / 4}];
142 if (cell.type != CellType::kBraille) {
143 cell.content.character = "⠀"; // 3 bytes.
144 cell.type = CellType::kBraille;
145 }
146
147 cell.content.character[1] |= g_map_braille[x % 2][y % 4][0]; // NOLINT
148 cell.content.character[2] |= g_map_braille[x % 2][y % 4][1]; // NOLINT
149}
150
151/// @brief Efface un point braille.
152/// @param x la coordonnée x du point.
153/// @param y la coordonnée y du point.
154void Canvas::DrawPointOff(int x, int y) {
155 if (!IsIn(x, y)) {
156 return;
157 }
158 Cell& cell = storage_[XY{x / 2, y / 4}];
159 if (cell.type != CellType::kBraille) {
160 cell.content.character = "⠀"; // 3 byt
161 cell.type = CellType::kBraille;
162 }
163
164 cell.content.character[1] &= ~(g_map_braille[x % 2][y % 4][0]); // NOLINT
165 cell.content.character[2] &= ~(g_map_braille[x % 2][y % 4][1]); // NOLINT
166}
167
168/// @brief Inverse un point braille. Un point rempli sera effacé, et un point vide
169/// sera dessiné.
170/// @param x la coordonnée x du point.
171/// @param y la coordonnée y du point.
172void Canvas::DrawPointToggle(int x, int y) {
173 if (!IsIn(x, y)) {
174 return;
175 }
176 Cell& cell = storage_[XY{x / 2, y / 4}];
177 if (cell.type != CellType::kBraille) {
178 cell.content.character = "⠀"; // 3 byt
179 cell.type = CellType::kBraille;
180 }
181
182 cell.content.character[1] ^= g_map_braille[x % 2][y % 4][0]; // NOLINT
183 cell.content.character[2] ^= g_map_braille[x % 2][y % 4][1]; // NOLINT
184}
185
186/// @brief Dessine une ligne de points braille.
187/// @param x1 la coordonnée x du premier point.
188/// @param y1 la coordonnée y du premier point.
189/// @param x2 la coordonnée x du second point.
190/// @param y2 la coordonnée y du second point.
191void Canvas::DrawPointLine(int x1, int y1, int x2, int y2) {
192 DrawPointLine(x1, y1, x2, y2, [](Pixel& /*pixel*/) {});
193}
194
195/// @brief Dessine une ligne de points braille.
196/// @param x1 la coordonnée x du premier point.
197/// @param y1 la coordonnée y du premier point.
198/// @param x2 la coordonnée x du second point.
199/// @param y2 la coordonnée y du second point.
200/// @param color la couleur de la ligne.
201void Canvas::DrawPointLine(int x1, int y1, int x2, int y2, const Color& color) {
202 DrawPointLine(x1, y1, x2, y2,
203 [color](Pixel& p) { p.foreground_color = color; });
204}
205
206/// @brief Dessine une ligne de points braille.
207/// @param x1 la coordonnée x du premier point.
208/// @param y1 la coordonnée y du premier point.
209/// @param x2 la coordonnée x du second point.
210/// @param y2 la coordonnée y du second point.
211/// @param style le style de la ligne.
213 int y1,
214 int x2,
215 int y2,
216 const Stylizer& style) {
217 const int dx = std::abs(x2 - x1);
218 const int dy = std::abs(y2 - y1);
219 const int sx = x1 < x2 ? 1 : -1;
220 const int sy = y1 < y2 ? 1 : -1;
221 const int length = std::max(dx, dy);
222
223 if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
224 return;
225 }
226 if (dx + dx > width_ * height_) {
227 return;
228 }
229
230 int error = dx - dy;
231 for (int i = 0; i < length; ++i) {
232 DrawPoint(x1, y1, true, style);
233 if (2 * error >= -dy) {
234 error -= dy;
235 x1 += sx;
236 }
237 if (2 * error <= dx) {
238 error += dx;
239 y1 += sy;
240 }
241 }
242 DrawPoint(x2, y2, true, style);
243}
244
245/// @brief Dessine un cercle de points braille.
246/// @param x la coordonnée x du centre du cercle.
247/// @param y la coordonnée y du centre du cercle.
248/// @param radius le rayon du cercle.
249void Canvas::DrawPointCircle(int x, int y, int radius) {
250 DrawPointCircle(x, y, radius, [](Pixel& /*pixel*/) {});
251}
252
253/// @brief Dessine un cercle de points braille.
254/// @param x la coordonnée x du centre du cercle.
255/// @param y la coordonnée y du centre du cercle.
256/// @param radius le rayon du cercle.
257/// @param color la couleur du cercle.
258void Canvas::DrawPointCircle(int x, int y, int radius, const Color& color) {
259 DrawPointCircle(x, y, radius,
260 [color](Pixel& p) { p.foreground_color = color; });
261}
262
263/// @brief Dessine un cercle de points braille.
264/// @param x la coordonnée x du centre du cercle.
265/// @param y la coordonnée y du centre du cercle.
266/// @param radius le rayon du cercle.
267/// @param style le style du cercle.
268void Canvas::DrawPointCircle(int x, int y, int radius, const Stylizer& style) {
269 DrawPointEllipse(x, y, radius, radius, style);
270}
271
272/// @brief Dessine un cercle rempli de points braille.
273/// @param x la coordonnée x du centre du cercle.
274/// @param y la coordonnée y du centre du cercle.
275/// @param radius le rayon du cercle.
276void Canvas::DrawPointCircleFilled(int x, int y, int radius) {
277 DrawPointCircleFilled(x, y, radius, [](Pixel& /*pixel*/) {});
278}
279
280/// @brief Dessine un cercle rempli de points braille.
281/// @param x la coordonnée x du centre du cercle.
282/// @param y la coordonnée y du centre du cercle.
283/// @param radius le rayon du cercle.
284/// @param color la couleur du cercle.
286 int y,
287 int radius,
288 const Color& color) {
289 DrawPointCircleFilled(x, y, radius,
290 [color](Pixel& p) { p.foreground_color = color; });
291}
292
293/// @brief Dessine un cercle rempli de points braille.
294/// @param x la coordonnée x du centre du cercle.
295/// @param y la coordonnée y du centre du cercle.
296/// @param radius le rayon du cercle.
297/// @param style le style du cercle.
299 int y,
300 int radius,
301 const Stylizer& style) {
302 DrawPointEllipseFilled(x, y, radius, radius, style);
303}
304
305/// @brief Dessine une ellipse de points braille.
306/// @param x la coordonnée x du centre de l'ellipse.
307/// @param y la coordonnée y du centre de l'ellipse.
308/// @param r1 le rayon de l'ellipse le long de l'axe des x.
309/// @param r2 le rayon de l'ellipse le long de l'axe des y.
310void Canvas::DrawPointEllipse(int x, int y, int r1, int r2) {
311 DrawPointEllipse(x, y, r1, r2, [](Pixel& /*pixel*/) {});
312}
313
314/// @brief Dessine une ellipse de points braille.
315/// @param x la coordonnée x du centre de l'ellipse.
316/// @param y la coordonnée y du centre de l'ellipse.
317/// @param r1 le rayon de l'ellipse le long de l'axe des x.
318/// @param r2 le rayon de l'ellipse le long de l'axe des y.
319/// @param color la couleur de l'ellipse.
321 int y,
322 int r1,
323 int r2,
324 const Color& color) {
325 DrawPointEllipse(x, y, r1, r2,
326 [color](Pixel& p) { p.foreground_color = color; });
327}
328
329/// @brief Dessine une ellipse de points braille.
330/// @param x1 la coordonnée x du centre de l'ellipse.
331/// @param y1 la coordonnée y du centre de l'ellipse.
332/// @param r1 le rayon de l'ellipse le long de l'axe des x.
333/// @param r2 le rayon de l'ellipse le long de l'axe des y.
334/// @param s le style de l'ellipse.
336 int y1,
337 int r1,
338 int r2,
339 const Stylizer& s) {
340 int x = -r1;
341 int y = 0;
342 int e2 = r2;
343 int dx = (1 + 2 * x) * e2 * e2;
344 int dy = x * x;
345 int err = dx + dy;
346
347 do { // NOLINT
348 DrawPoint(x1 - x, y1 + y, true, s);
349 DrawPoint(x1 + x, y1 + y, true, s);
350 DrawPoint(x1 + x, y1 - y, true, s);
351 DrawPoint(x1 - x, y1 - y, true, s);
352 e2 = 2 * err;
353 if (e2 >= dx) {
354 x++;
355 err += dx += 2 * r2 * r2;
356 }
357 if (e2 <= dy) {
358 y++;
359 err += dy += 2 * r1 * r1;
360 }
361 } while (x <= 0);
362
363 while (y++ < r2) {
364 DrawPoint(x1, y1 + y, true, s);
365 DrawPoint(x1, y1 - y, true, s);
366 }
367}
368
369/// @brief Dessine une ellipse remplie de points braille.
370/// @param x1 la coordonnée x du centre de l'ellipse.
371/// @param y1 la coordonnée y du centre de l'ellipse.
372/// @param r1 le rayon de l'ellipse le long de l'axe des x.
373/// @param r2 le rayon de l'ellipse le long de l'axe des y.
374void Canvas::DrawPointEllipseFilled(int x1, int y1, int r1, int r2) {
375 DrawPointEllipseFilled(x1, y1, r1, r2, [](Pixel& /*pixel*/) {});
376}
377
378/// @brief Dessine une ellipse remplie de points braille.
379/// @param x1 la coordonnée x du centre de l'ellipse.
380/// @param y1 la coordonnée y du centre de l'ellipse.
381/// @param r1 le rayon de l'ellipse le long de l'axe des x.
382/// @param r2 le rayon de l'ellipse le long de l'axe des y.
383/// @param color la couleur de l'ellipse.
385 int y1,
386 int r1,
387 int r2,
388 const Color& color) {
389 DrawPointEllipseFilled(x1, y1, r1, r2,
390 [color](Pixel& p) { p.foreground_color = color; });
391}
392
393/// @brief Dessine une ellipse remplie de points braille.
394/// @param x1 la coordonnée x du centre de l'ellipse.
395/// @param y1 la coordonnée y du centre de l'ellipse.
396/// @param r1 le rayon de l'ellipse le long de l'axe des x.
397/// @param r2 le rayon de l'ellipse le long de l'axe des y.
398/// @param s le style de l'ellipse.
400 int y1,
401 int r1,
402 int r2,
403 const Stylizer& s) {
404 int x = -r1;
405 int y = 0;
406 int e2 = r2;
407 int dx = (1 + 2 * x) * e2 * e2;
408 int dy = x * x;
409 int err = dx + dy;
410
411 do { // NOLINT
412 for (int xx = x1 + x; xx <= x1 - x; ++xx) {
413 DrawPoint(xx, y1 + y, true, s);
414 DrawPoint(xx, y1 - y, true, s);
415 }
416 e2 = 2 * err;
417 if (e2 >= dx) {
418 x++;
419 err += dx += 2 * r2 * r2;
420 }
421 if (e2 <= dy) {
422 y++;
423 err += dy += 2 * r1 * r1;
424 }
425 } while (x <= 0);
426
427 while (y++ < r2) {
428 for (int yy = y1 - y; yy <= y1 + y; ++yy) {
429 DrawPoint(x1, yy, true, s);
430 }
431 }
432}
433
434/// @brief Dessine un bloc.
435/// @param x la coordonnée x du bloc.
436/// @param y la coordonnée y du bloc.
437/// @param value indique si le bloc est rempli ou non.
438void Canvas::DrawBlock(int x, int y, bool value) {
439 DrawBlock(x, y, value, [](Pixel& /*pixel*/) {});
440}
441
442/// @brief Dessine un bloc.
443/// @param x la coordonnée x du bloc.
444/// @param y la coordonnée y du bloc.
445/// @param value indique si le bloc est rempli ou non.
446/// @param color la couleur du bloc.
447void Canvas::DrawBlock(int x, int y, bool value, const Color& color) {
448 DrawBlock(x, y, value, [color](Pixel& p) { p.foreground_color = color; });
449}
450
451/// @brief Dessine un bloc.
452/// @param x la coordonnée x du bloc.
453/// @param y la coordonnée y du bloc.
454/// @param value indique si le bloc est rempli ou non.
455/// @param style le style du bloc.
456void Canvas::DrawBlock(int x, int y, bool value, const Stylizer& style) {
457 Style(x, y, style);
458 if (value) {
459 DrawBlockOn(x, y);
460 } else {
461 DrawBlockOff(x, y);
462 }
463}
464
465/// @brief Dessine un bloc.
466/// @param x la coordonnée x du bloc.
467/// @param y la coordonnée y du bloc.
468void Canvas::DrawBlockOn(int x, int y) {
469 if (!IsIn(x, y)) {
470 return;
471 }
472 y /= 2;
473 Cell& cell = storage_[XY{x / 2, y / 2}];
474 if (cell.type != CellType::kBlock) {
475 cell.content.character = " ";
476 cell.type = CellType::kBlock;
477 }
478
479 const uint8_t bit = (x % 2) * 2 + y % 2;
480 uint8_t value = g_map_block_inversed.at(cell.content.character);
481 value |= 1U << bit;
482 cell.content.character = g_map_block[value];
483}
484
485/// @brief Efface un bloc.
486/// @param x la coordonnée x du bloc.
487/// @param y la coordonnée y du bloc.
488void Canvas::DrawBlockOff(int x, int y) {
489 if (!IsIn(x, y)) {
490 return;
491 }
492 Cell& cell = storage_[XY{x / 2, y / 4}];
493 if (cell.type != CellType::kBlock) {
494 cell.content.character = " ";
495 cell.type = CellType::kBlock;
496 }
497 y /= 2;
498
499 const uint8_t bit = (y % 2) * 2 + x % 2;
500 uint8_t value = g_map_block_inversed.at(cell.content.character);
501 value &= ~(1U << bit);
502 cell.content.character = g_map_block[value];
503}
504
505/// @brief Inverse un bloc. S'il est rempli, il sera effacé. S'il est vide,
506/// il sera rempli.
507/// @param x la coordonnée x du bloc.
508/// @param y la coordonnée y du bloc.
509void Canvas::DrawBlockToggle(int x, int y) {
510 if (!IsIn(x, y)) {
511 return;
512 }
513 Cell& cell = storage_[XY{x / 2, y / 4}];
514 if (cell.type != CellType::kBlock) {
515 cell.content.character = " ";
516 cell.type = CellType::kBlock;
517 }
518 y /= 2;
519
520 const uint8_t bit = (y % 2) * 2 + x % 2;
521 uint8_t value = g_map_block_inversed.at(cell.content.character);
522 value ^= 1U << bit;
523 cell.content.character = g_map_block[value];
524}
525
526/// @brief Dessine une ligne de caractères de bloc.
527/// @param x1 la coordonnée x du premier point de la ligne.
528/// @param y1 la coordonnée y du premier point de la ligne.
529/// @param x2 la coordonnée x du second point de la ligne.
530/// @param y2 la coordonnée y du second point de la ligne.
531void Canvas::DrawBlockLine(int x1, int y1, int x2, int y2) {
532 DrawBlockLine(x1, y1, x2, y2, [](Pixel& /*pixel*/) {});
533}
534
535/// @brief Dessine une ligne de caractères de bloc.
536/// @param x1 la coordonnée x du premier point de la ligne.
537/// @param y1 la coordonnée y du premier point de la ligne.
538/// @param x2 la coordonnée x du second point de la ligne.
539/// @param y2 la coordonnée y du second point de la ligne.
540/// @param color la couleur de la ligne.
541void Canvas::DrawBlockLine(int x1, int y1, int x2, int y2, const Color& color) {
542 DrawBlockLine(x1, y1, x2, y2,
543 [color](Pixel& p) { p.foreground_color = color; });
544}
545
546/// @brief Dessine une ligne de caractères de bloc.
547/// @param x1 la coordonnée x du premier point de la ligne.
548/// @param y1 la coordonnée y du premier point de la ligne.
549/// @param x2 la coordonnée x du second point de la ligne.
550/// @param y2 la coordonnée y du second point de la ligne.
551/// @param style le style de la ligne.
553 int y1,
554 int x2,
555 int y2,
556 const Stylizer& style) {
557 y1 /= 2;
558 y2 /= 2;
559
560 const int dx = std::abs(x2 - x1);
561 const int dy = std::abs(y2 - y1);
562 const int sx = x1 < x2 ? 1 : -1;
563 const int sy = y1 < y2 ? 1 : -1;
564 const int length = std::max(dx, dy);
565
566 if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
567 return;
568 }
569 if (dx + dx > width_ * height_) {
570 return;
571 }
572
573 int error = dx - dy;
574 for (int i = 0; i < length; ++i) {
575 DrawBlock(x1, y1 * 2, true, style);
576 if (2 * error >= -dy) {
577 error -= dy;
578 x1 += sx;
579 }
580 if (2 * error <= dx) {
581 error += dx;
582 y1 += sy;
583 }
584 }
585 DrawBlock(x2, y2 * 2, true, style);
586}
587
588/// @brief Dessine un cercle de caractères de bloc.
589/// @param x la coordonnée x du centre du cercle.
590/// @param y la coordonnée y du centre du cercle.
591/// @param radius le rayon du cercle.
592void Canvas::DrawBlockCircle(int x, int y, int radius) {
593 DrawBlockCircle(x, y, radius, nostyle);
594}
595
596/// @brief Dessine un cercle de caractères de bloc.
597/// @param x la coordonnée x du centre du cercle.
598/// @param y la coordonnée y du centre du cercle.
599/// @param radius le rayon du cercle.
600/// @param color la couleur du cercle.
601void Canvas::DrawBlockCircle(int x, int y, int radius, const Color& color) {
602 DrawBlockCircle(x, y, radius,
603 [color](Pixel& p) { p.foreground_color = color; });
604}
605
606/// @brief Dessine un cercle de caractères de bloc.
607/// @param x la coordonnée x du centre du cercle.
608/// @param y la coordonnée y du centre du cercle.
609/// @param radius le rayon du cercle.
610/// @param style le style du cercle.
611void Canvas::DrawBlockCircle(int x, int y, int radius, const Stylizer& style) {
612 DrawBlockEllipse(x, y, radius, radius, style);
613}
614
615/// @brief Dessine un cercle rempli de caractères de bloc.
616/// @param x la coordonnée x du centre du cercle.
617/// @param y la coordonnée y du centre du cercle.
618/// @param radius le rayon du cercle.
619void Canvas::DrawBlockCircleFilled(int x, int y, int radius) {
620 DrawBlockCircleFilled(x, y, radius, nostyle);
621}
622
623/// @brief Dessine un cercle rempli de caractères de bloc.
624/// @param x la coordonnée x du centre du cercle.
625/// @param y la coordonnée y du centre du cercle.
626/// @param radius le rayon du cercle.
627/// @param color la couleur du cercle.
629 int y,
630 int radius,
631 const Color& color) {
632 DrawBlockCircleFilled(x, y, radius,
633 [color](Pixel& p) { p.foreground_color = color; });
634}
635
636/// @brief Dessine un cercle rempli de caractères de bloc.
637/// @param x la coordonnée x du centre du cercle.
638/// @param y la coordonnée y du centre du cercle.
639/// @param radius le rayon du cercle.
640/// @param s le style du cercle.
642 int y,
643 int radius,
644 const Stylizer& s) {
645 DrawBlockEllipseFilled(x, y, radius, radius, s);
646}
647
648/// @brief Dessine une ellipse de caractères de bloc.
649/// @param x la coordonnée x du centre de l'ellipse.
650/// @param y la coordonnée y du centre de l'ellipse.
651/// @param r1 le rayon de l'ellipse le long de l'axe des x.
652/// @param r2 le rayon de l'ellipse le long de l'axe des y.
653void Canvas::DrawBlockEllipse(int x, int y, int r1, int r2) {
654 DrawBlockEllipse(x, y, r1, r2, nostyle);
655}
656
657/// @brief Dessine une ellipse de caractères de bloc.
658/// @param x la coordonnée x du centre de l'ellipse.
659/// @param y la coordonnée y du centre de l'ellipse.
660/// @param r1 le rayon de l'ellipse le long de l'axe des x.
661/// @param r2 le rayon de l'ellipse le long de l'axe des y.
662/// @param color la couleur de l'ellipse.
664 int y,
665 int r1,
666 int r2,
667 const Color& color) {
668 DrawBlockEllipse(x, y, r1, r2,
669 [color](Pixel& p) { p.foreground_color = color; });
670}
671
672/// @brief Dessine une ellipse de caractères de bloc.
673/// @param x1 la coordonnée x du centre de l'ellipse.
674/// @param y1 la coordonnée y du centre de l'ellipse.
675/// @param r1 le rayon de l'ellipse le long de l'axe des x.
676/// @param r2 le rayon de l'ellipse le long de l'axe des y.
677/// @param s le style de l'ellipse.
679 int y1,
680 int r1,
681 int r2,
682 const Stylizer& s) {
683 y1 /= 2;
684 r2 /= 2;
685 int x = -r1;
686 int y = 0;
687 int e2 = r2;
688 int dx = (1 + 2 * x) * e2 * e2;
689 int dy = x * x;
690 int err = dx + dy;
691
692 do { // NOLINT
693 DrawBlock(x1 - x, 2 * (y1 + y), true, s);
694 DrawBlock(x1 + x, 2 * (y1 + y), true, s);
695 DrawBlock(x1 + x, 2 * (y1 - y), true, s);
696 DrawBlock(x1 - x, 2 * (y1 - y), true, s);
697 e2 = 2 * err;
698 if (e2 >= dx) {
699 x++;
700 err += dx += 2 * r2 * r2;
701 }
702 if (e2 <= dy) {
703 y++;
704 err += dy += 2 * r1 * r1;
705 }
706 } while (x <= 0);
707
708 while (y++ < r2) {
709 DrawBlock(x1, 2 * (y1 + y), true, s);
710 DrawBlock(x1, 2 * (y1 - y), true, s);
711 }
712}
713
714/// @brief Dessine une ellipse remplie de caractères de bloc.
715/// @param x la coordonnée x du centre de l'ellipse.
716/// @param y la coordonnée y du centre de l'ellipse.
717/// @param r1 le rayon de l'ellipse le long de l'axe des x.
718/// @param r2 le rayon de l'ellipse le long de l'axe des y.
719void Canvas::DrawBlockEllipseFilled(int x, int y, int r1, int r2) {
720 DrawBlockEllipseFilled(x, y, r1, r2, nostyle);
721}
722
723/// @brief Dessine une ellipse remplie de caractères de bloc.
724/// @param x la coordonnée x du centre de l'ellipse.
725/// @param y la coordonnée y du centre de l'ellipse.
726/// @param r1 le rayon de l'ellipse le long de l'axe des x.
727/// @param r2 le rayon de l'ellipse le long de l'axe des y.
728/// @param color la couleur de l'ellipse.
730 int y,
731 int r1,
732 int r2,
733 const Color& color) {
734 DrawBlockEllipseFilled(x, y, r1, r2,
735 [color](Pixel& p) { p.foreground_color = color; });
736}
737
738/// @brief Dessine une ellipse remplie de caractères de bloc.
739/// @param x1 la coordonnée x du centre de l'ellipse.
740/// @param y1 la coordonnée y du centre de l'ellipse.
741/// @param r1 le rayon de l'ellipse le long de l'axe des x.
742/// @param r2 le rayon de l'ellipse le long de l'axe des y.
743/// @param s le style de l'ellipse.
745 int y1,
746 int r1,
747 int r2,
748 const Stylizer& s) {
749 y1 /= 2;
750 r2 /= 2;
751 int x = -r1;
752 int y = 0;
753 int e2 = r2;
754 int dx = (1 + 2 * x) * e2 * e2;
755 int dy = x * x;
756 int err = dx + dy;
757
758 do { // NOLINT
759 for (int xx = x1 + x; xx <= x1 - x; ++xx) {
760 DrawBlock(xx, 2 * (y1 + y), true, s);
761 DrawBlock(xx, 2 * (y1 - y), true, s);
762 }
763 e2 = 2 * err;
764 if (e2 >= dx) {
765 x++;
766 err += dx += 2 * r2 * r2;
767 }
768 if (e2 <= dy) {
769 y++;
770 err += dy += 2 * r1 * r1;
771 }
772 } while (x <= 0);
773
774 while (y++ < r2) {
775 for (int yy = y1 + y; yy <= y1 - y; ++yy) {
776 DrawBlock(x1, 2 * yy, true, s);
777 }
778 }
779}
780
781/// @brief Dessine un morceau de texte.
782/// @param x la coordonnée x du texte.
783/// @param y la coordonnée y du texte.
784/// @param value le texte à dessiner.
785void Canvas::DrawText(int x, int y, const std::string& value) {
786 DrawText(x, y, value, nostyle);
787}
788
789/// @brief Dessine un morceau de texte.
790/// @param x la coordonnée x du texte.
791/// @param y la coordonnée y du texte.
792/// @param value le texte à dessiner.
793/// @param color la couleur du texte.
795 int y,
796 const std::string& value,
797 const Color& color) {
798 DrawText(x, y, value, [color](Pixel& p) { p.foreground_color = color; });
799}
800
801/// @brief Dessine un morceau de texte.
802/// @param x la coordonnée x du texte.
803/// @param y la coordonnée y du texte.
804/// @param value le texte à dessiner.
805/// @param style le style du texte.
807 int y,
808 const std::string& value,
809 const Stylizer& style) {
810 for (const auto& it : Utf8ToGlyphs(value)) {
811 if (!IsIn(x, y)) {
812 x += 2;
813 continue;
814 }
815 Cell& cell = storage_[XY{x / 2, y / 4}];
816 cell.type = CellType::kCell;
817 cell.content.character = it;
818 style(cell.content);
819 x += 2;
820 }
821}
822
823/// @brief Dessine directement un pixel prédéfini à la coordonnée donnée
824/// @param x la coordonnée x du pixel.
825/// @param y la coordonnée y du pixel.
826/// @param p le pixel à dessiner.
827void Canvas::DrawPixel(int x, int y, const Pixel& p) {
828 Cell& cell = storage_[XY{x / 2, y / 4}];
829 cell.type = CellType::kCell;
830 cell.content = p;
831}
832
833/// @brief Dessine une image prédéfinie, avec le coin supérieur gauche à la coordonnée donnée
834/// Vous pouvez fournir des coordonnées négatives pour aligner l'image comme vous le souhaitez -
835/// seule la partie 'visible' sera dessinée
836/// @param x la coordonnée x correspondant au coin supérieur gauche de l'image.
837/// @param y la coordonnée y correspondant au coin supérieur gauche de l'image.
838/// @param image l'image à dessiner.
839void Canvas::DrawImage(int x, int y, const Image& image) {
840 x /= 2;
841 y /= 4;
842 const int dx_begin = std::max(0, -x);
843 const int dy_begin = std::max(0, -y);
844 const int dx_end = std::min(image.dimx(), width_ - x);
845 const int dy_end = std::min(image.dimy(), height_ - y);
846
847 for (int dy = dy_begin; dy < dy_end; ++dy) {
848 for (int dx = dx_begin; dx < dx_end; ++dx) {
849 Cell& cell = storage_[XY{
850 x + dx,
851 y + dy,
852 }];
853 cell.type = CellType::kCell;
854 cell.content = image.PixelAt(dx, dy);
855 }
856 }
857}
858
859/// @brief Modifie un pixel à un emplacement donné.
860/// @param style une fonction qui modifie le pixel.
861void Canvas::Style(int x, int y, const Stylizer& style) {
862 if (IsIn(x, y)) {
863 style(storage_[XY{x / 2, y / 4}].content);
864 }
865}
866
867namespace {
868
869class CanvasNodeBase : public Node {
870 public:
871 CanvasNodeBase() = default;
872
873 void Render(Screen& screen) override {
874 const Canvas& c = canvas();
875 const int y_max = std::min(c.height() / 4, box_.y_max - box_.y_min + 1);
876 const int x_max = std::min(c.width() / 2, box_.x_max - box_.x_min + 1);
877 for (int y = 0; y < y_max; ++y) {
878 for (int x = 0; x < x_max; ++x) {
879 screen.PixelAt(box_.x_min + x, box_.y_min + y) = c.GetPixel(x, y);
880 }
881 }
882 }
883
884 virtual const Canvas& canvas() = 0;
885};
886
887} // namespace
888
889/// @brief Produit un élément à partir d'un Canevas, ou une référence à un Canevas.
890// NOLINTNEXTLINE
892 class Impl : public CanvasNodeBase {
893 public:
894 explicit Impl(ConstRef<Canvas> canvas) : canvas_(std::move(canvas)) {
895 requirement_.min_x = (canvas_->width() + 1) / 2;
896 requirement_.min_y = (canvas_->height() + 3) / 4;
897 }
898 const Canvas& canvas() final { return *canvas_; }
899 ConstRef<Canvas> canvas_;
900 };
901 return std::make_shared<Impl>(canvas);
902}
903
904/// @brief Produit un élément dessinant un canevas de la taille demandée.
905/// @param width la largeur du canevas.
906/// @param height la hauteur du canevas.
907/// @param fn une fonction dessinant le canevas.
908Element canvas(int width, int height, std::function<void(Canvas&)> fn) {
909 class Impl : public CanvasNodeBase {
910 public:
911 Impl(int width, int height, std::function<void(Canvas&)> fn)
912 : width_(width), height_(height), fn_(std::move(fn)) {}
913
914 void ComputeRequirement() final {
915 requirement_.min_x = (width_ + 1) / 2;
916 requirement_.min_y = (height_ + 3) / 4;
917 }
918
919 void Render(Screen& screen) final {
920 const int width = (box_.x_max - box_.x_min + 1) * 2;
921 const int height = (box_.y_max - box_.y_min + 1) * 4;
922 canvas_ = Canvas(width, height);
923 fn_(canvas_);
924 CanvasNodeBase::Render(screen);
925 }
926
927 const Canvas& canvas() final { return canvas_; }
928 Canvas canvas_;
929 int width_;
930 int height_;
931 std::function<void(Canvas&)> fn_;
932 };
933 return std::make_shared<Impl>(width, height, std::move(fn));
934}
935
936/// @brief Produit un élément dessinant un canevas.
937/// @param fn une fonction dessinant le canevas.
938Element canvas(std::function<void(Canvas&)> fn) {
939 const int default_dim = 12;
940 return canvas(default_dim, default_dim, std::move(fn));
941}
942
943} // namespace ftxui
Un adaptateur. Possède ou référence un objet immuable.
Definition ref.hpp:17
auto screen
ButtonOption Style()
void DrawImage(int x, int y, const Image &)
Dessine une image prédéfinie, avec le coin supérieur gauche à la coordonnée donnée Vous pouvez fourni...
void DrawBlockLine(int x1, int y1, int x2, int y2)
Dessine une ligne de caractères de bloc.
void DrawPointEllipseFilled(int x, int y, int r1, int r2)
Dessine une ellipse remplie de points braille.
void DrawPointLine(int x1, int y1, int x2, int y2)
Dessine une ligne de points braille.
void DrawText(int x, int y, const std::string &value)
Dessine un morceau de texte.
Canvas()=default
std::function< void(Pixel &)> Stylizer
Definition canvas.hpp:48
void DrawBlockOn(int x, int y)
Dessine un bloc.
void DrawPointCircleFilled(int x, int y, int radius)
Dessine un cercle rempli de points braille.
void DrawPointOn(int x, int y)
Dessine un point braille.
void DrawPointOff(int x, int y)
Efface un point braille.
Pixel GetPixel(int x, int y) const
Récupère le contenu d'une cellule.
void DrawBlockEllipseFilled(int x1, int y1, int r1, int r2)
Dessine une ellipse remplie de caractères de bloc.
void DrawPointEllipse(int x, int y, int r1, int r2)
Dessine une ellipse de points braille.
void DrawPoint(int x, int y, bool value)
Dessine un point braille.
void DrawBlockEllipse(int x1, int y1, int r1, int r2)
Dessine une ellipse de caractères de bloc.
void DrawBlockToggle(int x, int y)
Inverse un bloc. S'il est rempli, il sera effacé. S'il est vide, il sera rempli.
void DrawBlockCircle(int x1, int y1, int radius)
Dessine un cercle de caractères de bloc.
void DrawBlockCircleFilled(int x1, int y1, int radius)
Dessine un cercle rempli de caractères de bloc.
void DrawPointCircle(int x, int y, int radius)
Dessine un cercle de points braille.
int height() const
Definition canvas.hpp:45
void DrawBlockOff(int x, int y)
Efface un bloc.
int width() const
Definition canvas.hpp:44
void DrawBlock(int x, int y, bool value)
Dessine un bloc.
void Style(int x, int y, const Stylizer &style)
Modifie un pixel à un emplacement donné.
void DrawPointToggle(int x, int y)
Inverse un point braille. Un point rempli sera effacé, et un point vide sera dessiné.
void DrawPixel(int x, int y, const Pixel &)
Dessine directement un pixel prédéfini à la coordonnée donnée.
Node est la classe de base pour tous les éléments de l'arbre DOM.
Definition node.hpp:36
void Render(Screen &screen, const Element &element)
Affiche un élément sur un ftxui::Screen.
Definition node.cpp:83
Decorator color(Color)
Décore en utilisant une couleur de premier plan.
Canvas est un tampon dessinable associé aux opérations de dessin.
Definition canvas.hpp:38
int dimy() const
Definition image.hpp:36
Color foreground_color
Definition pixel.hpp:49
std::string character
Definition pixel.hpp:45
Pixel & PixelAt(int x, int y)
Accède à une cellule (Pixel) à une position donnée.
Definition image.cpp:43
int dimx() const
Definition image.hpp:35
Color est une classe qui représente une couleur dans l'interface utilisateur du terminal.
Definition color.hpp:21
Une grille rectangulaire de pixels.
Definition image.hpp:17
Une grille rectangulaire de pixels.
Definition screen.hpp:26
Un caractère Unicode et son style associé.
Definition pixel.hpp:15
L'espace de noms FTXUI ftxui::
Definition animation.hpp:10
std::shared_ptr< Node > Element
Definition elements.hpp:22
std::vector< std::string > Utf8ToGlyphs(const std::string &input)
Definition string.cpp:1358
Element canvas(ConstRef< Canvas >)
Produit un élément à partir d'un Canevas, ou une référence à un Canevas.
void Render(Screen &screen, const Element &element)