mirror of
				https://github.com/ArthurSonzogni/FTXUI.git
				synced 2025-11-01 02:58:12 +08:00 
			
		
		
		
	Feature: Canvas (#287)
Draw using braille and block characters on a grid.
This commit is contained in:
		| @@ -7,6 +7,8 @@ unreleased (development) | |||||||
| ### Features: | ### Features: | ||||||
|  |  | ||||||
| #### DOM: | #### DOM: | ||||||
|  | - Add the `Canvas` class and `ElementFrom('canvas')` function. Together users of | ||||||
|  |   the library can draw using braille and block characters. | ||||||
| - Support `flexbox` dom elements. This is build symmetrically to the HTML one. | - Support `flexbox` dom elements. This is build symmetrically to the HTML one. | ||||||
|   All the following attributes are supported: direction, wrap, justify-content, |   All the following attributes are supported: direction, wrap, justify-content, | ||||||
|   align-items, align-content, gap |   align-items, align-content, gap | ||||||
|   | |||||||
| @@ -37,6 +37,7 @@ add_library(screen | |||||||
| ) | ) | ||||||
|  |  | ||||||
| add_library(dom | add_library(dom | ||||||
|  |   include/ftxui/dom/canvas.hpp | ||||||
|   include/ftxui/dom/elements.hpp |   include/ftxui/dom/elements.hpp | ||||||
|   include/ftxui/dom/flexbox_config.hpp |   include/ftxui/dom/flexbox_config.hpp | ||||||
|   include/ftxui/dom/node.hpp |   include/ftxui/dom/node.hpp | ||||||
| @@ -47,6 +48,7 @@ add_library(dom | |||||||
|   src/ftxui/dom/border.cpp |   src/ftxui/dom/border.cpp | ||||||
|   src/ftxui/dom/box_helper.cpp |   src/ftxui/dom/box_helper.cpp | ||||||
|   src/ftxui/dom/box_helper.hpp |   src/ftxui/dom/box_helper.hpp | ||||||
|  |   src/ftxui/dom/canvas.cpp | ||||||
|   src/ftxui/dom/clear_under.cpp |   src/ftxui/dom/clear_under.cpp | ||||||
|   src/ftxui/dom/color.cpp |   src/ftxui/dom/color.cpp | ||||||
|   src/ftxui/dom/composite_decorator.cpp |   src/ftxui/dom/composite_decorator.cpp | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| set(EXAMPLES_DIR ${CMAKE_CURRENT_SOURCE_DIR}) | set(EXAMPLES_DIR ${CMAKE_CURRENT_SOURCE_DIR}) | ||||||
| function(example name) | function(example name) | ||||||
|   add_executable(${name} ${name}.cpp) |   add_executable(ftxui_example_${name} ${name}.cpp) | ||||||
|   target_link_libraries(${name} PUBLIC ${DIRECTORY_LIB}) |   target_link_libraries(ftxui_example_${name} PUBLIC ${DIRECTORY_LIB}) | ||||||
|   file(RELATIVE_PATH dir ${EXAMPLES_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) |   file(RELATIVE_PATH dir ${EXAMPLES_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) | ||||||
|   set_property(GLOBAL APPEND PROPERTY FTXUI::EXAMPLES ${dir}/${name}) |   set_property(GLOBAL APPEND PROPERTY FTXUI::EXAMPLES ${dir}/${name}) | ||||||
| endfunction(example) | endfunction(example) | ||||||
|   | |||||||
| @@ -1,16 +1,17 @@ | |||||||
| set(DIRECTORY_LIB component) | set(DIRECTORY_LIB component) | ||||||
|  |  | ||||||
| example(button) | example(button) | ||||||
|  | example(canvas_animated) | ||||||
| example(checkbox) | example(checkbox) | ||||||
| example(checkbox_in_frame) | example(checkbox_in_frame) | ||||||
| example(composition) | example(composition) | ||||||
| example(dropdown) | example(dropdown) | ||||||
| example(flexbox) | example(flexbox_gallery) | ||||||
|  | example(focus) | ||||||
| example(gallery) | example(gallery) | ||||||
| example(homescreen) | example(homescreen) | ||||||
| example(input) | example(input) | ||||||
| example(maybe) | example(maybe) | ||||||
| example(focus) |  | ||||||
| example(menu) | example(menu) | ||||||
| example(menu2) | example(menu2) | ||||||
| example(menu_entries) | example(menu_entries) | ||||||
|   | |||||||
							
								
								
									
										257
									
								
								examples/component/canvas_animated.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										257
									
								
								examples/component/canvas_animated.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,257 @@ | |||||||
|  | #include <stddef.h>                // for size_t | ||||||
|  | #include <stdio.h>                 // for getchar | ||||||
|  | #include <ftxui/dom/elements.hpp>  // for operator|, size, Element, text, hcenter, Decorator, Fit, WIDTH, hflow, window, EQUAL, GREATER_THAN, HEIGHT, bold, border, dim, LESS_THAN | ||||||
|  | #include <ftxui/screen/screen.hpp>  // for Full, Screen | ||||||
|  | #include <memory>                   // for allocator, shared_ptr | ||||||
|  | #include <string>  // for operator+, to_string, char_traits, string | ||||||
|  |  | ||||||
|  | #include "ftxui/component/component.hpp" | ||||||
|  | #include "ftxui/component/screen_interactive.hpp" | ||||||
|  |  | ||||||
|  | #include <cmath> | ||||||
|  | int main(int argc, const char* argv[]) { | ||||||
|  |   using namespace ftxui; | ||||||
|  |  | ||||||
|  |   int mouse_x = 0; | ||||||
|  |   int mouse_y = 0; | ||||||
|  |  | ||||||
|  |   // A triangle following the mouse, using braille characters. | ||||||
|  |   auto renderer_line_braille = Renderer([&] { | ||||||
|  |     auto c = Canvas(100, 100); | ||||||
|  |     c.DrawText(0,0, "Several lines (braille)"); | ||||||
|  |     c.DrawPointLine(mouse_x, mouse_y, 80, 10, Color::Red); | ||||||
|  |     c.DrawPointLine(80, 10, 80, 40, Color::Blue); | ||||||
|  |     c.DrawPointLine(80, 40, mouse_x, mouse_y, Color::Green); | ||||||
|  |     return ElementFrom(std::move(c)); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   // A triangle following the mouse, using block characters. | ||||||
|  |   auto renderer_line_block = Renderer([&] { | ||||||
|  |     auto c = Canvas(100, 100); | ||||||
|  |     c.DrawText(0,0, "Several lines (block)"); | ||||||
|  |     c.DrawBlockLine(mouse_x, mouse_y, 80, 10, Color::Red); | ||||||
|  |     c.DrawBlockLine(80, 10, 80, 40, Color::Blue); | ||||||
|  |     c.DrawBlockLine(80, 40, mouse_x, mouse_y, Color::Green); | ||||||
|  |     return ElementFrom(std::move(c)); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   // A circle following the mouse, using braille characters. | ||||||
|  |   auto renderer_circle_braille = Renderer([&] { | ||||||
|  |     auto c = Canvas(100, 100); | ||||||
|  |     c.DrawText(0,0, "A circle (braille)"); | ||||||
|  |     c.DrawPointCircle(mouse_x, mouse_y, 30); | ||||||
|  |     return ElementFrom(std::move(c)); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   // A circle following the mouse, using block characters. | ||||||
|  |   auto renderer_circle_block = Renderer([&] { | ||||||
|  |     auto c = Canvas(100, 100); | ||||||
|  |     c.DrawText(0,0, "A circle (block)"); | ||||||
|  |     c.DrawBlockCircle(mouse_x, mouse_y, 30); | ||||||
|  |     return ElementFrom(std::move(c)); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   // A filled circle following the mouse, using braille characters. | ||||||
|  |   auto renderer_circle_filled_braille = Renderer([&] { | ||||||
|  |     auto c = Canvas(100, 100); | ||||||
|  |     c.DrawText(0,0, "A circle filled (braille)"); | ||||||
|  |     c.DrawPointCircleFilled(mouse_x, mouse_y, 30); | ||||||
|  |     return ElementFrom(std::move(c)); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   // A filled circle following the mouse, using block characters. | ||||||
|  |   auto renderer_circle_filled_block = Renderer([&] { | ||||||
|  |     auto c = Canvas(100, 100); | ||||||
|  |     c.DrawText(0,0, "A circle filled (block)"); | ||||||
|  |     c.DrawBlockCircleFilled(mouse_x, mouse_y, 30); | ||||||
|  |     return ElementFrom(std::move(c)); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   // An ellipse following the mouse, using braille characters. | ||||||
|  |   auto renderer_ellipse_braille = Renderer([&] { | ||||||
|  |     auto c = Canvas(100, 100); | ||||||
|  |     c.DrawText(0,0, "An ellipse (braille)"); | ||||||
|  |     c.DrawPointEllipse(mouse_x / 2, mouse_y / 2, mouse_x / 2, mouse_y / 2); | ||||||
|  |     return ElementFrom(std::move(c)); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   // An ellipse following the mouse, using block characters. | ||||||
|  |   auto renderer_ellipse_block = Renderer([&] { | ||||||
|  |     auto c = Canvas(100, 100); | ||||||
|  |     c.DrawText(0,0, "An ellipse (block)"); | ||||||
|  |     c.DrawBlockEllipse(mouse_x / 2, mouse_y / 2, mouse_x / 2, mouse_y / 2); | ||||||
|  |     return ElementFrom(std::move(c)); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   // An ellipse following the mouse filled, using braille characters. | ||||||
|  |   auto renderer_ellipse_filled_braille = Renderer([&] { | ||||||
|  |     auto c = Canvas(100, 100); | ||||||
|  |     c.DrawText(0,0, "A filled ellipse (braille)"); | ||||||
|  |     c.DrawPointEllipseFilled(mouse_x / 2, mouse_y / 2, mouse_x / 2, | ||||||
|  |                               mouse_y / 2); | ||||||
|  |     return ElementFrom(std::move(c)); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   // An ellipse following the mouse filled, using block characters. | ||||||
|  |   auto renderer_ellipse_filled_block = Renderer([&] { | ||||||
|  |     auto c = Canvas(100, 100); | ||||||
|  |     c.DrawText(0,0, "A filled ellipse (block)"); | ||||||
|  |     c.DrawBlockEllipseFilled(mouse_x / 2, mouse_y / 2, mouse_x / 2, | ||||||
|  |                               mouse_y / 2); | ||||||
|  |     c.DrawBlockEllipse(mouse_x / 2, mouse_y / 2, mouse_x / 2, mouse_y / 2); | ||||||
|  |     return ElementFrom(std::move(c)); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   // A text following the mouse | ||||||
|  |   auto renderer_text = Renderer([&] { | ||||||
|  |     auto c = Canvas(100, 100); | ||||||
|  |     c.DrawText(0, 0, "A piece of text"); | ||||||
|  |     c.DrawText(mouse_x, mouse_y, "This is a piece of text with effects", | ||||||
|  |                [](Pixel& p) { | ||||||
|  |                  p.foreground_color = Color::Red; | ||||||
|  |                  p.underlined = true; | ||||||
|  |                  p.bold = true; | ||||||
|  |                }); | ||||||
|  |     return ElementFrom(std::move(c)); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   auto renderer_plot_1 = Renderer([&] { | ||||||
|  |     auto c = Canvas(100, 100); | ||||||
|  |     c.DrawText(0, 0, "A graph"); | ||||||
|  |  | ||||||
|  |     std::vector<int> ys(100); | ||||||
|  |     for (int x = 0; x < 100; x++) { | ||||||
|  |       float dx = x - mouse_x; | ||||||
|  |       float dy = 50; | ||||||
|  |       ys[x] = dy + 20 * cos(dx * 0.14) + 10 * sin(dx * 0.42); | ||||||
|  |     } | ||||||
|  |     for (int x = 1; x < 99; x++) | ||||||
|  |       c.DrawPointLine(x, ys[x], x + 1, ys[x + 1]); | ||||||
|  |  | ||||||
|  |     return ElementFrom(std::move(c)); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   auto renderer_plot_2 = Renderer([&] { | ||||||
|  |     auto c = Canvas(100, 100); | ||||||
|  |     c.DrawText(0, 0, "A symmetrical graph filled"); | ||||||
|  |     std::vector<int> ys(100); | ||||||
|  |     for (int x = 0; x < 100; x++) { | ||||||
|  |       ys[x] = 30 +  // | ||||||
|  |               10 * cos(x * 0.2 - mouse_x * 0.05) + // | ||||||
|  |               5 * sin(x * 0.4) + // | ||||||
|  |               5 * sin(x * 0.3 - mouse_y * 0.05); // | ||||||
|  |     } | ||||||
|  |     for (int x = 0; x < 100; x++) { | ||||||
|  |       c.DrawPointLine(x, 50+ys[x], x, 50-ys[x], Color::Red); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return ElementFrom(std::move(c)); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   auto renderer_plot_3 = Renderer([&] { | ||||||
|  |     auto c = Canvas(100, 100); | ||||||
|  |     c.DrawText(0, 0, "A 2D gaussian plot"); | ||||||
|  |     int size = 15; | ||||||
|  |  | ||||||
|  |     // mouse_x = 5mx + 3*my  | ||||||
|  |     // mouse_y = 0mx + -5my + 90 | ||||||
|  |     float my = (mouse_y - 90) / -5.f; | ||||||
|  |     float mx = (mouse_x - 3 * my) / 5.f; | ||||||
|  |     std::vector<std::vector<float>> ys(size, std::vector<float>(size)); | ||||||
|  |     for (int y = 0; y < size; y++) { | ||||||
|  |       for (int x = 0; x < size; x++) { | ||||||
|  |         float dx = x-mx; | ||||||
|  |         float dy = y-my; | ||||||
|  |         ys[y][x] = -1.5 + 3.0 * std::exp(-0.2f * (dx*dx+dy*dy)); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     for (int y = 0; y < size; y++) { | ||||||
|  |       for (int x = 0; x < size; x++) { | ||||||
|  |         if (x != 0) { | ||||||
|  |           c.DrawPointLine( | ||||||
|  |               5 * (x - 1) + 3 * (y - 0), 90 - 5 * (y - 0) - 5 * ys[y][x - 1], | ||||||
|  |               5 * (x - 0) + 3 * (y - 0), 90 - 5 * (y - 0) - 5 * ys[y][x]); | ||||||
|  |         } | ||||||
|  |         if (y != 0) { | ||||||
|  |           c.DrawPointLine( | ||||||
|  |               5 * (x - 0) + 3 * (y - 1), 90 - 5 * (y - 1) - 5 * ys[y - 1][x], | ||||||
|  |               5 * (x - 0) + 3 * (y - 0), 90 - 5 * (y - 0) - 5 * ys[y][x]); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return ElementFrom(std::move(c)); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   int selected_tab = 0; | ||||||
|  |   auto tab = Container::Tab({ | ||||||
|  |       renderer_line_braille, | ||||||
|  |       renderer_line_block, | ||||||
|  |       renderer_circle_braille, | ||||||
|  |       renderer_circle_block, | ||||||
|  |       renderer_circle_filled_braille, | ||||||
|  |       renderer_circle_filled_block, | ||||||
|  |       renderer_ellipse_braille, | ||||||
|  |       renderer_ellipse_block, | ||||||
|  |       renderer_ellipse_filled_braille, | ||||||
|  |       renderer_ellipse_filled_block, | ||||||
|  |  | ||||||
|  |       renderer_plot_1, | ||||||
|  |       renderer_plot_2, | ||||||
|  |       renderer_plot_3, | ||||||
|  |  | ||||||
|  |       renderer_text, | ||||||
|  |   }, &selected_tab); | ||||||
|  |  | ||||||
|  |   // This capture the last mouse position. | ||||||
|  |   auto tab_with_mouse = CatchEvent(tab, [&](Event e) { | ||||||
|  |     if (e.is_mouse()) { | ||||||
|  |       mouse_x = (e.mouse().x - 1) * 2; | ||||||
|  |       mouse_y = (e.mouse().y - 1) * 4; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   std::vector<std::string> tab_titles = { | ||||||
|  |       "line (braille)", | ||||||
|  |       "line (block)", | ||||||
|  |       "circle (braille)", | ||||||
|  |       "circle (block)", | ||||||
|  |       "circle filled (braille)", | ||||||
|  |       "circle filled (block)", | ||||||
|  |       "ellipse (braille)", | ||||||
|  |       "ellipse (block)", | ||||||
|  |       "ellipse filled (braille)", | ||||||
|  |       "ellipse filled (block)", | ||||||
|  |       "plot_1 simple", | ||||||
|  |       "plot_2 filled", | ||||||
|  |       "plot_3 3D", | ||||||
|  |       "text", | ||||||
|  |   }; | ||||||
|  |   auto tab_toggle = Menu(&tab_titles, &selected_tab); | ||||||
|  |  | ||||||
|  |   auto component = Container::Horizontal({ | ||||||
|  |       tab_with_mouse, | ||||||
|  |       tab_toggle, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   // Add some separator to decorate the whole component: | ||||||
|  |   auto component_renderer = Renderer(component, [&] { | ||||||
|  |     return hbox({ | ||||||
|  |                tab_with_mouse->Render(), | ||||||
|  |                separator(), | ||||||
|  |                tab_toggle->Render(), | ||||||
|  |            }) | | ||||||
|  |            border; | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   auto screen = ScreenInteractive::FitComponent(); | ||||||
|  |   screen.Loop(component_renderer); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Copyright 2021 Arthur Sonzogni. All rights reserved. | ||||||
|  | // Use of this source code is governed by the MIT license that can be found in | ||||||
|  | // the LICENSED file. | ||||||
| @@ -37,8 +37,8 @@ int main(int argc, const char* argv[]) { | |||||||
|   float focus_x = 0.0f; |   float focus_x = 0.0f; | ||||||
|   float focus_y = 0.0f; |   float focus_y = 0.0f; | ||||||
|  |  | ||||||
|   auto slider_x = Slider("x", &focus_x, 0.f, 1.f, 0.05f); |   auto slider_x = Slider("x", &focus_x, 0.f, 1.f, 0.01f); | ||||||
|   auto slider_y = Slider("y", &focus_y, 0.f, 1.f, 0.05f); |   auto slider_y = Slider("y", &focus_y, 0.f, 1.f, 0.01f); | ||||||
|  |  | ||||||
|   auto renderer = Renderer( |   auto renderer = Renderer( | ||||||
|       Container::Vertical({ |       Container::Vertical({ | ||||||
|   | |||||||
| @@ -406,8 +406,7 @@ int main(int argc, const char* argv[]) { | |||||||
|                    make_box(6, 3), |                    make_box(6, 3), | ||||||
|                }), |                }), | ||||||
|            }) | |            }) | | ||||||
|            // vscroll_indicator | yflex; |            vscroll_indicator | yframe | flex; | ||||||
|            yflex | vscroll_indicator; |  | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   auto paragraph_renderer_right = Renderer([] { |   auto paragraph_renderer_right = Renderer([] { | ||||||
|   | |||||||
| @@ -7,11 +7,11 @@ example(color_info_palette256) | |||||||
| example(color_truecolor_HSV) | example(color_truecolor_HSV) | ||||||
| example(color_truecolor_RGB) | example(color_truecolor_RGB) | ||||||
| example(dbox) | example(dbox) | ||||||
|  | example(canvas) | ||||||
| example(gauge) | example(gauge) | ||||||
| example(graph) | example(graph) | ||||||
| example(gridbox) | example(gridbox) | ||||||
| example(hflow) | example(hflow) | ||||||
| example(vflow) |  | ||||||
| example(html_like) | example(html_like) | ||||||
| example(package_manager) | example(package_manager) | ||||||
| example(paragraph) | example(paragraph) | ||||||
| @@ -28,4 +28,5 @@ example(style_inverted) | |||||||
| example(style_underlined) | example(style_underlined) | ||||||
| example(table) | example(table) | ||||||
| example(vbox_hbox) | example(vbox_hbox) | ||||||
|  | example(vflow) | ||||||
| example(window) | example(window) | ||||||
|   | |||||||
							
								
								
									
										50
									
								
								examples/dom/canvas.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								examples/dom/canvas.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | |||||||
|  | #include <stddef.h>                // for size_t | ||||||
|  | #include <stdio.h>                 // for getchar | ||||||
|  | #include <ftxui/dom/elements.hpp>  // for operator|, size, Element, text, hcenter, Decorator, Fit, WIDTH, hflow, window, EQUAL, GREATER_THAN, HEIGHT, bold, border, dim, LESS_THAN | ||||||
|  | #include <ftxui/screen/screen.hpp>  // for Full, Screen | ||||||
|  | #include <memory>                   // for allocator, shared_ptr | ||||||
|  | #include <string>  // for operator+, to_string, char_traits, string | ||||||
|  |  | ||||||
|  | #include "ftxui/dom/flexbox_config.hpp"  // for ftxui | ||||||
|  | #include "ftxui/dom/node.hpp"            // for Render | ||||||
|  |  | ||||||
|  | #include <cmath> | ||||||
|  | int main(int argc, const char* argv[]) { | ||||||
|  |   using namespace ftxui; | ||||||
|  |  | ||||||
|  |   auto canvas = Canvas(100, 100); | ||||||
|  |  | ||||||
|  |   canvas.DrawText(0, 0, "This is a canvas", [](Pixel& p) -> void { | ||||||
|  |     p.foreground_color = Color::Red; | ||||||
|  |     p.underlined = true; | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   // Triangle: | ||||||
|  |   canvas.DrawPointLine(10, 10, 80, 10, Color::Red); | ||||||
|  |   canvas.DrawPointLine(80, 10, 80, 40, Color::Blue); | ||||||
|  |   canvas.DrawPointLine(80, 40, 10, 10, Color::Green); | ||||||
|  |  | ||||||
|  |   // Circle, not filled and filled: | ||||||
|  |   canvas.DrawPointCircle(30, 50, 20); | ||||||
|  |   canvas.DrawPointCircleFilled(40, 40, 10); | ||||||
|  |  | ||||||
|  |   // Plot a function: | ||||||
|  |   std::vector<int> ys(100); | ||||||
|  |   for (int x = 0; x < 100; x++) | ||||||
|  |     ys[x] = 80 + 20 * cos(x * 0.2); | ||||||
|  |   for (int x = 0; x < 99; x++)  | ||||||
|  |     canvas.DrawPointLine(x, ys[x], x + 1, ys[x + 1], Color::Red); | ||||||
|  |  | ||||||
|  |   auto document = ElementFrom(&canvas) | border; | ||||||
|  |  | ||||||
|  |   auto screen = Screen::Create(Dimension::Fit(document)); | ||||||
|  |   Render(screen, document); | ||||||
|  |   screen.Print(); | ||||||
|  |   getchar(); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Copyright 2020 Arthur Sonzogni. All rights reserved. | ||||||
|  | // Use of this source code is governed by the MIT license that can be found in | ||||||
|  | // the LICENSE file. | ||||||
							
								
								
									
										38
									
								
								examples/dom/flexbox.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								examples/dom/flexbox.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | #include <stddef.h>                // for size_t | ||||||
|  | #include <stdio.h>                 // for getchar | ||||||
|  | #include <ftxui/dom/elements.hpp>  // for operator|, size, Element, text, hcenter, Decorator, Fit, WIDTH, hflow, window, EQUAL, GREATER_THAN, HEIGHT, bold, border, dim, LESS_THAN | ||||||
|  | #include <ftxui/screen/screen.hpp>  // for Full, Screen | ||||||
|  | #include <memory>                   // for allocator, shared_ptr | ||||||
|  | #include <string>  // for operator+, to_string, char_traits, string | ||||||
|  |  | ||||||
|  | #include "ftxui/dom/flexbox_config.hpp"  // for ftxui | ||||||
|  | #include "ftxui/dom/node.hpp"            // for Render | ||||||
|  |  | ||||||
|  | int main(int argc, const char* argv[]) { | ||||||
|  |   using namespace ftxui; | ||||||
|  |  | ||||||
|  |   auto image = Canvas(100, 100); | ||||||
|  |  | ||||||
|  |   auto document = vbox({ | ||||||
|  |     make_box("header"), | ||||||
|  |     hbox({ | ||||||
|  |       make_box("left side"), | ||||||
|  |       make_box("center") | flex, | ||||||
|  |       make_box("right side"), | ||||||
|  |     }) | flex, | ||||||
|  |     make_box("footer") | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   //auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document)); | ||||||
|  |   //auto screen = Screen::Create(Dimension::Fit(document)); | ||||||
|  |   auto screen = Screen::Create(Dimension::Full()); | ||||||
|  |   Render(screen, document); | ||||||
|  |   screen.Print(); | ||||||
|  |   getchar(); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Copyright 2020 Arthur Sonzogni. All rights reserved. | ||||||
|  | // Use of this source code is governed by the MIT license that can be found in | ||||||
|  | // the LICENSE file. | ||||||
| @@ -71,7 +71,10 @@ | |||||||
|       postRun: [], |       postRun: [], | ||||||
|       onRuntimeInitialized: () => {}, |       onRuntimeInitialized: () => {}, | ||||||
|     }; |     }; | ||||||
|     document.querySelector("#example_script").src = example + '.js'; |  | ||||||
|  |     const words = example.split('/') | ||||||
|  |     words[1] = "ftxui_example_" + words[1] + ".js" | ||||||
|  |     document.querySelector("#example_script").src = words.join('/'); | ||||||
|   </script> |   </script> | ||||||
|  |  | ||||||
|   <style> |   <style> | ||||||
|   | |||||||
							
								
								
									
										137
									
								
								include/ftxui/dom/canvas.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								include/ftxui/dom/canvas.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | |||||||
|  | #ifndef FTXUI_DOM_CANVAS_HPP | ||||||
|  | #define FTXUI_DOM_CANVAS_HPP | ||||||
|  |  | ||||||
|  | #include "ftxui/screen/color.hpp" | ||||||
|  | #include "ftxui/screen/screen.hpp" | ||||||
|  | #include <unordered_map> | ||||||
|  | #include <functional> | ||||||
|  |  | ||||||
|  | namespace ftxui { | ||||||
|  |  | ||||||
|  | struct Canvas { | ||||||
|  |  public: | ||||||
|  |   Canvas() {} | ||||||
|  |   Canvas(int width, int height); | ||||||
|  |  | ||||||
|  |   // Getters: | ||||||
|  |   int width() const { return width_; } | ||||||
|  |   int height() const { return height_; } | ||||||
|  |   Pixel GetPixel(int x, int y) const; | ||||||
|  |  | ||||||
|  |   using Stylizer = std::function<void(Pixel&)>; | ||||||
|  |  | ||||||
|  |   // Draws using braille characters -------------------------------------------- | ||||||
|  |   void DrawPointOn(int x, int y); | ||||||
|  |   void DrawPointOff(int x, int y); | ||||||
|  |   void DrawPointToggle(int x, int y); | ||||||
|  |   void DrawPoint(int x, int y, bool value); | ||||||
|  |   void DrawPoint(int x, int y, bool value, const Stylizer& s); | ||||||
|  |   void DrawPoint(int x, int y, bool value, const Color& color); | ||||||
|  |   void DrawPointLine(int x1, int y1, int x2, int y2); | ||||||
|  |   void DrawPointLine(int x1, int y1, int x2, int y2, const Stylizer& s); | ||||||
|  |   void DrawPointLine(int x1, int y1, int x2, int y2, const Color& color); | ||||||
|  |   void DrawPointCircle(int x, int y, int radius); | ||||||
|  |   void DrawPointCircle(int x, int y, int radius, const Stylizer& s); | ||||||
|  |   void DrawPointCircle(int x, int y, int radius, const Color& color); | ||||||
|  |   void DrawPointCircleFilled(int x, int y, int radius); | ||||||
|  |   void DrawPointCircleFilled(int x, | ||||||
|  |                              int y, | ||||||
|  |                              int radius, | ||||||
|  |                              const Stylizer& s); | ||||||
|  |   void DrawPointCircleFilled(int x, int y, int radius, const Color& color); | ||||||
|  |   void DrawPointEllipse(int x, int y, int r1, int r2); | ||||||
|  |   void DrawPointEllipse(int x, int y, int r1, int r2, const Color& color); | ||||||
|  |   void DrawPointEllipse(int x, int y, int r1, int r2, const Stylizer& s); | ||||||
|  |   void DrawPointEllipseFilled(int x, int y, int r1, int r2); | ||||||
|  |   void DrawPointEllipseFilled(int x, | ||||||
|  |                                int y, | ||||||
|  |                                int r1, | ||||||
|  |                                int r2, | ||||||
|  |                                const Color& color); | ||||||
|  |   void DrawPointEllipseFilled(int x, | ||||||
|  |                                int y, | ||||||
|  |                                int r1, | ||||||
|  |                                int r2, | ||||||
|  |                                const Stylizer& s); | ||||||
|  |  | ||||||
|  |   // Draw using box characters ------------------------------------------------- | ||||||
|  |   // Block are of size 1x2. y is considered to be a multiple of 2. | ||||||
|  |   void DrawBlockOn(int x, int y); | ||||||
|  |   void DrawBlockOff(int x, int y); | ||||||
|  |   void DrawBlockToggle(int x, int y); | ||||||
|  |   void DrawBlock(int x, int y, bool value); | ||||||
|  |   void DrawBlock(int x, int y, bool value, const Stylizer& s); | ||||||
|  |   void DrawBlock(int x, int y, bool value, const Color& color); | ||||||
|  |   void DrawBlockLine(int x1, int y1, int x2, int y2); | ||||||
|  |   void DrawBlockLine(int x1, int y1, int x2, int y2, const Stylizer& s); | ||||||
|  |   void DrawBlockLine(int x1, int y1, int x2, int y2, const Color& color); | ||||||
|  |   void DrawBlockCircle(int x1, int y1, int radius); | ||||||
|  |   void DrawBlockCircle(int x1, int y1, int radius, const Stylizer& s); | ||||||
|  |   void DrawBlockCircle(int x1, int y1, int radius, const Color& color); | ||||||
|  |   void DrawBlockCircleFilled(int x1, int y1, int radius); | ||||||
|  |   void DrawBlockCircleFilled(int x1, int y1, int radius, const Stylizer& s); | ||||||
|  |   void DrawBlockCircleFilled(int x1, int y1, int radius, const Color& color); | ||||||
|  |   void DrawBlockEllipse(int x1, int y1, int r1, int r2); | ||||||
|  |   void DrawBlockEllipse(int x1, int y1, int r1, int r2, const Stylizer& s); | ||||||
|  |   void DrawBlockEllipse(int x1, int y1, int r1, int r2, const Color& color); | ||||||
|  |   void DrawBlockEllipseFilled(int x1, int y1, int r1, int r2); | ||||||
|  |   void DrawBlockEllipseFilled(int x1, | ||||||
|  |                                int y1, | ||||||
|  |                                int r1, | ||||||
|  |                                int r2, | ||||||
|  |                                const Stylizer& s); | ||||||
|  |   void DrawBlockEllipseFilled(int x1, | ||||||
|  |                                int y1, | ||||||
|  |                                int r1, | ||||||
|  |                                int r2, | ||||||
|  |                                const Color& color); | ||||||
|  |  | ||||||
|  |   // Draw using normal characters ---------------------------------------------- | ||||||
|  |   // Draw using character of size 2x4 at position (x,y) | ||||||
|  |   // x is considered to be a multiple of 2. | ||||||
|  |   // y is considered to be a multiple of 4. | ||||||
|  |   void DrawText(int x, int y, const std::string& value); | ||||||
|  |   void DrawText(int x, int y, const std::string& value, const Color& color); | ||||||
|  |   void DrawText(int x, | ||||||
|  |                 int y, | ||||||
|  |                 const std::string& value, | ||||||
|  |                 const Stylizer& style); | ||||||
|  |  | ||||||
|  |   // Decorator: | ||||||
|  |   // x is considered to be a multiple of 2. | ||||||
|  |   // y is considered to be a multiple of 4. | ||||||
|  |   void Style(int x, int y, const Stylizer& style); | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   bool IsIn(int x, int y) const { | ||||||
|  |     return x >= 0 && x < width_ && y >= 0 && y < height_; | ||||||
|  |   } | ||||||
|  |   enum CellType { | ||||||
|  |     kBraille, | ||||||
|  |     kBlock, | ||||||
|  |     kText, | ||||||
|  |   }; | ||||||
|  |   struct Cell { | ||||||
|  |     CellType type = kText; | ||||||
|  |     Pixel content; | ||||||
|  |   }; | ||||||
|  |   struct XY { | ||||||
|  |     int x; | ||||||
|  |     int y; | ||||||
|  |     bool operator==(const XY& other) const { | ||||||
|  |       return x == other.x && y == other.y; | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   struct XYHash { | ||||||
|  |     size_t operator()(const XY& xy) const { return xy.x * 1024 + xy.y; } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   int width_ = 0; | ||||||
|  |   int height_ = 0; | ||||||
|  |   std::unordered_map<XY, Cell, XYHash> storage_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace ftxui | ||||||
|  |  | ||||||
|  | #endif // FTXUI_DOM_CANVAS_HPP | ||||||
| @@ -4,12 +4,14 @@ | |||||||
| #include <functional> | #include <functional> | ||||||
| #include <memory> | #include <memory> | ||||||
|  |  | ||||||
|  | #include "ftxui/dom/canvas.hpp" | ||||||
| #include "ftxui/dom/flexbox_config.hpp" | #include "ftxui/dom/flexbox_config.hpp" | ||||||
| #include "ftxui/dom/node.hpp" | #include "ftxui/dom/node.hpp" | ||||||
| #include "ftxui/screen/box.hpp" | #include "ftxui/screen/box.hpp" | ||||||
| #include "ftxui/screen/color.hpp" | #include "ftxui/screen/color.hpp" | ||||||
| #include "ftxui/screen/screen.hpp" | #include "ftxui/screen/screen.hpp" | ||||||
| #include "ftxui/screen/terminal.hpp" | #include "ftxui/screen/terminal.hpp" | ||||||
|  | #include "ftxui/util/ref.hpp" | ||||||
|  |  | ||||||
| namespace ftxui { | namespace ftxui { | ||||||
| class Node; | class Node; | ||||||
| @@ -57,6 +59,7 @@ Element paragraphAlignCenter(std::string text); | |||||||
| Element paragraphAlignJustify(std::string text); | Element paragraphAlignJustify(std::string text); | ||||||
| Element graph(GraphFunction); | Element graph(GraphFunction); | ||||||
| Element emptyElement(); | Element emptyElement(); | ||||||
|  | Element ElementFrom(ConstRef<Canvas>); | ||||||
|  |  | ||||||
| // -- Decorator --- | // -- Decorator --- | ||||||
| Element bold(Element); | Element bold(Element); | ||||||
|   | |||||||
							
								
								
									
										595
									
								
								src/ftxui/dom/canvas.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										595
									
								
								src/ftxui/dom/canvas.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,595 @@ | |||||||
|  | #include "ftxui/dom/canvas.hpp" | ||||||
|  |  | ||||||
|  | #include <map> | ||||||
|  |  | ||||||
|  | #include "ftxui/dom/elements.hpp" | ||||||
|  | #include "ftxui/screen/screen.hpp" | ||||||
|  |  | ||||||
|  | namespace ftxui { | ||||||
|  |  | ||||||
|  | namespace { | ||||||
|  |  | ||||||
|  | // Base UTF8 pattern: | ||||||
|  | // 11100010 10100000 10000000 // empty | ||||||
|  |  | ||||||
|  | // Pattern for the individuel dots: | ||||||
|  | // ┌──────┬───────┐ | ||||||
|  | // │dot1  │ dot4  │ | ||||||
|  | // ├──────┼───────┤ | ||||||
|  | // │dot2  │ dot5  │ | ||||||
|  | // ├──────┼───────┤ | ||||||
|  | // │dot3  │ dot6  │ | ||||||
|  | // ├──────┼───────┤ | ||||||
|  | // │dot0-1│ dot0-2│ | ||||||
|  | // └──────┴───────┘ | ||||||
|  | // 11100010 10100000 10000001 // dot1 | ||||||
|  | // 11100010 10100000 10000010 // dot2 | ||||||
|  | // 11100010 10100000 10000100 // dot3 | ||||||
|  | // 11100010 10100001 10000000 // dot0-1 | ||||||
|  | // 11100010 10100000 10001000 // dot4 | ||||||
|  | // 11100010 10100000 10010000 // dot5 | ||||||
|  | // 11100010 10100000 10100000 // dot6 | ||||||
|  | // 11100010 10100010 10000000 // dot0-2 | ||||||
|  |  | ||||||
|  | uint8_t g_map_braille[2][4][2] = { | ||||||
|  |     { | ||||||
|  |         {0b00000000, 0b00000001},  // dot1 | ||||||
|  |         {0b00000000, 0b00000010},  // dot2 | ||||||
|  |         {0b00000000, 0b00000100},  // dot3 | ||||||
|  |         {0b00000001, 0b00000000},  // dot0-1 | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         {0b00000000, 0b00001000},  // dot4 | ||||||
|  |         {0b00000000, 0b00010000},  // dot5 | ||||||
|  |         {0b00000000, 0b00100000},  // dot6 | ||||||
|  |         {0b00000010, 0b00000000},  // dot0-2 | ||||||
|  |     }, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | std::vector<std::string> g_map_block = { | ||||||
|  |     " ", "▘", "▖", "▌", "▝", "▀", "▞", "▛", | ||||||
|  |     "▗", "▚", "▄", "▙", "▐", "▜", "▟", "█", | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const std::map<std::string, uint8_t> g_map_block_inversed = { | ||||||
|  |     {" ", 0b0000}, {"▘", 0b0001}, {"▖", 0b0010}, {"▌", 0b0011}, | ||||||
|  |     {"▝", 0b0100}, {"▀", 0b0101}, {"▞", 0b0110}, {"▛", 0b0111}, | ||||||
|  |     {"▗", 0b1000}, {"▚", 0b1001}, {"▄", 0b1010}, {"▙", 0b1011}, | ||||||
|  |     {"▐", 0b1100}, {"▜", 0b1101}, {"▟", 0b1110}, {"█", 0b1111}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace | ||||||
|  |  | ||||||
|  | Canvas::Canvas(int width, int height) | ||||||
|  |     : width_(width), height_(height), storage_(width_ * height_ / 8) {} | ||||||
|  |  | ||||||
|  | Pixel Canvas::GetPixel(int x, int y) const { | ||||||
|  |   auto it = storage_.find(XY{x / 2, y / 4}); | ||||||
|  |   return (it == storage_.end()) ? Pixel{} : it->second.content; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPoint(int x, int y, bool value) { | ||||||
|  |   DrawPoint(x, y, value, [](Pixel&) {}); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPoint(int x, int y, bool value, const Color& color) { | ||||||
|  |   DrawPoint(x, y, value, [color](Pixel& p) { p.foreground_color = color; }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPoint(int x, int y, bool value, const Stylizer& style) { | ||||||
|  |   Style(x, y, style); | ||||||
|  |   if (value) | ||||||
|  |     DrawPointOn(x, y); | ||||||
|  |   else | ||||||
|  |     DrawPointOff(x, y); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPointOn(int x, int y) { | ||||||
|  |   if (!IsIn(x,y)) | ||||||
|  |     return; | ||||||
|  |   Cell& cell = storage_[XY{x / 2, y / 4}]; | ||||||
|  |   if (cell.type != CellType::kBraille) { | ||||||
|  |     cell.content.character = "⠀";  // 3 bytes. | ||||||
|  |     cell.type = CellType::kBraille; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   cell.content.character[1] |= g_map_braille[x % 2][y % 4][0]; | ||||||
|  |   cell.content.character[2] |= g_map_braille[x % 2][y % 4][1]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPointOff(int x, int y) { | ||||||
|  |   if (!IsIn(x,y)) | ||||||
|  |     return; | ||||||
|  |   Cell& cell = storage_[XY{x / 2, y / 4}]; | ||||||
|  |   if (cell.type != CellType::kBraille) { | ||||||
|  |     cell.content.character = "⠀";  // 3 byt | ||||||
|  |     cell.type = CellType::kBraille; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   cell.content.character[1] &= ~(g_map_braille[x % 2][y % 4][0]); | ||||||
|  |   cell.content.character[2] &= ~(g_map_braille[x % 2][y % 4][1]); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPointToggle(int x, int y) { | ||||||
|  |   if (!IsIn(x,y)) | ||||||
|  |     return; | ||||||
|  |   Cell& cell = storage_[XY{x / 2, y / 4}]; | ||||||
|  |   if (cell.type != CellType::kBraille) { | ||||||
|  |     cell.content.character = "⠀";  // 3 byt | ||||||
|  |     cell.type = CellType::kBraille; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   cell.content.character[1] ^= g_map_braille[x % 2][y % 4][0]; | ||||||
|  |   cell.content.character[2] ^= g_map_braille[x % 2][y % 4][1]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPointLine(int x1, int y1, int x2, int y2) { | ||||||
|  |   DrawPointLine(x1, y1, x2, y2, [](Pixel&) {}); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPointLine(int x1, int y1, int x2, int y2, const Color& color) { | ||||||
|  |   DrawPointLine(x1, y1, x2, y2, | ||||||
|  |                 [color](Pixel& p) { p.foreground_color = color; }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPointLine(int x1, | ||||||
|  |                            int y1, | ||||||
|  |                            int x2, | ||||||
|  |                            int y2, | ||||||
|  |                            const Stylizer& style) { | ||||||
|  |   const int dx = std::abs(x2 - x1); | ||||||
|  |   const int dy = std::abs(y2 - y1); | ||||||
|  |   const int sx = x1 < x2 ? 1 : -1; | ||||||
|  |   const int sy = y1 < y2 ? 1 : -1; | ||||||
|  |   const int length = std::max(dx, dy); | ||||||
|  |  | ||||||
|  |   if (!IsIn(x1, y1) && !IsIn(x2, y2)) | ||||||
|  |     return; | ||||||
|  |   if (dx + dx > width_ * height_) | ||||||
|  |     return; | ||||||
|  |  | ||||||
|  |   int error = dx - dy; | ||||||
|  |   for (int i = 0; i < length; ++i) { | ||||||
|  |     DrawPoint(x1, y1, true, style); | ||||||
|  |     if (2 * error >= -dy) { | ||||||
|  |       error -= dy; | ||||||
|  |       x1 += sx; | ||||||
|  |     } | ||||||
|  |     if (2 * error <= dx) { | ||||||
|  |       error += dx; | ||||||
|  |       y1 += sy; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   DrawPoint(x2, y2, true, style); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPointCircle(int x1, int y1, int radius) { | ||||||
|  |   DrawPointCircle(x1, y1, radius, [](Pixel&) {}); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPointCircle(int x1, int y1, int radius, const Color& color) { | ||||||
|  |   DrawPointCircle(x1, y1, radius, | ||||||
|  |                   [color](Pixel& p) { p.foreground_color = color; }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPointCircle(int x1, | ||||||
|  |                              int y1, | ||||||
|  |                              int radius, | ||||||
|  |                              const Stylizer& style) { | ||||||
|  |   DrawPointEllipse(x1, y1, radius, radius, style); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPointCircleFilled(int x1, int y1, int radius) { | ||||||
|  |   DrawPointCircleFilled(x1, y1, radius, [](Pixel&) {}); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPointCircleFilled(int x1, | ||||||
|  |                                    int y1, | ||||||
|  |                                    int radius, | ||||||
|  |                                    const Color& color) { | ||||||
|  |   DrawPointCircleFilled(x1, y1, radius, | ||||||
|  |                         [color](Pixel& p) { p.foreground_color = color; }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPointCircleFilled(int x1, | ||||||
|  |                                    int y1, | ||||||
|  |                                    int radius, | ||||||
|  |                                    const Stylizer& style) { | ||||||
|  |   DrawPointEllipseFilled(x1, y1, radius, radius, style); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPointEllipse(int x1, int y1, int r1, int r2) { | ||||||
|  |   DrawPointEllipse(x1, y1, r1, r2, [](Pixel&) {}); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPointEllipse(int x1, | ||||||
|  |                                int y1, | ||||||
|  |                                int r1, | ||||||
|  |                                int r2, | ||||||
|  |                                const Color& color) { | ||||||
|  |   DrawPointEllipse(x1, y1, r1, r2, | ||||||
|  |                     [color](Pixel& p) { p.foreground_color = color; }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPointEllipse(int x1, | ||||||
|  |                                int y1, | ||||||
|  |                                int r1, | ||||||
|  |                                int r2, | ||||||
|  |                                const Stylizer& s) { | ||||||
|  |   int x = -r1; | ||||||
|  |   int y = 0; | ||||||
|  |   int e2 = r2; | ||||||
|  |   int dx = (1 + 2 * x) * e2 * e2; | ||||||
|  |   int dy = x * x; | ||||||
|  |   int err = dx + dy; | ||||||
|  |  | ||||||
|  |   do { | ||||||
|  |     DrawPoint(x1 - x, y1 + y, true, s); | ||||||
|  |     DrawPoint(x1 + x, y1 + y, true, s); | ||||||
|  |     DrawPoint(x1 + x, y1 - y, true, s); | ||||||
|  |     DrawPoint(x1 - x, y1 - y, true, s); | ||||||
|  |     e2 = 2 * err; | ||||||
|  |     if (e2 >= dx) { | ||||||
|  |       x++; | ||||||
|  |       err += dx += 2 * r2 * r2; | ||||||
|  |     } | ||||||
|  |     if (e2 <= dy) { | ||||||
|  |       y++; | ||||||
|  |       err += dy += 2 * r1 * r1; | ||||||
|  |     } | ||||||
|  |   } while (x <= 0); | ||||||
|  |  | ||||||
|  |   while (y++ < r2) { | ||||||
|  |     DrawPoint(x1, y1 + y, true, s); | ||||||
|  |     DrawPoint(x1, y1 - y, true, s); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPointEllipseFilled(int x1, int y1, int r1, int r2) { | ||||||
|  |   DrawPointEllipseFilled(x1, y1, r1, r2, [](Pixel&) {}); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPointEllipseFilled(int x1, | ||||||
|  |                                      int y1, | ||||||
|  |                                      int r1, | ||||||
|  |                                      int r2, | ||||||
|  |                                      const Color& color) { | ||||||
|  |   DrawPointEllipseFilled(x1, y1, r1, r2, | ||||||
|  |                           [color](Pixel& p) { p.foreground_color = color; }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawPointEllipseFilled(int x1, | ||||||
|  |                                      int y1, | ||||||
|  |                                      int r1, | ||||||
|  |                                      int r2, | ||||||
|  |                                      const Stylizer& s) { | ||||||
|  |   int x = -r1; | ||||||
|  |   int y = 0; | ||||||
|  |   int e2 = r2; | ||||||
|  |   int dx = (1 + 2 * x) * e2 * e2; | ||||||
|  |   int dy = x * x; | ||||||
|  |   int err = dx + dy; | ||||||
|  |  | ||||||
|  |   do { | ||||||
|  |     for (int xx = x1 + x; xx <= x1 - x; ++xx) { | ||||||
|  |       DrawPoint(xx, y1 + y, true, s); | ||||||
|  |       DrawPoint(xx, y1 - y, true, s); | ||||||
|  |     } | ||||||
|  |     e2 = 2 * err; | ||||||
|  |     if (e2 >= dx) { | ||||||
|  |       x++; | ||||||
|  |       err += dx += 2 * (long)r2 * r2; | ||||||
|  |     } | ||||||
|  |     if (e2 <= dy) { | ||||||
|  |       y++; | ||||||
|  |       err += dy += 2 * (long)r1 * r1; | ||||||
|  |     } | ||||||
|  |   } while (x <= 0); | ||||||
|  |  | ||||||
|  |   while (y++ < r2) { | ||||||
|  |     for (int yy = y1 - y; yy <= y1 + y; ++yy) { | ||||||
|  |       DrawPoint(x1, yy, true, s); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlock(int x, int y, bool value) { | ||||||
|  |   DrawBlock(x, y, value, [](Pixel&) {}); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlock(int x, int y, bool value, const Color& color) { | ||||||
|  |   DrawBlock(x, y, value, [color](Pixel& p) { p.foreground_color = color; }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlock(int x, int y, bool value, const Stylizer& style) { | ||||||
|  |   Style(x, y, style); | ||||||
|  |   if (value) | ||||||
|  |     DrawBlockOn(x, y); | ||||||
|  |   else | ||||||
|  |     DrawBlockOff(x, y); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlockOn(int x, int y) { | ||||||
|  |   if (!IsIn(x,y)) | ||||||
|  |     return; | ||||||
|  |   y /= 2; | ||||||
|  |   Cell& cell = storage_[XY{x / 2, y / 2}]; | ||||||
|  |   if (cell.type != CellType::kBlock) { | ||||||
|  |     cell.content.character = " "; | ||||||
|  |     cell.type = CellType::kBlock; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   int bit = (x % 2) * 2 + y % 2; | ||||||
|  |   uint8_t value = g_map_block_inversed.at(cell.content.character); | ||||||
|  |   value |= 1 << bit; | ||||||
|  |   cell.content.character = g_map_block[value]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlockOff(int x, int y) { | ||||||
|  |   if (!IsIn(x,y)) | ||||||
|  |     return; | ||||||
|  |   Cell& cell = storage_[XY{x / 2, y / 4}]; | ||||||
|  |   if (cell.type != CellType::kBlock) { | ||||||
|  |     cell.content.character = " "; | ||||||
|  |     cell.type = CellType::kBlock; | ||||||
|  |   } | ||||||
|  |   y /= 2; | ||||||
|  |  | ||||||
|  |   int bit = (y % 2) * 2 + x % 2; | ||||||
|  |   uint8_t value = g_map_block_inversed.at(cell.content.character); | ||||||
|  |   value &= ~(1 << bit); | ||||||
|  |   cell.content.character = g_map_block[value]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlockToggle(int x, int y) { | ||||||
|  |   if (!IsIn(x,y)) | ||||||
|  |     return; | ||||||
|  |   Cell& cell = storage_[XY{x / 2, y / 4}]; | ||||||
|  |   if (cell.type != CellType::kBlock) { | ||||||
|  |     cell.content.character = " "; | ||||||
|  |     cell.type = CellType::kBlock; | ||||||
|  |   } | ||||||
|  |   y /= 2; | ||||||
|  |  | ||||||
|  |   int bit = (y % 2) * 2 + x % 2; | ||||||
|  |   uint8_t value = g_map_block_inversed.at(cell.content.character); | ||||||
|  |   value ^= 1 << bit; | ||||||
|  |   cell.content.character = g_map_block[value]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlockLine(int x1, int y1, int x2, int y2) { | ||||||
|  |   DrawBlockLine(x1, y1, x2, y2, [](Pixel&) {}); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlockLine(int x1, int y1, int x2, int y2, const Color& color) { | ||||||
|  |   DrawBlockLine(x1, y1, x2, y2, | ||||||
|  |                 [color](Pixel& p) { p.foreground_color = color; }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlockLine(int x1, | ||||||
|  |                            int y1, | ||||||
|  |                            int x2, | ||||||
|  |                            int y2, | ||||||
|  |                            const Stylizer& style) { | ||||||
|  |   y1 /= 2; | ||||||
|  |   y2 /= 2; | ||||||
|  |  | ||||||
|  |   const int dx = std::abs(x2 - x1); | ||||||
|  |   const int dy = std::abs(y2 - y1); | ||||||
|  |   const int sx = x1 < x2 ? 1 : -1; | ||||||
|  |   const int sy = y1 < y2 ? 1 : -1; | ||||||
|  |   const int length = std::max(dx, dy); | ||||||
|  |  | ||||||
|  |   if (!IsIn(x1, y1) && !IsIn(x2, y2)) | ||||||
|  |     return; | ||||||
|  |   if (dx + dx > width_ * height_) | ||||||
|  |     return; | ||||||
|  |  | ||||||
|  |   int error = dx - dy; | ||||||
|  |   for (int i = 0; i < length; ++i) { | ||||||
|  |     DrawBlock(x1, y1 * 2, true, style); | ||||||
|  |     if (2 * error >= -dy) { | ||||||
|  |       error -= dy; | ||||||
|  |       x1 += sx; | ||||||
|  |     } | ||||||
|  |     if (2 * error <= dx) { | ||||||
|  |       error += dx; | ||||||
|  |       y1 += sy; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   DrawBlock(x2, y2 * 2, true, style); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlockCircle(int x1, int y1, int radius) { | ||||||
|  |   DrawBlockCircle(x1, y1, radius, [](Pixel&) {}); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlockCircle(int x1, int y1, int radius, const Color& color) { | ||||||
|  |   DrawBlockCircle(x1, y1, radius, | ||||||
|  |                   [color](Pixel& p) { p.foreground_color = color; }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlockCircle(int x1, | ||||||
|  |                              int y1, | ||||||
|  |                              int radius, | ||||||
|  |                              const Stylizer& style) { | ||||||
|  |   DrawBlockEllipse(x1, y1, radius, radius, style); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlockCircleFilled(int x1, int y1, int radius) { | ||||||
|  |   DrawBlockCircleFilled(x1, y1, radius, [](Pixel&) {}); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlockCircleFilled(int x1, | ||||||
|  |                                    int y1, | ||||||
|  |                                    int radius, | ||||||
|  |                                    const Color& color) { | ||||||
|  |   DrawBlockCircleFilled(x1, y1, radius, | ||||||
|  |                         [color](Pixel& p) { p.foreground_color = color; }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlockCircleFilled(int x1, | ||||||
|  |                                    int y1, | ||||||
|  |                                    int radius, | ||||||
|  |                                    const Stylizer& s) { | ||||||
|  |   DrawBlockEllipseFilled(x1, y1, radius, radius, s); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlockEllipse(int x1, int y1, int r1, int r2) { | ||||||
|  |   DrawBlockEllipse(x1, y1, r1, r2, [](Pixel&) {}); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlockEllipse(int x1, | ||||||
|  |                                int y1, | ||||||
|  |                                int r1, | ||||||
|  |                                int r2, | ||||||
|  |                                const Color& color) { | ||||||
|  |   DrawBlockEllipse(x1, y1, r1, r2, | ||||||
|  |                     [color](Pixel& p) { p.foreground_color = color; }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlockEllipse(int x1, | ||||||
|  |                                int y1, | ||||||
|  |                                int r1, | ||||||
|  |                                int r2, | ||||||
|  |                                const Stylizer& s) { | ||||||
|  |   y1 /= 2; | ||||||
|  |   r2 /= 2; | ||||||
|  |   int x = -r1; | ||||||
|  |   int y = 0; | ||||||
|  |   int e2 = r2; | ||||||
|  |   int dx = (1 + 2 * x) * e2 * e2; | ||||||
|  |   int dy = x * x; | ||||||
|  |   int err = dx + dy; | ||||||
|  |  | ||||||
|  |   do { | ||||||
|  |     DrawBlock(x1 - x, 2 * (y1 + y), true, s); | ||||||
|  |     DrawBlock(x1 + x, 2 * (y1 + y), true, s); | ||||||
|  |     DrawBlock(x1 + x, 2 * (y1 - y), true, s); | ||||||
|  |     DrawBlock(x1 - x, 2 * (y1 - y), true, s); | ||||||
|  |     e2 = 2 * err; | ||||||
|  |     if (e2 >= dx) { | ||||||
|  |       x++; | ||||||
|  |       err += dx += 2 * r2 * r2; | ||||||
|  |     } | ||||||
|  |     if (e2 <= dy) { | ||||||
|  |       y++; | ||||||
|  |       err += dy += 2 * r1 * r1; | ||||||
|  |     } | ||||||
|  |   } while (x <= 0); | ||||||
|  |  | ||||||
|  |   while (y++ < r2) { | ||||||
|  |     DrawBlock(x1, 2 * (y1 + y), true, s); | ||||||
|  |     DrawBlock(x1, 2 * (y1 - y), true, s); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlockEllipseFilled(int x1, int y1, int r1, int r2) { | ||||||
|  |   DrawBlockEllipseFilled(x1, y1, r1, r2, [](Pixel&) {}); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlockEllipseFilled(int x1, | ||||||
|  |                                      int y1, | ||||||
|  |                                      int r1, | ||||||
|  |                                      int r2, | ||||||
|  |                                      const Color& color) { | ||||||
|  |   DrawBlockEllipseFilled(x1, y1, r1, r2, | ||||||
|  |                           [color](Pixel& p) { p.foreground_color = color; }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawBlockEllipseFilled(int x1, | ||||||
|  |                                      int y1, | ||||||
|  |                                      int r1, | ||||||
|  |                                      int r2, | ||||||
|  |                                      const Stylizer& s) { | ||||||
|  |   y1 /= 2; | ||||||
|  |   r2 /= 2; | ||||||
|  |   int x = -r1; | ||||||
|  |   int y = 0; | ||||||
|  |   int e2 = r2; | ||||||
|  |   int dx = (1 + 2 * x) * e2 * e2; | ||||||
|  |   int dy = x * x; | ||||||
|  |   int err = dx + dy; | ||||||
|  |  | ||||||
|  |   do { | ||||||
|  |     for(int xx = x1+x; xx <= x1-x; ++xx) { | ||||||
|  |       DrawBlock(xx, 2 * (y1 + y), true, s); | ||||||
|  |       DrawBlock(xx, 2 * (y1 - y), true, s); | ||||||
|  |     } | ||||||
|  |     e2 = 2 * err; | ||||||
|  |     if (e2 >= dx) { | ||||||
|  |       x++; | ||||||
|  |       err += dx += 2 * r2 * r2; | ||||||
|  |     } | ||||||
|  |     if (e2 <= dy) { | ||||||
|  |       y++; | ||||||
|  |       err += dy += 2 * r1 * r1; | ||||||
|  |     } | ||||||
|  |   } while (x <= 0); | ||||||
|  |  | ||||||
|  |   while (y++ < r2) { | ||||||
|  |     for(int yy = y1+y; yy <= y1-y; ++yy) { | ||||||
|  |       DrawBlock(x1, 2 * yy, true, s); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawText(int x, int y, const std::string& value) { | ||||||
|  |   DrawText(x, y, value, [](Pixel&) {}); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawText(int x, | ||||||
|  |                       int y, | ||||||
|  |                       const std::string& value, | ||||||
|  |                       const Color& color) { | ||||||
|  |   DrawText(x, y, value, [color](Pixel& p) { p.foreground_color = color; }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::DrawText(int x, | ||||||
|  |                       int y, | ||||||
|  |                       const std::string& value, | ||||||
|  |                       const Stylizer& style) { | ||||||
|  |   x /= 2; | ||||||
|  |   y /= 4; | ||||||
|  |   for (const auto& it : Utf8ToGlyphs(value)) { | ||||||
|  |     if (!IsIn(x, y)) | ||||||
|  |       continue; | ||||||
|  |     Cell& cell = storage_[XY{x, y}]; | ||||||
|  |     cell.type = CellType::kText; | ||||||
|  |     cell.content.character = it; | ||||||
|  |     style(cell.content); | ||||||
|  |     x++; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Canvas::Style(int x, int y, const Stylizer& style) { | ||||||
|  |   if (IsIn(x, y)) | ||||||
|  |     style(storage_[XY{x / 2, y / 4}].content); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Element ElementFrom(ConstRef<Canvas> canvas) { | ||||||
|  |   class Impl : public Node { | ||||||
|  |    public: | ||||||
|  |     Impl(ConstRef<Canvas> canvas) : canvas_(canvas) { | ||||||
|  |       requirement_.min_x = (canvas_->width() + 1) / 2; | ||||||
|  |       requirement_.min_y = (canvas_->height() + 3) / 4; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void Render(Screen& screen) override { | ||||||
|  |       int y_max = std::min(requirement_.min_y, box_.y_max - box_.y_min + 1); | ||||||
|  |       int x_max = std::min(requirement_.min_x, box_.x_max - box_.x_min + 1); | ||||||
|  |       for (int y = 0; y < y_max; ++y) { | ||||||
|  |         for (int x = 0; x < x_max; ++x) { | ||||||
|  |           screen.PixelAt(box_.x_min + x, box_.y_min + y) = | ||||||
|  |               canvas_->GetPixel(x * 2, y * 4); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |    private: | ||||||
|  |     ConstRef<Canvas> canvas_; | ||||||
|  |   }; | ||||||
|  |   return std::make_shared<Impl>(std::move(canvas)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace ftxui | ||||||
		Reference in New Issue
	
	Block a user
	 Arthur Sonzogni
					Arthur Sonzogni