mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2024-11-25 12:11:33 +08:00
Feature: Canvas (#287)
Draw using braille and block characters on a grid.
This commit is contained in:
parent
7e5cd23b4c
commit
0d47dd19ab
@ -7,6 +7,8 @@ unreleased (development)
|
||||
### Features:
|
||||
|
||||
#### 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.
|
||||
All the following attributes are supported: direction, wrap, justify-content,
|
||||
align-items, align-content, gap
|
||||
|
@ -37,6 +37,7 @@ add_library(screen
|
||||
)
|
||||
|
||||
add_library(dom
|
||||
include/ftxui/dom/canvas.hpp
|
||||
include/ftxui/dom/elements.hpp
|
||||
include/ftxui/dom/flexbox_config.hpp
|
||||
include/ftxui/dom/node.hpp
|
||||
@ -47,6 +48,7 @@ add_library(dom
|
||||
src/ftxui/dom/border.cpp
|
||||
src/ftxui/dom/box_helper.cpp
|
||||
src/ftxui/dom/box_helper.hpp
|
||||
src/ftxui/dom/canvas.cpp
|
||||
src/ftxui/dom/clear_under.cpp
|
||||
src/ftxui/dom/color.cpp
|
||||
src/ftxui/dom/composite_decorator.cpp
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
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_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({
|
||||
|
@ -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([] {
|
||||
|
@ -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
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: [],
|
||||
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>
|
||||
|
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 <memory>
|
||||
|
||||
#include "ftxui/dom/canvas.hpp"
|
||||
#include "ftxui/dom/flexbox_config.hpp"
|
||||
#include "ftxui/dom/node.hpp"
|
||||
#include "ftxui/screen/box.hpp"
|
||||
#include "ftxui/screen/color.hpp"
|
||||
#include "ftxui/screen/screen.hpp"
|
||||
#include "ftxui/screen/terminal.hpp"
|
||||
#include "ftxui/util/ref.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
class Node;
|
||||
@ -57,6 +59,7 @@ Element paragraphAlignCenter(std::string text);
|
||||
Element paragraphAlignJustify(std::string text);
|
||||
Element graph(GraphFunction);
|
||||
Element emptyElement();
|
||||
Element ElementFrom(ConstRef<Canvas>);
|
||||
|
||||
// -- Decorator ---
|
||||
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
|
Loading…
Reference in New Issue
Block a user