FTXUI  4.1.1
C++ functional terminal UI.
Loading...
Searching...
No Matches
canvas.cpp
Go to the documentation of this file.
2
3#include <algorithm> // for max, min
4#include <cmath> // for abs
5#include <cstdint> // for uint8_t
6#include <cstdlib> // for abs
7#include <ftxui/screen/color.hpp> // for Color
8#include <map> // for map
9#include <memory> // for make_shared
10#include <utility> // for move, pair
11#include <vector> // for vector
12
13#include "ftxui/dom/elements.hpp" // for Element, canvas
14#include "ftxui/dom/node.hpp" // for Node
15#include "ftxui/dom/requirement.hpp" // for Requirement
16#include "ftxui/screen/box.hpp" // for Box
17#include "ftxui/screen/screen.hpp" // for Pixel, Screen
18#include "ftxui/screen/string.hpp" // for Utf8ToGlyphs
19#include "ftxui/util/ref.hpp" // for ConstRef
20
21namespace ftxui {
22
23namespace {
24
25// Base UTF8 pattern:
26// 11100010 10100000 10000000 // empty
27
28// Pattern for the individuel dots:
29// ┌──────┬───────┐
30// │dot1 │ dot4 │
31// ├──────┼───────┤
32// │dot2 │ dot5 │
33// ├──────┼───────┤
34// │dot3 │ dot6 │
35// ├──────┼───────┤
36// │dot0-1│ dot0-2│
37// └──────┴───────┘
38// 11100010 10100000 10000001 // dot1
39// 11100010 10100000 10000010 // dot2
40// 11100010 10100000 10000100 // dot3
41// 11100010 10100001 10000000 // dot0-1
42// 11100010 10100000 10001000 // dot4
43// 11100010 10100000 10010000 // dot5
44// 11100010 10100000 10100000 // dot6
45// 11100010 10100010 10000000 // dot0-2
46
47// NOLINTNEXTLINE
48uint8_t g_map_braille[2][4][2] = {
49 {
50 {0b00000000, 0b00000001}, // NOLINT | dot1
51 {0b00000000, 0b00000010}, // NOLINT | dot2
52 {0b00000000, 0b00000100}, // NOLINT | dot3
53 {0b00000001, 0b00000000}, // NOLINT | dot0-1
54 },
55 {
56 {0b00000000, 0b00001000}, // NOLINT | dot4
57 {0b00000000, 0b00010000}, // NOLINT | dot5
58 {0b00000000, 0b00100000}, // NOLINT | dot6
59 {0b00000010, 0b00000000}, // NOLINT | dot0-2
60 },
61};
62
63// NOLINTNEXTLINE
64std::vector<std::string> g_map_block = {
65 " ", "▘", "▖", "▌", "▝", "▀", "▞", "▛",
66 "▗", "▚", "▄", "▙", "▐", "▜", "▟", "█",
67};
68
69// NOLINTNEXTLINE
70const std::map<std::string, uint8_t> g_map_block_inversed = {
71 {" ", 0b0000}, {"▘", 0b0001}, {"▖", 0b0010}, {"▌", 0b0011},
72 {"▝", 0b0100}, {"▀", 0b0101}, {"▞", 0b0110}, {"▛", 0b0111},
73 {"▗", 0b1000}, {"▚", 0b1001}, {"▄", 0b1010}, {"▙", 0b1011},
74 {"▐", 0b1100}, {"▜", 0b1101}, {"▟", 0b1110}, {"█", 0b1111},
75};
76
77constexpr auto nostyle = [](Pixel& /*pixel*/) {};
78
79} // namespace
80
81/// @brief Constructor.
82/// @param width the width of the canvas. A cell is a 2x8 braille dot.
83/// @param height the height of the canvas. A cell is a 2x8 braille dot.
84Canvas::Canvas(int width, int height)
85 : width_(width),
86 height_(height),
87 storage_(width_ * height_ / 8 /* NOLINT */) {}
88
89/// @brief Get the content of a cell.
90/// @param x the x coordinate of the cell.
91/// @param y the y coordinate of the cell.
92Pixel Canvas::GetPixel(int x, int y) const {
93 auto it = storage_.find(XY{x, y});
94 return (it == storage_.end()) ? Pixel{} : it->second.content;
95}
96
97/// @brief Draw a braille dot.
98/// @param x the x coordinate of the dot.
99/// @param y the y coordinate of the dot.
100/// @param value whether the dot is filled or not.
101void Canvas::DrawPoint(int x, int y, bool value) {
102 DrawPoint(x, y, value, [](Pixel& /*pixel*/) {});
103}
104
105/// @brief Draw a braille dot.
106/// @param x the x coordinate of the dot.
107/// @param y the y coordinate of the dot.
108/// @param value whether the dot is filled or not.
109/// @param color the color of the dot.
110void Canvas::DrawPoint(int x, int y, bool value, const Color& color) {
111 DrawPoint(x, y, value, [color](Pixel& p) { p.foreground_color = color; });
112}
113
114/// @brief Draw a braille dot.
115/// @param x the x coordinate of the dot.
116/// @param y the y coordinate of the dot.
117/// @param value whether the dot is filled or not.
118/// @param style the style of the cell.
119void Canvas::DrawPoint(int x, int y, bool value, const Stylizer& style) {
120 Style(x, y, style);
121 if (value) {
122 DrawPointOn(x, y);
123 } else {
124 DrawPointOff(x, y);
125 }
126}
127
128/// @brief Draw a braille dot.
129/// @param x the x coordinate of the dot.
130/// @param y the y coordinate of the dot.
131void Canvas::DrawPointOn(int x, int y) {
132 if (!IsIn(x, y)) {
133 return;
134 }
135 Cell& cell = storage_[XY{x / 2, y / 4}];
136 if (cell.type != CellType::kBraille) {
137 cell.content.character = "⠀"; // 3 bytes.
138 cell.type = CellType::kBraille;
139 }
140
141 cell.content.character[1] |= g_map_braille[x % 2][y % 4][0]; // NOLINT
142 cell.content.character[2] |= g_map_braille[x % 2][y % 4][1]; // NOLINT
143}
144
145/// @brief Erase a braille dot.
146/// @param x the x coordinate of the dot.
147/// @param y the y coordinate of the dot.
148void Canvas::DrawPointOff(int x, int y) {
149 if (!IsIn(x, y)) {
150 return;
151 }
152 Cell& cell = storage_[XY{x / 2, y / 4}];
153 if (cell.type != CellType::kBraille) {
154 cell.content.character = "⠀"; // 3 byt
155 cell.type = CellType::kBraille;
156 }
157
158 cell.content.character[1] &= ~(g_map_braille[x % 2][y % 4][0]); // NOLINT
159 cell.content.character[2] &= ~(g_map_braille[x % 2][y % 4][1]); // NOLINT
160}
161
162/// @brief Toggle a braille dot. A filled one will be erased, and the other will
163/// be drawn.
164/// @param x the x coordinate of the dot.
165/// @param y the y coordinate of the dot.
166void Canvas::DrawPointToggle(int x, int y) {
167 if (!IsIn(x, y)) {
168 return;
169 }
170 Cell& cell = storage_[XY{x / 2, y / 4}];
171 if (cell.type != CellType::kBraille) {
172 cell.content.character = "⠀"; // 3 byt
173 cell.type = CellType::kBraille;
174 }
175
176 cell.content.character[1] ^= g_map_braille[x % 2][y % 4][0]; // NOLINT
177 cell.content.character[2] ^= g_map_braille[x % 2][y % 4][1]; // NOLINT
178}
179
180/// @brief Draw a line made of braille dots.
181/// @param x1 the x coordinate of the first dot.
182/// @param y1 the y coordinate of the first dot.
183/// @param x2 the x coordinate of the second dot.
184/// @param y2 the y coordinate of the second dot.
185void Canvas::DrawPointLine(int x1, int y1, int x2, int y2) {
186 DrawPointLine(x1, y1, x2, y2, [](Pixel& /*pixel*/) {});
187}
188
189/// @brief Draw a line made of braille dots.
190/// @param x1 the x coordinate of the first dot.
191/// @param y1 the y coordinate of the first dot.
192/// @param x2 the x coordinate of the second dot.
193/// @param y2 the y coordinate of the second dot.
194/// @param color the color of the line.
195void Canvas::DrawPointLine(int x1, int y1, int x2, int y2, const Color& color) {
196 DrawPointLine(x1, y1, x2, y2,
197 [color](Pixel& p) { p.foreground_color = color; });
198}
199
200/// @brief Draw a line made of braille dots.
201/// @param x1 the x coordinate of the first dot.
202/// @param y1 the y coordinate of the first dot.o
203/// @param x2 the x coordinate of the second dot.
204/// @param y2 the y coordinate of the second dot.
205/// @param style the style of the line.
207 int y1,
208 int x2,
209 int y2,
210 const Stylizer& style) {
211 const int dx = std::abs(x2 - x1);
212 const int dy = std::abs(y2 - y1);
213 const int sx = x1 < x2 ? 1 : -1;
214 const int sy = y1 < y2 ? 1 : -1;
215 const int length = std::max(dx, dy);
216
217 if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
218 return;
219 }
220 if (dx + dx > width_ * height_) {
221 return;
222 }
223
224 int error = dx - dy;
225 for (int i = 0; i < length; ++i) {
226 DrawPoint(x1, y1, true, style);
227 if (2 * error >= -dy) {
228 error -= dy;
229 x1 += sx;
230 }
231 if (2 * error <= dx) {
232 error += dx;
233 y1 += sy;
234 }
235 }
236 DrawPoint(x2, y2, true, style);
237}
238
239/// @brief Draw a circle made of braille dots.
240/// @param x the x coordinate of the center of the circle.
241/// @param y the y coordinate of the center of the circle.
242/// @param radius the radius of the circle.
243void Canvas::DrawPointCircle(int x, int y, int radius) {
244 DrawPointCircle(x, y, radius, [](Pixel& /*pixel*/) {});
245}
246
247/// @brief Draw a circle made of braille dots.
248/// @param x the x coordinate of the center of the circle.
249/// @param y the y coordinate of the center of the circle.
250/// @param radius the radius of the circle.
251/// @param color the color of the circle.
252void Canvas::DrawPointCircle(int x, int y, int radius, const Color& color) {
253 DrawPointCircle(x, y, radius,
254 [color](Pixel& p) { p.foreground_color = color; });
255}
256
257/// @brief Draw a circle made of braille dots.
258/// @param x the x coordinate of the center of the circle.
259/// @param y the y coordinate of the center of the circle.
260/// @param radius the radius of the circle.
261/// @param style the style of the circle.
262void Canvas::DrawPointCircle(int x, int y, int radius, const Stylizer& style) {
263 DrawPointEllipse(x, y, radius, radius, style);
264}
265
266/// @brief Draw a filled circle made of braille dots.
267/// @param x the x coordinate of the center of the circle.
268/// @param y the y coordinate of the center of the circle.
269/// @param radius the radius of the circle.
270void Canvas::DrawPointCircleFilled(int x, int y, int radius) {
271 DrawPointCircleFilled(x, y, radius, [](Pixel& /*pixel*/) {});
272}
273
274/// @brief Draw a filled circle made of braille dots.
275/// @param x the x coordinate of the center of the circle.
276/// @param y the y coordinate of the center of the circle.
277/// @param radius the radius of the circle.
278/// @param color the color of the circle.
280 int y,
281 int radius,
282 const Color& color) {
283 DrawPointCircleFilled(x, y, radius,
284 [color](Pixel& p) { p.foreground_color = color; });
285}
286
287/// @brief Draw a filled circle made of braille dots.
288/// @param x the x coordinate of the center of the circle.
289/// @param y the y coordinate of the center of the circle.
290/// @param radius the radius of the circle.
291/// @param style the style of the circle.
293 int y,
294 int radius,
295 const Stylizer& style) {
296 DrawPointEllipseFilled(x, y, radius, radius, style);
297}
298
299/// @brief Draw an ellipse made of braille dots.
300/// @param x the x coordinate of the center of the ellipse.
301/// @param y the y coordinate of the center of the ellipse.
302/// @param r1 the radius of the ellipse along the x axis.
303/// @param r2 the radius of the ellipse along the y axis.
304void Canvas::DrawPointEllipse(int x, int y, int r1, int r2) {
305 DrawPointEllipse(x, y, r1, r2, [](Pixel& /*pixel*/) {});
306}
307
308/// @brief Draw an ellipse made of braille dots.
309/// @param x the x coordinate of the center of the ellipse.
310/// @param y the y coordinate of the center of the ellipse.
311/// @param r1 the radius of the ellipse along the x axis.
312/// @param r2 the radius of the ellipse along the y axis.
313/// @param color the color of the ellipse.
315 int y,
316 int r1,
317 int r2,
318 const Color& color) {
319 DrawPointEllipse(x, y, r1, r2,
320 [color](Pixel& p) { p.foreground_color = color; });
321}
322
323/// @brief Draw an ellipse made of braille dots.
324/// @param x the x coordinate of the center of the ellipse.
325/// @param y the y coordinate of the center of the ellipse.
326/// @param r1 the radius of the ellipse along the x axis.
327/// @param r2 the radius of the ellipse along the y axis.
328/// @param style the style of the ellipse.
330 int y1,
331 int r1,
332 int r2,
333 const Stylizer& s) {
334 int x = -r1;
335 int y = 0;
336 int e2 = r2;
337 int dx = (1 + 2 * x) * e2 * e2;
338 int dy = x * x;
339 int err = dx + dy;
340
341 do {
342 DrawPoint(x1 - x, y1 + y, true, s);
343 DrawPoint(x1 + x, y1 + y, true, s);
344 DrawPoint(x1 + x, y1 - y, true, s);
345 DrawPoint(x1 - x, y1 - y, true, s);
346 e2 = 2 * err;
347 if (e2 >= dx) {
348 x++;
349 err += dx += 2 * r2 * r2;
350 }
351 if (e2 <= dy) {
352 y++;
353 err += dy += 2 * r1 * r1;
354 }
355 } while (x <= 0);
356
357 while (y++ < r2) {
358 DrawPoint(x1, y1 + y, true, s);
359 DrawPoint(x1, y1 - y, true, s);
360 }
361}
362
363/// @brief Draw a filled ellipse made of braille dots.
364/// @param x the x coordinate of the center of the ellipse.
365/// @param y the y coordinate of the center of the ellipse.
366/// @param r1 the radius of the ellipse along the x axis.
367/// @param r2 the radius of the ellipse along the y axis.
368void Canvas::DrawPointEllipseFilled(int x1, int y1, int r1, int r2) {
369 DrawPointEllipseFilled(x1, y1, r1, r2, [](Pixel& /*pixel*/) {});
370}
371
372/// @brief Draw a filled ellipse made of braille dots.
373/// @param x the x coordinate of the center of the ellipse.
374/// @param y the y coordinate of the center of the ellipse.
375/// @param r1 the radius of the ellipse along the x axis.
376/// @param r2 the radius of the ellipse along the y axis.
377/// @param color the color of the ellipse.
379 int y1,
380 int r1,
381 int r2,
382 const Color& color) {
383 DrawPointEllipseFilled(x1, y1, r1, r2,
384 [color](Pixel& p) { p.foreground_color = color; });
385}
386
387/// @brief Draw a filled ellipse made of braille dots.
388/// @param x the x coordinate of the center of the ellipse.
389/// @param y the y coordinate of the center of the ellipse.
390/// @param r1 the radius of the ellipse along the x axis.
391/// @param r2 the radius of the ellipse along the y axis.
392/// @param style the style of the ellipse.
394 int y1,
395 int r1,
396 int r2,
397 const Stylizer& s) {
398 int x = -r1;
399 int y = 0;
400 int e2 = r2;
401 int dx = (1 + 2 * x) * e2 * e2;
402 int dy = x * x;
403 int err = dx + dy;
404
405 do {
406 for (int xx = x1 + x; xx <= x1 - x; ++xx) {
407 DrawPoint(xx, y1 + y, true, s);
408 DrawPoint(xx, y1 - y, true, s);
409 }
410 e2 = 2 * err;
411 if (e2 >= dx) {
412 x++;
413 err += dx += 2 * (long)r2 * r2; // NOLINT
414 }
415 if (e2 <= dy) {
416 y++;
417 err += dy += 2 * (long)r1 * r1; // NOLINT
418 }
419 } while (x <= 0);
420
421 while (y++ < r2) {
422 for (int yy = y1 - y; yy <= y1 + y; ++yy) {
423 DrawPoint(x1, yy, true, s);
424 }
425 }
426}
427
428/// @brief Draw a block.
429/// @param x the x coordinate of the block.
430/// @param y the y coordinate of the block.
431/// @param value whether the block is filled or not.
432void Canvas::DrawBlock(int x, int y, bool value) {
433 DrawBlock(x, y, value, [](Pixel& /*pixel*/) {});
434}
435
436/// @brief Draw a block.
437/// @param x the x coordinate of the block.
438/// @param y the y coordinate of the block.
439/// @param value whether the block is filled or not.
440/// @param color the color of the block.
441void Canvas::DrawBlock(int x, int y, bool value, const Color& color) {
442 DrawBlock(x, y, value, [color](Pixel& p) { p.foreground_color = color; });
443}
444
445/// @brief Draw a block.
446/// @param x the x coordinate of the block.
447/// @param y the y coordinate of the block.
448/// @param value whether the block is filled or not.
449/// @param style the style of the block.
450void Canvas::DrawBlock(int x, int y, bool value, const Stylizer& style) {
451 Style(x, y, style);
452 if (value) {
453 DrawBlockOn(x, y);
454 } else {
455 DrawBlockOff(x, y);
456 }
457}
458
459/// @brief Draw a block.
460/// @param x the x coordinate of the block.
461/// @param y the y coordinate of the block.
462void Canvas::DrawBlockOn(int x, int y) {
463 if (!IsIn(x, y)) {
464 return;
465 }
466 y /= 2;
467 Cell& cell = storage_[XY{x / 2, y / 2}];
468 if (cell.type != CellType::kBlock) {
469 cell.content.character = " ";
470 cell.type = CellType::kBlock;
471 }
472
473 const uint8_t bit = (x % 2) * 2 + y % 2;
474 uint8_t value = g_map_block_inversed.at(cell.content.character);
475 value |= 1U << bit;
476 cell.content.character = g_map_block[value];
477}
478
479/// @brief Erase a block.
480/// @param x the x coordinate of the block.
481/// @param y the y coordinate of the block.
482void Canvas::DrawBlockOff(int x, int y) {
483 if (!IsIn(x, y)) {
484 return;
485 }
486 Cell& cell = storage_[XY{x / 2, y / 4}];
487 if (cell.type != CellType::kBlock) {
488 cell.content.character = " ";
489 cell.type = CellType::kBlock;
490 }
491 y /= 2;
492
493 const uint8_t bit = (y % 2) * 2 + x % 2;
494 uint8_t value = g_map_block_inversed.at(cell.content.character);
495 value &= ~(1U << bit);
496 cell.content.character = g_map_block[value];
497}
498
499/// @brief Toggle a block. If it is filled, it will be erased. If it is empty,
500/// it will be filled.
501/// @param x the x coordinate of the block.
502/// @param y the y coordinate of the block.
503void Canvas::DrawBlockToggle(int x, int y) {
504 if (!IsIn(x, y)) {
505 return;
506 }
507 Cell& cell = storage_[XY{x / 2, y / 4}];
508 if (cell.type != CellType::kBlock) {
509 cell.content.character = " ";
510 cell.type = CellType::kBlock;
511 }
512 y /= 2;
513
514 const uint8_t bit = (y % 2) * 2 + x % 2;
515 uint8_t value = g_map_block_inversed.at(cell.content.character);
516 value ^= 1U << bit;
517 cell.content.character = g_map_block[value];
518}
519
520/// @brief Draw a line made of block characters.
521/// @param x1 the x coordinate of the first point of the line.
522/// @param y1 the y coordinate of the first point of the line.
523/// @param x2 the x coordinate of the second point of the line.
524/// @param y2 the y coordinate of the second point of the line.
525void Canvas::DrawBlockLine(int x1, int y1, int x2, int y2) {
526 DrawBlockLine(x1, y1, x2, y2, [](Pixel& /*pixel*/) {});
527}
528
529/// @brief Draw a line made of block characters.
530/// @param x1 the x coordinate of the first point of the line.
531/// @param y1 the y coordinate of the first point of the line.
532/// @param x2 the x coordinate of the second point of the line.
533/// @param y2 the y coordinate of the second point of the line.
534/// @param color the color of the line.
535void Canvas::DrawBlockLine(int x1, int y1, int x2, int y2, const Color& color) {
536 DrawBlockLine(x1, y1, x2, y2,
537 [color](Pixel& p) { p.foreground_color = color; });
538}
539
540/// @brief Draw a line made of block characters.
541/// @param x1 the x coordinate of the first point of the line.
542/// @param y1 the y coordinate of the first point of the line.
543/// @param x2 the x coordinate of the second point of the line.
544/// @param y2 the y coordinate of the second point of the line.
545/// @param style the style of the line.
547 int y1,
548 int x2,
549 int y2,
550 const Stylizer& style) {
551 y1 /= 2;
552 y2 /= 2;
553
554 const int dx = std::abs(x2 - x1);
555 const int dy = std::abs(y2 - y1);
556 const int sx = x1 < x2 ? 1 : -1;
557 const int sy = y1 < y2 ? 1 : -1;
558 const int length = std::max(dx, dy);
559
560 if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
561 return;
562 }
563 if (dx + dx > width_ * height_) {
564 return;
565 }
566
567 int error = dx - dy;
568 for (int i = 0; i < length; ++i) {
569 DrawBlock(x1, y1 * 2, true, style);
570 if (2 * error >= -dy) {
571 error -= dy;
572 x1 += sx;
573 }
574 if (2 * error <= dx) {
575 error += dx;
576 y1 += sy;
577 }
578 }
579 DrawBlock(x2, y2 * 2, true, style);
580}
581
582/// @brief Draw a circle made of block characters.
583/// @param x the x coordinate of the center of the circle.
584/// @param y the y coordinate of the center of the circle.
585/// @param radius the radius of the circle.
586void Canvas::DrawBlockCircle(int x, int y, int radius) {
587 DrawBlockCircle(x, y, radius, nostyle);
588}
589
590/// @brief Draw a circle made of block characters.
591/// @param x the x coordinate of the center of the circle.
592/// @param y the y coordinate of the center of the circle.
593/// @param radius the radius of the circle.
594/// @param color the color of the circle.
595void Canvas::DrawBlockCircle(int x, int y, int radius, const Color& color) {
596 DrawBlockCircle(x, y, radius,
597 [color](Pixel& p) { p.foreground_color = color; });
598}
599
600/// @brief Draw a circle made of block characters.
601/// @param x the x coordinate of the center of the circle.
602/// @param y the y coordinate of the center of the circle.
603/// @param radius the radius of the circle.
604/// @param style the style of the circle.
605void Canvas::DrawBlockCircle(int x, int y, int radius, const Stylizer& style) {
606 DrawBlockEllipse(x, y, radius, radius, style);
607}
608
609/// @brief Draw a filled circle made of block characters.
610/// @param x the x coordinate of the center of the circle.
611/// @param y the y coordinate of the center of the circle.
612/// @param radius the radius of the circle.
613void Canvas::DrawBlockCircleFilled(int x, int y, int radius) {
614 DrawBlockCircleFilled(x, y, radius, nostyle);
615}
616
617/// @brief Draw a filled circle made of block characters.
618/// @param x the x coordinate of the center of the circle.
619/// @param y the y coordinate of the center of the circle.
620/// @param radius the radius of the circle.
621/// @param color the color of the circle.
623 int y,
624 int radius,
625 const Color& color) {
626 DrawBlockCircleFilled(x, y, radius,
627 [color](Pixel& p) { p.foreground_color = color; });
628}
629
630/// @brief Draw a filled circle made of block characters.
631/// @param x the x coordinate of the center of the circle.
632/// @param y the y coordinate of the center of the circle.
633/// @param radius the radius of the circle.
634/// @param style the style of the circle.
636 int y,
637 int radius,
638 const Stylizer& s) {
639 DrawBlockEllipseFilled(x, y, radius, radius, s);
640}
641
642/// @brief Draw an ellipse made of block characters.
643/// @param x the x coordinate of the center of the ellipse.
644/// @param y the y coordinate of the center of the ellipse.
645/// @param r1 the radius of the ellipse along the x axis.
646/// @param r2 the radius of the ellipse along the y axis.
647void Canvas::DrawBlockEllipse(int x, int y, int r1, int r2) {
648 DrawBlockEllipse(x, y, r1, r2, nostyle);
649}
650
651/// @brief Draw an ellipse made of block characters.
652/// @param x the x coordinate of the center of the ellipse.
653/// @param y the y coordinate of the center of the ellipse.
654/// @param r1 the radius of the ellipse along the x axis.
655/// @param r2 the radius of the ellipse along the y axis.
656/// @param color the color of the ellipse.
658 int y,
659 int r1,
660 int r2,
661 const Color& color) {
662 DrawBlockEllipse(x, y, r1, r2,
663 [color](Pixel& p) { p.foreground_color = color; });
664}
665
666/// @brief Draw an ellipse made of block characters.
667/// @param x the x coordinate of the center of the ellipse.
668/// @param y the y coordinate of the center of the ellipse.
669/// @param r1 the radius of the ellipse along the x axis.
670/// @param r2 the radius of the ellipse along the y axis.
671/// @param style the style of the ellipse.
673 int y1,
674 int r1,
675 int r2,
676 const Stylizer& s) {
677 y1 /= 2;
678 r2 /= 2;
679 int x = -r1;
680 int y = 0;
681 int e2 = r2;
682 int dx = (1 + 2 * x) * e2 * e2;
683 int dy = x * x;
684 int err = dx + dy;
685
686 do {
687 DrawBlock(x1 - x, 2 * (y1 + y), true, s);
688 DrawBlock(x1 + x, 2 * (y1 + y), true, s);
689 DrawBlock(x1 + x, 2 * (y1 - y), true, s);
690 DrawBlock(x1 - x, 2 * (y1 - y), true, s);
691 e2 = 2 * err;
692 if (e2 >= dx) {
693 x++;
694 err += dx += 2 * r2 * r2;
695 }
696 if (e2 <= dy) {
697 y++;
698 err += dy += 2 * r1 * r1;
699 }
700 } while (x <= 0);
701
702 while (y++ < r2) {
703 DrawBlock(x1, 2 * (y1 + y), true, s);
704 DrawBlock(x1, 2 * (y1 - y), true, s);
705 }
706}
707
708/// @brief Draw a filled ellipse made of block characters.
709/// @param x the x coordinate of the center of the ellipse.
710/// @param y the y coordinate of the center of the ellipse.
711/// @param r1 the radius of the ellipse along the x axis.
712/// @param r2 the radius of the ellipse along the y axis.
713void Canvas::DrawBlockEllipseFilled(int x, int y, int r1, int r2) {
714 DrawBlockEllipseFilled(x, y, r1, r2, nostyle);
715}
716
717/// @brief Draw a filled ellipse made of block characters.
718/// @param x the x coordinate of the center of the ellipse.
719/// @param y the y coordinate of the center of the ellipse.
720/// @param r1 the radius of the ellipse along the x axis.
721/// @param r2 the radius of the ellipse along the y axis.
722/// @param color the color of the ellipse.
724 int y,
725 int r1,
726 int r2,
727 const Color& color) {
728 DrawBlockEllipseFilled(x, y, r1, r2,
729 [color](Pixel& p) { p.foreground_color = color; });
730}
731
732/// @brief Draw a filled ellipse made of block characters.
733/// @param x the x coordinate of the center of the ellipse.
734/// @param y the y coordinate of the center of the ellipse.
735/// @param r1 the radius of the ellipse along the x axis.
736/// @param r2 the radius of the ellipse along the y axis.
737/// @param style the style of the ellipse.
739 int y1,
740 int r1,
741 int r2,
742 const Stylizer& s) {
743 y1 /= 2;
744 r2 /= 2;
745 int x = -r1;
746 int y = 0;
747 int e2 = r2;
748 int dx = (1 + 2 * x) * e2 * e2;
749 int dy = x * x;
750 int err = dx + dy;
751
752 do {
753 for (int xx = x1 + x; xx <= x1 - x; ++xx) {
754 DrawBlock(xx, 2 * (y1 + y), true, s);
755 DrawBlock(xx, 2 * (y1 - y), true, s);
756 }
757 e2 = 2 * err;
758 if (e2 >= dx) {
759 x++;
760 err += dx += 2 * r2 * r2;
761 }
762 if (e2 <= dy) {
763 y++;
764 err += dy += 2 * r1 * r1;
765 }
766 } while (x <= 0);
767
768 while (y++ < r2) {
769 for (int yy = y1 + y; yy <= y1 - y; ++yy) {
770 DrawBlock(x1, 2 * yy, true, s);
771 }
772 }
773}
774
775/// @brief Draw a piece of text.
776/// @param x the x coordinate of the text.
777/// @param y the y coordinate of the text.
778/// @param value the text to draw.
779void Canvas::DrawText(int x, int y, const std::string& value) {
780 DrawText(x, y, value, nostyle);
781}
782
783/// @brief Draw a piece of text.
784/// @param x the x coordinate of the text.
785/// @param y the y coordinate of the text.
786/// @param value the text to draw.
787/// @param color the color of the text.
789 int y,
790 const std::string& value,
791 const Color& color) {
792 DrawText(x, y, value, [color](Pixel& p) { p.foreground_color = color; });
793}
794
795/// @brief Draw a piece of text.
796/// @param x the x coordinate of the text.
797/// @param y the y coordinate of the text.
798/// @param value the text to draw.
799/// @param style the style of the text.
801 int y,
802 const std::string& value,
803 const Stylizer& style) {
804 for (const auto& it : Utf8ToGlyphs(value)) {
805 if (!IsIn(x, y)) {
806 x += 2;
807 continue;
808 }
809 Cell& cell = storage_[XY{x / 2, y / 4}];
810 cell.type = CellType::kText;
811 cell.content.character = it;
812 style(cell.content);
813 x += 2;
814 }
815}
816
817/// @brief Modify a pixel at a given location.
818/// @param style a function that modifies the pixel.
819void Canvas::Style(int x, int y, const Stylizer& style) {
820 if (IsIn(x, y)) {
821 style(storage_[XY{x / 2, y / 4}].content);
822 }
823}
824
825namespace {
826
827class CanvasNodeBase : public Node {
828 public:
829 CanvasNodeBase() = default;
830
831 void Render(Screen& screen) override {
832 const Canvas& c = canvas();
833 const int y_max = std::min(c.height() / 4, box_.y_max - box_.y_min + 1);
834 const int x_max = std::min(c.width() / 2, box_.x_max - box_.x_min + 1);
835 for (int y = 0; y < y_max; ++y) {
836 for (int x = 0; x < x_max; ++x) {
837 screen.PixelAt(box_.x_min + x, box_.y_min + y) = c.GetPixel(x, y);
838 }
839 }
840 }
841
842 virtual const Canvas& canvas() = 0;
843};
844
845} // namespace
846
847/// @brief Produce an element from a Canvas, or a reference to a Canvas.
849 class Impl : public CanvasNodeBase {
850 public:
851 explicit Impl(ConstRef<Canvas> canvas) : canvas_(std::move(canvas)) {
852 requirement_.min_x = (canvas_->width() + 1) / 2;
853 requirement_.min_y = (canvas_->height() + 3) / 4;
854 }
855 const Canvas& canvas() final { return *canvas_; }
856 ConstRef<Canvas> canvas_;
857 };
858 return std::make_shared<Impl>(std::move(canvas));
859}
860
861/// @brief Produce an element drawing a canvas of requested size.
862/// @param width the width of the canvas.
863/// @param height the height of the canvas.
864/// @param fn a function drawing the canvas.
865Element canvas(int width, int height, std::function<void(Canvas&)> fn) {
866 class Impl : public CanvasNodeBase {
867 public:
868 Impl(int width, int height, std::function<void(Canvas&)> fn)
869 : width_(width), height_(height), fn_(std::move(fn)) {}
870
871 void ComputeRequirement() final {
872 requirement_.min_x = (width_ + 1) / 2;
873 requirement_.min_y = (height_ + 3) / 4;
874 }
875
876 void Render(Screen& screen) final {
877 const int width = (box_.x_max - box_.x_min + 1) * 2;
878 const int height = (box_.y_max - box_.y_min + 1) * 4;
879 canvas_ = Canvas(width, height);
880 fn_(canvas_);
881 CanvasNodeBase::Render(screen);
882 }
883
884 const Canvas& canvas() final { return canvas_; }
885 Canvas canvas_;
886 int width_;
887 int height_;
888 std::function<void(Canvas&)> fn_;
889 };
890 return std::make_shared<Impl>(width, height, std::move(fn));
891}
892
893/// @brief Produce an element drawing a canvas.
894/// @param fn a function drawing the canvas.
895Element canvas(std::function<void(Canvas&)> fn) {
896 const int default_dim = 12;
897 return canvas(default_dim, default_dim, std::move(fn));
898}
899
900} // namespace ftxui
901
902// Copyright 2021 Arthur Sonzogni. All rights reserved.
903// Use of this source code is governed by the MIT license that can be found in
904// the LICENSE file.
A class representing terminal colors.
Definition color.hpp:18
An adapter. Own or reference an immutable object.
Definition ref.hpp:11
A rectangular grid of Pixel.
Definition screen.hpp:57
Pixel & PixelAt(int x, int y)
Access a Pixel at a given position.
Definition screen.cpp:470
std::shared_ptr< Node > Element
Definition elements.hpp:19
std::vector< std::string > Utf8ToGlyphs(const std::string &input)
Definition string.cpp:1727
Element canvas(ConstRef< Canvas >)
Produce an element from a Canvas, or a reference to a Canvas.
Definition canvas.cpp:848
void Render(Screen &screen, const Element &element)
Display an element on a ftxui::Screen.
Definition node.cpp:44
Decorator color(Color)
Decorate using a foreground color.
Definition color.cpp:86
void DrawBlockLine(int x1, int y1, int x2, int y2)
Draw a line made of block characters.
Definition canvas.cpp:525
void DrawPointEllipseFilled(int x, int y, int r1, int r2)
Draw a filled ellipse made of braille dots.
Definition canvas.cpp:368
void DrawPointLine(int x1, int y1, int x2, int y2)
Draw a line made of braille dots.
Definition canvas.cpp:185
void DrawText(int x, int y, const std::string &value)
Draw a piece of text.
Definition canvas.cpp:779
Canvas()=default
std::function< void(Pixel &)> Stylizer
Definition canvas.hpp:30
void DrawBlockOn(int x, int y)
Draw a block.
Definition canvas.cpp:462
void DrawPointCircleFilled(int x, int y, int radius)
Draw a filled circle made of braille dots.
Definition canvas.cpp:270
void DrawPointOn(int x, int y)
Draw a braille dot.
Definition canvas.cpp:131
void DrawPointOff(int x, int y)
Erase a braille dot.
Definition canvas.cpp:148
Pixel GetPixel(int x, int y) const
Get the content of a cell.
Definition canvas.cpp:92
void DrawBlockEllipseFilled(int x1, int y1, int r1, int r2)
Draw a filled ellipse made of block characters.
Definition canvas.cpp:713
void DrawPointEllipse(int x, int y, int r1, int r2)
Draw an ellipse made of braille dots.
Definition canvas.cpp:304
void DrawPoint(int x, int y, bool value)
Draw a braille dot.
Definition canvas.cpp:101
void DrawBlockEllipse(int x1, int y1, int r1, int r2)
Draw an ellipse made of block characters.
Definition canvas.cpp:647
void DrawBlockToggle(int x, int y)
Toggle a block. If it is filled, it will be erased. If it is empty, it will be filled.
Definition canvas.cpp:503
void DrawBlockCircle(int x1, int y1, int radius)
Draw a circle made of block characters.
Definition canvas.cpp:586
void DrawBlockCircleFilled(int x1, int y1, int radius)
Draw a filled circle made of block characters.
Definition canvas.cpp:613
void DrawPointCircle(int x, int y, int radius)
Draw a circle made of braille dots.
Definition canvas.cpp:243
int height() const
Definition canvas.hpp:27
void DrawBlockOff(int x, int y)
Erase a block.
Definition canvas.cpp:482
int width() const
Definition canvas.hpp:26
void DrawBlock(int x, int y, bool value)
Draw a block.
Definition canvas.cpp:432
void Style(int x, int y, const Stylizer &style)
Modify a pixel at a given location.
Definition canvas.cpp:819
void DrawPointToggle(int x, int y)
Toggle a braille dot. A filled one will be erased, and the other will be drawn.
Definition canvas.cpp:166
A unicode character and its associated style.
Definition screen.hpp:16
Color foreground_color
Definition screen.hpp:25
std::string character
Definition screen.hpp:21