FTXUI  5.0.0
C++ functional terminal UI.
Loading...
Searching...
No Matches
canvas.cpp
Go to the documentation of this file.
1// Copyright 2021 Arthur Sonzogni. All rights reserved.
2// Use of this source code is governed by the MIT license that can be found in
3// the LICENSE file.
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 the width of the canvas. A cell is a 2x4 braille dot.
89/// @param height the height of the canvas. A cell is a 2x4 braille dot.
90Canvas::Canvas(int width, int height)
91 : width_(width),
92 height_(height),
93 storage_(width_ * height_ / 8 /* NOLINT */) {}
94
95/// @brief Get the content of a cell.
96/// @param x the x coordinate of the cell.
97/// @param y the y coordinate of the cell.
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 Draw a braille dot.
104/// @param x the x coordinate of the dot.
105/// @param y the y coordinate of the dot.
106/// @param value whether the dot is filled or not.
107void Canvas::DrawPoint(int x, int y, bool value) {
108 DrawPoint(x, y, value, [](Pixel& /*pixel*/) {});
109}
110
111/// @brief Draw a braille dot.
112/// @param x the x coordinate of the dot.
113/// @param y the y coordinate of the dot.
114/// @param value whether the dot is filled or not.
115/// @param color the color of the dot.
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 Draw a braille dot.
121/// @param x the x coordinate of the dot.
122/// @param y the y coordinate of the dot.
123/// @param value whether the dot is filled or not.
124/// @param style the style of the cell.
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 Draw a braille dot.
135/// @param x the x coordinate of the dot.
136/// @param y the y coordinate of the dot.
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 Erase a braille dot.
152/// @param x the x coordinate of the dot.
153/// @param y the y coordinate of the dot.
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 Toggle a braille dot. A filled one will be erased, and the other will
169/// be drawn.
170/// @param x the x coordinate of the dot.
171/// @param y the y coordinate of the dot.
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 Draw a line made of braille dots.
187/// @param x1 the x coordinate of the first dot.
188/// @param y1 the y coordinate of the first dot.
189/// @param x2 the x coordinate of the second dot.
190/// @param y2 the y coordinate of the second dot.
191void Canvas::DrawPointLine(int x1, int y1, int x2, int y2) {
192 DrawPointLine(x1, y1, x2, y2, [](Pixel& /*pixel*/) {});
193}
194
195/// @brief Draw a line made of braille dots.
196/// @param x1 the x coordinate of the first dot.
197/// @param y1 the y coordinate of the first dot.
198/// @param x2 the x coordinate of the second dot.
199/// @param y2 the y coordinate of the second dot.
200/// @param color the color of the line.
201void Canvas::DrawPointLine(int x1, int y1, int x2, int y2, const Color& color) {
203 [color](Pixel& p) { p.foreground_color = color; });
204}
205
206/// @brief Draw a line made of braille dots.
207/// @param x1 the x coordinate of the first dot.
208/// @param y1 the y coordinate of the first dot.o
209/// @param x2 the x coordinate of the second dot.
210/// @param y2 the y coordinate of the second dot.
211/// @param style the style of the line.
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 Draw a circle made of braille dots.
246/// @param x the x coordinate of the center of the circle.
247/// @param y the y coordinate of the center of the circle.
248/// @param radius the radius of the circle.
249void Canvas::DrawPointCircle(int x, int y, int radius) {
250 DrawPointCircle(x, y, radius, [](Pixel& /*pixel*/) {});
251}
252
253/// @brief Draw a circle made of braille dots.
254/// @param x the x coordinate of the center of the circle.
255/// @param y the y coordinate of the center of the circle.
256/// @param radius the radius of the circle.
257/// @param color the color of the circle.
258void Canvas::DrawPointCircle(int x, int y, int radius, const Color& color) {
260 [color](Pixel& p) { p.foreground_color = color; });
261}
262
263/// @brief Draw a circle made of braille dots.
264/// @param x the x coordinate of the center of the circle.
265/// @param y the y coordinate of the center of the circle.
266/// @param radius the radius of the circle.
267/// @param style the style of the circle.
268void Canvas::DrawPointCircle(int x, int y, int radius, const Stylizer& style) {
270}
271
272/// @brief Draw a filled circle made of braille dots.
273/// @param x the x coordinate of the center of the circle.
274/// @param y the y coordinate of the center of the circle.
275/// @param radius the radius of the circle.
276void Canvas::DrawPointCircleFilled(int x, int y, int radius) {
277 DrawPointCircleFilled(x, y, radius, [](Pixel& /*pixel*/) {});
278}
279
280/// @brief Draw a filled circle made of braille dots.
281/// @param x the x coordinate of the center of the circle.
282/// @param y the y coordinate of the center of the circle.
283/// @param radius the radius of the circle.
284/// @param color the color of the circle.
286 int y,
287 int radius,
288 const Color& color) {
290 [color](Pixel& p) { p.foreground_color = color; });
291}
292
293/// @brief Draw a filled circle made of braille dots.
294/// @param x the x coordinate of the center of the circle.
295/// @param y the y coordinate of the center of the circle.
296/// @param radius the radius of the circle.
297/// @param style the style of the circle.
299 int y,
300 int radius,
301 const Stylizer& style) {
303}
304
305/// @brief Draw an ellipse made of braille dots.
306/// @param x the x coordinate of the center of the ellipse.
307/// @param y the y coordinate of the center of the ellipse.
308/// @param r1 the radius of the ellipse along the x axis.
309/// @param r2 the radius of the ellipse along the y axis.
310void Canvas::DrawPointEllipse(int x, int y, int r1, int r2) {
311 DrawPointEllipse(x, y, r1, r2, [](Pixel& /*pixel*/) {});
312}
313
314/// @brief Draw an ellipse made of braille dots.
315/// @param x the x coordinate of the center of the ellipse.
316/// @param y the y coordinate of the center of the ellipse.
317/// @param r1 the radius of the ellipse along the x axis.
318/// @param r2 the radius of the ellipse along the y axis.
319/// @param color the color of the 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 Draw an ellipse made of braille dots.
330/// @param x1 the x coordinate of the center of the ellipse.
331/// @param y1 the y coordinate of the center of the ellipse.
332/// @param r1 the radius of the ellipse along the x axis.
333/// @param r2 the radius of the ellipse along the y axis.
334/// @param s the style of the 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 Draw a filled ellipse made of braille dots.
370/// @param x1 the x coordinate of the center of the ellipse.
371/// @param y1 the y coordinate of the center of the ellipse.
372/// @param r1 the radius of the ellipse along the x axis.
373/// @param r2 the radius of the ellipse along the y axis.
374void Canvas::DrawPointEllipseFilled(int x1, int y1, int r1, int r2) {
375 DrawPointEllipseFilled(x1, y1, r1, r2, [](Pixel& /*pixel*/) {});
376}
377
378/// @brief Draw a filled ellipse made of braille dots.
379/// @param x1 the x coordinate of the center of the ellipse.
380/// @param y1 the y coordinate of the center of the ellipse.
381/// @param r1 the radius of the ellipse along the x axis.
382/// @param r2 the radius of the ellipse along the y axis.
383/// @param color the color of the ellipse.
385 int y1,
386 int r1,
387 int r2,
388 const Color& color) {
390 [color](Pixel& p) { p.foreground_color = color; });
391}
392
393/// @brief Draw a filled ellipse made of braille dots.
394/// @param x1 the x coordinate of the center of the ellipse.
395/// @param y1 the y coordinate of the center of the ellipse.
396/// @param r1 the radius of the ellipse along the x axis.
397/// @param r2 the radius of the ellipse along the y axis.
398/// @param s the style of the 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 Draw a block.
435/// @param x the x coordinate of the block.
436/// @param y the y coordinate of the block.
437/// @param value whether the block is filled or not.
438void Canvas::DrawBlock(int x, int y, bool value) {
439 DrawBlock(x, y, value, [](Pixel& /*pixel*/) {});
440}
441
442/// @brief Draw a block.
443/// @param x the x coordinate of the block.
444/// @param y the y coordinate of the block.
445/// @param value whether the block is filled or not.
446/// @param color the color of the block.
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 Draw a block.
452/// @param x the x coordinate of the block.
453/// @param y the y coordinate of the block.
454/// @param value whether the block is filled or not.
455/// @param style the style of the block.
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 Draw a block.
466/// @param x the x coordinate of the block.
467/// @param y the y coordinate of the block.
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 Erase a block.
486/// @param x the x coordinate of the block.
487/// @param y the y coordinate of the block.
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 Toggle a block. If it is filled, it will be erased. If it is empty,
506/// it will be filled.
507/// @param x the x coordinate of the block.
508/// @param y the y coordinate of the block.
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 Draw a line made of block characters.
527/// @param x1 the x coordinate of the first point of the line.
528/// @param y1 the y coordinate of the first point of the line.
529/// @param x2 the x coordinate of the second point of the line.
530/// @param y2 the y coordinate of the second point of the line.
531void Canvas::DrawBlockLine(int x1, int y1, int x2, int y2) {
532 DrawBlockLine(x1, y1, x2, y2, [](Pixel& /*pixel*/) {});
533}
534
535/// @brief Draw a line made of block characters.
536/// @param x1 the x coordinate of the first point of the line.
537/// @param y1 the y coordinate of the first point of the line.
538/// @param x2 the x coordinate of the second point of the line.
539/// @param y2 the y coordinate of the second point of the line.
540/// @param color the color of the line.
541void Canvas::DrawBlockLine(int x1, int y1, int x2, int y2, const Color& color) {
543 [color](Pixel& p) { p.foreground_color = color; });
544}
545
546/// @brief Draw a line made of block characters.
547/// @param x1 the x coordinate of the first point of the line.
548/// @param y1 the y coordinate of the first point of the line.
549/// @param x2 the x coordinate of the second point of the line.
550/// @param y2 the y coordinate of the second point of the line.
551/// @param style the style of the line.
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 Draw a circle made of block characters.
589/// @param x the x coordinate of the center of the circle.
590/// @param y the y coordinate of the center of the circle.
591/// @param radius the radius of the circle.
592void Canvas::DrawBlockCircle(int x, int y, int radius) {
594}
595
596/// @brief Draw a circle made of block characters.
597/// @param x the x coordinate of the center of the circle.
598/// @param y the y coordinate of the center of the circle.
599/// @param radius the radius of the circle.
600/// @param color the color of the circle.
601void Canvas::DrawBlockCircle(int x, int y, int radius, const Color& color) {
603 [color](Pixel& p) { p.foreground_color = color; });
604}
605
606/// @brief Draw a circle made of block characters.
607/// @param x the x coordinate of the center of the circle.
608/// @param y the y coordinate of the center of the circle.
609/// @param radius the radius of the circle.
610/// @param style the style of the circle.
611void Canvas::DrawBlockCircle(int x, int y, int radius, const Stylizer& style) {
613}
614
615/// @brief Draw a filled circle made of block characters.
616/// @param x the x coordinate of the center of the circle.
617/// @param y the y coordinate of the center of the circle.
618/// @param radius the radius of the circle.
619void Canvas::DrawBlockCircleFilled(int x, int y, int radius) {
621}
622
623/// @brief Draw a filled circle made of block characters.
624/// @param x the x coordinate of the center of the circle.
625/// @param y the y coordinate of the center of the circle.
626/// @param radius the radius of the circle.
627/// @param color the color of the circle.
629 int y,
630 int radius,
631 const Color& color) {
633 [color](Pixel& p) { p.foreground_color = color; });
634}
635
636/// @brief Draw a filled circle made of block characters.
637/// @param x the x coordinate of the center of the circle.
638/// @param y the y coordinate of the center of the circle.
639/// @param radius the radius of the circle.
640/// @param s the style of the circle.
642 int y,
643 int radius,
644 const Stylizer& s) {
646}
647
648/// @brief Draw an ellipse made of block characters.
649/// @param x the x coordinate of the center of the ellipse.
650/// @param y the y coordinate of the center of the ellipse.
651/// @param r1 the radius of the ellipse along the x axis.
652/// @param r2 the radius of the ellipse along the y axis.
653void Canvas::DrawBlockEllipse(int x, int y, int r1, int r2) {
654 DrawBlockEllipse(x, y, r1, r2, nostyle);
655}
656
657/// @brief Draw an ellipse made of block characters.
658/// @param x the x coordinate of the center of the ellipse.
659/// @param y the y coordinate of the center of the ellipse.
660/// @param r1 the radius of the ellipse along the x axis.
661/// @param r2 the radius of the ellipse along the y axis.
662/// @param color the color of the 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 Draw an ellipse made of block characters.
673/// @param x1 the x coordinate of the center of the ellipse.
674/// @param y1 the y coordinate of the center of the ellipse.
675/// @param r1 the radius of the ellipse along the x axis.
676/// @param r2 the radius of the ellipse along the y axis.
677/// @param s the style of the 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 Draw a filled ellipse made of block characters.
715/// @param x the x coordinate of the center of the ellipse.
716/// @param y the y coordinate of the center of the ellipse.
717/// @param r1 the radius of the ellipse along the x axis.
718/// @param r2 the radius of the ellipse along the y axis.
719void Canvas::DrawBlockEllipseFilled(int x, int y, int r1, int r2) {
721}
722
723/// @brief Draw a filled ellipse made of block characters.
724/// @param x the x coordinate of the center of the ellipse.
725/// @param y the y coordinate of the center of the ellipse.
726/// @param r1 the radius of the ellipse along the x axis.
727/// @param r2 the radius of the ellipse along the y axis.
728/// @param color the color of the ellipse.
730 int y,
731 int r1,
732 int r2,
733 const Color& color) {
735 [color](Pixel& p) { p.foreground_color = color; });
736}
737
738/// @brief Draw a filled ellipse made of block characters.
739/// @param x1 the x coordinate of the center of the ellipse.
740/// @param y1 the y coordinate of the center of the ellipse.
741/// @param r1 the radius of the ellipse along the x axis.
742/// @param r2 the radius of the ellipse along the y axis.
743/// @param s the style of the 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 Draw a piece of text.
782/// @param x the x coordinate of the text.
783/// @param y the y coordinate of the text.
784/// @param value the text to draw.
785void Canvas::DrawText(int x, int y, const std::string& value) {
786 DrawText(x, y, value, nostyle);
787}
788
789/// @brief Draw a piece of text.
790/// @param x the x coordinate of the text.
791/// @param y the y coordinate of the text.
792/// @param value the text to draw.
793/// @param color the color of the text.
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 Draw a piece of text.
802/// @param x the x coordinate of the text.
803/// @param y the y coordinate of the text.
804/// @param value the text to draw.
805/// @param style the style of the text.
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 Directly draw a predefined pixel at the given coordinate
824/// @param x the x coordinate of the pixel.
825/// @param y the y coordinate of the pixel.
826/// @param p the pixel to draw.
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 Draw a predefined image, with top-left corner at the given coordinate
834/// You can supply negative coordinates to align the image however you like -
835/// only the 'visible' portion will be drawn
836/// @param x the x coordinate corresponding to the top-left corner of the image.
837/// @param y the y coordinate corresponding to the top-left corner of the image.
838/// @param image the image to draw.
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 Modify a pixel at a given location.
860/// @param style a function that modifies the 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 Produce an element from a Canvas, or a reference to a Canvas.
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_; }
900 };
901 return std::make_shared<Impl>(canvas);
902}
903
904/// @brief Produce an element drawing a canvas of requested size.
905/// @param width the width of the canvas.
906/// @param height the height of the canvas.
907/// @param fn a function drawing the canvas.
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_; }
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 Produce an element drawing a canvas.
937/// @param fn a function drawing the canvas.
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
A class representing terminal colors.
Definition color.hpp:20
An adapter. Own or reference an immutable object.
Definition ref.hpp:17
A rectangular grid of Pixel.
Definition image.hpp:17
A rectangular grid of Pixel.
Definition screen.hpp:27
std::shared_ptr< Node > Element
Definition elements.hpp:22
std::shared_ptr< T > Make(Args &&... args)
Definition component.hpp:26
std::vector< std::string > Utf8ToGlyphs(const std::string &input)
Definition string.cpp:1357
Element canvas(ConstRef< Canvas >)
Produce an element from a Canvas, or a reference to a Canvas.
Definition canvas.cpp:891
void Render(Screen &screen, const Element &element)
Display an element on a ftxui::Screen.
Definition node.cpp:72
Decorator color(Color)
Decorate using a foreground color.
Definition color.cpp:110
void DrawImage(int x, int y, const Image &)
Draw a predefined image, with top-left corner at the given coordinate You can supply negative coordin...
Definition canvas.cpp:839
void DrawBlockLine(int x1, int y1, int x2, int y2)
Draw a line made of block characters.
Definition canvas.cpp:531
void DrawPointEllipseFilled(int x, int y, int r1, int r2)
Draw a filled ellipse made of braille dots.
Definition canvas.cpp:374
void DrawPointLine(int x1, int y1, int x2, int y2)
Draw a line made of braille dots.
Definition canvas.cpp:191
void DrawText(int x, int y, const std::string &value)
Draw a piece of text.
Definition canvas.cpp:785
Canvas()=default
std::function< void(Pixel &)> Stylizer
Definition canvas.hpp:33
void DrawBlockOn(int x, int y)
Draw a block.
Definition canvas.cpp:468
void DrawPointCircleFilled(int x, int y, int radius)
Draw a filled circle made of braille dots.
Definition canvas.cpp:276
void DrawPointOn(int x, int y)
Draw a braille dot.
Definition canvas.cpp:137
void DrawPointOff(int x, int y)
Erase a braille dot.
Definition canvas.cpp:154
Pixel GetPixel(int x, int y) const
Get the content of a cell.
Definition canvas.cpp:98
void DrawBlockEllipseFilled(int x1, int y1, int r1, int r2)
Draw a filled ellipse made of block characters.
Definition canvas.cpp:719
void DrawPointEllipse(int x, int y, int r1, int r2)
Draw an ellipse made of braille dots.
Definition canvas.cpp:310
void DrawPoint(int x, int y, bool value)
Draw a braille dot.
Definition canvas.cpp:107
void DrawBlockEllipse(int x1, int y1, int r1, int r2)
Draw an ellipse made of block characters.
Definition canvas.cpp:653
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:509
void DrawBlockCircle(int x1, int y1, int radius)
Draw a circle made of block characters.
Definition canvas.cpp:592
void DrawBlockCircleFilled(int x1, int y1, int radius)
Draw a filled circle made of block characters.
Definition canvas.cpp:619
void DrawPointCircle(int x, int y, int radius)
Draw a circle made of braille dots.
Definition canvas.cpp:249
int height() const
Definition canvas.hpp:30
void DrawBlockOff(int x, int y)
Erase a block.
Definition canvas.cpp:488
int width() const
Definition canvas.hpp:29
void DrawBlock(int x, int y, bool value)
Draw a block.
Definition canvas.cpp:438
void Style(int x, int y, const Stylizer &style)
Modify a pixel at a given location.
Definition canvas.cpp:861
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:172
void DrawPixel(int x, int y, const Pixel &)
Directly draw a predefined pixel at the given coordinate.
Definition canvas.cpp:827
A Unicode character and its associated style.
Definition pixel.hpp:15
Color foreground_color
Definition pixel.hpp:47