60uint8_t g_map_braille[2][4][2] = {
62 {0b00000000, 0b00000001},
63 {0b00000000, 0b00000010},
64 {0b00000000, 0b00000100},
65 {0b00000001, 0b00000000},
68 {0b00000000, 0b00001000},
69 {0b00000000, 0b00010000},
70 {0b00000000, 0b00100000},
71 {0b00000010, 0b00000000},
76std::vector<std::string> g_map_block = {
77 " ",
"▘",
"▖",
"▌",
"▝",
"▀",
"▞",
"▛",
78 "▗",
"▚",
"▄",
"▙",
"▐",
"▜",
"▟",
"█",
82const std::map<std::string, uint8_t> g_map_block_inversed = {
83 {
" ", 0b0000}, {
"▘", 0b0001}, {
"▖", 0b0010}, {
"▌", 0b0011},
84 {
"▝", 0b0100}, {
"▀", 0b0101}, {
"▞", 0b0110}, {
"▛", 0b0111},
85 {
"▗", 0b1000}, {
"▚", 0b1001}, {
"▄", 0b1010}, {
"▙", 0b1011},
86 {
"▐", 0b1100}, {
"▜", 0b1101}, {
"▟", 0b1110}, {
"█", 0b1111},
89constexpr auto nostyle = [](Cell& ) {};
96Canvas::Canvas(
int width,
int height)
97 : width_(std::max(0, width)),
98 height_(std::max(0, height)),
99 storage_(static_cast<size_t>(width_) * static_cast<size_t>(height_) /
105Cell Canvas::GetCell(
int x,
int y)
const {
106 auto it = storage_.find(XY{x,
y});
107 return (it == storage_.end()) ? Cell() : it->second.content;
114void Canvas::DrawPoint(
int x,
int y,
bool value) {
115 DrawPoint(x, y, value, [](Cell& ) {});
123void Canvas::DrawPoint(
int x,
int y,
bool value,
const Color& color) {
124 DrawPoint(x, y, value, [color](Cell& p) { p.foreground_color = color; });
132void Canvas::DrawPoint(
int x,
int y,
bool value,
const Stylizer& style) {
144void Canvas::DrawPointOn(
int x,
int y) {
148 CanvasCell& cell = storage_[XY{x / 2,
y / 4}];
149 if (cell.type != CellType::kBraille) {
150 cell.content.character =
"⠀";
151 cell.type = CellType::kBraille;
154 cell.content.character[1] |= g_map_braille[x % 2][
y % 4][0];
155 cell.content.character[2] |= g_map_braille[x % 2][
y % 4][1];
161void Canvas::DrawPointOff(
int x,
int y) {
165 CanvasCell& cell = storage_[XY{x / 2,
y / 4}];
166 if (cell.type != CellType::kBraille) {
167 cell.content.character =
"⠀";
168 cell.type = CellType::kBraille;
171 cell.content.character[1] &= ~(g_map_braille[x % 2][
y % 4][0]);
172 cell.content.character[2] &= ~(g_map_braille[x % 2][
y % 4][1]);
178void Canvas::DrawPointToggle(
int x,
int y) {
182 CanvasCell& cell = storage_[XY{x / 2,
y / 4}];
183 if (cell.type != CellType::kBraille) {
184 cell.content.character =
"⠀";
185 cell.type = CellType::kBraille;
188 cell.content.character[1] ^= g_map_braille[x % 2][
y % 4][0];
189 cell.content.character[2] ^= g_map_braille[x % 2][
y % 4][1];
197void Canvas::DrawPointLine(
int x1,
int y1,
int x2,
int y2) {
198 DrawPointLine(x1, y1, x2, y2, [](Cell& ) {});
207void Canvas::DrawPointLine(
int x1,
int y1,
int x2,
int y2,
const Color& color) {
208 DrawPointLine(x1, y1, x2, y2,
209 [color](Cell& p) { p.foreground_color = color; });
218void Canvas::DrawPointLine(
int x1,
222 const Stylizer& style) {
223 const int dx = std::abs(x2 - x1);
224 const int dy = std::abs(y2 - y1);
225 const int sx = x1 < x2 ? 1 : -1;
226 const int sy = y1 < y2 ? 1 : -1;
227 const int length = std::max(dx, dy);
229 if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
232 if (dx + dx > width_ * height_) {
237 for (
int i = 0; i < length; ++i) {
238 DrawPoint(x1, y1,
true, style);
239 if (2 * error >= -dy) {
243 if (2 * error <= dx) {
248 DrawPoint(x2, y2,
true, style);
255void Canvas::DrawPointCircle(
int x,
int y,
int radius) {
256 DrawPointCircle(x, y, radius, [](Cell& ) {});
264void Canvas::DrawPointCircle(
int x,
int y,
int radius,
const Color& color) {
265 DrawPointCircle(x, y, radius,
266 [color](Cell& p) { p.foreground_color = color; });
274void Canvas::DrawPointCircle(
int x,
int y,
int radius,
const Stylizer& style) {
275 DrawPointEllipse(x, y, radius, radius, style);
282void Canvas::DrawPointCircleFilled(
int x,
int y,
int radius) {
283 DrawPointCircleFilled(x, y, radius, [](Cell& ) {});
291void Canvas::DrawPointCircleFilled(
int x,
294 const Color& color) {
295 DrawPointCircleFilled(x, y, radius,
296 [color](Cell& p) { p.foreground_color = color; });
304void Canvas::DrawPointCircleFilled(
int x,
307 const Stylizer& style) {
308 DrawPointEllipseFilled(x, y, radius, radius, style);
316void Canvas::DrawPointEllipse(
int x,
int y,
int r1,
int r2) {
317 DrawPointEllipse(x, y, r1, r2, [](Cell& ) {});
326void Canvas::DrawPointEllipse(
int x,
330 const Color& color) {
331 DrawPointEllipse(x, y, r1, r2,
332 [color](Cell& p) { p.foreground_color = color; });
341void Canvas::DrawPointEllipse(
int x1,
349 int dx = (1 + 2 * x) * e2 * e2;
354 DrawPoint(x1 - x, y1 + y,
true, s);
355 DrawPoint(x1 + x, y1 + y,
true, s);
356 DrawPoint(x1 + x, y1 - y,
true, s);
357 DrawPoint(x1 - x, y1 - y,
true, s);
361 err += dx += 2 * r2 * r2;
365 err += dy += 2 * r1 * r1;
370 DrawPoint(x1, y1 + y,
true, s);
371 DrawPoint(x1, y1 - y,
true, s);
380void Canvas::DrawPointEllipseFilled(
int x1,
int y1,
int r1,
int r2) {
381 DrawPointEllipseFilled(x1, y1, r1, r2, [](Cell& ) {});
390void Canvas::DrawPointEllipseFilled(
int x1,
394 const Color& color) {
395 DrawPointEllipseFilled(x1, y1, r1, r2,
396 [color](Cell& p) { p.foreground_color = color; });
405void Canvas::DrawPointEllipseFilled(
int x1,
413 int dx = (1 + 2 * x) * e2 * e2;
418 for (
int xx = x1 + x; xx <= x1 - x; ++xx) {
419 DrawPoint(xx, y1 + y,
true, s);
420 DrawPoint(xx, y1 - y,
true, s);
425 err += dx += 2 * r2 * r2;
429 err += dy += 2 * r1 * r1;
434 for (
int yy = y1 - y; yy <= y1 +
y; ++yy) {
435 DrawPoint(x1, yy,
true, s);
444void Canvas::DrawBlock(
int x,
int y,
bool value) {
445 DrawBlock(x, y, value, [](Cell& ) {});
453void Canvas::DrawBlock(
int x,
int y,
bool value,
const Color& color) {
454 DrawBlock(x, y, value, [color](Cell& p) { p.foreground_color = color; });
462void Canvas::DrawBlock(
int x,
int y,
bool value,
const Stylizer& style) {
474void Canvas::DrawBlockOn(
int x,
int y) {
479 CanvasCell& cell = storage_[XY{x / 2,
y / 2}];
480 if (cell.type != CellType::kBlock) {
481 cell.content.character =
" ";
482 cell.type = CellType::kBlock;
485 const uint8_t bit = (x % 2) * 2 + y % 2;
486 uint8_t
value = g_map_block_inversed.at(cell.content.character);
488 cell.content.character = g_map_block[
value];
494void Canvas::DrawBlockOff(
int x,
int y) {
498 CanvasCell& cell = storage_[XY{x / 2,
y / 4}];
499 if (cell.type != CellType::kBlock) {
500 cell.content.character =
" ";
501 cell.type = CellType::kBlock;
505 const uint8_t bit = (
y % 2) * 2 + x % 2;
506 uint8_t
value = g_map_block_inversed.at(cell.content.character);
507 value &= ~(1U << bit);
508 cell.content.character = g_map_block[
value];
514void Canvas::DrawBlockToggle(
int x,
int y) {
518 CanvasCell& cell = storage_[XY{x / 2,
y / 4}];
519 if (cell.type != CellType::kBlock) {
520 cell.content.character =
" ";
521 cell.type = CellType::kBlock;
525 const uint8_t bit = (
y % 2) * 2 + x % 2;
526 uint8_t
value = g_map_block_inversed.at(cell.content.character);
528 cell.content.character = g_map_block[
value];
536void Canvas::DrawBlockLine(
int x1,
int y1,
int x2,
int y2) {
537 DrawBlockLine(x1, y1, x2, y2, [](Cell& ) {});
546void Canvas::DrawBlockLine(
int x1,
int y1,
int x2,
int y2,
const Color& color) {
547 DrawBlockLine(x1, y1, x2, y2,
548 [color](Cell& p) { p.foreground_color = color; });
557void Canvas::DrawBlockLine(
int x1,
561 const Stylizer& style) {
565 const int dx = std::abs(x2 - x1);
566 const int dy = std::abs(y2 - y1);
567 const int sx = x1 < x2 ? 1 : -1;
568 const int sy = y1 < y2 ? 1 : -1;
569 const int length = std::max(dx, dy);
571 if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
574 if (dx + dx > width_ * height_) {
579 for (
int i = 0; i < length; ++i) {
580 DrawBlock(x1, y1 * 2,
true, style);
581 if (2 * error >= -dy) {
585 if (2 * error <= dx) {
590 DrawBlock(x2, y2 * 2,
true, style);
597void Canvas::DrawBlockCircle(
int x,
int y,
int radius) {
598 DrawBlockCircle(x, y, radius, nostyle);
606void Canvas::DrawBlockCircle(
int x,
int y,
int radius,
const Color& color) {
607 DrawBlockCircle(x, y, radius,
608 [color](Cell& p) { p.foreground_color = color; });
616void Canvas::DrawBlockCircle(
int x,
int y,
int radius,
const Stylizer& style) {
617 DrawBlockEllipse(x, y, radius, radius, style);
624void Canvas::DrawBlockCircleFilled(
int x,
int y,
int radius) {
625 DrawBlockCircleFilled(x, y, radius, nostyle);
633void Canvas::DrawBlockCircleFilled(
int x,
636 const Color& color) {
637 DrawBlockCircleFilled(x, y, radius,
638 [color](Cell& p) { p.foreground_color = color; });
646void Canvas::DrawBlockCircleFilled(
int x,
650 DrawBlockEllipseFilled(x, y, radius, radius, s);
658void Canvas::DrawBlockEllipse(
int x,
int y,
int r1,
int r2) {
659 DrawBlockEllipse(x, y, r1, r2, nostyle);
668void Canvas::DrawBlockEllipse(
int x,
672 const Color& color) {
673 DrawBlockEllipse(x, y, r1, r2,
674 [color](Cell& p) { p.foreground_color = color; });
683void Canvas::DrawBlockEllipse(
int x1,
693 int dx = (1 + 2 * x) * e2 * e2;
698 DrawBlock(x1 - x, 2 * (y1 + y),
true, s);
699 DrawBlock(x1 + x, 2 * (y1 + y),
true, s);
700 DrawBlock(x1 + x, 2 * (y1 - y),
true, s);
701 DrawBlock(x1 - x, 2 * (y1 - y),
true, s);
705 err += dx += 2 * r2 * r2;
709 err += dy += 2 * r1 * r1;
714 DrawBlock(x1, 2 * (y1 + y),
true, s);
715 DrawBlock(x1, 2 * (y1 - y),
true, s);
724void Canvas::DrawBlockEllipseFilled(
int x,
int y,
int r1,
int r2) {
725 DrawBlockEllipseFilled(x, y, r1, r2, nostyle);
734void Canvas::DrawBlockEllipseFilled(
int x,
738 const Color& color) {
739 DrawBlockEllipseFilled(x, y, r1, r2,
740 [color](Cell& p) { p.foreground_color = color; });
749void Canvas::DrawBlockEllipseFilled(
int x1,
759 int dx = (1 + 2 * x) * e2 * e2;
764 for (
int xx = x1 + x; xx <= x1 - x; ++xx) {
765 DrawBlock(xx, 2 * (y1 + y),
true, s);
766 DrawBlock(xx, 2 * (y1 - y),
true, s);
771 err += dx += 2 * r2 * r2;
775 err += dy += 2 * r1 * r1;
780 for (
int yy = y1 + y; yy <= y1 -
y; ++yy) {
781 DrawBlock(x1, 2 * yy,
true, s);
790void Canvas::DrawText(
int x,
int y, std::string_view
value) {
791 DrawText(x, y, value, nostyle);
799void Canvas::DrawText(
int x,
801 std::string_view
value,
802 const Color& color) {
803 DrawText(x, y, value, [color](Cell& p) { p.foreground_color = color; });
811void Canvas::DrawText(
int x,
813 std::string_view
value,
814 const Stylizer& style) {
815 for (
const auto& it : Utf8ToGlyphs(value)) {
820 CanvasCell& cell = storage_[XY{x / 2,
y / 4}];
821 cell.type = CellType::kCell;
822 cell.content.character = it;
832void Canvas::DrawCell(
int x,
int y,
const Cell& p) {
833 CanvasCell& cell = storage_[XY{x / 2,
y / 4}];
834 cell.type = CellType::kCell;
844void Canvas::DrawSurface(
int x,
int y,
const Surface& image) {
847 const int dx_begin = std::max(0, -x);
848 const int dy_begin = std::max(0, -y);
849 const int dx_end = std::min(image.dimx(), width_ - x);
850 const int dy_end = std::min(image.dimy(), height_ - y);
852 for (
int dy = dy_begin; dy < dy_end; ++dy) {
853 for (
int dx = dx_begin; dx < dx_end; ++dx) {
854 CanvasCell& cell = storage_[XY{
858 cell.type = CellType::kCell;
859 cell.content = image.CellAt(dx, dy);
868void Canvas::Style(
int x,
int y,
const Stylizer& style) {
870 style(storage_[XY{x / 2,
y / 4}].content);
876class CanvasNodeBase :
public Node {
878 CanvasNodeBase() =
default;
880 void Render(Screen& screen)
override {
881 const Canvas& c =
canvas();
882 const int y_max = std::min(c.height() / 4, box_.y_max - box_.y_min + 1);
883 const int x_max = std::min(c.width() / 2, box_.x_max - box_.x_min + 1);
884 for (
int y = 0;
y < y_max; ++
y) {
885 for (
int x = 0; x < x_max; ++x) {
886 screen.CellAt(box_.x_min + x, box_.y_min +
y) = c.GetCell(x,
y);
891 virtual const Canvas&
canvas() = 0;
899#if defined(__GNUC__) && !defined(__clang__)
900#pragma GCC diagnostic push
901#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
903 class Impl :
public CanvasNodeBase {
906 requirement_.min_x = (canvas_->width() + 1) / 2;
907 requirement_.min_y = (canvas_->height() + 3) / 4;
909 const Canvas& canvas()
final {
return *canvas_; }
912 return std::make_shared<Impl>(canvas);
913#if defined(__GNUC__) && !defined(__clang__)
914#pragma GCC diagnostic pop
923 class Impl :
public CanvasNodeBase {
925 Impl(
int width,
int height, std::function<
void(Canvas&)> fn)
926 : width_(width), height_(height), fn_(std::move(fn)) {}
928 void ComputeRequirement()
final {
929 requirement_.min_x = (width_ + 1) / 2;
930 requirement_.min_y = (height_ + 3) / 4;
933 void Render(Screen& screen)
final {
934 const int width = (box_.x_max - box_.x_min + 1) * 2;
935 const int height = (box_.y_max - box_.y_min + 1) * 4;
936 canvas_ = Canvas(width, height);
938 CanvasNodeBase::Render(screen);
941 const Canvas& canvas()
final {
return canvas_; }
945 std::function<void(Canvas&)> fn_;
947 return std::make_shared<Impl>(width, height, std::move(fn));
953 const int default_dim = 12;
954 return canvas(default_dim, default_dim, std::move(fn));
アダプター。不変オブジェクトを所有または参照します。
std::shared_ptr< Node > Element
void Render(Screen &screen, Node *node, Selection &selection)
Element canvas(int width, int height, std::function< void(Canvas &)>)
要求されたサイズのキャンバスを描画する要素を生成します。