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