FTXUI 6.1.9
C++ functional terminal UI.
Loading...
Searching...
No Matches
src/ftxui/dom/canvas.cpp
Go to the documentation of this file.
1// Copyright 2021 Arthur Sonzogni. Todos los derechos reservados.
2// El uso de este código fuente se rige por la licencia MIT que se puede encontrar en
3// el archivo 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 Constructor.
88/// @param width el ancho del lienzo. Una celda es un punto braille de 2x4.
89/// @param height la altura del lienzo. Una celda es un punto braille de 2x4.
90Canvas::Canvas(int width, int height)
91 : width_(width),
92 height_(height),
93 storage_(width_ * height_ / 8 /* NOLINT */) {}
94
95/// @brief Obtiene el contenido de una celda.
96/// @param x la coordenada x de la celda.
97/// @param y la coordenada y de la celda.
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 Dibuja un punto braille.
104/// @param x la coordenada x del punto.
105/// @param y la coordenada y del punto.
106/// @param value si el punto está relleno o no.
107void Canvas::DrawPoint(int x, int y, bool value) {
108 DrawPoint(x, y, value, [](Pixel& /*pixel*/) {});
109}
110
111/// @brief Dibuja un punto braille.
112/// @param x la coordenada x del punto.
113/// @param y la coordenada y del punto.
114/// @param value si el punto está relleno o no.
115/// @param color el color del punto.
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 Dibuja un punto braille.
121/// @param x la coordenada x del punto.
122/// @param y la coordenada y del punto.
123/// @param value si el punto está relleno o no.
124/// @param style el estilo de la celda.
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 Dibuja un punto braille.
135/// @param x la coordenada x del punto.
136/// @param y la coordenada y del punto.
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 Borra un punto braille.
152/// @param x la coordenada x del punto.
153/// @param y la coordenada y del punto.
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 Alterna un punto braille. Uno relleno será borrado, y el otro será
169/// dibujado.
170/// @param x la coordenada x del punto.
171/// @param y la coordenada y del punto.
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 Dibuja una línea de puntos braille.
187/// @param x1 la coordenada x del primer punto.
188/// @param y1 la coordenada y del primer punto.
189/// @param x2 la coordenada x del segundo punto.
190/// @param y2 la coordenada y del segundo punto.
191void Canvas::DrawPointLine(int x1, int y1, int x2, int y2) {
192 DrawPointLine(x1, y1, x2, y2, [](Pixel& /*pixel*/) {});
193}
194
195/// @brief Dibuja una línea de puntos braille.
196/// @param x1 la coordenada x del primer punto.
197/// @param y1 la coordenada y del primer punto.
198/// @param x2 la coordenada x del segundo punto.
199/// @param y2 la coordenada y del segundo punto.
200/// @param color el color de la línea.
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 Dibuja una línea de puntos braille.
207/// @param x1 la coordenada x del primer punto.
208/// @param y1 la coordenada y del primer punto.
209/// @param x2 la coordenada x del segundo punto.
210/// @param y2 la coordenada y del segundo punto.
211/// @param style el estilo de la línea.
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 Dibuja un círculo de puntos braille.
246/// @param x la coordenada x del centro del círculo.
247/// @param y la coordenada y del centro del círculo.
248/// @param radius el radio del círculo.
249void Canvas::DrawPointCircle(int x, int y, int radius) {
250 DrawPointCircle(x, y, radius, [](Pixel& /*pixel*/) {});
251}
252
253/// @brief Dibuja un círculo de puntos braille.
254/// @param x la coordenada x del centro del círculo.
255/// @param y la coordenada y del centro del círculo.
256/// @param radius el radio del círculo.
257/// @param color el color del círculo.
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 Dibuja un círculo de puntos braille.
264/// @param x la coordenada x del centro del círculo.
265/// @param y la coordenada y del centro del círculo.
266/// @param radius el radio del círculo.
267/// @param style el estilo del círculo.
268void Canvas::DrawPointCircle(int x, int y, int radius, const Stylizer& style) {
269 DrawPointEllipse(x, y, radius, radius, style);
270}
271
272/// @brief Dibuja un círculo relleno de puntos braille.
273/// @param x la coordenada x del centro del círculo.
274/// @param y la coordenada y del centro del círculo.
275/// @param radius el radio del círculo.
276void Canvas::DrawPointCircleFilled(int x, int y, int radius) {
277 DrawPointCircleFilled(x, y, radius, [](Pixel& /*pixel*/) {});
278}
279
280/// @brief Dibuja un círculo relleno de puntos braille.
281/// @param x la coordenada x del centro del círculo.
282/// @param y la coordenada y del centro del círculo.
283/// @param radius el radio del círculo.
284/// @param color el color del círculo.
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 Dibuja un círculo relleno de puntos braille.
294/// @param x la coordenada x del centro del círculo.
295/// @param y la coordenada y del centro del círculo.
296/// @param radius el radio del círculo.
297/// @param style el estilo del círculo.
299 int y,
300 int radius,
301 const Stylizer& style) {
302 DrawPointEllipseFilled(x, y, radius, radius, style);
303}
304
305/// @brief Dibuja una elipse de puntos braille.
306/// @param x la coordenada x del centro de la elipse.
307/// @param y la coordenada y del centro de la elipse.
308/// @param r1 el radio de la elipse a lo largo del eje x.
309/// @param r2 el radio de la elipse a lo largo del eje y.
310void Canvas::DrawPointEllipse(int x, int y, int r1, int r2) {
311 DrawPointEllipse(x, y, r1, r2, [](Pixel& /*pixel*/) {});
312}
313
314/// @brief Dibuja una elipse de puntos braille.
315/// @param x la coordenada x del centro de la elipse.
316/// @param y la coordenada y del centro de la elipse.
317/// @param r1 el radio de la elipse a lo largo del eje x.
318/// @param r2 el radio de la elipse a lo largo del eje y.
319/// @param color el color de la elipse.
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 Dibuja una elipse de puntos braille.
330/// @param x1 la coordenada x del centro de la elipse.
331/// @param y1 la coordenada y del centro de la elipse.
332/// @param r1 el radio de la elipse a lo largo del eje x.
333/// @param r2 el radio de la elipse a lo largo del eje y.
334/// @param s el estilo de la elipse.
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 Dibuja una elipse rellena de puntos braille.
370/// @param x1 la coordenada x del centro de la elipse.
371/// @param y1 la coordenada y del centro de la elipse.
372/// @param r1 el radio de la elipse a lo largo del eje x.
373/// @param r2 el radio de la elipse a lo largo del eje y.
374void Canvas::DrawPointEllipseFilled(int x1, int y1, int r1, int r2) {
375 DrawPointEllipseFilled(x1, y1, r1, r2, [](Pixel& /*pixel*/) {});
376}
377
378/// @brief Dibuja una elipse rellena de puntos braille.
379/// @param x1 la coordenada x del centro de la elipse.
380/// @param y1 la coordenada y del centro de la elipse.
381/// @param r1 el radio de la elipse a lo largo del eje x.
382/// @param r2 el radio de la elipse a lo largo del eje y.
383/// @param color el color de la elipse.
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 Dibuja una elipse rellena de puntos braille.
394/// @param x1 la coordenada x del centro de la elipse.
395/// @param y1 la coordenada y del centro de la elipse.
396/// @param r1 el radio de la elipse a lo largo del eje x.
397/// @param r2 el radio de la elipse a lo largo del eje y.
398/// @param s el estilo de la elipse.
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 Dibuja un bloque.
435/// @param x la coordenada x del bloque.
436/// @param y la coordenada y del bloque.
437/// @param value si el bloque está relleno o no.
438void Canvas::DrawBlock(int x, int y, bool value) {
439 DrawBlock(x, y, value, [](Pixel& /*pixel*/) {});
440}
441
442/// @brief Dibuja un bloque.
443/// @param x la coordenada x del bloque.
444/// @param y la coordenada y del bloque.
445/// @param value si el bloque está relleno o no.
446/// @param color el color del bloque.
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 Dibuja un bloque.
452/// @param x la coordenada x del bloque.
453/// @param y la coordenada y del bloque.
454/// @param value si el bloque está relleno o no.
455/// @param style el estilo del bloque.
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 Dibuja un bloque.
466/// @param x la coordenada x del bloque.
467/// @param y la coordenada y del bloque.
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 Borra un bloque.
486/// @param x la coordenada x del bloque.
487/// @param y la coordenada y del bloque.
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 Alterna un bloque. Si está relleno, se borrará. Si está vacío,
506/// se rellenará.
507/// @param x la coordenada x del bloque.
508/// @param y la coordenada y del bloque.
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 Dibuja una línea de caracteres de bloque.
527/// @param x1 la coordenada x del primer punto de la línea.
528/// @param y1 la coordenada y del primer punto de la línea.
529/// @param x2 la coordenada x del segundo punto de la línea.
530/// @param y2 la coordenada y del segundo punto de la línea.
531void Canvas::DrawBlockLine(int x1, int y1, int x2, int y2) {
532 DrawBlockLine(x1, y1, x2, y2, [](Pixel& /*pixel*/) {});
533}
534
535/// @brief Dibuja una línea de caracteres de bloque.
536/// @param x1 la coordenada x del primer punto de la línea.
537/// @param y1 la coordenada y del primer punto de la línea.
538/// @param x2 la coordenada x del segundo punto de la línea.
539/// @param y2 la coordenada y del segundo punto de la línea.
540/// @param color el color de la línea.
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 Dibuja una línea de caracteres de bloque.
547/// @param x1 la coordenada x del primer punto de la línea.
548/// @param y1 la coordenada y del primer punto de la línea.
549/// @param x2 la coordenada x del segundo punto de la línea.
550/// @param y2 la coordenada y del segundo punto de la línea.
551/// @param style el estilo de la línea.
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 Dibuja un círculo de caracteres de bloque.
589/// @param x la coordenada x del centro del círculo.
590/// @param y la coordenada y del centro del círculo.
591/// @param radius el radio del círculo.
592void Canvas::DrawBlockCircle(int x, int y, int radius) {
593 DrawBlockCircle(x, y, radius, nostyle);
594}
595
596/// @brief Dibuja un círculo de caracteres de bloque.
597/// @param x la coordenada x del centro del círculo.
598/// @param y la coordenada y del centro del círculo.
599/// @param radius el radio del círculo.
600/// @param color el color del círculo.
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 Dibuja un círculo de caracteres de bloque.
607/// @param x la coordenada x del centro del círculo.
608/// @param y la coordenada y del centro del círculo.
609/// @param radius el radio del círculo.
610/// @param style el estilo del círculo.
611void Canvas::DrawBlockCircle(int x, int y, int radius, const Stylizer& style) {
612 DrawBlockEllipse(x, y, radius, radius, style);
613}
614
615/// @brief Dibuja un círculo relleno de caracteres de bloque.
616/// @param x la coordenada x del centro del círculo.
617/// @param y la coordenada y del centro del círculo.
618/// @param radius el radio del círculo.
619void Canvas::DrawBlockCircleFilled(int x, int y, int radius) {
620 DrawBlockCircleFilled(x, y, radius, nostyle);
621}
622
623/// @brief Dibuja un círculo relleno de caracteres de bloque.
624/// @param x la coordenada x del centro del círculo.
625/// @param y la coordenada y del centro del círculo.
626/// @param radius el radio del círculo.
627/// @param color el color del círculo.
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 Dibuja un círculo relleno de caracteres de bloque.
637/// @param x la coordenada x del centro del círculo.
638/// @param y la coordenada y del centro del círculo.
639/// @param radius el radio del círculo.
640/// @param s el estilo del círculo.
642 int y,
643 int radius,
644 const Stylizer& s) {
645 DrawBlockEllipseFilled(x, y, radius, radius, s);
646}
647
648/// @brief Dibuja una elipse de caracteres de bloque.
649/// @param x la coordenada x del centro de la elipse.
650/// @param y la coordenada y del centro de la elipse.
651/// @param r1 el radio de la elipse a lo largo del eje x.
652/// @param r2 el radio de la elipse a lo largo del eje y.
653void Canvas::DrawBlockEllipse(int x, int y, int r1, int r2) {
654 DrawBlockEllipse(x, y, r1, r2, nostyle);
655}
656
657/// @brief Dibuja una elipse de caracteres de bloque.
658/// @param x la coordenada x del centro de la elipse.
659/// @param y la coordenada y del centro de la elipse.
660/// @param r1 el radio de la elipse a lo largo del eje x.
661/// @param r2 el radio de la elipse a lo largo del eje y.
662/// @param color el color de la elipse.
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 Dibuja una elipse de caracteres de bloque.
673/// @param x1 la coordenada x del centro de la elipse.
674/// @param y1 la coordenada y del centro de la elipse.
675/// @param r1 el radio de la elipse a lo largo del eje x.
676/// @param r2 el radio de la elipse a lo largo del eje y.
677/// @param s el estilo de la elipse.
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 Dibuja una elipse rellena de caracteres de bloque.
715/// @param x el coordenada x del centro de la elipse.
716/// @param y el coordenada y del centro de la elipse.
717/// @param r1 el radio de la elipse a lo largo del eje x.
718/// @param r2 el radio de la elipse a lo largo del eje y.
719 DrawBlockEllipseFilled(x, y, r1, r2, nostyle);
720}
721
722/// @brief Dibuja una elipse rellena de caracteres de bloque.
723/// @param x la coordenada x del centro de la elipse.
724/// @param y la coordenada y del centro de la elipse.
725/// @param r1 el radio de la elipse a lo largo del eje x.
726/// @param r2 el radio de la elipse a lo largo del eje y.
727/// @param color el color de la elipse.
728void Canvas::DrawBlockEllipseFilled(int x,
729 int y,
730 int r1,
731 int r2,
732 const Color& color) {
733 DrawBlockEllipseFilled(x, y, r1, r2,
734 [color](Pixel& p) { p.foreground_color = color; });
735}
736
737/// @brief Dibuja una elipse rellena de caracteres de bloque.
738/// @param x1 la coordenada x del centro de la elipse.
739/// @param y1 la coordenada y del centro de la elipse.
740/// @param r1 el radio de la elipse a lo largo del eje x.
741/// @param r2 el radio de la elipse a lo largo del eje y.
742/// @param s el estilo de la elipse.
743void Canvas::DrawBlockEllipseFilled(int x1,
744 int y1,
745 int r1,
746 int r2,
747 const Stylizer& s) {
748 y1 /= 2;
749 r2 /= 2;
750 int x = -r1;
751 int y = 0;
752 int e2 = r2;
753 int dx = (1 + 2 * x) * e2 * e2;
754 int dy = x * x;
755 int err = dx + dy;
756
757 do { // NOLINT
758 for (int xx = x1 + x; xx <= x1 - x; ++xx) {
759 DrawBlock(xx, 2 * (y1 + y), true, s);
760 DrawBlock(xx, 2 * (y1 - y), true, s);
761 }
762 e2 = 2 * err;
763 if (e2 >= dx) {
764 x++;
765 err += dx += 2 * r2 * r2;
766 }
767 if (e2 <= dy) {
768 y++;
769 err += dy += 2 * r1 * r1;
770 }
771 } while (x <= 0);
772
773 while (y++ < r2) {
774 for (int yy = y1 + y; yy <= y1 - y; ++yy) {
775 DrawBlock(x1, 2 * yy, true, s);
776 }
777 }
778}
779
780/// @brief Dibuja un trozo de texto.
781/// @param x la coordenada x del texto.
782/// @param y la coordenada y del texto.
783/// @param value el texto a dibujar.
784void Canvas::DrawText(int x, int y, const std::string& value) {
785 DrawText(x, y, value, nostyle);
786}
787
788/// @brief Dibuja un trozo de texto.
789/// @param x la coordenada x del texto.
790/// @param y la coordenada y del texto.
791/// @param value el texto a dibujar.
792/// @param color el color del texto.
793void Canvas::DrawText(int x,
794 int y,
795 const std::string& value,
796 const Color& color) {
797 DrawText(x, y, value, [color](Pixel& p) { p.foreground_color = color; });
798}
799
800/// @brief Dibuja un trozo de texto.
801/// @param x la coordenada x del texto.
802/// @param y la coordenada y del texto.
803/// @param value el texto a dibujar.
804/// @param style el estilo del texto.
805void Canvas::DrawText(int x,
806 int y,
807 const std::string& value,
808 const Stylizer& style) {
809 for (const auto& it : Utf8ToGlyphs(value)) {
810 if (!IsIn(x, y)) {
811 x += 2;
812 continue;
813 }
814 Cell& cell = storage_[XY{x / 2, y / 4}];
815 cell.type = CellType::kCell;
816 cell.content.character = it;
817 style(cell.content);
818 x += 2;
819 }
820}
821
822/// @brief Dibuja directamente un píxel predefinido en la coordenada dada
823/// @param x la coordenada x del píxel.
824/// @param y la coordenada y del píxel.
825/// @param p el píxel a dibujar.
826void Canvas::DrawPixel(int x, int y, const Pixel& p) {
827 Cell& cell = storage_[XY{x / 2, y / 4}];
828 cell.type = CellType::kCell;
829 cell.content = p;
830}
831
832/// @brief Dibuja una imagen predefinida, con la esquina superior izquierda en la coordenada dada
833/// Puedes proporcionar coordenadas negativas para alinear la imagen como desees -
834/// solo se dibujará la porción 'visible'
835/// @param x la coordenada x correspondiente a la esquina superior izquierda de la imagen.
836/// @param y la coordenada y correspondiente a la esquina superior izquierda de la imagen.
837/// @param image la imagen a dibujar.
838void Canvas::DrawImage(int x, int y, const Image& image) {
839 x /= 2;
840 y /= 4;
841 const int dx_begin = std::max(0, -x);
842 const int dy_begin = std::max(0, -y);
843 const int dx_end = std::min(image.dimx(), width_ - x);
844 const int dy_end = std::min(image.dimy(), height_ - y);
845
846 for (int dy = dy_begin; dy < dy_end; ++dy) {
847 for (int dx = dx_begin; dx < dx_end; ++dx) {
848 Cell& cell = storage_[XY{
849 x + dx,
850 y + dy,
851 }];
852 cell.type = CellType::kCell;
853 cell.content = image.PixelAt(dx, dy);
854 }
855 }
856}
857
858/// @brief Modifica un píxel en una ubicación dada.
859/// @param style una función que modifica el píxel.
860void Canvas::Style(int x, int y, const Stylizer& style) {
861 if (IsIn(x, y)) {
862 style(storage_[XY{x / 2, y / 4}].content);
863 }
864}
865
866namespace {
867
868class CanvasNodeBase : public Node {
869 public:
870 CanvasNodeBase() = default;
871
872 void Render(Screen& screen) override {
873 const Canvas& c = canvas();
874 const int y_max = std::min(c.height() / 4, box_.y_max - box_.y_min + 1);
875 const int x_max = std::min(c.width() / 2, box_.x_max - box_.x_min + 1);
876 for (int y = 0; y < y_max; ++y) {
877 for (int x = 0; x < x_max; ++x) {
878 screen.PixelAt(box_.x_min + x, box_.y_min + y) = c.GetPixel(x, y);
879 }
880 }
881 }
882
883 virtual const Canvas& canvas() = 0;
884};
885
886} // namespace
887
888/// @brief Produce un elemento a partir de un Canvas, o una referencia a un Canvas.
889// NOLINTNEXTLINE
890Element canvas(ConstRef<Canvas> canvas) {
891 class Impl : public CanvasNodeBase {
892 public:
893 explicit Impl(ConstRef<Canvas> canvas) : canvas_(std::move(canvas)) {
894 requirement_.min_x = (canvas_->width() + 1) / 2;
895 requirement_.min_y = (canvas_->height() + 3) / 4;
896 }
897 const Canvas& canvas() final { return *canvas_; }
898 ConstRef<Canvas> canvas_;
899 };
900 return std::make_shared<Impl>(canvas);
901}
902
903/// @brief Produce un elemento dibujando un lienzo del tamaño solicitado.
904/// @param width el ancho del lienzo.
905/// @param height la altura del lienzo.
906/// @param fn una función que dibuja el lienzo.
907Element canvas(int width, int height, std::function<void(Canvas&)> fn) {
908 class Impl : public CanvasNodeBase {
909 public:
910 Impl(int width, int height, std::function<void(Canvas&)> fn)
911 : width_(width), height_(height), fn_(std::move(fn)) {}
912
913 void ComputeRequirement() final {
914 requirement_.min_x = (width_ + 1) / 2;
915 requirement_.min_y = (height_ + 3) / 4;
916 }
917
918 void Render(Screen& screen) final {
919 const int width = (box_.x_max - box_.x_min + 1) * 2;
920 const int height = (box_.y_max - box_.y_min + 1) * 4;
921 canvas_ = Canvas(width, height);
922 fn_(canvas_);
923 CanvasNodeBase::Render(screen);
924 }
925
926 const Canvas& canvas() final { return canvas_; }
927 Canvas canvas_;
928 int width_;
929 int height_;
930 std::function<void(Canvas&)> fn_;
931 };
932 return std::make_shared<Impl>(width, height, std::move(fn));
933}
934
935/// @brief Produce un elemento dibujando un lienzo.
936/// @param fn una función que dibuja el lienzo.
937Element canvas(std::function<void(Canvas&)> fn) {
938 const int default_dim = 12;
939 return canvas(default_dim, default_dim, std::move(fn));
940}
941
942} // namespace ftxui
auto screen
ButtonOption Style()
void DrawBlockLine(int x1, int y1, int x2, int y2)
Dibuja una línea de caracteres de bloque.
void DrawPointEllipseFilled(int x, int y, int r1, int r2)
Dibuja una elipse rellena de puntos braille.
void DrawPointLine(int x1, int y1, int x2, int y2)
Dibuja una línea de puntos braille.
Canvas()=default
std::function< void(Pixel &)> Stylizer
Definition canvas.hpp:48
void DrawBlockOn(int x, int y)
Dibuja un bloque.
void DrawPointCircleFilled(int x, int y, int radius)
Dibuja un círculo relleno de puntos braille.
void DrawPointOn(int x, int y)
Dibuja un punto braille.
void DrawPointOff(int x, int y)
Borra un punto braille.
Pixel GetPixel(int x, int y) const
Obtiene el contenido de una celda.
void DrawBlockEllipseFilled(int x1, int y1, int r1, int r2)
void DrawPointEllipse(int x, int y, int r1, int r2)
Dibuja una elipse de puntos braille.
void DrawPoint(int x, int y, bool value)
Dibuja un punto braille.
void DrawBlockEllipse(int x1, int y1, int r1, int r2)
Dibuja una elipse de caracteres de bloque.
void DrawBlockToggle(int x, int y)
Alterna un bloque. Si está relleno, se borrará. Si está vacío, se rellenará.
void DrawBlockCircle(int x1, int y1, int radius)
Dibuja un círculo de caracteres de bloque.
void DrawBlockCircleFilled(int x1, int y1, int radius)
Dibuja un círculo relleno de caracteres de bloque.
void DrawPointCircle(int x, int y, int radius)
Dibuja un círculo de puntos braille.
int height() const
Definition canvas.hpp:45
void DrawBlockOff(int x, int y)
Borra un bloque.
int width() const
Definition canvas.hpp:44
void DrawBlock(int x, int y, bool value)
Dibuja un bloque.
void DrawPointToggle(int x, int y)
Alterna un punto braille. Uno relleno será borrado, y el otro será dibujado.
Node es la clase base para todos los elementos en el árbol DOM.
Definition node.hpp:37
Decorator color(Color)
Decora usando un color de primer plano.
Canvas es un búfer dibujable asociado con operaciones de dibujo.
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)
Accede a una celda (Pixel) en una posición dada.
Definition image.cpp:43
int dimx() const
Definition image.hpp:35
Color es una clase que representa un color en la interfaz de usuario de la terminal.
Definition color.hpp:21
Una cuadrícula rectangular de píxeles.
Definition image.hpp:17
Una cuadrícula rectangular de píxeles.
Definition screen.hpp:26
Un carácter Unicode y su estilo asociado.
Definition pixel.hpp:15
El espacio de nombres ftxui:: de FTXUI.
Definition animation.hpp:10
DrawBlockEllipseFilled(x, y, r1, r2, nostyle)
Dibuja una elipse rellena de caracteres de bloque.
Element canvas(ConstRef< Canvas > canvas)
Produce un elemento a partir de un Canvas, o una referencia a un Canvas.