54uint8_t g_map_braille[2][4][2] = {
56 {0b00000000, 0b00000001},
57 {0b00000000, 0b00000010},
58 {0b00000000, 0b00000100},
59 {0b00000001, 0b00000000},
62 {0b00000000, 0b00001000},
63 {0b00000000, 0b00010000},
64 {0b00000000, 0b00100000},
65 {0b00000010, 0b00000000},
70std::vector<std::string> g_map_block = {
71 " ",
"▘",
"▖",
"▌",
"▝",
"▀",
"▞",
"▛",
72 "▗",
"▚",
"▄",
"▙",
"▐",
"▜",
"▟",
"█",
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},
83constexpr auto nostyle = [](Pixel& ) {};
93 storage_(width_ * height_ / 8 ) {}
99 auto it = storage_.find(XY{x, y});
100 return (it == storage_.end()) ?
Pixel() : it->second.content;
141 Cell& cell = storage_[XY{x / 2, y / 4}];
142 if (cell.type != CellType::kBraille) {
144 cell.type = CellType::kBraille;
147 cell.content.
character[1] |= g_map_braille[x % 2][y % 4][0];
148 cell.content.
character[2] |= g_map_braille[x % 2][y % 4][1];
158 Cell& cell = storage_[XY{x / 2, y / 4}];
159 if (cell.type != CellType::kBraille) {
161 cell.type = CellType::kBraille;
164 cell.content.
character[1] &= ~(g_map_braille[x % 2][y % 4][0]);
165 cell.content.
character[2] &= ~(g_map_braille[x % 2][y % 4][1]);
176 Cell& cell = storage_[XY{x / 2, y / 4}];
177 if (cell.type != CellType::kBraille) {
179 cell.type = CellType::kBraille;
182 cell.content.
character[1] ^= g_map_braille[x % 2][y % 4][0];
183 cell.content.
character[2] ^= g_map_braille[x % 2][y % 4][1];
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);
223 if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
226 if (dx + dx > width_ * height_) {
231 for (
int i = 0; i < length; ++i) {
233 if (2 * error >= -dy) {
237 if (2 * error <= dx) {
288 const Color& color) {
324 const Color& color) {
343 int dx = (1 + 2 * x) * e2 * e2;
355 err += dx += 2 * r2 * r2;
359 err += dy += 2 * r1 * r1;
388 const Color& color) {
407 int dx = (1 + 2 * x) * e2 * e2;
412 for (
int xx = x1 + x; xx <= x1 - x; ++xx) {
419 err += dx += 2 * r2 * r2;
423 err += dy += 2 * r1 * r1;
428 for (
int yy = y1 - y; yy <= y1 + y; ++yy) {
473 Cell& cell = storage_[XY{x / 2, y / 2}];
474 if (cell.type != CellType::kBlock) {
476 cell.type = CellType::kBlock;
479 const uint8_t bit = (x % 2) * 2 + y % 2;
480 uint8_t value = g_map_block_inversed.at(cell.content.
character);
482 cell.content.
character = g_map_block[value];
492 Cell& cell = storage_[XY{x / 2, y / 4}];
493 if (cell.type != CellType::kBlock) {
495 cell.type = CellType::kBlock;
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];
513 Cell& cell = storage_[XY{x / 2, y / 4}];
514 if (cell.type != CellType::kBlock) {
516 cell.type = CellType::kBlock;
520 const uint8_t bit = (y % 2) * 2 + x % 2;
521 uint8_t value = g_map_block_inversed.at(cell.content.
character);
523 cell.content.
character = g_map_block[value];
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);
566 if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
569 if (dx + dx > width_ * height_) {
574 for (
int i = 0; i < length; ++i) {
576 if (2 * error >= -dy) {
580 if (2 * error <= dx) {
631 const Color& color) {
667 const Color& color) {
688 int dx = (1 + 2 * x) * e2 * e2;
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);
700 err += dx += 2 * r2 * r2;
704 err += dy += 2 * r1 * r1;
728void Canvas::DrawBlockEllipseFilled(
int x,
732 const Color& color) {
733 DrawBlockEllipseFilled(x, y, r1, r2,
734 [color](Pixel& p) { p.foreground_color = color; });
743void Canvas::DrawBlockEllipseFilled(
int x1,
753 int dx = (1 + 2 * x) * e2 * e2;
758 for (
int xx = x1 + x; xx <= x1 - x; ++xx) {
759 DrawBlock(xx, 2 * (y1 + y),
true, s);
760 DrawBlock(xx, 2 * (y1 - y),
true, s);
765 err += dx += 2 * r2 * r2;
769 err += dy += 2 * r1 * r1;
774 for (
int yy = y1 + y; yy <= y1 - y; ++yy) {
775 DrawBlock(x1, 2 * yy,
true, s);
784void Canvas::DrawText(
int x,
int y,
const std::string& value) {
785 DrawText(x, y, value, nostyle);
793void Canvas::DrawText(
int x,
795 const std::string& value,
796 const Color& color) {
805void Canvas::DrawText(
int x,
807 const std::string& value,
809 for (
const auto& it : Utf8ToGlyphs(value)) {
814 Cell& cell = storage_[XY{x / 2, y / 4}];
815 cell.type = CellType::kCell;
826void Canvas::DrawPixel(
int x,
int y,
const Pixel& p) {
827 Cell& cell = storage_[XY{x / 2, y / 4}];
828 cell.type = CellType::kCell;
838void Canvas::DrawImage(
int x,
int y,
const Image& image) {
841 const int dx_begin = std::max(0, -x);
842 const int dy_begin = std::max(0, -y);
843 const int dx_end = std::min(image.
dimx(), width_ - x);
844 const int dy_end = std::min(image.
dimy(), height_ - y);
846 for (
int dy = dy_begin; dy < dy_end; ++dy) {
847 for (
int dx = dx_begin; dx < dx_end; ++dx) {
848 Cell& cell = storage_[XY{
852 cell.type = CellType::kCell;
853 cell.content = image.
PixelAt(dx, dy);
860void Canvas::Style(
int x,
int y,
const Stylizer& style) {
862 style(storage_[XY{x / 2, y / 4}].content);
868class CanvasNodeBase :
public Node {
870 CanvasNodeBase() =
default;
874 const int y_max = std::min(c.
height() / 4, box_.y_max - box_.y_min + 1);
875 const int x_max = std::min(c.
width() / 2, box_.x_max - box_.x_min + 1);
876 for (
int y = 0; y < y_max; ++y) {
877 for (
int x = 0; x < x_max; ++x) {
878 screen.PixelAt(box_.x_min + x, box_.y_min + y) = c.
GetPixel(x, y);
883 virtual const Canvas&
canvas() = 0;
891 class Impl :
public CanvasNodeBase {
893 explicit Impl(ConstRef<Canvas> canvas) : canvas_(std::move(canvas)) {
894 requirement_.min_x = (canvas_->width() + 1) / 2;
895 requirement_.min_y = (canvas_->height() + 3) / 4;
897 const Canvas&
canvas()
final {
return *canvas_; }
898 ConstRef<Canvas> canvas_;
900 return std::make_shared<Impl>(canvas);
907Element
canvas(
int width,
int height, std::function<
void(Canvas&)> fn) {
908 class Impl :
public CanvasNodeBase {
910 Impl(
int width,
int height, std::function<
void(Canvas&)> fn)
911 : width_(width), height_(height), fn_(std::move(fn)) {}
913 void ComputeRequirement()
final {
914 requirement_.min_x = (width_ + 1) / 2;
915 requirement_.min_y = (height_ + 3) / 4;
918 void Render(Screen&
screen)
final {
919 const int width = (box_.x_max - box_.x_min + 1) * 2;
920 const int height = (box_.y_max - box_.y_min + 1) * 4;
921 canvas_ = Canvas(width, height);
923 CanvasNodeBase::Render(
screen);
926 const Canvas&
canvas()
final {
return canvas_; }
930 std::function<void(Canvas&)> fn_;
932 return std::make_shared<Impl>(width, height, std::move(fn));
937Element
canvas(std::function<
void(Canvas&)> fn) {
938 const int default_dim = 12;
939 return canvas(default_dim, default_dim, std::move(fn));
void DrawBlockLine(int x1, int y1, int x2, int y2)
Dibuja una línea de caracteres de bloque.
void DrawPointEllipseFilled(int x, int y, int r1, int r2)
Dibuja una elipse rellena de puntos braille.
void DrawPointLine(int x1, int y1, int x2, int y2)
Dibuja una línea de puntos braille.
std::function< void(Pixel &)> Stylizer
void DrawBlockOn(int x, int y)
Dibuja un bloque.
void DrawPointCircleFilled(int x, int y, int radius)
Dibuja un círculo relleno de puntos braille.
void DrawPointOn(int x, int y)
Dibuja un punto braille.
void DrawPointOff(int x, int y)
Borra un punto braille.
Pixel GetPixel(int x, int y) const
Obtiene el contenido de una celda.
void DrawBlockEllipseFilled(int x1, int y1, int r1, int r2)
void DrawPointEllipse(int x, int y, int r1, int r2)
Dibuja una elipse de puntos braille.
void DrawPoint(int x, int y, bool value)
Dibuja un punto braille.
void DrawBlockEllipse(int x1, int y1, int r1, int r2)
Dibuja una elipse de caracteres de bloque.
void DrawBlockToggle(int x, int y)
Alterna un bloque. Si está relleno, se borrará. Si está vacío, se rellenará.
void DrawBlockCircle(int x1, int y1, int radius)
Dibuja un círculo de caracteres de bloque.
void DrawBlockCircleFilled(int x1, int y1, int radius)
Dibuja un círculo relleno de caracteres de bloque.
void DrawPointCircle(int x, int y, int radius)
Dibuja un círculo de puntos braille.
void DrawBlockOff(int x, int y)
Borra un bloque.
void DrawBlock(int x, int y, bool value)
Dibuja un bloque.
void DrawPointToggle(int x, int y)
Alterna un punto braille. Uno relleno será borrado, y el otro será dibujado.
Node es la clase base para todos los elementos en el árbol DOM.
Decorator color(Color)
Decora usando un color de primer plano.
Canvas es un búfer dibujable asociado con operaciones de dibujo.
Pixel & PixelAt(int x, int y)
Accede a una celda (Pixel) en una posición dada.
Color es una clase que representa un color en la interfaz de usuario de la terminal.
Una cuadrícula rectangular de píxeles.
Una cuadrícula rectangular de píxeles.
Un carácter Unicode y su estilo asociado.
El espacio de nombres ftxui:: de FTXUI.
DrawBlockEllipseFilled(x, y, r1, r2, nostyle)
Dibuja una elipse rellena de caracteres de bloque.
Element canvas(ConstRef< Canvas > canvas)
Produce un elemento a partir de un Canvas, o una referencia a un Canvas.