FTXUI 6.1.9
C++ functional terminal UI.
Loading...
Searching...
No Matches
src/ftxui/dom/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 コンストラクタ。
88/// @param width キャンバスの幅。セルは2x4の点字ドットです。
89/// @param height キャンバスの高さ。セルは2x4の点字ドットです。
90Canvas::Canvas(int width, int height)
91 : width_(width),
92 height_(height),
93 storage_(width_ * height_ / 8 /* NOLINT */) {}
94
95/// @brief セルの内容を取得します。
96/// @param x セルのx座標。
97/// @param y セルのy座標。
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 点字ドットを描画します。
104/// @param x ドットのx座標。
105/// @param y ドットのy座標。
106/// @param value ドットが塗りつぶされているかどうか。
107void Canvas::DrawPoint(int x, int y, bool value) {
108 DrawPoint(x, y, value, [](Pixel& /*pixel*/) {});
109}
110
111/// @brief 点字ドットを描画します。
112/// @param x ドットのx座標。
113/// @param y ドットのy座標。
114/// @param value ドットが塗りつぶされているかどうか。
115/// @param color ドットの色。
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 点字ドットを描画します。
121/// @param x ドットのx座標。
122/// @param y ドットのy座標。
123/// @param value ドットが塗りつぶされているかどうか。
124/// @param style セルのスタイル。
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 点字ドットを描画します。
135/// @param x ドットのx座標。
136/// @param y ドットのy座標。
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 点字ドットを消去します。
152/// @param x ドットのx座標。
153/// @param y ドットのy座標。
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 点字ドットを切り替えます。塗りつぶされたものは消去され、それ以外は描画されます。
169/// @param x ドットのx座標。
170/// @param y ドットのy座標。
171void Canvas::DrawPointToggle(int x, int y) {
172 if (!IsIn(x, y)) {
173 return;
174 }
175 Cell& cell = storage_[XY{x / 2, y / 4}];
176 if (cell.type != CellType::kBraille) {
177 cell.content.character = "⠀"; // 3 byt
178 cell.type = CellType::kBraille;
179 }
180
181 cell.content.character[1] ^= g_map_braille[x % 2][y % 4][0]; // NOLINT
182 cell.content.character[2] ^= g_map_braille[x % 2][y % 4][1]; // NOLINT
183}
184
185/// @brief 点字ドットで線を描画します。
186/// @param x1 最初のドットのx座標。
187/// @param y1 最初のドットのy座標。
188/// @param x2 2番目のドットのx座標。
189/// @param y2 2番目のドットのy座標。
190void Canvas::DrawPointLine(int x1, int y1, int x2, int y2) {
191 DrawPointLine(x1, y1, x2, y2, [](Pixel& /*pixel*/) {});
192}
193
194/// @brief 点字ドットで線を描画します。
195/// @param x1 最初のドットのx座標。
196/// @param y1 最初のドットのy座標。
197/// @param x2 2番目のドットのx座標。
198/// @param y2 2番目のドットのy座標。
199/// @param color 線の色。
200void Canvas::DrawPointLine(int x1, int y1, int x2, int y2, const Color& color) {
201 DrawPointLine(x1, y1, x2, y2,
202 [color](Pixel& p) { p.foreground_color = color; });
203}
204
205/// @brief 点字ドットで線を描画します。
206/// @param x1 最初のドットのx座標。
207/// @param y1 最初のドットのy座標。
208/// @param x2 2番目のドットのx座標。
209/// @param y2 2番目のドットのy座標。
210/// @param style 線のスタイル。
212 int y1,
213 int x2,
214 int y2,
215 const Stylizer& style) {
216 const int dx = std::abs(x2 - x1);
217 const int dy = std::abs(y2 - y1);
218 const int sx = x1 < x2 ? 1 : -1;
219 const int sy = y1 < y2 ? 1 : -1;
220 const int length = std::max(dx, dy);
221
222 if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
223 return;
224 }
225 if (dx + dx > width_ * height_) {
226 return;
227 }
228
229 int error = dx - dy;
230 for (int i = 0; i < length; ++i) {
231 DrawPoint(x1, y1, true, style);
232 if (2 * error >= -dy) {
233 error -= dy;
234 x1 += sx;
235 }
236 if (2 * error <= dx) {
237 error += dx;
238 y1 += sy;
239 }
240 }
241 DrawPoint(x2, y2, true, style);
242}
243
244/// @brief 点字ドットで円を描画します。
245/// @param x 円の中心のx座標。
246/// @param y 円の中心のy座標。
247/// @param radius 円の半径。
248void Canvas::DrawPointCircle(int x, int y, int radius) {
249 DrawPointCircle(x, y, radius, [](Pixel& /*pixel*/) {});
250}
251
252/// @brief 点字ドットで円を描画します。
253/// @param x 円の中心のx座標。
254/// @param y 円の中心のy座標。
255/// @param radius 円の半径。
256/// @param color 円の色。
257void Canvas::DrawPointCircle(int x, int y, int radius, const Color& color) {
258 DrawPointCircle(x, y, radius,
259 [color](Pixel& p) { p.foreground_color = color; });
260}
261
262/// @brief 点字ドットで円を描画します。
263/// @param x 円の中心のx座標。
264/// @param y 円の中心のy座標。
265/// @param radius 円の半径。
266/// @param style 円のスタイル。
267void Canvas::DrawPointCircle(int x, int y, int radius, const Stylizer& style) {
268 DrawPointEllipse(x, y, radius, radius, style);
269}
270
271/// @brief 点字ドットで塗りつぶされた円を描画します。
272/// @param x 円の中心のx座標。
273/// @param y 円の中心のy座標。
274/// @param radius 円の半径。
275void Canvas::DrawPointCircleFilled(int x, int y, int radius) {
276 DrawPointCircleFilled(x, y, radius, [](Pixel& /*pixel*/) {});
277}
278
279/// @brief 点字ドットで塗りつぶされた円を描画します。
280/// @param x 円の中心のx座標。
281/// @param y 円の中心のy座標。
282/// @param radius 円の半径。
283/// @param color 円の色。
285 int y,
286 int radius,
287 const Color& color) {
288 DrawPointCircleFilled(x, y, radius,
289 [color](Pixel& p) { p.foreground_color = color; });
290}
291
292/// @brief 点字ドットで塗りつぶされた円を描画します。
293/// @param x 円の中心のx座標。
294/// @param y 円の中心のy座標。
295/// @param radius 円の半径。
296/// @param style 円のスタイル。
298 int y,
299 int radius,
300 const Stylizer& style) {
301 DrawPointEllipseFilled(x, y, radius, radius, style);
302}
303
304/// @brief 点字ドットで楕円を描画します。
305/// @param x 楕円の中心のx座標。
306/// @param y 楕円の中心のy座標。
307/// @param r1 x軸に沿った楕円の半径。
308/// @param r2 y軸に沿った楕円の半径。
309void Canvas::DrawPointEllipse(int x, int y, int r1, int r2) {
310 DrawPointEllipse(x, y, r1, r2, [](Pixel& /*pixel*/) {});
311}
312
313/// @brief 点字ドットで楕円を描画します。
314/// @param x 楕円の中心のx座標。
315/// @param y 楕円の中心のy座標。
316/// @param r1 x軸に沿った楕円の半径。
317/// @param r2 y軸に沿った楕円の半径。
318/// @param color 楕円の色。
320 int y,
321 int r1,
322 int r2,
323 const Color& color) {
324 DrawPointEllipse(x, y, r1, r2,
325 [color](Pixel& p) { p.foreground_color = color; });
326}
327
328/// @brief 点字ドットで楕円を描画します。
329/// @param x1 楕円の中心のx座標。
330/// @param y1 楕円の中心のy座標。
331/// @param r1 x軸に沿った楕円の半径。
332/// @param r2 y軸に沿った楕円の半径。
333/// @param s 楕円のスタイル。
335 int y1,
336 int r1,
337 int r2,
338 const Stylizer& s) {
339 int x = -r1;
340 int y = 0;
341 int e2 = r2;
342 int dx = (1 + 2 * x) * e2 * e2;
343 int dy = x * x;
344 int err = dx + dy;
345
346 do { // NOLINT
347 DrawPoint(x1 - x, y1 + y, true, s);
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 e2 = 2 * err;
352 if (e2 >= dx) {
353 x++;
354 err += dx += 2 * r2 * r2;
355 }
356 if (e2 <= dy) {
357 y++;
358 err += dy += 2 * r1 * r1;
359 }
360 } while (x <= 0);
361
362 while (y++ < r2) {
363 DrawPoint(x1, y1 + y, true, s);
364 DrawPoint(x1, y1 - y, true, s);
365 }
366}
367
368/// @brief 点字ドットで塗りつぶされた楕円を描画します。
369/// @param x1 楕円の中心のx座標。
370/// @param y1 楕円の中心のy座標。
371/// @param r1 x軸に沿った楕円の半径。
372/// @param r2 y軸に沿った楕円の半径。
373void Canvas::DrawPointEllipseFilled(int x1, int y1, int r1, int r2) {
374 DrawPointEllipseFilled(x1, y1, r1, r2, [](Pixel& /*pixel*/) {});
375}
376
377/// @brief 点字ドットで塗りつぶされた楕円を描画します。
378/// @param x1 楕円の中心のx座標。
379/// @param y1 楕円の中心のy座標。
380/// @param r1 x軸に沿った楕円の半径。
381/// @param r2 y軸に沿った楕円の半径。
382/// @param color 楕円の色。
384 int y1,
385 int r1,
386 int r2,
387 const Color& color) {
388 DrawPointEllipseFilled(x1, y1, r1, r2,
389 [color](Pixel& p) { p.foreground_color = color; });
390}
391
392/// @brief 点字ドットで塗りつぶされた楕円を描画します。
393/// @param x1 楕円の中心のx座標。
394/// @param y1 楕円の中心のy座標。
395/// @param r1 x軸に沿った楕円の半径。
396/// @param r2 y軸に沿った楕円の半径。
397/// @param s 楕円のスタイル。
399 int y1,
400 int r1,
401 int r2,
402 const Stylizer& s) {
403 int x = -r1;
404 int y = 0;
405 int e2 = r2;
406 int dx = (1 + 2 * x) * e2 * e2;
407 int dy = x * x;
408 int err = dx + dy;
409
410 do { // NOLINT
411 for (int xx = x1 + x; xx <= x1 - x; ++xx) {
412 DrawPoint(xx, y1 + y, true, s);
413 DrawPoint(xx, y1 - y, true, s);
414 }
415 e2 = 2 * err;
416 if (e2 >= dx) {
417 x++;
418 err += dx += 2 * r2 * r2;
419 }
420 if (e2 <= dy) {
421 y++;
422 err += dy += 2 * r1 * r1;
423 }
424 } while (x <= 0);
425
426 while (y++ < r2) {
427 for (int yy = y1 - y; yy <= y1 + y; ++yy) {
428 DrawPoint(x1, yy, true, s);
429 }
430 }
431}
432
433/// @brief ブロックを描画します。
434/// @param x ブロックのx座標。
435/// @param y ブロックのy座標。
436/// @param value ブロックが塗りつぶされているかどうか。
437void Canvas::DrawBlock(int x, int y, bool value) {
438 DrawBlock(x, y, value, [](Pixel& /*pixel*/) {});
439}
440
441/// @brief ブロックを描画します。
442/// @param x ブロックのx座標。
443/// @param y ブロックのy座標。
444/// @param value ブロックが塗りつぶされているかどうか。
445/// @param color ブロックの色。
446void Canvas::DrawBlock(int x, int y, bool value, const Color& color) {
447 DrawBlock(x, y, value, [color](Pixel& p) { p.foreground_color = color; });
448}
449
450/// @brief ブロックを描画します。
451/// @param x ブロックのx座標。
452/// @param y ブロックのy座標。
453/// @param value ブロックが塗りつぶされているかどうか。
454/// @param style ブロックのスタイル。
455void Canvas::DrawBlock(int x, int y, bool value, const Stylizer& style) {
456 Style(x, y, style);
457 if (value) {
458 DrawBlockOn(x, y);
459 } else {
460 DrawBlockOff(x, y);
461 }
462}
463
464/// @brief ブロックを描画します。
465/// @param x ブロックのx座標。
466/// @param y ブロックのy座標。
467void Canvas::DrawBlockOn(int x, int y) {
468 if (!IsIn(x, y)) {
469 return;
470 }
471 y /= 2;
472 Cell& cell = storage_[XY{x / 2, y / 2}];
473 if (cell.type != CellType::kBlock) {
474 cell.content.character = " ";
475 cell.type = CellType::kBlock;
476 }
477
478 const uint8_t bit = (x % 2) * 2 + y % 2;
479 uint8_t value = g_map_block_inversed.at(cell.content.character);
480 value |= 1U << bit;
481 cell.content.character = g_map_block[value];
482}
483
484/// @brief ブロックを消去します。
485/// @param x ブロックのx座標。
486/// @param y ブロックのy座標。
487void Canvas::DrawBlockOff(int x, int y) {
488 if (!IsIn(x, y)) {
489 return;
490 }
491 Cell& cell = storage_[XY{x / 2, y / 4}];
492 if (cell.type != CellType::kBlock) {
493 cell.content.character = " ";
494 cell.type = CellType::kBlock;
495 }
496 y /= 2;
497
498 const uint8_t bit = (y % 2) * 2 + x % 2;
499 uint8_t value = g_map_block_inversed.at(cell.content.character);
500 value &= ~(1U << bit);
501 cell.content.character = g_map_block[value];
502}
503
504/// @brief ブロックを切り替えます。塗りつぶされている場合は消去され、空の場合は塗りつぶされます。
505/// @param x ブロックのx座標。
506/// @param y ブロックのy座標。
507void Canvas::DrawBlockToggle(int x, int y) {
508 if (!IsIn(x, y)) {
509 return;
510 }
511 Cell& cell = storage_[XY{x / 2, y / 4}];
512 if (cell.type != CellType::kBlock) {
513 cell.content.character = " ";
514 cell.type = CellType::kBlock;
515 }
516 y /= 2;
517
518 const uint8_t bit = (y % 2) * 2 + x % 2;
519 uint8_t value = g_map_block_inversed.at(cell.content.character);
520 value ^= 1U << bit;
521 cell.content.character = g_map_block[value];
522}
523
524/// @brief ブロック文字で線を描画します。
525/// @param x1 線の最初の点のx座標。
526/// @param y1 線の最初の点のy座標。
527/// @param x2 線の2番目の点のx座標。
528/// @param y2 線の2番目の点のy座標。
529void Canvas::DrawBlockLine(int x1, int y1, int x2, int y2) {
530 DrawBlockLine(x1, y1, x2, y2, [](Pixel& /*pixel*/) {});
531}
532
533/// @brief ブロック文字で線を描画します。
534/// @param x1 線の最初の点のx座標。
535/// @param y1 線の最初の点のy座標。
536/// @param x2 線の2番目の点のx座標。
537/// @param y2 線の2番目の点のy座標。
538/// @param color 線の色。
539void Canvas::DrawBlockLine(int x1, int y1, int x2, int y2, const Color& color) {
540 DrawBlockLine(x1, y1, x2, y2,
541 [color](Pixel& p) { p.foreground_color = color; });
542}
543
544/// @brief ブロック文字で線を描画します。
545/// @param x1 線の最初の点のx座標。
546/// @param y1 線の最初の点のy座標。
547/// @param x2 線の2番目の点のx座標。
548/// @param y2 線の2番目の点のy座標。
549/// @param style 線のスタイル。
551 int y1,
552 int x2,
553 int y2,
554 const Stylizer& style) {
555 y1 /= 2;
556 y2 /= 2;
557
558 const int dx = std::abs(x2 - x1);
559 const int dy = std::abs(y2 - y1);
560 const int sx = x1 < x2 ? 1 : -1;
561 const int sy = y1 < y2 ? 1 : -1;
562 const int length = std::max(dx, dy);
563
564 if (!IsIn(x1, y1) && !IsIn(x2, y2)) {
565 return;
566 }
567 if (dx + dx > width_ * height_) {
568 return;
569 }
570
571 int error = dx - dy;
572 for (int i = 0; i < length; ++i) {
573 DrawBlock(x1, y1 * 2, true, style);
574 if (2 * error >= -dy) {
575 error -= dy;
576 x1 += sx;
577 }
578 if (2 * error <= dx) {
579 error += dx;
580 y1 += sy;
581 }
582 }
583 DrawBlock(x2, y2 * 2, true, style);
584}
585
586/// @brief ブロック文字で円を描画します。
587/// @param x 円の中心のx座標。
588/// @param y 円の中心のy座標。
589/// @param radius 円の半径。
590void Canvas::DrawBlockCircle(int x, int y, int radius) {
591 DrawBlockCircle(x, y, radius, nostyle);
592}
593
594/// @brief ブロック文字で円を描画します。
595/// @param x 円の中心のx座標。
596/// @param y 円の中心のy座標。
597/// @param radius 円の半径。
598/// @param color 円の色。
599void Canvas::DrawBlockCircle(int x, int y, int radius, const Color& color) {
600 DrawBlockCircle(x, y, radius,
601 [color](Pixel& p) { p.foreground_color = color; });
602}
603
604/// @brief ブロック文字で円を描画します。
605/// @param x 円の中心のx座標。
606/// @param y 円の中心のy座標。
607/// @param radius 円の半径。
608/// @param style 円のスタイル。
609void Canvas::DrawBlockCircle(int x, int y, int radius, const Stylizer& style) {
610 DrawBlockEllipse(x, y, radius, radius, style);
611}
612
613/// @brief ブロック文字で塗りつぶされた円を描画します。
614/// @param x 円の中心のx座標。
615/// @param y 円の中心のy座標。
616/// @param radius 円の半径。
617void Canvas::DrawBlockCircleFilled(int x, int y, int radius) {
618 DrawBlockCircleFilled(x, y, radius, nostyle);
619}
620
621/// @brief ブロック文字で塗りつぶされた円を描画します。
622/// @param x 円の中心のx座標。
623/// @param y 円の中心のy座標。
624/// @param radius 円の半径。
625/// @param color 円の色。
627 int y,
628 int radius,
629 const Color& color) {
630 DrawBlockCircleFilled(x, y, radius,
631 [color](Pixel& p) { p.foreground_color = color; });
632}
633
634/// @brief ブロック文字で塗りつぶされた円を描画します。
635/// @param x 円の中心のx座標。
636/// @param y 円の中心のy座標。
637/// @param radius 円の半径。
638/// @param s 円のスタイル。
640 int y,
641 int radius,
642 const Stylizer& s) {
643 DrawBlockEllipseFilled(x, y, radius, radius, s);
644}
645
646/// @brief ブロック文字で楕円を描画します。
647/// @param x 楕円の中心のx座標。
648/// @param y 楕円の中心のy座標。
649/// @param r1 x軸に沿った楕円の半径。
650/// @param r2 y軸に沿った楕円の半径。
651void Canvas::DrawBlockEllipse(int x, int y, int r1, int r2) {
652 DrawBlockEllipse(x, y, r1, r2, nostyle);
653}
654
655/// @brief ブロック文字で楕円を描画します。
656/// @param x 楕円の中心のx座標。
657/// @param y 楕円の中心のy座標。
658/// @param r1 x軸に沿った楕円の半径。
659/// @param r2 y軸に沿った楕円の半径。
660/// @param color 楕円の色。
662 int y,
663 int r1,
664 int r2,
665 const Color& color) {
666 DrawBlockEllipse(x, y, r1, r2,
667 [color](Pixel& p) { p.foreground_color = color; });
668}
669
670/// @brief ブロック文字で楕円を描画します。
671/// @param x1 楕円の中心のx座標。
672/// @param y1 楕円の中心のy座標。
673/// @param r1 x軸に沿った楕円の半径。
674/// @param r2 y軸に沿った楕円の半径。
675/// @param s 楕円のスタイル。
677 int y1,
678 int r1,
679 int r2,
680 const Stylizer& s) {
681 y1 /= 2;
682 r2 /= 2;
683 int x = -r1;
684 int y = 0;
685 int e2 = r2;
686 int dx = (1 + 2 * x) * e2 * e2;
687 int dy = x * x;
688 int err = dx + dy;
689
690 do { // NOLINT
691 DrawBlock(x1 - x, 2 * (y1 + y), true, s);
692 DrawBlock(x1 + x, 2 * (y1 + y), true, s);
693 DrawBlock(x1 + x, 2 * (y1 - y), true, s);
694 DrawBlock(x1 - x, 2 * (y1 - y), true, s);
695 e2 = 2 * err;
696 if (e2 >= dx) {
697 x++;
698 err += dx += 2 * r2 * r2;
699 }
700 if (e2 <= dy) {
701 y++;
702 err += dy += 2 * r1 * r1;
703 }
704 } while (x <= 0);
705
706 while (y++ < r2) {
707 DrawBlock(x1, 2 * (y1 + y), true, s);
708 DrawBlock(x1, 2 * (y1 - y), true, s);
709 }
710}
711
712/// @brief ブロック文字で塗りつぶされた楕円を描画します。
713/// @param x 楕円の中心のx座標。
714/// @param y 楕円の中心のy座標。
715/// @param r1 x軸に沿った楕円の半径。
716/// @param r2 y軸に沿った楕円の半径。
717void Canvas::DrawBlockEllipseFilled(int x, int y, int r1, int r2) {
718 DrawBlockEllipseFilled(x, y, r1, r2, nostyle);
719}
720
721/// @brief ブロック文字で塗りつぶされた楕円を描画します。
722/// @param x 楕円の中心のx座標。
723/// @param y 楕円の中心のy座標。
724/// @param r1 x軸に沿った楕円の半径。
725/// @param r2 y軸に沿った楕円の半径。
726/// @param color 楕円の色。
728 int y,
729 int r1,
730 int r2,
731 const Color& color) {
732 DrawBlockEllipseFilled(x, y, r1, r2,
733 [color](Pixel& p) { p.foreground_color = color; });
734}
735
736/// @brief ブロック文字で塗りつぶされた楕円を描画します。
737/// @param x1 楕円の中心のx座標。
738/// @param y1 楕円の中心のy座標。
739/// @param r1 x軸に沿った楕円の半径。
740/// @param r2 y軸に沿った楕円の半径。
741/// @param s 楕円のスタイル。
743 int y1,
744 int r1,
745 int r2,
746 const Stylizer& s) {
747 y1 /= 2;
748 r2 /= 2;
749 int x = -r1;
750 int y = 0;
751 int e2 = r2;
752 int dx = (1 + 2 * x) * e2 * e2;
753 int dy = x * x;
754 int err = dx + dy;
755
756 do { // NOLINT
757 for (int xx = x1 + x; xx <= x1 - x; ++xx) {
758 DrawBlock(xx, 2 * (y1 + y), true, s);
759 DrawBlock(xx, 2 * (y1 - y), true, s);
760 }
761 e2 = 2 * err;
762 if (e2 >= dx) {
763 x++;
764 err += dx += 2 * r2 * r2;
765 }
766 if (e2 <= dy) {
767 y++;
768 err += dy += 2 * r1 * r1;
769 }
770 } while (x <= 0);
771
772 while (y++ < r2) {
773 for (int yy = y1 + y; yy <= y1 - y; ++yy) {
774 DrawBlock(x1, 2 * yy, true, s);
775 }
776 }
777}
778
779/// @brief テキストを描画します。
780/// @param x テキストのx座標。
781/// @param y テキストのy座標。
782/// @param value 描画するテキスト。
783void Canvas::DrawText(int x, int y, const std::string& value) {
784 DrawText(x, y, value, nostyle);
785}
786
787/// @brief テキストを描画します。
788/// @param x テキストのx座標。
789/// @param y テキストのy座標。
790/// @param value 描画するテキスト。
791/// @param color テキストの色。
793 int y,
794 const std::string& value,
795 const Color& color) {
796 DrawText(x, y, value, [color](Pixel& p) { p.foreground_color = color; });
797}
798
799/// @brief テキストを描画します。
800/// @param x テキストのx座標。
801/// @param y テキストのy座標。
802/// @param value 描画するテキスト。
803/// @param style テキストのスタイル。
805 int y,
806 const std::string& value,
807 const Stylizer& style) {
808 for (const auto& it : Utf8ToGlyphs(value)) {
809 if (!IsIn(x, y)) {
810 x += 2;
811 continue;
812 }
813 Cell& cell = storage_[XY{x / 2, y / 4}];
814 cell.type = CellType::kCell;
815 cell.content.character = it;
816 style(cell.content);
817 x += 2;
818 }
819}
820
821/// @brief 指定された座標に定義済みのピクセルを直接描画します
822/// @param x ピクセルのx座標。
823/// @param y ピクセルのy座標。
824/// @param p 描画するピクセル。
825void Canvas::DrawPixel(int x, int y, const Pixel& p) {
826 Cell& cell = storage_[XY{x / 2, y / 4}];
827 cell.type = CellType::kCell;
828 cell.content = p;
829}
830
831/// @brief 定義済み画像を、指定された座標を左上隅として描画します
832/// 負の座標を指定して画像を自由に配置できます。
833/// 表示される部分のみが描画されます。
834/// @param x 画像の左上隅に対応するx座標。
835/// @param y 画像の左上隅に対応するy座標。
836/// @param image 描画する画像。
837void Canvas::DrawImage(int x, int y, const Image& image) {
838 x /= 2;
839 y /= 4;
840 const int dx_begin = std::max(0, -x);
841 const int dy_begin = std::max(0, -y);
842 const int dx_end = std::min(image.dimx(), width_ - x);
843 const int dy_end = std::min(image.dimy(), height_ - y);
844
845 for (int dy = dy_begin; dy < dy_end; ++dy) {
846 for (int dx = dx_begin; dx < dx_end; ++dx) {
847 Cell& cell = storage_[XY{
848 x + dx,
849 y + dy,
850 }];
851 cell.type = CellType::kCell;
852 cell.content = image.PixelAt(dx, dy);
853 }
854 }
855}
856
857/// @brief 指定された位置のピクセルを変更します。
858/// @param style ピクセルを変更する関数。
859void Canvas::Style(int x, int y, const Stylizer& style) {
860 if (IsIn(x, y)) {
861 style(storage_[XY{x / 2, y / 4}].content);
862 }
863}
864
865namespace {
866
867class CanvasNodeBase : public Node {
868 public:
869 CanvasNodeBase() = default;
870
871 void Render(Screen& screen) override {
872 const Canvas& c = canvas();
873 const int y_max = std::min(c.height() / 4, box_.y_max - box_.y_min + 1);
874 const int x_max = std::min(c.width() / 2, box_.x_max - box_.x_min + 1);
875 for (int y = 0; y < y_max; ++y) {
876 for (int x = 0; x < x_max; ++x) {
877 screen.PixelAt(box_.x_min + x, box_.y_min + y) = c.GetPixel(x, y);
878 }
879 }
880 }
881
882 virtual const Canvas& canvas() = 0;
883};
884
885} // namespace
886
887/// @brief CanvasまたはCanvasへの参照から要素を生成します。
888// NOLINTNEXTLINE
890 class Impl : public CanvasNodeBase {
891 public:
892 explicit Impl(ConstRef<Canvas> canvas) : canvas_(std::move(canvas)) {
893 requirement_.min_x = (canvas_->width() + 1) / 2;
894 requirement_.min_y = (canvas_->height() + 3) / 4;
895 }
896 const Canvas& canvas() final { return *canvas_; }
897 ConstRef<Canvas> canvas_;
898 };
899 return std::make_shared<Impl>(canvas);
900}
901
902/// @brief 要求されたサイズのキャンバスを描画する要素を生成します。
903/// @param width キャンバスの幅。
904/// @param height キャンバスの高さ。
905/// @param fn キャンバスを描画する関数。
906Element canvas(int width, int height, std::function<void(Canvas&)> fn) {
907 class Impl : public CanvasNodeBase {
908 public:
909 Impl(int width, int height, std::function<void(Canvas&)> fn)
910 : width_(width), height_(height), fn_(std::move(fn)) {}
911
912 void ComputeRequirement() final {
913 requirement_.min_x = (width_ + 1) / 2;
914 requirement_.min_y = (height_ + 3) / 4;
915 }
916
917 void Render(Screen& screen) final {
918 const int width = (box_.x_max - box_.x_min + 1) * 2;
919 const int height = (box_.y_max - box_.y_min + 1) * 4;
920 canvas_ = Canvas(width, height);
921 fn_(canvas_);
922 CanvasNodeBase::Render(screen);
923 }
924
925 const Canvas& canvas() final { return canvas_; }
926 Canvas canvas_;
927 int width_;
928 int height_;
929 std::function<void(Canvas&)> fn_;
930 };
931 return std::make_shared<Impl>(width, height, std::move(fn));
932}
933
934/// @brief キャンバスを描画する要素を生成します。
935/// @param fn キャンバスを描画する関数。
936Element canvas(std::function<void(Canvas&)> fn) {
937 const int default_dim = 12;
938 return canvas(default_dim, default_dim, std::move(fn));
939}
940
941} // namespace ftxui
アダプター。不変オブジェクトを所有または参照します。
Definition ref.hpp:16
ButtonOption Style()
void DrawImage(int x, int y, const Image &)
定義済み画像を、指定された座標を左上隅として描画します 負の座標を指定して画像を自由に配置できます。 表示される部分のみが描画されます。
void DrawBlockLine(int x1, int y1, int x2, int y2)
ブロック文字で線を描画します。
void DrawPointEllipseFilled(int x, int y, int r1, int r2)
点字ドットで塗りつぶされた楕円を描画します。
void DrawPointLine(int x1, int y1, int x2, int y2)
点字ドットで線を描画します。
void DrawText(int x, int y, const std::string &value, const Color &color)
テキストを描画します。
Canvas()=default
std::function< void(Pixel &)> Stylizer
Definition canvas.hpp:46
void DrawPointCircleFilled(int x, int y, int radius)
点字ドットで塗りつぶされた円を描画します。
void DrawPointOn(int x, int y)
点字ドットを描画します。
void DrawPointOff(int x, int y)
点字ドットを消去します。
Pixel GetPixel(int x, int y) const
セルの内容を取得します。
void DrawBlockEllipseFilled(int x1, int y1, int r1, int r2)
ブロック文字で塗りつぶされた楕円を描画します。
void DrawPointEllipse(int x, int y, int r1, int r2)
点字ドットで楕円を描画します。
void DrawPoint(int x, int y, bool value)
点字ドットを描画します。
void DrawBlockEllipse(int x1, int y1, int r1, int r2)
ブロック文字で楕円を描画します。
void DrawBlockToggle(int x, int y)
ブロックを切り替えます。塗りつぶされている場合は消去され、空の場合は塗りつぶされます。
void DrawBlockCircle(int x1, int y1, int radius)
ブロック文字で円を描画します。
void DrawBlockCircleFilled(int x1, int y1, int radius)
ブロック文字で塗りつぶされた円を描画します。
void DrawPointCircle(int x, int y, int radius)
点字ドットで円を描画します。
void DrawBlockOff(int x, int y)
ブロックを消去します。
void DrawBlock(int x, int y, bool value)
ブロックを描画します。
void DrawPointToggle(int x, int y)
点字ドットを切り替えます。塗りつぶされたものは消去され、それ以外は描画されます。
void Render(Screen &screen, const Element &element)
要素をftxui::Screenに表示します。
Definition node.cpp:84
Decorator color(Color)
前景色を使用して装飾します。
Canvasは、描画操作に関連付けられた描画可能なバッファです。
Definition canvas.hpp:36
int dimy() const
Definition image.hpp:36
Color foreground_color
Definition pixel.hpp:48
std::string character
Definition pixel.hpp:44
Pixel & PixelAt(int x, int y)
指定された位置のセル (ピクセル) にアクセスします。
Definition image.cpp:41
int dimx() const
Definition image.hpp:35
Colorは、ターミナルユーザーインターフェースにおける色を表すクラスです。
Definition color.hpp:25
ピクセルの長方形グリッド。
Definition image.hpp:17
ピクセルの長方形グリッド。
Definition screen.hpp:25
Unicode文字とそれに関連付けられたスタイル。
Definition pixel.hpp:14
FTXUI ftxui:: 名前空間
Definition animation.hpp:9
std::shared_ptr< Node > Element
Definition elements.hpp:21
std::vector< std::string > Utf8ToGlyphs(const std::string &input)
Definition string.cpp:1351
Element canvas(ConstRef< Canvas >)
CanvasまたはCanvasへの参照から要素を生成します。