Files
Renderive/Core2/tests/Core2_Integration_Tests.cpp
T
2026-08-10 19:55:29 +08:00

220 lines
8.4 KiB
C++

#include "Core2/export.h"
#include "Core2/plottable/Curve_Sampling.h"
#include "Core2/render/Blend2D_Cache.h"
#include <gtest/gtest.h>
#include <array>
#include <cstddef>
#include <vector>
namespace renderive {
namespace {
TEST(Renderive_Core2, RootIdentityAndRefreshDiagnosticsComeFromKernelState) {
Plot_Core plot;
plot.init();
const auto root = plot.root_renderable();
ASSERT_TRUE(root);
root->set_object_name("renamed-root");
EXPECT_EQ(plot.root_renderable(), root);
plot.set_viewport_size({32, 24});
plot.activate_view();
plot.set_max_render_fps(144.0);
ASSERT_TRUE(plot.render_frame());
const auto diagnostics = plot.diagnostics();
EXPECT_DOUBLE_EQ(diagnostics.refresh.frequency_hz, 144.0);
EXPECT_EQ(diagnostics.refresh.frame_count, 1u);
}
TEST(Renderive_Core2, EveryCurveInterpolationModeHasDistinctSamplingSemantics) {
const std::array<double, 2> edge_values{0.0, 10.0};
const Range domain{0.0, 1.0};
const auto nearest = detail::curve_sampling::interpolate(
edge_values, domain, Line_Interpolation_Mode::Nearest_Sample);
const auto linear = detail::curve_sampling::interpolate(
edge_values, domain, Line_Interpolation_Mode::Linear_Value);
const auto power = detail::curve_sampling::interpolate(
edge_values, domain, Line_Interpolation_Mode::Linear_Power_Domain);
const auto step_left = detail::curve_sampling::interpolate(
edge_values, domain, Line_Interpolation_Mode::Step_Left);
const auto step_right = detail::curve_sampling::interpolate(
edge_values, domain, Line_Interpolation_Mode::Step_Right);
EXPECT_EQ(nearest.size(), 4u);
EXPECT_EQ(linear.size(), 2u);
ASSERT_EQ(power.size(), 5u);
EXPECT_NEAR(power[2].value, 7.4036269, 1e-6);
ASSERT_EQ(step_left.size(), 3u);
ASSERT_EQ(step_right.size(), 3u);
EXPECT_DOUBLE_EQ(step_left[1].coordinate, 1.0);
EXPECT_DOUBLE_EQ(step_left[1].value, 0.0);
EXPECT_DOUBLE_EQ(step_right[1].coordinate, 0.0);
EXPECT_DOUBLE_EQ(step_right[1].value, 10.0);
const std::array<double, 4> curved_values{0.0, 10.0, 0.0, 10.0};
const auto cubic = detail::curve_sampling::interpolate(
curved_values, {0.0, 3.0}, Line_Interpolation_Mode::Cubic_Value);
ASSERT_EQ(cubic.size(), 13u);
EXPECT_NEAR(cubic[2].value, 5.625, 1e-9);
const std::array<double, 11> visible_values{};
const Axis_Transform visible_x{{4.0, 6.0}, 0.0, 100.0};
const Axis_Transform visible_y{{0.0, 1.0}, 0.0, 100.0};
const auto clipped = detail::curve_points(
visible_values, {0.0, 10.0}, visible_x, visible_y, true,
Line_Interpolation_Mode::Linear_Value);
EXPECT_LT(clipped.size(), visible_values.size());
ASSERT_GE(clipped.size(), 2u);
EXPECT_LE(clipped.front().x, 0.0);
EXPECT_GE(clipped.back().x, 100.0);
}
TEST(Renderive_Core2, BicubicHeatmapUsesRealCubicResampling) {
const std::array<Pixel, 16> source{
pack_rgba(255, 0, 0), pack_rgba(0, 0, 0), pack_rgba(255, 255, 255), pack_rgba(0, 0, 255),
pack_rgba(0, 255, 0), pack_rgba(255, 255, 255), pack_rgba(0, 0, 0), pack_rgba(255, 0, 0),
pack_rgba(0, 0, 255), pack_rgba(0, 0, 0), pack_rgba(255, 255, 255), pack_rgba(0, 255, 0),
pack_rgba(255, 255, 255), pack_rgba(255, 0, 0), pack_rgba(0, 255, 0), pack_rgba(0, 0, 0)
};
const auto render = [&source](Image_Interpolation_Mode interpolation) {
detail::Blend2D_Color_Cache cache;
{
detail::Painter painter(cache, {11, 11});
painter.heatmap({0.0, 0.0, 11.0, 11.0}, 4, 4, source, interpolation);
}
const Image_View view = cache.view();
std::vector<Pixel> pixels;
pixels.reserve(static_cast<std::size_t>(view.width) * view.height);
for (int y = 0; y < view.height; ++y) {
const auto* row = reinterpret_cast<const Pixel*>(
view.data + static_cast<std::ptrdiff_t>(y) * view.stride);
pixels.insert(pixels.end(), row, row + view.width);
}
return pixels;
};
const auto bilinear = render(Image_Interpolation_Mode::Bilinear);
const auto bicubic = render(Image_Interpolation_Mode::Bicubic);
EXPECT_EQ(bilinear.size(), bicubic.size());
EXPECT_NE(bilinear, bicubic);
}
TEST(Renderive_Core2, KernelSceneRendersBusinessObjectsIntoBlend2DFrame) {
Plot_Core plot;
plot.init();
plot.set_viewport_size({320, 180});
plot.activate_view();
const auto root = plot.root_renderable();
ASSERT_TRUE(root);
auto frequency_axis = Frequency_Axis::Builder(root, Orientation::Horizontal)
.set_x(32)
.set_y(150)
.set_pixel_length(270)
.set_coord_range({88.0, 108.0})
.build();
frequency_axis->set_locale({','});
frequency_axis->set_label_rotation_degrees(15);
EXPECT_EQ(frequency_axis->tick_label(88.5), "88,5 Hz");
frequency_axis->set_label_precision(4);
EXPECT_EQ(frequency_axis->tick_label(1'234'567.0), "1,2346 MHz");
auto power_axis = Axis::Builder(root, Orientation::Vertical)
.set_x(32)
.set_y(8)
.set_pixel_length(142)
.set_coord_range({-120.0, 0.0})
.build();
auto spectrum = Spectrum::Builder(root, frequency_axis, power_axis)
.set_frequency_range({88.0, 108.0})
.set_frequency_point_size(64)
.set_max_hold_visible(true)
.build();
std::vector<double> samples(64);
for (std::size_t index = 0; index < samples.size(); ++index)
samples[index] = -100.0 + static_cast<double>(index % 24) * 3.0;
spectrum->update_samples(samples);
ASSERT_TRUE(plot.render_frame());
bool saw_frame = false;
bool saw_drawn_pixel = false;
plot.with_frame([&](Image_View view) {
saw_frame = !view.empty() && view.width == 320 && view.height == 180;
if (!saw_frame)
return;
for (int y = 0; y < view.height && !saw_drawn_pixel; ++y) {
const auto* row = view.data + static_cast<std::ptrdiff_t>(y) * view.stride;
for (int x = 0; x < view.width * static_cast<int>(sizeof(Pixel)); ++x) {
if (row[x] != std::byte{}) {
saw_drawn_pixel = true;
break;
}
}
}
});
EXPECT_TRUE(saw_frame);
EXPECT_TRUE(saw_drawn_pixel);
EXPECT_EQ(plot.diagnostics().refresh.frame_count, 1u);
EXPECT_FALSE(plot.render_frame());
spectrum->update_samples(samples);
EXPECT_TRUE(plot.render_frame());
frequency_axis->set_use_wheel(true);
const Range before_zoom = frequency_axis->coord_range();
Wheel_Event wheel;
wheel.position = {160.0, 150.0};
wheel.angle_delta_y = 120.0;
plot.dispatch_event(wheel);
EXPECT_TRUE(wheel.is_accepted());
EXPECT_LT(frequency_axis->coord_range().size(), before_zoom.size());
EXPECT_TRUE(plot.render_frame());
plot.remove_renderable(spectrum);
EXPECT_TRUE(plot.render_frame(true));
EXPECT_FALSE(plot.render_frame());
}
TEST(Renderive_Core2, TimeAxisUsesOneFontStateAndFormatsConfiguredLabels) {
Plot_Core plot;
plot.init();
const auto root = plot.root_renderable();
auto axis = Time_Axis::Builder(root, Orientation::Horizontal)
.set_time_format("hh-mm-ss.zzz")
.set_font(Font{18.0, 700, true})
.set_tick_label_spacing_px(17)
.build();
ASSERT_TRUE(axis);
const int tick = axis->append_time(Time_Of_Day{3'723'045});
EXPECT_EQ(axis->tick_label(tick), "01-02-03.045");
EXPECT_EQ(axis->font(), (Font{18.0, 700, true}));
EXPECT_EQ(axis->unit_text_font(), axis->font());
EXPECT_EQ(axis->tick_label_spacing_px(), 17);
}
TEST(Renderive_Core2, PerformanceOverlayConsumesKernelFrameDiagnostics) {
Plot_Core plot;
plot.init();
plot.set_viewport_size({160, 90});
plot.activate_view();
plot.set_max_render_fps(120.0);
Performance_Overlay_Options options;
options.log.enabled = false;
const auto overlay = attach_performance_overlay(plot, options);
ASSERT_TRUE(overlay);
set_performance_plot_name(plot, "integration");
set_performance_overlay_enabled(plot, true);
ASSERT_TRUE(plot.render_frame(true));
const auto snapshot = overlay->display_snapshot();
ASSERT_TRUE(snapshot);
EXPECT_EQ(snapshot->plot_name, "integration");
EXPECT_EQ(snapshot->viewport_size, (Size{160, 90}));
EXPECT_FALSE(snapshot->lines.empty());
EXPECT_EQ(snapshot->frame_count, 1u);
EXPECT_DOUBLE_EQ(plot.max_render_fps(), 120.0);
}
} // namespace
} // namespace renderive