48uint8_t g_map_braille[2][4][2] = {
50 {0b00000000, 0b00000001},
51 {0b00000000, 0b00000010},
52 {0b00000000, 0b00000100},
53 {0b00000001, 0b00000000},
56 {0b00000000, 0b00001000},
57 {0b00000000, 0b00010000},
58 {0b00000000, 0b00100000},
59 {0b00000010, 0b00000000},
64std::vector<std::string> g_map_block = {
65 " ",
"▘",
"▖",
"▌",
"▝",
"▀",
"▞",
"▛",
66 "▗",
"▚",
"▄",
"▙",
"▐",
"▜",
"▟",
"█",
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},
77constexpr auto nostyle = [](Pixel& ) {};
87 storage_(width_ * height_ / 8 ) {}
93 auto it = storage_.find(XY{x, y});
94 return (it == storage_.end()) ?
Pixel{} : it->second.content;
135 Cell& cell = storage_[XY{x / 2, y / 4}];
136 if (cell.type != CellType::kBraille) {
138 cell.type = CellType::kBraille;
141 cell.content.
character[1] |= g_map_braille[x % 2][y % 4][0];
142 cell.content.
character[2] |= g_map_braille[x % 2][y % 4][1];
152 Cell& cell = storage_[XY{x / 2, y / 4}];
153 if (cell.type != CellType::kBraille) {
155 cell.type = CellType::kBraille;
158 cell.content.
character[1] &= ~(g_map_braille[x % 2][y % 4][0]);
159 cell.content.
character[2] &= ~(g_map_braille[x % 2][y % 4][1]);
170 Cell& cell = storage_[XY{x / 2, y / 4}];
171 if (cell.type != CellType::kBraille) {
173 cell.type = CellType::kBraille;
176 cell.content.
character[1] ^= g_map_braille[x % 2][y % 4][0];
177 cell.content.
character[2] ^= g_map_braille[x % 2][y % 4][1];
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);
217 if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
220 if (dx + dx > width_ * height_) {
225 for (
int i = 0; i < length; ++i) {
227 if (2 * error >= -dy) {
231 if (2 * error <= dx) {
337 int dx = (1 + 2 * x) * e2 * e2;
349 err += dx += 2 * r2 * r2;
353 err += dy += 2 * r1 * r1;
401 int dx = (1 + 2 * x) * e2 * e2;
406 for (
int xx = x1 + x; xx <= x1 - x; ++xx) {
413 err += dx += 2 * (long)r2 * r2;
417 err += dy += 2 * (long)r1 * r1;
422 for (
int yy = y1 - y; yy <= y1 + y; ++yy) {
467 Cell& cell = storage_[XY{x / 2, y / 2}];
468 if (cell.type != CellType::kBlock) {
470 cell.type = CellType::kBlock;
473 const uint8_t bit = (x % 2) * 2 + y % 2;
474 uint8_t value = g_map_block_inversed.at(cell.content.
character);
476 cell.content.
character = g_map_block[value];
486 Cell& cell = storage_[XY{x / 2, y / 4}];
487 if (cell.type != CellType::kBlock) {
489 cell.type = CellType::kBlock;
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];
507 Cell& cell = storage_[XY{x / 2, y / 4}];
508 if (cell.type != CellType::kBlock) {
510 cell.type = CellType::kBlock;
514 const uint8_t bit = (y % 2) * 2 + x % 2;
515 uint8_t value = g_map_block_inversed.at(cell.content.
character);
517 cell.content.
character = g_map_block[value];
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);
560 if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
563 if (dx + dx > width_ * height_) {
568 for (
int i = 0; i < length; ++i) {
570 if (2 * error >= -dy) {
574 if (2 * error <= dx) {
682 int dx = (1 + 2 * x) * e2 * e2;
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);
694 err += dx += 2 * r2 * r2;
698 err += dy += 2 * r1 * r1;
748 int dx = (1 + 2 * x) * e2 * e2;
753 for (
int xx = x1 + x; xx <= x1 - x; ++xx) {
760 err += dx += 2 * r2 * r2;
764 err += dy += 2 * r1 * r1;
769 for (
int yy = y1 + y; yy <= y1 - y; ++yy) {
790 const std::string& value,
802 const std::string& value,
809 Cell& cell = storage_[XY{x / 2, y / 4}];
810 cell.type = CellType::kText;
821 style(storage_[XY{x / 2, y / 4}].content);
827class CanvasNodeBase :
public Node {
829 CanvasNodeBase() =
default;
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) {
842 virtual const Canvas&
canvas() = 0;
849 class Impl :
public CanvasNodeBase {
852 requirement_.min_x = (canvas_->width() + 1) / 2;
853 requirement_.min_y = (canvas_->height() + 3) / 4;
858 return std::make_shared<Impl>(std::move(
canvas));
866 class Impl :
public CanvasNodeBase {
868 Impl(
int width,
int height, std::function<
void(
Canvas&)> fn)
869 : width_(width), height_(height), fn_(std::move(fn)) {}
871 void ComputeRequirement()
final {
872 requirement_.min_x = (width_ + 1) / 2;
873 requirement_.min_y = (height_ + 3) / 4;
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);
881 CanvasNodeBase::Render(screen);
888 std::function<void(
Canvas&)> fn_;
890 return std::make_shared<Impl>(width, height, std::move(fn));
896 const int default_dim = 12;
897 return canvas(default_dim, default_dim, std::move(fn));
A class representing terminal colors.
An adapter. Own or reference an immutable object.
A rectangular grid of Pixel.
Pixel & PixelAt(int x, int y)
Access a Pixel at a given position.
std::shared_ptr< Node > Element
std::vector< std::string > Utf8ToGlyphs(const std::string &input)
Element canvas(ConstRef< Canvas >)
Produce an element from a Canvas, or a reference to a Canvas.
void Render(Screen &screen, const Element &element)
Display an element on a ftxui::Screen.
Decorator color(Color)
Decorate using a foreground color.
void DrawBlockLine(int x1, int y1, int x2, int y2)
Draw a line made of block characters.
void DrawPointEllipseFilled(int x, int y, int r1, int r2)
Draw a filled ellipse made of braille dots.
void DrawPointLine(int x1, int y1, int x2, int y2)
Draw a line made of braille dots.
void DrawText(int x, int y, const std::string &value)
Draw a piece of text.
std::function< void(Pixel &)> Stylizer
void DrawBlockOn(int x, int y)
Draw a block.
void DrawPointCircleFilled(int x, int y, int radius)
Draw a filled circle made of braille dots.
void DrawPointOn(int x, int y)
Draw a braille dot.
void DrawPointOff(int x, int y)
Erase a braille dot.
Pixel GetPixel(int x, int y) const
Get the content of a cell.
void DrawBlockEllipseFilled(int x1, int y1, int r1, int r2)
Draw a filled ellipse made of block characters.
void DrawPointEllipse(int x, int y, int r1, int r2)
Draw an ellipse made of braille dots.
void DrawPoint(int x, int y, bool value)
Draw a braille dot.
void DrawBlockEllipse(int x1, int y1, int r1, int r2)
Draw an ellipse made of block characters.
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.
void DrawBlockCircle(int x1, int y1, int radius)
Draw a circle made of block characters.
void DrawBlockCircleFilled(int x1, int y1, int radius)
Draw a filled circle made of block characters.
void DrawPointCircle(int x, int y, int radius)
Draw a circle made of braille dots.
void DrawBlockOff(int x, int y)
Erase a block.
void DrawBlock(int x, int y, bool value)
Draw a block.
void Style(int x, int y, const Stylizer &style)
Modify a pixel at a given location.
void DrawPointToggle(int x, int y)
Toggle a braille dot. A filled one will be erased, and the other will be drawn.
A unicode character and its associated style.