builder 和 运行时修改隔离开

This commit is contained in:
2026-08-13 11:25:38 +08:00
parent 9e598a0318
commit 4a48e62aea
57 changed files with 1512 additions and 2592 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
namespace renderive {
Abs_Axis::Abs_Axis(::Scene_Base& scene) : Renderable(scene, true) {}
Abs_Axis::Abs_Axis() : Renderable(true) {}
Abs_Axis::~Abs_Axis() = default;
+1 -1
View File
@@ -9,7 +9,7 @@ namespace renderive {
class LIB_DECL Abs_Axis : public Renderable {
public:
explicit Abs_Axis(::Scene_Base& scene);
Abs_Axis();
~Abs_Axis() override;
[[nodiscard]] virtual Axis_Transform transform() const = 0;
+12 -21
View File
@@ -1,24 +1,17 @@
#pragma once
#include "../renderable/Renderable.h"
#include "Axis_Types.h"
#include <renderive/scene/base/Scene_Base.hpp>
#include <memory>
#include <utility>
namespace renderive::detail {
template <class Product, class Properties, class...>
class Axis_Renderable_Builder {
public:
using Self = Axis_Renderable_Builder;
Axis_Renderable_Builder(std::shared_ptr<Renderable> parent, Orientation orientation)
: parent_(std::move(parent)) {
Axis_Renderable_Builder(renderive_Owner<Renderable> parent, Orientation orientation, ::Scene_Base::Attach_Builder* attach_builder = nullptr)
: parent_(std::move(parent)), attach_builder_(attach_builder) {
properties_.orientation = orientation;
}
Self& set_x(int value) { properties_.x = value; return *this; }
Self& set_y(int value) { properties_.y = value; return *this; }
Self& set_orientation(Orientation value) { properties_.orientation = value; return *this; }
@@ -32,7 +25,6 @@ public:
Self& set_unit_text_pen(Pen value) { properties_.unit_text_pen = value; return *this; }
Self& set_unit_text_background_brush(Brush value) { properties_.unit_text_background_brush = value; return *this; }
Self& set_label_rotation_degrees(int value) { properties_.label_rotation_degrees = value; return *this; }
Self& set_coord_range(Range value) requires requires(Properties properties) { properties.coordinates = value; } {
properties_.coordinates = value;
return *this;
@@ -69,21 +61,20 @@ public:
properties_.newest_at_start = value;
return *this;
}
std::shared_ptr<Product> build() const {
renderive_Owner<Product> build() const {
if (!parent_)
return {};
auto result = parent_->scene().template make_renderable<Product>(properties_);
parent_->scene().attach_renderable(result, {
.display_parents = {parent_},
.dependency_parents = {parent_}
});
auto result = renderive_Owner<Product>::make(properties_);
if (attach_builder_) {
attach_builder_->attach(result);
attach_builder_->add_parent(::Scene_Base::Relationship::display, result, parent_);
attach_builder_->add_parent(::Scene_Base::Relationship::dependency, result, parent_);
}
return result;
}
private:
std::shared_ptr<Renderable> parent_;
renderive_Owner<Renderable> parent_;
Properties properties_;
::Scene_Base::Attach_Builder* attach_builder_{};
};
} // namespace renderive::detail
}
+2 -3
View File
@@ -25,10 +25,9 @@ class Axis_State_Strategy
using Base = Double_State_Strategy<Abs_Axis, State, Atomic_Spin_Mutex, State_Observer>;
public:
Axis_State_Strategy(::Scene_Base& scene, State state)
explicit Axis_State_Strategy(State state)
: Base(state,
With_Observer<State_Observer>{State_Observer(Renderable_State_Observer(this))},
scene) {}
With_Observer<State_Observer>{State_Observer(Renderable_State_Observer(this))}) {}
template <auto Member, Property_Member_Assignable<State, Member> Value>
Axis_State_Strategy& set(Value&& value) {
+2 -2
View File
@@ -6,8 +6,8 @@
namespace renderive::detail {
Frequency_Axis_Control::Frequency_Axis_Control(::Scene_Base& scene, const Axis_Properties& properties)
: Axis(scene, properties) {}
Frequency_Axis_Control::Frequency_Axis_Control(const Axis_Properties& properties)
: Axis(properties) {}
std::string Frequency_Axis_Control::format_tick_label(double tick,
const Axis_Properties& state) const {
+1 -1
View File
@@ -8,7 +8,7 @@ namespace detail {
class LIB_DECL Frequency_Axis_Control : public Axis {
public:
Frequency_Axis_Control(::Scene_Base& scene, const Axis_Properties& properties);
explicit Frequency_Axis_Control(const Axis_Properties& properties);
protected:
[[nodiscard]] std::string format_tick_label(double tick, const Axis_Properties& state) const override;
};
+2 -2
View File
@@ -2,8 +2,8 @@
namespace renderive::detail {
Axis_Control::Axis_Control(::Scene_Base& scene, const Axis_Properties& properties)
: Axis_State_Strategy(scene, properties) {}
Axis_Control::Axis_Control(const Axis_Properties& properties)
: Axis_State_Strategy(properties) {}
void Axis_Control::handle_event(const Event& event) {
if (event.type == Event_Type::Wheel && get<&Axis_Properties::wheel>()) {
+1 -1
View File
@@ -34,7 +34,7 @@ struct Axis_Interaction_State {
class LIB_DECL Axis_Control : public Axis_State_Strategy<Axis_Properties>, public Event_Handler {
public:
Axis_Control(::Scene_Base& scene, const Axis_Properties& properties);
explicit Axis_Control(const Axis_Properties& properties);
void handle_event(const Event& event) override;
void publish() override;
+2 -2
View File
@@ -16,8 +16,8 @@ Time_Axis_State time_state_from(const Time_Axis_Properties& properties) {
} // namespace
Time_Axis_Control::Time_Axis_Control(::Scene_Base& scene, const Time_Axis_Properties& properties)
: Axis_State_Strategy(scene, time_state_from(properties)) {}
Time_Axis_Control::Time_Axis_Control(const Time_Axis_Properties& properties)
: Axis_State_Strategy(time_state_from(properties)) {}
std::size_t Time_Axis_Control::time_point_count() const {
return read([](const Time_Axis_State& state) { return state.samples.size(); });
+1 -1
View File
@@ -27,7 +27,7 @@ struct Time_Axis_State : Time_Axis_Properties {
class LIB_DECL Time_Axis_Control : public Axis_State_Strategy<Time_Axis_State> {
public:
Time_Axis_Control(::Scene_Base& scene, const Time_Axis_Properties& properties);
explicit Time_Axis_Control(const Time_Axis_Properties& properties);
[[nodiscard]] std::size_t time_point_count() const;
int append_time(Time_Of_Day time);
[[nodiscard]] Time_Of_Day tick_to_time(int tick) const;
+5 -5
View File
@@ -23,16 +23,16 @@ struct Afterglow_Prepare_Buffer {
};
}
struct Afterglow_Control::Impl {
Impl(Afterglow_Control& owner, std::shared_ptr<Frequency_Axis> frequency, std::shared_ptr<Axis> power)
Impl(Afterglow_Control& owner, renderive_Owner<Frequency_Axis> frequency, renderive_Owner<Axis> power)
: frequency_axis(std::move(frequency)), power_axis(std::move(power)), history(owner) {}
std::shared_ptr<Frequency_Axis> frequency_axis;
std::shared_ptr<Axis> power_axis;
renderive_Owner<Frequency_Axis> frequency_axis;
renderive_Owner<Axis> power_axis;
Afterglow_History history;
Adaptive_Render_Partitioner partitioner;
Afterglow_Prepare_Buffer prepare_buffer;
};
Afterglow_Control::Afterglow_Control(::Scene_Base& scene, const Afterglow_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Axis> power_axis)
: Plottable_State(scene, properties), impl_(std::make_unique<Impl>(*this, std::move(frequency_axis), std::move(power_axis))) {}
Afterglow_Control::Afterglow_Control(const Afterglow_Properties& properties, renderive_Owner<Frequency_Axis> frequency_axis, renderive_Owner<Axis> power_axis)
: Plottable_State(properties), impl_(std::make_unique<Impl>(*this, std::move(frequency_axis), std::move(power_axis))) {}
Afterglow_Control::~Afterglow_Control() = default;
std::size_t Afterglow_Control::history_count() const {
return impl_->history.size();
+1 -1
View File
@@ -21,7 +21,7 @@ namespace detail {
class LIB_DECL Afterglow_Control : public Plottable_State<Afterglow_Properties>,
public ::Render_Frame_Completion {
public:
Afterglow_Control(::Scene_Base& scene, const Afterglow_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Axis> power_axis);
Afterglow_Control(const Afterglow_Properties& properties, renderive_Owner<Frequency_Axis> frequency_axis, renderive_Owner<Axis> power_axis);
~Afterglow_Control() override;
[[nodiscard]] std::size_t history_count() const;
[[nodiscard]] std::size_t latest_spectrum_point_count() const;
@@ -21,15 +21,15 @@ struct Constellation_Diagram_Control::Impl {
std::vector<PointF> anchors;
std::vector<PointF> points;
};
Impl(Constellation_Diagram_Control& owner, std::shared_ptr<Axis> i, std::shared_ptr<Axis> q)
Impl(Constellation_Diagram_Control& owner, renderive_Owner<Axis> i, renderive_Owner<Axis> q)
: i_axis(std::move(i)), q_axis(std::move(q)), points(owner) {}
std::shared_ptr<Axis> i_axis;
std::shared_ptr<Axis> q_axis;
renderive_Owner<Axis> i_axis;
renderive_Owner<Axis> q_axis;
Constellation_History points;
Prepare_Buffer prepare_buffer;
};
Constellation_Diagram_Control::Constellation_Diagram_Control(::Scene_Base& scene, const Constellation_Diagram_Properties& properties, std::shared_ptr<Axis> i_axis, std::shared_ptr<Axis> q_axis)
: Plottable_State(scene, properties), impl_(std::make_unique<Impl>(*this, std::move(i_axis), std::move(q_axis))) {}
Constellation_Diagram_Control::Constellation_Diagram_Control(const Constellation_Diagram_Properties& properties, renderive_Owner<Axis> i_axis, renderive_Owner<Axis> q_axis)
: Plottable_State(properties), impl_(std::make_unique<Impl>(*this, std::move(i_axis), std::move(q_axis))) {}
Constellation_Diagram_Control::~Constellation_Diagram_Control() = default;
void Constellation_Diagram_Control::append_point(PointF point) {
const auto now = std::chrono::steady_clock::now();
+1 -1
View File
@@ -19,7 +19,7 @@ struct Constellation_Diagram_Properties {
namespace detail {
class LIB_DECL Constellation_Diagram_Control : public Plottable_State<Constellation_Diagram_Properties> {
public:
Constellation_Diagram_Control(::Scene_Base& scene, const Constellation_Diagram_Properties& properties, std::shared_ptr<Axis> i_axis, std::shared_ptr<Axis> q_axis);
Constellation_Diagram_Control(const Constellation_Diagram_Properties& properties, renderive_Owner<Axis> i_axis, renderive_Owner<Axis> q_axis);
~Constellation_Diagram_Control() override;
void append_point(PointF point);
[[nodiscard]] std::size_t point_count() const;
+5 -5
View File
@@ -14,15 +14,15 @@ struct Frequency_Trace_Control::Impl {
struct Prepare_Buffer {
std::vector<PointF> points;
};
Impl(Frequency_Trace_Control& owner, std::shared_ptr<Time_Axis> time, std::shared_ptr<Axis> value)
Impl(Frequency_Trace_Control& owner, renderive_Owner<Time_Axis> time, renderive_Owner<Axis> value)
: time_axis(std::move(time)), value_axis(std::move(value)), samples(owner) {}
std::shared_ptr<Time_Axis> time_axis;
std::shared_ptr<Axis> value_axis;
renderive_Owner<Time_Axis> time_axis;
renderive_Owner<Axis> value_axis;
Frequency_Trace_History samples;
Prepare_Buffer prepare_buffer;
};
Frequency_Trace_Control::Frequency_Trace_Control(::Scene_Base& scene, const Frequency_Trace_Properties& properties, std::shared_ptr<Time_Axis> time_axis, std::shared_ptr<Axis> value_axis)
: Plottable_State(scene, properties), impl_(std::make_unique<Impl>(*this, std::move(time_axis), std::move(value_axis))) {}
Frequency_Trace_Control::Frequency_Trace_Control(const Frequency_Trace_Properties& properties, renderive_Owner<Time_Axis> time_axis, renderive_Owner<Axis> value_axis)
: Plottable_State(properties), impl_(std::make_unique<Impl>(*this, std::move(time_axis), std::move(value_axis))) {}
Frequency_Trace_Control::~Frequency_Trace_Control() = default;
void Frequency_Trace_Control::append_sample(int tick, double value) {
const int limit = std::max(2, impl_->time_axis->get<&Time_Axis_Properties::visible_count>());
+1 -1
View File
@@ -8,7 +8,7 @@ struct Frequency_Trace_Properties {
namespace detail {
class LIB_DECL Frequency_Trace_Control : public Plottable_State<Frequency_Trace_Properties> {
public:
Frequency_Trace_Control(::Scene_Base& scene, const Frequency_Trace_Properties& properties, std::shared_ptr<Time_Axis> time_axis, std::shared_ptr<Axis> value_axis);
Frequency_Trace_Control(const Frequency_Trace_Properties& properties, renderive_Owner<Time_Axis> time_axis, renderive_Owner<Axis> value_axis);
~Frequency_Trace_Control() override;
void append_sample(int tick, double value);
void append_sample(Time_Of_Day time, double value);
+2 -3
View File
@@ -60,11 +60,10 @@ public:
using State_Observer = Observer_State<Renderable_State_Observer>;
using Base = Double_State_Strategy<Renderable, Properties, Atomic_Spin_Mutex,
State_Observer>;
Plottable_State(::Scene_Base& scene, const Properties& properties)
explicit Plottable_State(const Properties& properties)
: Base(properties,
With_Observer<State_Observer>{
State_Observer(Renderable_State_Observer(this))},
scene) {}
State_Observer(Renderable_State_Observer(this))}) {}
template <auto Member, Property_Member_Assignable<Properties, Member> Value>
Plottable_State& set(Value&& value) {
Base::template set<Member>(std::forward<Value>(value));
@@ -29,16 +29,16 @@ struct Selection_Rectangle_Overlay_Control::Impl {
RectF active_selection;
bool selecting{};
};
Impl(Selection_Rectangle_Overlay_Control& owner, std::shared_ptr<Abs_Axis> horizontal, std::shared_ptr<Abs_Axis> vertical)
Impl(Selection_Rectangle_Overlay_Control& owner, renderive_Owner<Abs_Axis> horizontal, renderive_Owner<Abs_Axis> vertical)
: horizontal_axis(std::move(horizontal)), vertical_axis(std::move(vertical)), regions(owner) {}
std::shared_ptr<Abs_Axis> horizontal_axis;
std::shared_ptr<Abs_Axis> vertical_axis;
renderive_Owner<Abs_Axis> horizontal_axis;
renderive_Owner<Abs_Axis> vertical_axis;
Plottable_History_Real_Time_Data<RectF, std::vector<RectF>> regions;
Selection_Interaction_State interaction;
Prepare_Buffer prepare_buffer;
};
Selection_Rectangle_Overlay_Control::Selection_Rectangle_Overlay_Control(::Scene_Base& scene, const Selection_Rectangle_Overlay_Properties& properties, std::shared_ptr<Abs_Axis> horizontal_axis, std::shared_ptr<Abs_Axis> vertical_axis)
: Plottable_State(scene, properties), impl_(std::make_unique<Impl>(*this, std::move(horizontal_axis), std::move(vertical_axis))) {}
Selection_Rectangle_Overlay_Control::Selection_Rectangle_Overlay_Control(const Selection_Rectangle_Overlay_Properties& properties, renderive_Owner<Abs_Axis> horizontal_axis, renderive_Owner<Abs_Axis> vertical_axis)
: Plottable_State(properties), impl_(std::make_unique<Impl>(*this, std::move(horizontal_axis), std::move(vertical_axis))) {}
Selection_Rectangle_Overlay_Control::~Selection_Rectangle_Overlay_Control() = default;
std::vector<RectF> Selection_Rectangle_Overlay_Control::selected_regions() const {
return impl_->regions.snapshot();
@@ -11,7 +11,7 @@ struct Selection_Rectangle_Overlay_Properties {
namespace detail {
class LIB_DECL Selection_Rectangle_Overlay_Control : public Plottable_State<Selection_Rectangle_Overlay_Properties>, public Paint_Overlay, public Event_Handler {
public:
Selection_Rectangle_Overlay_Control(::Scene_Base& scene, const Selection_Rectangle_Overlay_Properties& properties, std::shared_ptr<Abs_Axis> horizontal_axis, std::shared_ptr<Abs_Axis> vertical_axis);
Selection_Rectangle_Overlay_Control(const Selection_Rectangle_Overlay_Properties& properties, renderive_Owner<Abs_Axis> horizontal_axis, renderive_Owner<Abs_Axis> vertical_axis);
~Selection_Rectangle_Overlay_Control() override;
[[nodiscard]] std::vector<RectF> selected_regions() const;
void clear_selected_regions();
+5 -5
View File
@@ -137,18 +137,18 @@ double spectrum_power_at(const Spectrum_Properties& properties, const Spectrum_F
}
}
struct Spectrum_Control::Impl {
Impl(Spectrum_Control& owner, std::shared_ptr<Frequency_Axis> frequency, std::shared_ptr<Axis> power)
Impl(Spectrum_Control& owner, renderive_Owner<Frequency_Axis> frequency, renderive_Owner<Axis> power)
: frequency_axis(std::move(frequency)), power_axis(std::move(power)), frame(owner) {}
std::shared_ptr<Frequency_Axis> frequency_axis;
std::shared_ptr<Axis> power_axis;
renderive_Owner<Frequency_Axis> frequency_axis;
renderive_Owner<Axis> power_axis;
Plottable_Latest_Real_Time_Data<Spectrum_Frame> frame;
Spectrum_Interaction_State interaction;
std::mutex frame_update_mutex;
Adaptive_Render_Partitioner partitioner;
Spectrum_Prepare_Buffer prepare_buffer;
};
Spectrum_Control::Spectrum_Control(::Scene_Base& scene, const Spectrum_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Axis> power_axis)
: Plottable_State(scene, properties), impl_(std::make_unique<Impl>(*this, std::move(frequency_axis), std::move(power_axis))) {}
Spectrum_Control::Spectrum_Control(const Spectrum_Properties& properties, renderive_Owner<Frequency_Axis> frequency_axis, renderive_Owner<Axis> power_axis)
: Plottable_State(properties), impl_(std::make_unique<Impl>(*this, std::move(frequency_axis), std::move(power_axis))) {}
Spectrum_Control::~Spectrum_Control() = default;
void Spectrum_Control::update_samples(std::span<const double> values) {
if(get<&Spectrum_Properties::frequency_point_size>() <= 0)
+1 -1
View File
@@ -38,7 +38,7 @@ class LIB_DECL Spectrum_Control : public Plottable_State<Spectrum_Properties>,
public Event_Handler,
public ::Render_Frame_Completion {
public:
Spectrum_Control(::Scene_Base& scene, const Spectrum_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Axis> power_axis);
Spectrum_Control(const Spectrum_Properties& properties, renderive_Owner<Frequency_Axis> frequency_axis, renderive_Owner<Axis> power_axis);
~Spectrum_Control() override;
void update_samples(std::span<const double> values);
void update_samples(std::pmr::vector<double>&& values);
+5 -5
View File
@@ -23,15 +23,15 @@ struct Sweep_Spectrum_Control::Impl {
PointF current_second;
bool valid{};
};
Impl(Sweep_Spectrum_Control& owner, std::shared_ptr<Axis> frequency, std::shared_ptr<Axis> power)
Impl(Sweep_Spectrum_Control& owner, renderive_Owner<Axis> frequency, renderive_Owner<Axis> power)
: frequency_axis(std::move(frequency)), power_axis(std::move(power)), blocks(owner) {}
std::shared_ptr<Axis> frequency_axis;
std::shared_ptr<Axis> power_axis;
renderive_Owner<Axis> frequency_axis;
renderive_Owner<Axis> power_axis;
Sweep_Spectrum_History blocks;
Prepare_Buffer prepare_buffer;
};
Sweep_Spectrum_Control::Sweep_Spectrum_Control(::Scene_Base& scene, const Sweep_Spectrum_Properties& properties, std::shared_ptr<Axis> frequency_axis, std::shared_ptr<Axis> power_axis)
: Plottable_State(scene, properties), impl_(std::make_unique<Impl>(*this, std::move(frequency_axis), std::move(power_axis))) {}
Sweep_Spectrum_Control::Sweep_Spectrum_Control(const Sweep_Spectrum_Properties& properties, renderive_Owner<Axis> frequency_axis, renderive_Owner<Axis> power_axis)
: Plottable_State(properties), impl_(std::make_unique<Impl>(*this, std::move(frequency_axis), std::move(power_axis))) {}
Sweep_Spectrum_Control::~Sweep_Spectrum_Control() = default;
void Sweep_Spectrum_Control::append_block(std::span<const double> values) {
if(get<&Sweep_Spectrum_Properties::bins_per_block>() <= 0)
+1 -1
View File
@@ -16,7 +16,7 @@ struct Sweep_Spectrum_Properties {
namespace detail {
class LIB_DECL Sweep_Spectrum_Control : public Plottable_State<Sweep_Spectrum_Properties> {
public:
Sweep_Spectrum_Control(::Scene_Base& scene, const Sweep_Spectrum_Properties& properties, std::shared_ptr<Axis> frequency_axis, std::shared_ptr<Axis> power_axis);
Sweep_Spectrum_Control(const Sweep_Spectrum_Properties& properties, renderive_Owner<Axis> frequency_axis, renderive_Owner<Axis> power_axis);
~Sweep_Spectrum_Control() override;
void append_block(std::span<const double> values);
void append_block(std::pmr::vector<double>&& values);
+5 -5
View File
@@ -57,17 +57,17 @@ std::size_t waterfall_work_size(const Waterfall_Properties& state,
}
}
struct Waterfall_Control::Impl {
Impl(Waterfall_Control& owner, std::shared_ptr<Frequency_Axis> frequency, std::shared_ptr<Time_Axis> time)
Impl(Waterfall_Control& owner, renderive_Owner<Frequency_Axis> frequency, renderive_Owner<Time_Axis> time)
: frequency_axis(std::move(frequency)), time_axis(std::move(time)), rows(owner) {}
std::shared_ptr<Frequency_Axis> frequency_axis;
std::shared_ptr<Time_Axis> time_axis;
renderive_Owner<Frequency_Axis> frequency_axis;
renderive_Owner<Time_Axis> time_axis;
Waterfall_History rows;
Waterfall_Interaction_State interaction;
Adaptive_Render_Partitioner partitioner;
Waterfall_Prepare_Buffer prepare_buffer;
};
Waterfall_Control::Waterfall_Control(::Scene_Base& scene, const Waterfall_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Time_Axis> time_axis)
: Plottable_State(scene, properties), impl_(std::make_unique<Impl>(*this, std::move(frequency_axis), std::move(time_axis))) {}
Waterfall_Control::Waterfall_Control(const Waterfall_Properties& properties, renderive_Owner<Frequency_Axis> frequency_axis, renderive_Owner<Time_Axis> time_axis)
: Plottable_State(properties), impl_(std::make_unique<Impl>(*this, std::move(frequency_axis), std::move(time_axis))) {}
Waterfall_Control::~Waterfall_Control() = default;
void Waterfall_Control::append_row(int tick, std::span<const double> values) {
const std::size_t limit = static_cast<std::size_t>(
+1 -1
View File
@@ -22,7 +22,7 @@ class LIB_DECL Waterfall_Control : public Plottable_State<Waterfall_Properties>,
public Event_Handler,
public ::Render_Frame_Completion {
public:
Waterfall_Control(::Scene_Base& scene, const Waterfall_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Time_Axis> time_axis);
Waterfall_Control(const Waterfall_Properties& properties, renderive_Owner<Frequency_Axis> frequency_axis, renderive_Owner<Time_Axis> time_axis);
~Waterfall_Control() override;
void append_row(int tick, std::span<const double> values);
void append_row(int tick, std::pmr::vector<double>&& values);
+5 -7
View File
@@ -23,8 +23,8 @@ void Renderable_Observer::observe_state(Renderable_Observer_Event event,
observation_.publish_count = publish_count;
}
Renderable::Renderable(::Scene_Base& scene, bool cache_enabled)
: ::Renderable_Base(scene, {.cache_enabled = cache_enabled}) {}
Renderable::Renderable(bool cache_enabled)
: ::Renderable_Base({.cache_enabled = cache_enabled}) {}
Renderable::~Renderable() = default;
@@ -35,9 +35,7 @@ Renderable_Cache_Mode Renderable::get_cache_mode() const noexcept {
}
void Renderable::set_cache_mode(Renderable_Cache_Mode mode) {
scene().set_renderable_configuration(
shared_from_this(),
{.cache_enabled = mode == Renderable_Cache_Mode::Local_Pixel});
set_configuration({.cache_enabled = mode == Renderable_Cache_Mode::Local_Pixel});
}
std::string Renderable::object_name() const {
@@ -106,12 +104,12 @@ Renderable_Graph_Builder::Task Renderable::add_paint_task(
void Renderable::changed() noexcept {
invalidate_prepare();
scene().notify_model_dirty();
notify_scene_model_dirty();
}
void Renderable::paint_changed() noexcept {
invalidate_paint();
scene().notify_model_dirty();
notify_scene_model_dirty();
}
void Renderable::render_graph_changed() {
+2 -1
View File
@@ -4,6 +4,7 @@
#include "../event/Event.h"
#include <renderive/renderable/base/Renderable_Base.hpp>
#include <renderive/renderable/base/Renderive_Owner.hpp>
#include <atomic>
#include <concepts>
@@ -53,7 +54,7 @@ private:
class LIB_DECL Renderable : public ::Renderable_Base {
public:
explicit Renderable(::Scene_Base& scene, bool cache_enabled = true);
explicit Renderable(bool cache_enabled = true);
~Renderable() override;
[[nodiscard]] Renderable_Cache_Mode get_cache_mode() const noexcept;
+24 -45
View File
@@ -6,55 +6,33 @@
#include <algorithm>
#include <concepts>
#include <functional>
#include <memory>
#include <utility>
#include <vector>
namespace renderive {
class Abs_Axis;
namespace detail {
class Paint_Overlay;
template <class Renderable_Type>
void append_unique(std::vector<::Scene_Base::Renderable>& renderables,
const std::shared_ptr<Renderable_Type>& renderable) {
::Scene_Base::Renderable base = renderable;
if (base && std::find(renderables.begin(), renderables.end(), base) ==
renderables.end())
renderables.push_back(std::move(base));
}
template <class Renderable_Type, class Axis_Type>
requires std::derived_from<Renderable_Type, Renderable> &&
std::derived_from<Axis_Type, Abs_Axis>
void append_renderable_dependency(
::Scene_Base::Attach_Relationships& relationships,
const std::shared_ptr<Axis_Type>& axis) {
requires std::derived_from<Renderable_Type, Renderable> && std::derived_from<Axis_Type, Abs_Axis>
void attach_renderable_dependency(::Scene_Base::Attach_Builder& builder, const renderive_Owner<Renderable_Type>& renderable, const renderive_Owner<Axis_Type>& axis) {
if (!axis)
return;
append_unique(relationships.dependency_parents, axis);
builder.add_parent(::Scene_Base::Relationship::dependency, renderable, axis);
if constexpr (std::derived_from<Renderable_Type, Paint_Overlay>)
append_unique(relationships.display_parents, axis);
builder.add_parent(::Scene_Base::Relationship::display, renderable, axis);
else
append_unique(relationships.display_children, axis);
builder.add_parent(::Scene_Base::Relationship::display, axis, renderable);
}
template <class Renderable_Type, class Value>
void append_renderable_dependency(
::Scene_Base::Attach_Relationships&, const Value&) {}
void attach_renderable_dependency(::Scene_Base::Attach_Builder&, const renderive_Owner<Renderable_Type>&, const Value&) {}
template <class Control, class... Args>
requires std::derived_from<Control, Renderable>
auto renderable_attach_relationships(
const std::shared_ptr<Renderable>& parent, const Args&... args)
-> ::Scene_Base::Attach_Relationships {
::Scene_Base::Attach_Relationships relationships;
append_unique(relationships.display_parents, parent);
append_unique(relationships.dependency_parents, parent);
(append_renderable_dependency<Control>(relationships, args), ...);
return relationships;
void attach_renderable(::Scene_Base::Attach_Builder& builder, const renderive_Owner<Control>& renderable, const renderive_Owner<Renderable>& parent, const Args&... args) {
builder.attach(renderable);
builder.add_parent(::Scene_Base::Relationship::display, renderable, parent);
builder.add_parent(::Scene_Base::Relationship::dependency, renderable, parent);
(attach_renderable_dependency(builder, renderable, args), ...);
}
}
template <Property_Product Product_Type, Property_Set Properties_Type, Property_Validator<Properties_Type> Validator_Type = No_Property_Validator<Properties_Type>>
class Renderable_Builder {
public:
@@ -62,9 +40,10 @@ public:
using Properties = Properties_Type;
using Validator = Validator_Type;
using Self = Renderable_Builder;
Renderable_Builder() requires std::default_initializable<Properties> && std::default_initializable<Validator> : properties{}, validator{} {}
explicit Renderable_Builder(Properties properties) requires std::default_initializable<Validator> : properties(std::move(properties)), validator{} {}
Renderable_Builder(Properties properties, Validator validator) : properties(std::move(properties)), validator(std::move(validator)) {}
Renderable_Builder() requires std::default_initializable<Properties> && std::default_initializable<Validator> = default;
explicit Renderable_Builder(::Scene_Base::Attach_Builder* attach_builder) requires std::default_initializable<Properties> && std::default_initializable<Validator> : attach_builder_(attach_builder) {}
explicit Renderable_Builder(Properties properties, ::Scene_Base::Attach_Builder* attach_builder = nullptr) requires std::default_initializable<Validator> : properties(std::move(properties)), attach_builder_(attach_builder) {}
Renderable_Builder(Properties properties, Validator validator, ::Scene_Base::Attach_Builder* attach_builder = nullptr) : properties(std::move(properties)), validator(std::move(validator)), attach_builder_(attach_builder) {}
template <auto Member, Property_Member_Assignable<Properties, Member> Value>
Self& set(Value&& value) {
properties.*Member = std::forward<Value>(value);
@@ -79,27 +58,27 @@ public:
return properties;
}
template <class... Args>
requires std::derived_from<Product, Renderable> && std::constructible_from<Product, ::Scene_Base&, const Properties&, Args&&...>
std::shared_ptr<Product> build(const std::shared_ptr<Renderable>& parent, Args&&... args) const {
requires std::derived_from<Product, Renderable> && std::constructible_from<Product, const Properties&, const Args&...>
renderive_Owner<Product> build(const renderive_Owner<Renderable>& parent, Args&&... args) const {
if (!parent || !(valid_argument(args) && ...))
return {};
validator(properties);
auto relationships =
detail::renderable_attach_relationships<Product>(parent, args...);
auto result = parent->scene().template make_renderable<Product>(properties, std::forward<Args>(args)...);
parent->scene().attach_renderable(result, std::move(relationships));
auto result = renderive_Owner<Product>::make(properties, args...);
if (attach_builder_)
detail::attach_renderable(*attach_builder_, result, parent, args...);
return result;
}
private:
template <class T>
static bool valid_argument(const std::shared_ptr<T>& value) {
static bool valid_argument(const renderive_Owner<T>& value) {
return static_cast<bool>(value);
}
template <class T>
static bool valid_argument(const T&) {
return true;
}
Properties properties;
[[no_unique_address]] Validator validator;
Properties properties{};
[[no_unique_address]] Validator validator{};
::Scene_Base::Attach_Builder* attach_builder_{};
};
}
+2 -2
View File
@@ -11,8 +11,8 @@ private:
void paint(Painter&, const Paint_Render_Context&) override {}
};
}
std::shared_ptr<Renderable> make_renderable_group(::Scene_Base& scene, bool cache_enabled) {
return scene.make_renderable<Renderable_Group>(cache_enabled);
renderive_Owner<Renderable> make_renderable_group(bool cache_enabled) {
return renderive_Owner<Renderable_Group>::make(cache_enabled);
}
void record_performance_frame(Performance_Overlay& overlay, double duration_ms, const Low_Latency_Diagnostics& diagnostics, std::size_t renderable_count, Size viewport) {
overlay.record_frame(duration_ms, diagnostics, renderable_count, viewport);
+8 -40
View File
@@ -171,7 +171,7 @@ constexpr Frame_Control_Mode frame_control_mode() noexcept {
return Frame_Control_Mode::Low_Latency;
return Frame_Control_Mode::Playback;
}
LIB_DECL std::shared_ptr<Renderable> make_renderable_group(::Scene_Base& scene, bool cache_enabled);
LIB_DECL renderive_Owner<Renderable> make_renderable_group(bool cache_enabled);
LIB_DECL void record_performance_frame(Performance_Overlay& overlay, double duration_ms, const Low_Latency_Diagnostics& diagnostics, std::size_t renderable_count, Size viewport);
inline std::string observer_event_name(Frame_Control_Mode mode, std::uint32_t event) {
if (mode == Frame_Control_Mode::Manual) {
@@ -207,40 +207,22 @@ public:
std::lock_guard lock(initialization_mutex_);
if (root_renderable())
return;
auto root = detail::make_renderable_group(*this, true);
auto root = detail::make_renderable_group(true);
root->set_object_name("root");
auto builder = this->attach_builder();
builder.attach(root);
}
[[nodiscard]] std::shared_ptr<Renderable> root_renderable() const {
[[nodiscard]] renderive_Owner<Renderable> root_renderable() const {
const auto topology = this->topology_snapshot();
for (const auto& relationship : topology.display) {
if (relationship.parent)
continue;
auto base = std::const_pointer_cast<::Renderable_Base>(relationship.child);
if (auto renderable = std::dynamic_pointer_cast<Renderable>(base))
auto base = renderive_const_owner_cast<::Renderable_Base>(relationship.child);
if (auto renderable = renderive_dynamic_owner_cast<Renderable>(base))
return renderable;
}
return {};
}
[[nodiscard]] std::shared_ptr<Renderable> create_renderable_node(const std::shared_ptr<Renderable>& parent, std::string object_name = {}) {
auto renderable = detail::make_renderable_group(*this, true);
renderable->set_object_name(std::move(object_name));
attach_renderable_node(renderable, parent);
return renderable;
}
template <class T, class... Args>
requires std::derived_from<T, Renderable> && std::constructible_from<T, ::Scene_Base&, Args&&...>
std::shared_ptr<T> make_renderable(const std::shared_ptr<Renderable>& parent, Args&&... args) {
auto renderable = Kernel_Scene::template make_renderable<T>(std::forward<Args>(args)...);
attach_renderable_node(renderable, parent);
return renderable;
}
::Scene_Base::Mutation remove_renderable(const std::shared_ptr<Renderable>& renderable) {
if (!renderable || renderable == root_renderable())
return {};
return this->detach_renderable(renderable);
}
void set_background_color(Color color) {
if (background_color() == color)
return;
@@ -269,10 +251,10 @@ public:
void dispatch_event(const Event& event) {
const auto paint_order = this->paint_order_snapshot();
for (auto iterator = paint_order.rbegin(); iterator != paint_order.rend(); ++iterator) {
auto base = std::const_pointer_cast<::Renderable_Base>(*iterator);
if (auto renderable = std::dynamic_pointer_cast<Renderable>(base)) {
auto base = renderive_const_owner_cast<::Renderable_Base>(*iterator);
if (auto renderable = renderive_dynamic_owner_cast<Renderable>(base)) {
if (renderable->is_visible()) {
if (auto handler = std::dynamic_pointer_cast<Event_Handler>(base))
if (auto handler = renderive_dynamic_owner_cast<Event_Handler>(base))
handler->handle_event(event);
}
if (event.is_accepted())
@@ -475,20 +457,6 @@ public:
private:
explicit Basic_Scene2D(std::shared_ptr<detail::Frame_Observer_Data> observer)
: Kernel_Scene(*memory_resource(Memory_Domain::Plot_Frame), detail::Frame_Observer_State(detail::Frame_Observer{observer})), observer_(std::move(observer)) {}
void attach_renderable_node(const std::shared_ptr<Renderable>& renderable, const std::shared_ptr<Renderable>& parent) {
if (!renderable)
throw std::invalid_argument("renderable is null");
auto actual_parent = parent;
if (!actual_parent) {
actual_parent = root_renderable();
if (!actual_parent)
throw std::logic_error("Scene2D::init must be called before adding renderables");
}
this->attach_renderable(renderable, {
.display_parents = {actual_parent},
.dependency_parents = {actual_parent}
});
}
std::shared_ptr<detail::Frame_Observer_Data> observer_;
mutable std::mutex control_mutex_;
std::mutex initialization_mutex_;
+265 -180
View File
@@ -6,12 +6,20 @@
#include <array>
#include <chrono>
#include <cstddef>
#include <functional>
#include <memory_resource>
#include <set>
#include <thread>
#include <tuple>
#include <utility>
#include <vector>
namespace renderive {
namespace {
template <class Scene, class Build>
decltype(auto) build_initial(Scene& scene, Build&& build) {
auto attach = scene.attach_builder();
return std::invoke(std::forward<Build>(build), attach);
}
template <class Axis_Type>
concept Legacy_Axis_Property_Api = requires(Axis_Type& axis) {
axis.x();
@@ -143,28 +151,31 @@ TEST(Renderive_Core2, KernelSceneRendersBusinessObjectsIntoBlend2DFrame) {
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();
auto [frequency_axis, power_axis, spectrum] = build_initial(plot, [&](auto& attach) {
auto frequency = Frequency_Axis::Builder(root, Orientation::Horizontal, &attach)
.set_x(32)
.set_y(150)
.set_pixel_length(270)
.set_coord_range({88.0, 108.0})
.build();
auto power = Axis::Builder(root, Orientation::Vertical, &attach)
.set_x(32)
.set_y(8)
.set_pixel_length(142)
.set_coord_range({-120.0, 0.0})
.build();
auto value = Spectrum::Builder{&attach}
.set<&Spectrum::Properties::frequency_range>(Range{88.0, 108.0})
.set<&Spectrum::Properties::frequency_point_size>(64)
.set<&Spectrum::Properties::max_hold_visible>(true)
.build(root, frequency, power);
return std::tuple{frequency, power, value};
});
frequency_axis->set<&Axis_Base_Properties::locale>(Number_Locale{','});
frequency_axis->set<&Axis_Base_Properties::label_rotation_degrees>(15);
EXPECT_EQ(frequency_axis->tick_label(88.5), "88,5 Hz");
frequency_axis->set<&Axis_Properties::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{}
.set<&Spectrum::Properties::frequency_range>(Range{88.0, 108.0})
.set<&Spectrum::Properties::frequency_point_size>(64)
.set<&Spectrum::Properties::max_hold_visible>(true)
.build(root, frequency_axis, power_axis);
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;
@@ -201,19 +212,52 @@ TEST(Renderive_Core2, KernelSceneRendersBusinessObjectsIntoBlend2DFrame) {
EXPECT_TRUE(wheel.is_accepted());
EXPECT_LT(frequency_axis->get<&Axis_Properties::coordinates>().size(), before_zoom.size());
EXPECT_TRUE(plot.render_frame());
plot.remove_renderable(spectrum);
plot.edit_renderables([root, frequency_axis, power_axis, spectrum](auto& editor) {
editor.set_parent(::Scene_Base::Relationship::display, frequency_axis, root);
editor.set_parent(::Scene_Base::Relationship::display, power_axis, root);
editor.clear_parents(::Scene_Base::Relationship::display, spectrum);
editor.clear_parents(::Scene_Base::Relationship::dependency, spectrum);
editor.detach(spectrum);
});
EXPECT_TRUE(plot.render_frame(true));
EXPECT_FALSE(plot.render_frame());
}
TEST(Renderive_Core2, StandaloneRenderableBuilderJoinsSceneOnlyInsideRuntimeEditor) {
Scene2D plot;
plot.init();
const auto root = plot.root_renderable();
const auto [frequency, power] = build_initial(plot, [&](auto& attach) {
return std::pair{
Frequency_Axis::Builder(root, Orientation::Horizontal, &attach).build(),
Axis::Builder(root, Orientation::Vertical, &attach).build()
};
});
const auto spectrum = Spectrum::Builder{}.build(root, frequency, power);
ASSERT_TRUE(spectrum);
EXPECT_FALSE(spectrum->attached());
plot.edit_renderables([root, frequency, power, spectrum](auto& editor) {
editor.attach(spectrum);
editor.add_parent(::Scene_Base::Relationship::display, spectrum, root);
editor.add_parent(::Scene_Base::Relationship::dependency, spectrum, root);
editor.add_parent(::Scene_Base::Relationship::dependency, spectrum, frequency);
editor.add_parent(::Scene_Base::Relationship::dependency, spectrum, power);
editor.add_parent(::Scene_Base::Relationship::display, frequency, spectrum);
editor.add_parent(::Scene_Base::Relationship::display, power, spectrum);
});
EXPECT_TRUE(plot.render_frame(true));
EXPECT_TRUE(spectrum->attached());
}
TEST(Renderive_Core2, TimeAxisUsesOneFontStateAndFormatsConfiguredLabels) {
Scene2D 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();
auto axis = build_initial(plot, [&](auto& attach) {
return Time_Axis::Builder(root, Orientation::Horizontal, &attach)
.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");
@@ -285,24 +329,25 @@ TEST(Renderive_Core2, RetainedAxisAndTimeAxisApisRoundTripWithoutWebAdapters) {
plot.init();
const auto root = plot.root_renderable();
ASSERT_TRUE(root);
const auto axis = Axis::Builder(root, Orientation::Vertical)
.set_x(31)
.set_y(17)
.set_pixel_length(240)
.set_tick_length(13)
.set_sub_tick_length(7)
.set_color({10, 20, 30, 255})
.set_unit_text("dB")
.set_unit_text_font({15.0, 650, true})
.set_unit_text_pen({Color{40, 50, 60, 255}, 2.0})
.set_unit_text_background_brush(
{Color{3, 4, 5, 255}, Brush_Style::Solid})
.set_label_rotation_degrees(27)
.set_coord_range({-120.0, -20.0})
.set_label_precision(4)
.set_use_wheel(true)
.set_use_drag(true)
.build();
const auto axis = build_initial(plot, [&](auto& attach) {
return Axis::Builder(root, Orientation::Vertical, &attach)
.set_x(31)
.set_y(17)
.set_pixel_length(240)
.set_tick_length(13)
.set_sub_tick_length(7)
.set_color({10, 20, 30, 255})
.set_unit_text("dB")
.set_unit_text_font({15.0, 650, true})
.set_unit_text_pen({Color{40, 50, 60, 255}, 2.0})
.set_unit_text_background_brush({Color{3, 4, 5, 255}, Brush_Style::Solid})
.set_label_rotation_degrees(27)
.set_coord_range({-120.0, -20.0})
.set_label_precision(4)
.set_use_wheel(true)
.set_use_drag(true)
.build();
});
ASSERT_TRUE(axis);
EXPECT_EQ(axis->get<&Axis_Base_Properties::x>(), 31);
EXPECT_EQ(axis->get<&Axis_Base_Properties::y>(), 17);
@@ -333,16 +378,18 @@ TEST(Renderive_Core2, RetainedAxisAndTimeAxisApisRoundTripWithoutWebAdapters) {
EXPECT_GT(static_cast<int>(numeric_transform.pixel_length) + 1, 0);
EXPECT_GT(axis->tick_step(numeric_transform.coordinate_range), 0.0);
EXPECT_GE(axis->sub_tick_count(axis->tick_step(numeric_transform.coordinate_range)), 0);
const auto time_axis = Time_Axis::Builder(root, Orientation::Horizontal)
.set_x(12)
.set_y(280)
.set_pixel_length(300)
.set_visible_time_point_count(32)
.set_tick_label_spacing_px(19)
.set_time_format("hh:mm:ss.zzz")
.set_font({16.0, 700, true})
.set_newest_at_axis_start(true)
.build();
const auto time_axis = build_initial(plot, [&](auto& attach) {
return Time_Axis::Builder(root, Orientation::Horizontal, &attach)
.set_x(12)
.set_y(280)
.set_pixel_length(300)
.set_visible_time_point_count(32)
.set_tick_label_spacing_px(19)
.set_time_format("hh:mm:ss.zzz")
.set_font({16.0, 700, true})
.set_newest_at_axis_start(true)
.build();
});
ASSERT_TRUE(time_axis);
const int first = time_axis->append_time({3'723'004});
const int second = time_axis->append_time({3'724'005});
@@ -363,25 +410,28 @@ TEST(Renderive_Core2, RetainedSpectrumApisRoundTripAndMarkersRemainObservable) {
plot.set_viewport_size({360, 240});
plot.activate_view();
const auto root = plot.root_renderable();
const auto frequency = Frequency_Axis::Builder(root, Orientation::Horizontal)
.set_x(30).set_y(210).set_pixel_length(300)
.set_coord_range({90.0, 110.0}).build();
const auto power = Axis::Builder(root, Orientation::Vertical)
.set_x(30).set_y(20).set_pixel_length(190)
.set_coord_range({-20.0, -120.0}).build();
auto spectrum = Spectrum::Builder{}
.set<&Spectrum::Properties::frequency_range>(Range{90.0, 110.0})
.set<&Spectrum::Properties::frequency_point_size>(4)
.set<&Spectrum::Properties::center_frequency>(100.0)
.set<&Spectrum::Properties::sweep_frequency_range>(Range{96.0, 104.0})
.set<&Spectrum::Properties::max_hold_visible>(true)
.set<&Spectrum::Properties::min_hold_visible>(true)
.set<&Spectrum::Properties::max_marker_visible>(true)
.set<&Spectrum::Properties::use_min_marker>(true)
.set<&Spectrum::Properties::sweep_region_visible>(true)
.set<&Spectrum::Properties::visible_range_only>(false)
.set<&Spectrum::Properties::interpolation_mode>(Line_Interpolation_Mode::Cubic_Value)
.build(root, frequency, power);
auto [frequency, power, spectrum] = build_initial(plot, [&](auto& attach) {
auto frequency_axis = Frequency_Axis::Builder(root, Orientation::Horizontal, &attach)
.set_x(30).set_y(210).set_pixel_length(300)
.set_coord_range({90.0, 110.0}).build();
auto power_axis = Axis::Builder(root, Orientation::Vertical, &attach)
.set_x(30).set_y(20).set_pixel_length(190)
.set_coord_range({-20.0, -120.0}).build();
auto value = Spectrum::Builder{&attach}
.set<&Spectrum::Properties::frequency_range>(Range{90.0, 110.0})
.set<&Spectrum::Properties::frequency_point_size>(4)
.set<&Spectrum::Properties::center_frequency>(100.0)
.set<&Spectrum::Properties::sweep_frequency_range>(Range{96.0, 104.0})
.set<&Spectrum::Properties::max_hold_visible>(true)
.set<&Spectrum::Properties::min_hold_visible>(true)
.set<&Spectrum::Properties::max_marker_visible>(true)
.set<&Spectrum::Properties::use_min_marker>(true)
.set<&Spectrum::Properties::sweep_region_visible>(true)
.set<&Spectrum::Properties::visible_range_only>(false)
.set<&Spectrum::Properties::interpolation_mode>(Line_Interpolation_Mode::Cubic_Value)
.build(root, frequency_axis, power_axis);
return std::tuple{frequency_axis, power_axis, value};
});
ASSERT_TRUE(spectrum);
spectrum->set<&Spectrum::Properties::max_brush>(Brush{Color{1, 2, 3, 80}, Brush_Style::Solid});
spectrum->set<&Spectrum::Properties::current_brush>(Brush{Color{4, 5, 6, 90}, Brush_Style::Solid});
@@ -441,22 +491,25 @@ TEST(Renderive_Core2, RetainedHeatmapSweepAndTraceApisPreserveDataShapes) {
plot.set_viewport_size({420, 280});
plot.activate_view();
const auto root = plot.root_renderable();
const auto frequency = Frequency_Axis::Builder(root, Orientation::Horizontal)
.set_x(40).set_y(250).set_pixel_length(340)
.set_coord_range({0.0, 4.0}).build();
const auto power = Axis::Builder(root, Orientation::Vertical)
.set_x(40).set_y(20).set_pixel_length(230)
.set_coord_range({0.0, -120.0}).build();
const auto time = Time_Axis::Builder(root, Orientation::Vertical)
.set_x(40).set_y(20).set_pixel_length(230)
.set_visible_time_point_count(8).build();
auto waterfall = Waterfall::Builder{}
.set<&Waterfall::Properties::frequency_range>(Range{0.0, 4.0})
.set<&Waterfall::Properties::power_range>(Range{-120.0, 0.0})
.set<&Waterfall::Properties::frequency_bin_count>(4)
.set<&Waterfall::Properties::visible_range_only>(false)
.set<&Waterfall::Properties::interpolation_mode>(Image_Interpolation_Mode::Bicubic)
.build(root, frequency, time);
auto [frequency, power, time, waterfall] = build_initial(plot, [&](auto& attach) {
auto frequency_axis = Frequency_Axis::Builder(root, Orientation::Horizontal, &attach)
.set_x(40).set_y(250).set_pixel_length(340)
.set_coord_range({0.0, 4.0}).build();
auto power_axis = Axis::Builder(root, Orientation::Vertical, &attach)
.set_x(40).set_y(20).set_pixel_length(230)
.set_coord_range({0.0, -120.0}).build();
auto time_axis = Time_Axis::Builder(root, Orientation::Vertical, &attach)
.set_x(40).set_y(20).set_pixel_length(230)
.set_visible_time_point_count(8).build();
auto value = Waterfall::Builder{&attach}
.set<&Waterfall::Properties::frequency_range>(Range{0.0, 4.0})
.set<&Waterfall::Properties::power_range>(Range{-120.0, 0.0})
.set<&Waterfall::Properties::frequency_bin_count>(4)
.set<&Waterfall::Properties::visible_range_only>(false)
.set<&Waterfall::Properties::interpolation_mode>(Image_Interpolation_Mode::Bicubic)
.build(root, frequency_axis, time_axis);
return std::tuple{frequency_axis, power_axis, time_axis, value};
});
ASSERT_TRUE(waterfall);
EXPECT_EQ(waterfall->get<&Waterfall::Properties::frequency_range>(), (Range{0.0, 4.0}));
EXPECT_EQ(waterfall->get<&Waterfall::Properties::power_range>(), (Range{-120.0, 0.0}));
@@ -471,14 +524,16 @@ TEST(Renderive_Core2, RetainedHeatmapSweepAndTraceApisPreserveDataShapes) {
EXPECT_EQ(waterfall->row_count(), 2U);
EXPECT_EQ(waterfall->stored_point_count(), 8U);
EXPECT_EQ(waterfall->rendered_cell_count(), 8U);
auto afterglow = Afterglow::Builder{}
.set<&Afterglow::Properties::frequency_range>(Range{0.0, 4.0})
.set<&Afterglow::Properties::power_range>(Range{-120.0, 0.0})
.set<&Afterglow::Properties::frequency_point_size>(4)
.set<&Afterglow::Properties::power_point_size>(8)
.set<&Afterglow::Properties::interpolate>(false)
.set<&Afterglow::Properties::attenuation_rate>(0.35)
.build(root, frequency, power);
auto afterglow = build_initial(plot, [&](auto& attach) {
return Afterglow::Builder{&attach}
.set<&Afterglow::Properties::frequency_range>(Range{0.0, 4.0})
.set<&Afterglow::Properties::power_range>(Range{-120.0, 0.0})
.set<&Afterglow::Properties::frequency_point_size>(4)
.set<&Afterglow::Properties::power_point_size>(8)
.set<&Afterglow::Properties::interpolate>(false)
.set<&Afterglow::Properties::attenuation_rate>(0.35)
.build(root, frequency, power);
});
ASSERT_TRUE(afterglow);
EXPECT_EQ(afterglow->get<&Afterglow::Properties::frequency_range>(), (Range{0.0, 4.0}));
EXPECT_EQ(afterglow->get<&Afterglow::Properties::power_range>(), (Range{-120.0, 0.0}));
@@ -495,15 +550,17 @@ TEST(Renderive_Core2, RetainedHeatmapSweepAndTraceApisPreserveDataShapes) {
EXPECT_EQ(afterglow->rendered_cell_count(), 32U);
afterglow->set<&Afterglow::Properties::attenuation_rate>(2.0);
EXPECT_DOUBLE_EQ(afterglow->get<&Afterglow::Properties::attenuation_rate>(), 1.0);
auto sweep = Sweep_Spectrum::Builder{}
.set<&Sweep_Spectrum::Properties::frequency_range>(Range{0.0, 8.0})
.set<&Sweep_Spectrum::Properties::bins_per_block>(4)
.set<&Sweep_Spectrum::Properties::block_count>(2)
.set<&Sweep_Spectrum::Properties::pen>(Pen{Color{1, 120, 220, 255}, 2.0})
.set<&Sweep_Spectrum::Properties::current_frequency_pen>(Pen{Color{240, 80, 20, 255}, 3.0})
.set<&Sweep_Spectrum::Properties::visible_range_only>(false)
.set<&Sweep_Spectrum::Properties::interpolation_mode>(Line_Interpolation_Mode::Step_Right)
.build(root, frequency, power);
auto sweep = build_initial(plot, [&](auto& attach) {
return Sweep_Spectrum::Builder{&attach}
.set<&Sweep_Spectrum::Properties::frequency_range>(Range{0.0, 8.0})
.set<&Sweep_Spectrum::Properties::bins_per_block>(4)
.set<&Sweep_Spectrum::Properties::block_count>(2)
.set<&Sweep_Spectrum::Properties::pen>(Pen{Color{1, 120, 220, 255}, 2.0})
.set<&Sweep_Spectrum::Properties::current_frequency_pen>(Pen{Color{240, 80, 20, 255}, 3.0})
.set<&Sweep_Spectrum::Properties::visible_range_only>(false)
.set<&Sweep_Spectrum::Properties::interpolation_mode>(Line_Interpolation_Mode::Step_Right)
.build(root, frequency, power);
});
ASSERT_TRUE(sweep);
EXPECT_EQ(sweep->get<&Sweep_Spectrum::Properties::frequency_range>(), (Range{0.0, 8.0}));
EXPECT_EQ(sweep->get<&Sweep_Spectrum::Properties::bins_per_block>(), 4);
@@ -518,9 +575,11 @@ TEST(Renderive_Core2, RetainedHeatmapSweepAndTraceApisPreserveDataShapes) {
EXPECT_EQ(sweep->stored_block_count(), 2U);
EXPECT_EQ(sweep->stored_point_count(), 8U);
EXPECT_GT(sweep->rendered_point_count(), 0U);
auto trace = Frequency_Trace::Builder{}
.set<&Frequency_Trace::Properties::pen>(Pen{Color{120, 240, 80, 255}, 2.0})
.build(root, time, power);
auto trace = build_initial(plot, [&](auto& attach) {
return Frequency_Trace::Builder{&attach}
.set<&Frequency_Trace::Properties::pen>(Pen{Color{120, 240, 80, 255}, 2.0})
.build(root, time, power);
});
ASSERT_TRUE(trace);
trace->append_sample(Time_Of_Day{3000}, -80.0);
trace->append_sample(Time_Of_Day{4000}, -70.0);
@@ -534,18 +593,21 @@ TEST(Renderive_Core2, RetainedSelectionAndConstellationApisDriveInteractionAndLa
plot.set_viewport_size({360, 260});
plot.activate_view();
const auto root = plot.root_renderable();
const auto horizontal = Axis::Builder(root, Orientation::Horizontal)
.set_x(30).set_y(230).set_pixel_length(300)
.set_coord_range({-3.0, 3.0}).build();
const auto vertical = Axis::Builder(root, Orientation::Vertical)
.set_x(30).set_y(20).set_pixel_length(210)
.set_coord_range({3.0, -3.0}).build();
auto selection = Selection_Rectangle_Overlay::Builder{}
.set<&Selection_Rectangle_Overlay::Properties::label_font>(Font{14.0, 600, true})
.set<&Selection_Rectangle_Overlay::Properties::label_pen>(Pen{Color{20, 220, 180, 255}, 2.0})
.set<&Selection_Rectangle_Overlay::Properties::selection_brush>(Brush{Color{20, 80, 220, 60}, Brush_Style::Solid})
.set<&Selection_Rectangle_Overlay::Properties::selection_border_pen>(Pen{Color{240, 200, 60, 255}, 2.0, Line_Style::Dash})
.build(root, horizontal, vertical);
auto [horizontal, vertical, selection] = build_initial(plot, [&](auto& attach) {
auto horizontal_axis = Axis::Builder(root, Orientation::Horizontal, &attach)
.set_x(30).set_y(230).set_pixel_length(300)
.set_coord_range({-3.0, 3.0}).build();
auto vertical_axis = Axis::Builder(root, Orientation::Vertical, &attach)
.set_x(30).set_y(20).set_pixel_length(210)
.set_coord_range({3.0, -3.0}).build();
auto value = Selection_Rectangle_Overlay::Builder{&attach}
.set<&Selection_Rectangle_Overlay::Properties::label_font>(Font{14.0, 600, true})
.set<&Selection_Rectangle_Overlay::Properties::label_pen>(Pen{Color{20, 220, 180, 255}, 2.0})
.set<&Selection_Rectangle_Overlay::Properties::selection_brush>(Brush{Color{20, 80, 220, 60}, Brush_Style::Solid})
.set<&Selection_Rectangle_Overlay::Properties::selection_border_pen>(Pen{Color{240, 200, 60, 255}, 2.0, Line_Style::Dash})
.build(root, horizontal_axis, vertical_axis);
return std::tuple{horizontal_axis, vertical_axis, value};
});
ASSERT_TRUE(selection);
EXPECT_EQ(selection->get<&Selection_Rectangle_Overlay::Properties::label_font>(), (Font{14.0, 600, true}));
EXPECT_EQ(selection->get<&Selection_Rectangle_Overlay::Properties::label_pen>(), (Pen{Color{20, 220, 180, 255}, 2.0}));
@@ -571,7 +633,8 @@ TEST(Renderive_Core2, RetainedSelectionAndConstellationApisDriveInteractionAndLa
EXPECT_FALSE(selection->selected_regions().front().empty());
selection->clear_selected_regions();
EXPECT_TRUE(selection->selected_regions().empty());
auto constellation = Constellation_Diagram::Builder{}
auto constellation = build_initial(plot, [&](auto& attach) {
return Constellation_Diagram::Builder{&attach}
.set<&Constellation_Diagram::Properties::i_range>(Range{-2.0, 2.0})
.set<&Constellation_Diagram::Properties::q_range>(Range{-3.0, 3.0})
.set<&Constellation_Diagram::Properties::point_color>(Color{80, 220, 255, 255})
@@ -579,7 +642,8 @@ TEST(Renderive_Core2, RetainedSelectionAndConstellationApisDriveInteractionAndLa
.set<&Constellation_Diagram::Properties::point_lifetime_ms>(2000)
.set<&Constellation_Diagram::Properties::type>(Constellation_Diagram_Type::Psk16)
.set<&Constellation_Diagram::Properties::phase_offset_radians>(0.25)
.build(root, horizontal, vertical);
.build(root, horizontal, vertical);
});
ASSERT_TRUE(constellation);
EXPECT_EQ(constellation->get<&Constellation_Diagram::Properties::i_range>(), (Range{-2.0, 2.0}));
EXPECT_EQ(constellation->get<&Constellation_Diagram::Properties::q_range>(), (Range{-3.0, 3.0}));
@@ -598,11 +662,14 @@ TEST(Renderive_Core2, PlottablePropertiesPublishOnlyAtFrameBoundary) {
Scene2D plot;
plot.init();
const auto root = plot.root_renderable();
const auto frequency = Frequency_Axis::Builder(root, Orientation::Horizontal).build();
const auto power = Axis::Builder(root, Orientation::Vertical).build();
const auto spectrum = Spectrum::Builder{}.build(root, frequency, power);
const auto [frequency, power, spectrum] = build_initial(plot, [&](auto& attach) {
auto frequency_axis = Frequency_Axis::Builder(root, Orientation::Horizontal, &attach).build();
auto power_axis = Axis::Builder(root, Orientation::Vertical, &attach).build();
auto value = Spectrum::Builder{&attach}.build(root, frequency_axis, power_axis);
return std::tuple{frequency_axis, power_axis, value};
});
ASSERT_TRUE(spectrum);
const auto state = std::dynamic_pointer_cast<::State_Strategy_Base>(spectrum);
const auto state = renderive_dynamic_owner_cast<::State_Strategy_Base>(spectrum);
ASSERT_TRUE(state);
EXPECT_EQ(state->state_revision(), 0U);
spectrum->set<&Spectrum::Properties::frequency_point_size>(128);
@@ -616,9 +683,12 @@ TEST(Renderive_Core2, EveryStatefulRenderableOwnsItsStateObserver) {
Scene2D plot;
plot.init();
const auto root = plot.root_renderable();
const auto frequency = Frequency_Axis::Builder(root, Orientation::Horizontal).build();
const auto power = Axis::Builder(root, Orientation::Vertical).build();
const auto spectrum = Spectrum::Builder{}.build(root, frequency, power);
const auto [frequency, power, spectrum] = build_initial(plot, [&](auto& attach) {
auto frequency_axis = Frequency_Axis::Builder(root, Orientation::Horizontal, &attach).build();
auto power_axis = Axis::Builder(root, Orientation::Vertical, &attach).build();
auto value = Spectrum::Builder{&attach}.build(root, frequency_axis, power_axis);
return std::tuple{frequency_axis, power_axis, value};
});
ASSERT_TRUE(frequency);
ASSERT_TRUE(power);
ASSERT_TRUE(spectrum);
@@ -660,10 +730,15 @@ TEST(Renderive_Core2, PlottableDataUpdatesReachKernelRealTimeDataStrategy) {
Scene2D plot;
plot.init();
const auto root = plot.root_renderable();
const auto frequency = Frequency_Axis::Builder(root, Orientation::Horizontal).build();
const auto power = Axis::Builder(root, Orientation::Vertical).build();
const auto spectrum = Spectrum::Builder{}.build(root, frequency, power);
const auto afterglow = Afterglow::Builder{}.build(root, frequency, power);
const auto [frequency, power, spectrum] = build_initial(plot, [&](auto& attach) {
auto frequency_axis = Frequency_Axis::Builder(root, Orientation::Horizontal, &attach).build();
auto power_axis = Axis::Builder(root, Orientation::Vertical, &attach).build();
auto value = Spectrum::Builder{&attach}.build(root, frequency_axis, power_axis);
return std::tuple{frequency_axis, power_axis, value};
});
const auto afterglow = build_initial(plot, [&](auto& attach) {
return Afterglow::Builder{&attach}.build(root, frequency, power);
});
ASSERT_TRUE(spectrum);
ASSERT_TRUE(afterglow);
const auto before = plot.frame_observer_snapshot().observation_count;
@@ -726,32 +801,32 @@ TEST(Renderive_Core2, PartitionedPlotsBuildPropertyDrivenRenderGraphs) {
plot.init();
plot.set_viewport_size({320, 180});
const auto root = plot.root_renderable();
const auto frequency = Frequency_Axis::Builder(root, Orientation::Horizontal)
.set_pixel_length(320)
.set_coord_range({0.0, 10.0})
.build();
const auto power = Axis::Builder(root, Orientation::Vertical)
.set_pixel_length(180)
.set_coord_range({-120.0, 0.0})
.build();
const auto time = Time_Axis::Builder(root, Orientation::Vertical)
.set_pixel_length(180)
.build();
const auto spectrum = Spectrum::Builder{}
.set<&Spectrum::Properties::partition_mode>(
Render_Partition_Mode::Fixed)
.set<&Spectrum::Properties::partition_count>(4)
.build(root, frequency, power);
const auto waterfall = Waterfall::Builder{}
.set<&Waterfall::Properties::partition_mode>(
Render_Partition_Mode::Fixed)
.set<&Waterfall::Properties::partition_count>(4)
.build(root, frequency, time);
const auto afterglow = Afterglow::Builder{}
.set<&Afterglow::Properties::partition_mode>(
Render_Partition_Mode::Fixed)
.set<&Afterglow::Properties::partition_count>(4)
.build(root, frequency, power);
const auto [frequency, power, time, spectrum, waterfall, afterglow] = build_initial(plot, [&](auto& attach) {
auto frequency_axis = Frequency_Axis::Builder(root, Orientation::Horizontal, &attach)
.set_pixel_length(320)
.set_coord_range({0.0, 10.0})
.build();
auto power_axis = Axis::Builder(root, Orientation::Vertical, &attach)
.set_pixel_length(180)
.set_coord_range({-120.0, 0.0})
.build();
auto time_axis = Time_Axis::Builder(root, Orientation::Vertical, &attach)
.set_pixel_length(180)
.build();
auto spectrum_value = Spectrum::Builder{&attach}
.set<&Spectrum::Properties::partition_mode>(Render_Partition_Mode::Fixed)
.set<&Spectrum::Properties::partition_count>(4)
.build(root, frequency_axis, power_axis);
auto waterfall_value = Waterfall::Builder{&attach}
.set<&Waterfall::Properties::partition_mode>(Render_Partition_Mode::Fixed)
.set<&Waterfall::Properties::partition_count>(4)
.build(root, frequency_axis, time_axis);
auto afterglow_value = Afterglow::Builder{&attach}
.set<&Afterglow::Properties::partition_mode>(Render_Partition_Mode::Fixed)
.set<&Afterglow::Properties::partition_count>(4)
.build(root, frequency_axis, power_axis);
return std::tuple{frequency_axis, power_axis, time_axis, spectrum_value, waterfall_value, afterglow_value};
});
std::array<double, 1024> samples{};
spectrum->update_samples(samples);
waterfall->append_row(0, samples);
@@ -841,20 +916,21 @@ TEST(Renderive_Core2, DynamicWaterfallCaptureStressPreservesPlansSlotsAndExactSe
plot.init();
plot.set_viewport_size({640, 360});
const auto root = plot.root_renderable();
const auto frequency = Frequency_Axis::Builder(root, Orientation::Horizontal)
.set_pixel_length(640)
.set_coord_range({0.0, 32'767.0})
.build();
const auto time = Time_Axis::Builder(root, Orientation::Vertical)
.set_pixel_length(360)
.build();
const auto waterfall = Waterfall::Builder{}
.set<&Waterfall::Properties::partition_mode>(
Render_Partition_Mode::Fixed)
.set<&Waterfall::Properties::partition_count>(64)
.set<&Waterfall::Properties::frequency_range>(
Range{0.0, 32'767.0})
.build(root, frequency, time);
const auto [frequency, time, waterfall] = build_initial(plot, [&](auto& attach) {
auto frequency_axis = Frequency_Axis::Builder(root, Orientation::Horizontal, &attach)
.set_pixel_length(640)
.set_coord_range({0.0, 32'767.0})
.build();
auto time_axis = Time_Axis::Builder(root, Orientation::Vertical, &attach)
.set_pixel_length(360)
.build();
auto value = Waterfall::Builder{&attach}
.set<&Waterfall::Properties::partition_mode>(Render_Partition_Mode::Fixed)
.set<&Waterfall::Properties::partition_count>(64)
.set<&Waterfall::Properties::frequency_range>(Range{0.0, 32'767.0})
.build(root, frequency_axis, time_axis);
return std::tuple{frequency_axis, time_axis, value};
});
std::array<double, 32'768> samples{};
for (std::size_t index = 0; index < samples.size(); ++index)
samples[index] = static_cast<double>(index % 256);
@@ -899,9 +975,12 @@ TEST(Renderive_Core2, PaintOnlyStyleChangesPreservePrepareCache) {
plot.init();
plot.set_viewport_size({320, 180});
const auto root = plot.root_renderable();
const auto frequency = Frequency_Axis::Builder(root, Orientation::Horizontal).build();
const auto power = Axis::Builder(root, Orientation::Vertical).build();
const auto spectrum = Spectrum::Builder{}.build(root, frequency, power);
const auto [frequency, power, spectrum] = build_initial(plot, [&](auto& attach) {
auto frequency_axis = Frequency_Axis::Builder(root, Orientation::Horizontal, &attach).build();
auto power_axis = Axis::Builder(root, Orientation::Vertical, &attach).build();
auto value = Spectrum::Builder{&attach}.build(root, frequency_axis, power_axis);
return std::tuple{frequency_axis, power_axis, value};
});
const std::array samples{-80.0, -70.0, -60.0, -50.0};
spectrum->update_samples(samples);
ASSERT_TRUE(plot.render_frame(true));
@@ -946,9 +1025,12 @@ TEST(Renderive_Core2, PlottableAxesAreDataDependenciesAndPaintOverlays) {
Scene2D plot;
plot.init();
const auto root = plot.root_renderable();
const auto frequency = Frequency_Axis::Builder(root, Orientation::Horizontal).build();
const auto power = Axis::Builder(root, Orientation::Vertical).build();
const auto spectrum = Spectrum::Builder{}.build(root, frequency, power);
const auto [frequency, power, spectrum] = build_initial(plot, [&](auto& attach) {
auto frequency_axis = Frequency_Axis::Builder(root, Orientation::Horizontal, &attach).build();
auto power_axis = Axis::Builder(root, Orientation::Vertical, &attach).build();
auto value = Spectrum::Builder{&attach}.build(root, frequency_axis, power_axis);
return std::tuple{frequency_axis, power_axis, value};
});
ASSERT_TRUE(spectrum);
const auto topology = spectrum->scene().topology_snapshot();
const auto has_relationship = [](const auto& relationships, const auto* child, const auto* parent) {
@@ -980,9 +1062,12 @@ TEST(Renderive_Core2, InteractionOverlayPaintsAboveItsAxes) {
Scene2D plot;
plot.init();
const auto root = plot.root_renderable();
const auto horizontal = Axis::Builder(root, Orientation::Horizontal).build();
const auto vertical = Axis::Builder(root, Orientation::Vertical).build();
const auto selection = Selection_Rectangle_Overlay::Builder{}.build(root, horizontal, vertical);
const auto [horizontal, vertical, selection] = build_initial(plot, [&](auto& attach) {
auto horizontal_axis = Axis::Builder(root, Orientation::Horizontal, &attach).build();
auto vertical_axis = Axis::Builder(root, Orientation::Vertical, &attach).build();
auto value = Selection_Rectangle_Overlay::Builder{&attach}.build(root, horizontal_axis, vertical_axis);
return std::tuple{horizontal_axis, vertical_axis, value};
});
ASSERT_TRUE(selection);
const auto paint_order = selection->scene().paint_order_snapshot();
const auto position = [&paint_order](const auto* target) {