Feature: Canvas (#287)

Draw using braille and block characters on a grid.
This commit is contained in:
Arthur Sonzogni
2021-12-23 14:17:33 +01:00
committed by GitHub
parent 7e5cd23b4c
commit 0d47dd19ab
15 changed files with 1098 additions and 10 deletions

View File

@@ -1,7 +1,7 @@
set(EXAMPLES_DIR ${CMAKE_CURRENT_SOURCE_DIR})
function(example name)
add_executable(${name} ${name}.cpp)
target_link_libraries(${name} PUBLIC ${DIRECTORY_LIB})
add_executable(ftxui_example_${name} ${name}.cpp)
target_link_libraries(ftxui_example_${name} PUBLIC ${DIRECTORY_LIB})
file(RELATIVE_PATH dir ${EXAMPLES_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
set_property(GLOBAL APPEND PROPERTY FTXUI::EXAMPLES ${dir}/${name})
endfunction(example)

View File

@@ -1,16 +1,17 @@
set(DIRECTORY_LIB component)
example(button)
example(canvas_animated)
example(checkbox)
example(checkbox_in_frame)
example(composition)
example(dropdown)
example(flexbox)
example(flexbox_gallery)
example(focus)
example(gallery)
example(homescreen)
example(input)
example(maybe)
example(focus)
example(menu)
example(menu2)
example(menu_entries)

View 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.

View File

@@ -37,8 +37,8 @@ int main(int argc, const char* argv[]) {
float focus_x = 0.0f;
float focus_y = 0.0f;
auto slider_x = Slider("x", &focus_x, 0.f, 1.f, 0.05f);
auto slider_y = Slider("y", &focus_y, 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.01f);
auto renderer = Renderer(
Container::Vertical({

View File

@@ -406,8 +406,7 @@ int main(int argc, const char* argv[]) {
make_box(6, 3),
}),
}) |
// vscroll_indicator | yflex;
yflex | vscroll_indicator;
vscroll_indicator | yframe | flex;
});
auto paragraph_renderer_right = Renderer([] {

View File

@@ -7,11 +7,11 @@ example(color_info_palette256)
example(color_truecolor_HSV)
example(color_truecolor_RGB)
example(dbox)
example(canvas)
example(gauge)
example(graph)
example(gridbox)
example(hflow)
example(vflow)
example(html_like)
example(package_manager)
example(paragraph)
@@ -28,4 +28,5 @@ example(style_inverted)
example(style_underlined)
example(table)
example(vbox_hbox)
example(vflow)
example(window)

50
examples/dom/canvas.cpp Normal file
View 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
View 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.

View File

@@ -71,7 +71,10 @@
postRun: [],
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>
<style>