132 lines
5.6 KiB
C++
132 lines
5.6 KiB
C++
#include <render_2D/axis/Axis.hpp>
|
|
#include <render_2D/event/Event.hpp>
|
|
#include <scene.hpp>
|
|
#include <gtest/gtest.h>
|
|
namespace {
|
|
using namespace aethera;
|
|
using namespace aethera::render_2d;
|
|
template <typename Object>
|
|
std::unique_ptr<Object> build_axis() {
|
|
auto result = typename Object::Builder{}.build();
|
|
if (!result) std::terminate();
|
|
return std::move(result).value();
|
|
}
|
|
}
|
|
TEST(axis_dispatch, numeric_axis_public_shell_reads_final_private_state) {
|
|
using Object = Impl<Numeric_Axis>;
|
|
auto axis = build_axis<Object>();
|
|
axis->set<&Abs_Axis::Prop::position>(Point_F{10.0, 0.0});
|
|
axis->set<&Abs_Axis::Prop::pixel_length>(200.0);
|
|
axis->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{0.0, 20.0});
|
|
axis->advance();
|
|
EXPECT_EQ(axis->coordinate_range(), (Axis_Range{0.0, 20.0}));
|
|
EXPECT_DOUBLE_EQ(axis->coordinate_to_pixel(5.0), 60.0);
|
|
EXPECT_DOUBLE_EQ(axis->pixel_to_coordinate(60.0), 5.0);
|
|
EXPECT_EQ(axis->pixel_sample_count({0.0, 20.0}), 201);
|
|
EXPECT_DOUBLE_EQ(axis->tick_step({0.0, 20.0}), 5.0);
|
|
EXPECT_EQ(axis->tick_label(2.5), "2.5");
|
|
EXPECT_EQ(axis->sub_tick_count(5.0), 4);
|
|
}
|
|
TEST(axis_dispatch, point_conversion_uses_current_orientation) {
|
|
using Object = Impl<Numeric_Axis>;
|
|
auto axis = build_axis<Object>();
|
|
axis->set<&Abs_Axis::Prop::position>(Point_F{0.0, 20.0});
|
|
axis->set<&Abs_Axis::Prop::pixel_length>(100.0);
|
|
axis->set<&Abs_Axis::Prop::orientation>(Axis_Orientation::vertical);
|
|
axis->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{0.0, 10.0});
|
|
axis->advance();
|
|
EXPECT_DOUBLE_EQ(axis->point_to_coordinate({90.0, 70.0}), 5.0);
|
|
}
|
|
TEST(render_cache, copy_owns_independent_pixels) {
|
|
Blend2D_Cache original;
|
|
{
|
|
aethera::render_2d::detail::Painter painter(original, {32, 16});
|
|
painter.line({1.0, 1.0}, {30.0, 1.0}, Pen{Color::red_color(), 2.0});
|
|
}
|
|
Blend2D_Cache copy = original;
|
|
original.clear();
|
|
const Image_View view = copy.view();
|
|
ASSERT_FALSE(view.empty());
|
|
bool contains_color{};
|
|
for (int y = 0; y < view.height && !contains_color; ++y) {
|
|
const auto* row = reinterpret_cast<const Pixel*>(view.data + static_cast<std::ptrdiff_t>(y) * view.stride);
|
|
for (int x = 0; x < view.width; ++x) contains_color = contains_color || row[x] != 0;
|
|
}
|
|
EXPECT_TRUE(contains_color);
|
|
}
|
|
TEST(axis_render, scene_prepares_and_paints_axis_cache) {
|
|
using Object = Impl<Numeric_Axis>;
|
|
using Scene_Object = Impl<Scene>;
|
|
initialize_runtime(2);
|
|
auto axis = build_axis<Object>();
|
|
auto scene = build_axis<Scene_Object>();
|
|
axis->set<&Abs_Axis::Prop::position>(Point_F{8.0, 8.0});
|
|
axis->set<&Abs_Axis::Prop::canvas_size>(Size{160, 64});
|
|
axis->set<&Abs_Axis::Prop::pixel_length>(120.0);
|
|
ASSERT_TRUE((scene->edit_dependency_graph<Prepare_Data_Tag, Paint_Tag>([&](auto& prepare, auto& paint) {
|
|
prepare.add(axis.get());
|
|
paint.add(axis.get());
|
|
}).has_value()));
|
|
scene->process([](const auto&) {});
|
|
const Image_View view = axis->pending_buffer<Color_Cache>().view();
|
|
ASSERT_FALSE(view.empty());
|
|
bool contains_color{};
|
|
for (int y = 0; y < view.height && !contains_color; ++y) {
|
|
const auto* row = reinterpret_cast<const Pixel*>(view.data + static_cast<std::ptrdiff_t>(y) * view.stride);
|
|
for (int x = 0; x < view.width; ++x) contains_color = contains_color || row[x] != 0;
|
|
}
|
|
EXPECT_TRUE(contains_color);
|
|
}
|
|
TEST(axis_event, numeric_axis_wheel_zoom_and_drag_update_authoritative_range) {
|
|
using Object = Impl<Numeric_Axis>;
|
|
auto axis = build_axis<Object>();
|
|
axis->set<&Abs_Axis::Prop::position>(Point_F{0.0, 0.0});
|
|
axis->set<&Abs_Axis::Prop::pixel_length>(100.0);
|
|
axis->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{0.0, 10.0});
|
|
axis->advance();
|
|
Wheel_Event wheel;
|
|
wheel.position = {50.0, 0.0};
|
|
wheel.angle_delta_y = 120.0;
|
|
axis->dispatch_event(wheel);
|
|
EXPECT_TRUE(wheel.is_accepted());
|
|
axis->advance();
|
|
EXPECT_EQ(axis->coordinate_range(), (Axis_Range{0.5, 9.5}));
|
|
Pointer_Event press(Event_Type::pointer_press);
|
|
press.position = {50.0, 0.0};
|
|
press.button = Mouse_Button::left;
|
|
axis->dispatch_event(press);
|
|
Pointer_Event move(Event_Type::pointer_move);
|
|
move.position = {60.0, 0.0};
|
|
axis->dispatch_event(move);
|
|
EXPECT_TRUE(move.is_accepted());
|
|
axis->advance();
|
|
EXPECT_EQ(axis->coordinate_range(), (Axis_Range{-0.4, 8.6}));
|
|
}
|
|
TEST(axis_state, invalid_numeric_range_is_rejected_without_poisoning_pending_state) {
|
|
using Object = Impl<Numeric_Axis>;
|
|
auto axis = build_axis<Object>();
|
|
EXPECT_THROW((axis->set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{1.0, 1.0})), std::invalid_argument);
|
|
axis->advance();
|
|
EXPECT_EQ(axis->coordinate_range(), (Axis_Range{0.0, 20.0}));
|
|
}
|
|
TEST(axis_label, frequency_axis_selects_engineering_unit) {
|
|
using Object = Impl<Frequency_Axis>;
|
|
auto axis = build_axis<Object>();
|
|
axis->advance();
|
|
EXPECT_EQ(axis->tick_label(250.0), "250 Hz");
|
|
EXPECT_EQ(axis->tick_label(2'500.0), "2.5 kHz");
|
|
EXPECT_EQ(axis->tick_label(2'500'000.0), "2.5 MHz");
|
|
}
|
|
TEST(axis_time, sample_state_drives_range_lookup_and_label_without_snapshot) {
|
|
using Object = Impl<Time_Axis>;
|
|
auto axis = build_axis<Object>();
|
|
EXPECT_EQ(axis->append_time({3'723'004}), 0);
|
|
EXPECT_EQ(axis->time_point_count(), 0u);
|
|
axis->advance();
|
|
EXPECT_EQ(axis->time_point_count(), 1u);
|
|
EXPECT_EQ(axis->coordinate_range(), (Axis_Range{0.0, 1.0}));
|
|
EXPECT_EQ(axis->tick_to_time(0), (Time_Of_Day{3'723'004}));
|
|
EXPECT_EQ(axis->tick_label(0.0), "02:03.004");
|
|
EXPECT_FALSE(axis->tick_to_time(99).valid());
|
|
}
|