修复builder 调用方式

This commit is contained in:
2026-08-18 08:49:09 +08:00
parent 24315448f4
commit 97d2d1574b
19 changed files with 525 additions and 372 deletions
@@ -4,6 +4,8 @@
#include "renderive/base/memory/Memory_Resource.hpp"
#include <functional>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
template <Property_Product Product_Type, Property_Set Properties_Type, Property_Validator<Properties_Type> Validator_Type = No_Property_Validator<Properties_Type>>
class Property_Builder {
@@ -12,43 +14,97 @@ public:
using Properties = Properties_Type;
using Validator = Validator_Type;
using Self = Property_Builder;
Property_Builder() requires std::default_initializable<Properties> && std::default_initializable<Validator> : properties{}, validator{} {}
explicit Property_Builder(Properties properties) requires std::default_initializable<Validator> : properties(std::move(properties)), validator{} {}
Property_Builder(Properties properties, Validator validator) : properties(std::move(properties)), validator(std::move(validator)) {}
Property_Builder() requires std::default_initializable<Validator> {
bind_constructor();
}
template <class... Args> requires (sizeof...(Args) > 0) && std::default_initializable<Validator>
explicit Property_Builder(Args&&... args) {
bind_constructor(std::forward<Args>(args)...);
}
template <auto Member, Property_Member_Assignable<Properties, Member> Value>
Self& set(Value&& value) {
properties.*Member = std::forward < Value > (value);
properties_.*Member = std::forward<Value>(value);
return *this;
}
template <Property_Configurator<Properties> Configure>
Self& configure(Configure&& configure) {
std::invoke(std::forward < Configure > (configure), properties);
std::invoke(std::forward<Configure>(configure), properties_);
return *this;
}
const Properties& properties_value() const noexcept {
return properties;
return properties_;
}
template <class... Args> requires Property_Constructible<Product, Properties, Args&&...>
std::shared_ptr<Product> build(Args&&... args) const {
validator(properties);
return std::make_shared<Product>(properties, std::forward<Args>(args)...);
[[nodiscard]] std::shared_ptr<Product> build() {
validator_(properties_);
auto construct = std::exchange(shared_constructor_, {});
clear_constructors();
return construct(properties_);
}
template <class... Args> requires Property_Constructible<Product, Properties, Args&&...>
std::shared_ptr<Product> build_with_resource(std::pmr::memory_resource& memory_resource, Args&&... args) const {
validator(properties);
return std::allocate_shared<Product>(std::pmr::polymorphic_allocator<Product>(&memory_resource), properties, std::forward<Args>(args)...);
[[nodiscard]] std::shared_ptr<Product> build_with_resource(std::pmr::memory_resource& memory_resource) {
validator_(properties_);
auto construct = std::exchange(resource_shared_constructor_, {});
clear_constructors();
return construct(memory_resource, properties_);
}
template <class... Args> requires Property_Constructible<Product, Properties, Args&&...>
std::unique_ptr<Product> build_unique(Args&&... args) const {
validator(properties);
return std::make_unique<Product>(properties, std::forward<Args>(args)...);
[[nodiscard]] std::unique_ptr<Product> build_unique() {
validator_(properties_);
auto construct = std::exchange(unique_constructor_, {});
clear_constructors();
return construct(properties_);
}
template <class... Args> requires Property_Constructible<Product, Properties, Args&&...>
Memory_Resource_Unique_Ptr<Product> build_unique_with_resource(std::pmr::memory_resource& memory_resource, Args&&... args) const {
validator(properties);
return make_memory_resource_unique<Product>(memory_resource, properties, std::forward<Args>(args)...);
[[nodiscard]] Memory_Resource_Unique_Ptr<Product> build_unique_with_resource(std::pmr::memory_resource& memory_resource) {
validator_(properties_);
auto construct = std::exchange(resource_unique_constructor_, {});
clear_constructors();
return construct(memory_resource, properties_);
}
private:
Properties properties;
[[no_unique_address]] Validator validator;
using Shared_Constructor = std::function<std::shared_ptr<Product>(const Properties&)>;
using Resource_Shared_Constructor = std::function<std::shared_ptr<Product>(std::pmr::memory_resource&, const Properties&)>;
using Unique_Constructor = std::function<std::unique_ptr<Product>(const Properties&)>;
using Resource_Unique_Constructor = std::function<Memory_Resource_Unique_Ptr<Product>(std::pmr::memory_resource&, const Properties&)>;
template <class... Args>
void bind_constructor(Args&&... args) {
auto stored = std::make_shared<std::tuple<std::decay_t<Args>...>>(std::forward<Args>(args)...);
shared_constructor_ = [stored](const Properties& properties) mutable {
return std::apply(
[&](auto&... values) {
return std::make_shared<Product>(properties, std::move(values)...);
},
*stored);
};
resource_shared_constructor_ = [stored](std::pmr::memory_resource& memory_resource, const Properties& properties) mutable {
return std::apply(
[&](auto&... values) {
return std::allocate_shared<Product>(std::pmr::polymorphic_allocator<Product>(&memory_resource), properties, std::move(values)...);
},
*stored);
};
unique_constructor_ = [stored](const Properties& properties) mutable {
return std::apply(
[&](auto&... values) {
return std::make_unique<Product>(properties, std::move(values)...);
},
*stored);
};
resource_unique_constructor_ = [stored](std::pmr::memory_resource& memory_resource, const Properties& properties) mutable {
return std::apply(
[&](auto&... values) {
return make_memory_resource_unique<Product>(memory_resource, properties, std::move(values)...);
},
*stored);
};
}
void clear_constructors() {
shared_constructor_ = {};
resource_shared_constructor_ = {};
unique_constructor_ = {};
resource_unique_constructor_ = {};
}
Properties properties_{};
[[no_unique_address]] Validator validator_{};
Shared_Constructor shared_constructor_;
Resource_Shared_Constructor resource_shared_constructor_;
Unique_Constructor unique_constructor_;
Resource_Unique_Constructor resource_unique_constructor_;
};
@@ -34,6 +34,9 @@ template <class Layer, class Parent_Next, class Business_Builder>
struct Builder
: Parent_Next, Business_Builder {
using Business_Builder::Business_Builder;
using Business_Builder::set;
using Business_Builder::configure;
using Business_Builder::build;
};
template <class Layer>
struct State_Root
@@ -10,6 +10,7 @@ class Frame_Strategy_Real_Time_Data_Observer;
class Real_Time_Data_Base;
class Real_Time_Data_Binding;
class Scene_Base;
class Scene_2D_Base;
namespace renderive::inheritance {
struct Observation_Source;
}
@@ -51,5 +52,6 @@ private:
friend class Real_Time_Data_Base;
friend class Real_Time_Data_Binding;
friend class Scene_Base;
friend class Scene_2D_Base;
std::unique_ptr<Impl> d_ptr;
};
+26 -19
View File
@@ -2,44 +2,51 @@
#include "renderive/base/property/Concepts.hpp"
#include "renderive/base/property/Validators.hpp"
#include "renderive/scene/Inheritance.hpp"
#include <concepts>
#include <functional>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
namespace renderive::scene {
template <Property_Product Product_Type, Property_Set Properties_Type,
Property_Validator<Properties_Type> Validator_Type =
No_Property_Validator<Properties_Type>>
template <Property_Product Product_Type, Property_Set Properties_Type, Property_Validator<Properties_Type> Validator_Type = No_Property_Validator<Properties_Type>>
struct Scene_Builder {
using Product = Product_Type;
using Properties = Properties_Type;
using Validator = Validator_Type;
using Self = Scene_Builder;
Scene_Builder() requires std::default_initializable<Properties> &&
std::default_initializable<Validator>
= default;
explicit Scene_Builder(Properties properties) requires std::default_initializable<Validator> : properties_(std::move(properties)) {}
Scene_Builder(Properties properties, Validator validator) : properties_(std::move(properties)),
validator_(std::move(validator)) {}
template <auto Member,
Property_Member_Assignable<Properties, Member> Value>
Scene_Builder() requires std::default_initializable<Validator> : constructor_(make_constructor()) {}
template <class... Args> requires (sizeof...(Args) > 0) && std::default_initializable<Validator>
explicit Scene_Builder(Args&&... args) : constructor_(make_constructor(std::forward<Args>(args)...)) {}
template <auto Member, Property_Member_Assignable<Properties, Member> Value>
Self& set(Value&& value) {
properties_.*Member = std::forward < Value > (value);
properties_.*Member = std::forward<Value>(value);
return *this;
}
template <Property_Configurator<Properties> Configure>
Self& configure(Configure&& configure) {
std::invoke(std::forward < Configure > (configure), properties_);
std::invoke(std::forward<Configure>(configure), properties_);
return *this;
}
template <class... Args>
[[nodiscard]] std::unique_ptr<Product> build(Args&&... args) const {
[[nodiscard]] std::unique_ptr<Product> build() {
validator_(properties_);
return scene_inheritance::Builder_Access::make<Product>(
properties_, std::forward<Args>(args)...);
auto construct = std::exchange(constructor_, {});
return construct(properties_);
}
private:
using Constructor = std::function<std::unique_ptr<Product>(const Properties&)>;
template <class... Args>
static Constructor make_constructor(Args&&... args) {
auto stored = std::make_shared<std::tuple<std::decay_t<Args>...>>(std::forward<Args>(args)...);
return [stored = std::move(stored)](const Properties& properties) mutable {
return std::apply(
[&](auto&... values) {
return scene_inheritance::Builder_Access::make<Product>(properties, std::move(values)...);
},
*stored);
};
}
Properties properties_{};
[[no_unique_address]] Validator validator_{};
Constructor constructor_;
};
} // namespace renderive::scene
}
@@ -519,6 +519,10 @@ Scene_2D_Base::Impl::Impl(std::pmr::memory_resource& memory_resource) : Scene_Ba
Scene_2D_Base::Attach_Builder Scene_2D_Base::attach_builder() {
return Attach_Builder(*this);
}
Scene_2D_Base::Attach_Builder Scene_2D_Base::attach_builder(
const Renderable& anchor) {
return Attach_Builder(dynamic_cast<Scene_2D_Base&>(anchor->d_func().scene()));
}
Scene_Base::Edit_Operation Scene_2D_Base::edit_renderables(Renderable_Edit edit) {
if (!edit) {
std::promise<Scene_Edit_Error> promise;
@@ -261,6 +261,7 @@ public:
Scene_2D_Base();
explicit Scene_2D_Base(std::pmr::memory_resource& memory_resource);
Attach_Builder attach_builder();
[[nodiscard]] static Attach_Builder attach_builder(const Renderable& anchor);
[[nodiscard]] Edit_Operation edit_renderables(Renderable_Edit edit);
struct Observer
: Scene_Base::next_Observer<Observer> {};
@@ -2,14 +2,19 @@
#include <memory>
#include "Property_Test_Types.hpp"
TEST(property_builder_test, builds_independent_product) {
auto product = Property_Direct_Builder{Property_Direct_Properties{.value = 42}}.build(7);
auto product = Property_Direct_Builder{7}
.set<&Property_Direct_Properties::value>(42)
.build();
EXPECT_EQ(product->value, 42);
EXPECT_EQ(product->id, 7);
}
TEST(property_builder_test, configures_properties) {
auto product = Property_Direct_Builder{}.set<&Property_Direct_Properties::value>(10).configure([](Property_Direct_Properties& properties) {
properties.value += 5;
}).build(1);
auto product = Property_Direct_Builder{1}
.set<&Property_Direct_Properties::value>(10)
.configure([](Property_Direct_Properties& properties) {
properties.value += 5;
})
.build();
EXPECT_EQ(product->value, 15);
}
TEST(property_builder_test, supports_external_validator) {
@@ -17,7 +22,9 @@ TEST(property_builder_test, supports_external_validator) {
EXPECT_THROW(builder.build(), std::invalid_argument);
}
TEST(property_builder_test, builds_unique_product) {
auto product = Property_Direct_Builder{Property_Direct_Properties{.value = 12}}.build_unique(3);
auto product = Property_Direct_Builder{3}
.set<&Property_Direct_Properties::value>(12)
.build_unique();
static_assert(std::same_as<decltype(product), std::unique_ptr<Property_Direct_Product>>);
EXPECT_EQ(product->value, 12);
EXPECT_EQ(product->id, 3);
@@ -91,9 +91,12 @@ TEST(scene_memory_resource_test, supports_independent_resource_aware_components)
history.update(1);
history.update(2);
EXPECT_EQ(history.size(), 2);
Property_Builder<Scene_Memory_Resource_Test_Property_Product, Scene_Memory_Resource_Test_Properties> builder({3});
auto product = builder.build_with_resource(memory_resource, 4);
auto unique_product = builder.build_unique_with_resource(memory_resource, 5);
auto product = Property_Builder<Scene_Memory_Resource_Test_Property_Product, Scene_Memory_Resource_Test_Properties>{4}
.set<&Scene_Memory_Resource_Test_Properties::value>(3)
.build_with_resource(memory_resource);
auto unique_product = Property_Builder<Scene_Memory_Resource_Test_Property_Product, Scene_Memory_Resource_Test_Properties>{5}
.set<&Scene_Memory_Resource_Test_Properties::value>(3)
.build_unique_with_resource(memory_resource);
EXPECT_EQ(product->value, 7);
EXPECT_EQ(unique_product->value, 8);
EXPECT_GT(memory_resource.allocation_count(), 0);
+24 -21
View File
@@ -16,27 +16,30 @@ TEST(Renderive_Qt, WidgetLifecycleDrivesAndStopsKernelScene) {
renderive_Owner<Frequency_Axis> x_axis;
renderive_Owner<Axis> y_axis;
renderive_Owner<Spectrum> spectrum;
{
auto attach = plot.scene()->attach_builder();
x_axis = Frequency_Axis::Builder{&attach}
.set<&Frequency_Axis::Properties::orientation>(Orientation::Horizontal)
.set<&Frequency_Axis::Properties::x>(20)
.set<&Frequency_Axis::Properties::y>(100)
.set<&Frequency_Axis::Properties::pixel_length>(200)
.set<&Frequency_Axis::Properties::coordinates>(Range{0.0, 10.0})
.build(root);
y_axis = Axis::Builder{&attach}
.set<&Axis::Properties::orientation>(Orientation::Vertical)
.set<&Axis::Properties::x>(20)
.set<&Axis::Properties::y>(10)
.set<&Axis::Properties::pixel_length>(90)
.set<&Axis::Properties::coordinates>(Range{1.0, 0.0})
.build(root);
spectrum = Spectrum::Builder{&attach}
.set<&Spectrum::Properties::frequency_range>(Range{0.0, 10.0})
.set<&Spectrum::Properties::frequency_point_size>(8)
.build(root, x_axis, y_axis);
}
x_axis = Frequency_Axis::Builder{}
.set<&Frequency_Axis::Properties::orientation>(Orientation::Horizontal)
.set<&Frequency_Axis::Properties::x>(20)
.set<&Frequency_Axis::Properties::y>(100)
.set<&Frequency_Axis::Properties::pixel_length>(200)
.set<&Frequency_Axis::Properties::coordinates>(Range{0.0, 10.0})
.set_paint_rely({root})
.set_render_rely({root})
.build();
y_axis = Axis::Builder{}
.set<&Axis::Properties::orientation>(Orientation::Vertical)
.set<&Axis::Properties::x>(20)
.set<&Axis::Properties::y>(10)
.set<&Axis::Properties::pixel_length>(90)
.set<&Axis::Properties::coordinates>(Range{1.0, 0.0})
.set_paint_rely({root})
.set_render_rely({root})
.build();
spectrum = Spectrum::Builder(x_axis, y_axis)
.set<&Spectrum::Properties::frequency_range>(Range{0.0, 10.0})
.set<&Spectrum::Properties::frequency_point_size>(8)
.set_paint_rely({root})
.set_render_rely({root})
.build();
spectrum->update_samples(std::vector<double>{0.1, 0.3, 0.8, 0.5, 0.9, 0.4, 0.2, 0.7});
plot.show();
QTimer::singleShot(150, application, &QCoreApplication::quit);
@@ -2,32 +2,19 @@
#include "Renderable.h"
#include <renderive/base/property/Concepts.hpp>
#include <renderive/base/property/Validators.hpp>
#include <renderive/inheritance/Initialize.hpp>
#include <renderive/scene/base/Scene_Base.hpp>
#include <algorithm>
#include <concepts>
#include <functional>
#include <initializer_list>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
namespace renderive {
class Abs_Axis;
namespace detail {
struct Paint_Overlay;
template <class Renderable_Type, class Axis_Type> requires std::derived_from<Renderable_Type, Renderable> && std::derived_from<Axis_Type, Abs_Axis>
void attach_renderable_dependency(::Scene_2D_Base::Attach_Builder& builder, const renderive_Owner<Renderable_Type>& renderable, const renderive_Owner<Axis_Type>& axis) {
if (!axis) return;
builder.add_dependency_parent(renderable, axis);
if constexpr (std::derived_from<Renderable_Type, Paint_Overlay>) builder.add_display_parent(renderable, axis);
else builder.add_display_parent(axis, renderable);
}
template <class Renderable_Type, class Value>
void attach_renderable_dependency(::Scene_2D_Base::Attach_Builder&, const renderive_Owner<Renderable_Type>&, const Value&) {}
template <class Control, class... Args> requires std::derived_from<Control, Renderable>
void attach_renderable(::Scene_2D_Base::Attach_Builder& builder, const renderive_Owner<Control>& renderable, const renderive_Owner<Renderable>& parent, const Args&... args) {
builder.attach(renderable);
builder.add_display_parent(renderable, parent);
builder.add_dependency_parent(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>>
struct Renderable_Builder {
@@ -35,48 +22,86 @@ struct Renderable_Builder {
using Properties = Properties_Type;
using Validator = Validator_Type;
using Self = Renderable_Builder;
Renderable_Builder() requires std::default_initializable<Properties> && std::default_initializable<Validator> = default;
explicit Renderable_Builder(::Scene_2D_Base::Attach_Builder* attach_builder) requires std::default_initializable<Properties> && std::default_initializable<Validator> : attach_builder_(attach_builder) {}
explicit Renderable_Builder(Properties properties, ::Scene_2D_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_2D_Base::Attach_Builder* attach_builder = nullptr) : properties(std::move(properties)), validator(std::move(validator)), attach_builder_(attach_builder) {}
Renderable_Builder() requires std::default_initializable<Validator> : constructor_(make_constructor()) {}
template <class... Args> requires (sizeof...(Args) > 0) && std::default_initializable<Validator>
explicit Renderable_Builder(Args&&... args) {
(collect_constructor_rely(args), ...);
constructor_ = make_constructor(std::forward<Args>(args)...);
}
template <auto Member, Property_Member_Assignable<Properties, Member> Value>
Self& set(Value&& value) {
properties.*Member = std::forward<Value>(value);
properties_.*Member = std::forward<Value>(value);
return *this;
}
template <Property_Configurator<Properties> Configure>
Self& configure(Configure&& configure) {
std::invoke(std::forward<Configure>(configure), properties);
std::invoke(std::forward<Configure>(configure), properties_);
return *this;
}
Self& set_paint_rely(std::initializer_list<renderive_Owner<Renderable>> rely) {
paint_rely_.assign(rely);
return *this;
}
Self& set_render_rely(std::initializer_list<renderive_Owner<Renderable>> rely) {
render_rely_.assign(rely);
return *this;
}
const Properties& properties_value() const noexcept {
return properties;
return properties_;
}
template <class... Args> requires std::derived_from<Product, Renderable>
renderive_Owner<Product> build(const renderive_Owner<Renderable>& parent, Args&&... args) const {
if (!parent || !(valid_argument(args) && ...)) return {};
validator(properties);
return inheritance::initialize(
renderive_Owner<Product>(
renderable_inheritance::Builder_Access::make<Product>(
properties, args...)),
[&](renderive_Owner<Product>& result) {
if (attach_builder_)
detail::attach_renderable(*attach_builder_, result,
parent, args...);
});
[[nodiscard]] renderive_Owner<Product> build() {
if (!constructor_ || !valid_rely(paint_rely_) || !valid_rely(render_rely_) || !valid_rely(constructor_rely_)) return {};
const auto* anchor = scene_anchor();
if (!anchor) return {};
validator_(properties_);
auto construct = std::exchange(constructor_, {});
auto result = construct(properties_);
if (!result) return {};
auto attach = Scene_2D_Base::attach_builder(*anchor);
attach.attach(result);
for (const auto& parent : paint_rely_) attach.add_display_parent(result, parent);
for (const auto& parent : render_rely_) attach.add_dependency_parent(result, parent);
for (const auto& parent : constructor_rely_) {
attach.add_dependency_parent(result, parent);
if constexpr (std::derived_from<Product, detail::Paint_Overlay>) attach.add_display_parent(result, parent);
else attach.add_display_parent(parent, result);
}
return result;
}
private:
template <class T>
static bool valid_argument(const renderive_Owner<T>& value) {
return static_cast<bool>(value);
using Constructor = std::function<renderive_Owner<Product>(const Properties&)>;
template <class... Args>
static Constructor make_constructor(Args&&... args) {
auto stored = std::make_shared<std::tuple<std::decay_t<Args>...>>(std::forward<Args>(args)...);
return [stored = std::move(stored)](const Properties& properties) mutable {
return std::apply(
[&](auto&... values) {
return renderive_Owner<Product>(renderable_inheritance::Builder_Access::make<Product>(properties, std::move(values)...));
},
*stored);
};
}
template <class T> requires std::derived_from<T, Abs_Axis>
void collect_constructor_rely(const renderive_Owner<T>& value) {
constructor_rely_.emplace_back(value);
}
template <class T>
static bool valid_argument(const T&) {
void collect_constructor_rely(const T&) {}
static bool valid_rely(const std::vector<renderive_Owner<Renderable>>& rely) {
for (const auto& value : rely) if (!value) return false;
return true;
}
Properties properties{};
[[no_unique_address]] Validator validator{};
::Scene_2D_Base::Attach_Builder* attach_builder_{};
[[nodiscard]] const renderive_Owner<Renderable>* scene_anchor() const noexcept {
if (!paint_rely_.empty()) return &paint_rely_.front();
if (!render_rely_.empty()) return &render_rely_.front();
if (!constructor_rely_.empty()) return &constructor_rely_.front();
return nullptr;
}
Properties properties_{};
[[no_unique_address]] Validator validator_{};
Constructor constructor_;
std::vector<renderive_Owner<Renderable>> paint_rely_;
std::vector<renderive_Owner<Renderable>> render_rely_;
std::vector<renderive_Owner<Renderable>> constructor_rely_;
};
}
@@ -9,7 +9,11 @@ std::unique_ptr<Render_Scene_2D> make_scene(
Render_Scene_2D::State state = {}) {
auto strategy =
std::make_shared<Low_Latency_Render_Scene_2D_Strategy>();
return Render_Scene_2D::Builder{state}.build(std::move(strategy));
return Render_Scene_2D::Builder(std::move(strategy))
.configure([&](Render_Scene_2D::Properties& properties) {
properties = state;
})
.build();
}
TEST(RenderScene2DFramePipeline, BuilderOwnsConstructionAndState) {
@@ -8,8 +8,12 @@ namespace {
TEST(RenderScene2DIntegration, BuilderStatePublishesAtFrameBoundary) {
Render_Scene_2D::State initial;
initial.viewport = {64, 32};
auto scene = Render_Scene_2D::Builder{initial}.build(
std::make_shared<Manual_Render_Scene_2D_Strategy>());
auto scene = Render_Scene_2D::Builder(
std::make_shared<Manual_Render_Scene_2D_Strategy>())
.configure([&](Render_Scene_2D::Properties& properties) {
properties = initial;
})
.build();
scene->activate_view();
ASSERT_EQ(scene->render_frame().value(), Plot_Render_Status::rendered);
ASSERT_EQ(scene->wait_for_render(), Scene_Render_Result::none);
@@ -19,8 +23,12 @@ TEST(RenderScene2DIntegration, BuilderStatePublishesAtFrameBoundary) {
TEST(RenderScene2DIntegration, FrameImageMatchesConfiguredViewport) {
Render_Scene_2D::State state;
state.viewport = {48, 27};
auto scene = Render_Scene_2D::Builder{state}.build(
std::make_shared<Low_Latency_Render_Scene_2D_Strategy>());
auto scene = Render_Scene_2D::Builder(
std::make_shared<Low_Latency_Render_Scene_2D_Strategy>())
.configure([&](Render_Scene_2D::Properties& properties) {
properties = state;
})
.build();
scene->activate_view();
ASSERT_EQ(scene->render_frame().value(), Plot_Render_Status::rendered);
ASSERT_EQ(scene->wait_for_render(), Scene_Render_Result::none);
@@ -34,8 +42,12 @@ TEST(RenderScene2DIntegration, FrameImageMatchesConfiguredViewport) {
TEST(RenderScene2DIntegration, FrameStrategyIsReplacedInsideRenderableEdit) {
Render_Scene_2D::State state;
state.viewport = {48, 27};
auto scene = Render_Scene_2D::Builder{state}.build(
std::make_shared<Low_Latency_Render_Scene_2D_Strategy>());
auto scene = Render_Scene_2D::Builder(
std::make_shared<Low_Latency_Render_Scene_2D_Strategy>())
.configure([&](Render_Scene_2D::Properties& properties) {
properties = state;
})
.build();
const auto operation = scene->edit_renderables(
[](Scene_2D_Base::Renderable_Editor& editor) {
editor.replace_frame_control_strategy(
@@ -2,26 +2,23 @@
#include <renderive/base/property/Concepts.hpp>
#include <renderive/base/property/Validators.hpp>
#include <renderive/inheritance/Initialize.hpp>
#include <concepts>
#include <renderive/renderable/Inheritance.hpp>
#include <functional>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
namespace renderive::render_3d {
template <Property_Product Product_Type, Property_Set Properties_Type,
Property_Validator<Properties_Type> Validator_Type =
No_Property_Validator<Properties_Type>>
template <Property_Product Product_Type, Property_Set Properties_Type, Property_Validator<Properties_Type> Validator_Type = No_Property_Validator<Properties_Type>>
struct Renderable_Builder {
using Product = Product_Type;
using Properties = Properties_Type;
using Validator = Validator_Type;
using Self = Renderable_Builder;
Renderable_Builder() requires std::default_initializable<Properties> &&
std::default_initializable<Validator>
= default;
explicit Renderable_Builder(Properties properties) requires std::default_initializable<Validator> : properties_(std::move(properties)) {}
Renderable_Builder(Properties properties, Validator validator) : properties_(std::move(properties)), validator_(std::move(validator)) {}
template <auto Member,
Property_Member_Assignable<Properties, Member> Value>
Renderable_Builder() requires std::default_initializable<Validator> : constructor_(make_constructor()) {}
template <class... Args> requires (sizeof...(Args) > 0) && std::default_initializable<Validator>
explicit Renderable_Builder(Args&&... args) : constructor_(make_constructor(std::forward<Args>(args)...)) {}
template <auto Member, Property_Member_Assignable<Properties, Member> Value>
Self& set(Value&& value) {
properties_.*Member = std::forward<Value>(value);
return *this;
@@ -31,17 +28,27 @@ struct Renderable_Builder {
std::invoke(std::forward<Configure>(configure), properties_);
return *this;
}
template <class... Args>
std::shared_ptr<Product> build(Args&&... args) const {
[[nodiscard]] std::shared_ptr<Product> build() {
validator_(properties_);
auto result =
::renderive::renderable_inheritance::Builder_Access::make<Product>(
properties_, std::forward<Args>(args)...);
return ::renderive::inheritance::initialize(
std::move(result), [](auto&) {});
auto construct = std::exchange(constructor_, {});
auto result = construct(properties_);
return ::renderive::inheritance::initialize(std::move(result), [](auto&) {});
}
private:
using Constructor = std::function<std::shared_ptr<Product>(const Properties&)>;
template <class... Args>
static Constructor make_constructor(Args&&... args) {
auto stored = std::make_shared<std::tuple<std::decay_t<Args>...>>(std::forward<Args>(args)...);
return [stored = std::move(stored)](const Properties& properties) mutable {
return std::apply(
[&](auto&... values) {
return ::renderive::renderable_inheritance::Builder_Access::make<Product>(properties, std::move(values)...);
},
*stored);
};
}
Properties properties_{};
[[no_unique_address]] Validator validator_{};
Constructor constructor_;
};
} // namespace renderive::render_3d
}
@@ -7,7 +7,7 @@ namespace {
template <class Visual, class Item>
void exercise(Item item) {
auto visual = typename Visual::Builder{}.build(std::vector<Item>{item});
auto visual = typename Visual::Builder{std::vector<Item>{item}}.build();
ASSERT_TRUE(visual);
EXPECT_EQ(visual->item_count(), 1U);
ASSERT_EQ(visual->edit_items(
@@ -34,11 +34,11 @@ TEST(DatovizVisualFamilies, EveryPublicFamilyUsesBuilderOwnedStorage) {
}
TEST(DatovizVisualFamilies, BuilderValidationUsesStateProperties) {
auto volume = Volume_Visual::Builder{}
auto volume = Volume_Visual::Builder(std::vector<Voxel>(64))
.set<&Volume_State::field_width>(4U)
.set<&Volume_State::field_height>(4U)
.set<&Volume_State::field_depth>(4U)
.build(std::vector<Voxel>(64));
.build();
ASSERT_TRUE(volume);
EXPECT_EQ(volume->get<&Volume_State::field_depth>(), 4U);
EXPECT_THROW((Volume_Visual::Builder{}
@@ -9,13 +9,18 @@ namespace {
std::unique_ptr<Render_Scene_3D> make_scene(
Render_Scene_3D::State state,
const std::shared_ptr<Point_Visual>& visual) {
return Render_Scene_3D::Builder{state}.build(
visual, std::make_shared<Low_Latency_Render_Scene_3D_Strategy>());
return Render_Scene_3D::Builder(
visual, std::make_shared<Low_Latency_Render_Scene_3D_Strategy>())
.configure([&](Render_Scene_3D::Properties& properties) {
properties = state;
})
.build();
}
TEST(PointRenderIntegration, BuilderConstructsSceneFromStateAndStrategy) {
auto visual = Point_Visual::Builder{}.build(std::vector<Point>{
{{0.0F, 0.0F, 0.0F}, {255, 0, 0, 255}, 8.0F}});
auto visual = Point_Visual::Builder(std::vector<Point>{
{{0.0F, 0.0F, 0.0F}, {255, 0, 0, 255}, 8.0F}})
.build();
Render_Scene_3D::State state;
state.viewport = {320, 200};
try {
@@ -31,7 +36,7 @@ TEST(PointRenderIntegration, BuilderConstructsSceneFromStateAndStrategy) {
TEST(PointRenderIntegration, ObserverPublishesSceneAndVisualState) {
std::vector<Point> points(3);
for (auto& point : points) point.diameter_px = 1.0F;
auto visual = Point_Visual::Builder{}.build(std::move(points));
auto visual = Point_Visual::Builder(std::move(points)).build();
Render_Scene_3D::State state;
state.viewport = {160, 90};
try {
@@ -51,8 +56,9 @@ TEST(PointRenderIntegration, ObserverPublishesSceneAndVisualState) {
}
TEST(PointRenderIntegration, FrameStrategyIsReplacedInsideRenderableEdit) {
auto visual = Point_Visual::Builder{}.build(std::vector<Point>{
{{0.0F, 0.0F, 0.0F}, {255, 0, 0, 255}, 8.0F}});
auto visual = Point_Visual::Builder(std::vector<Point>{
{{0.0F, 0.0F, 0.0F}, {255, 0, 0, 255}, 8.0F}})
.build();
Render_Scene_3D::State state;
state.viewport = {64, 48};
try {
+6 -2
View File
@@ -9,8 +9,12 @@ namespace {
TEST(PointState, BuilderAndStateMutationShareOneAuthoritativeState) {
Point_State initial;
initial.style.stroke_width_px = 2.0F;
auto visual = Point_Visual::Builder{initial}.build(
std::vector<Point>{{{1.0F, 2.0F, 3.0F}, {1, 2, 3, 255}, 7.0F}});
auto visual = Point_Visual::Builder(
std::vector<Point>{{{1.0F, 2.0F, 3.0F}, {1, 2, 3, 255}, 7.0F}})
.configure([&](Point_Visual::Properties& properties) {
properties = initial;
})
.build();
ASSERT_TRUE(visual);
EXPECT_EQ(visual->get<&Point_State::style>(), initial.style);
auto changed = initial.style;
+4 -4
View File
@@ -12,10 +12,10 @@ struct Web_Plot_Session::Impl {
Impl() { rebuild(); }
void rebuild() {
Render_Scene_2D::State state;
state.viewport = viewport;
scene = Render_Scene_2D::Builder{state}.build(
std::make_shared<Low_Latency_Render_Scene_2D_Strategy>());
scene = Render_Scene_2D::Builder(
std::make_shared<Low_Latency_Render_Scene_2D_Strategy>())
.set<&Render_Scene_2D::Properties::viewport>(viewport)
.build();
scene->activate_view();
}
std::optional<Web_Response> handle(const Web_Event& event) {
+217 -207
View File
@@ -1,4 +1,5 @@
#include "Gallery_Scene2D.h"
#include "../Gallery_Capture_Json.h"
#include "../common/Observer_Json.h"
#include "../Pixel_Frame.h"
@@ -13,6 +14,7 @@
#include "render_2D/plottable/Sweep_Spectrum.h"
#include "render_2D/plottable/Waterfall.h"
#include "render_2D/scene/Render_Scene_2D.h"
#include <algorithm>
#include <chrono>
#include <cmath>
@@ -22,7 +24,7 @@
#include <span>
#include <string>
#include <vector>
#include <asio.hpp>
namespace renderive::web {
template <>
struct Observer_Pfr_Adapter<Render_Scene_2D::State> {
@@ -40,12 +42,16 @@ constexpr std::size_t sweep_block_count = 8;
std::shared_ptr<Frame_Control_Strategy_Base> make_strategy(
Gallery_Frame_Mode mode) {
switch (mode) {
case Gallery_Frame_Mode::Manual: return std::make_shared<Manual_Render_Scene_2D_Strategy>();
case Gallery_Frame_Mode::Low_Latency: return std::make_shared<Low_Latency_Render_Scene_2D_Strategy>();
case Gallery_Frame_Mode::Playback: return std::make_shared<Playback_Render_Scene_2D_Strategy>();
case Gallery_Frame_Mode::Manual:
return std::make_shared<Manual_Render_Scene_2D_Strategy>();
case Gallery_Frame_Mode::Low_Latency:
return std::make_shared<Low_Latency_Render_Scene_2D_Strategy>();
case Gallery_Frame_Mode::Playback:
return std::make_shared<Playback_Render_Scene_2D_Strategy>();
}
return {};
}
std::string_view mode_id(Gallery_Frame_Mode mode) {
switch (mode) {
case Gallery_Frame_Mode::Manual: return "manual";
@@ -54,54 +60,56 @@ std::string_view mode_id(Gallery_Frame_Mode mode) {
}
return "low_latency";
}
Gallery_Frame_Mode strategy_mode(const Frame_Control_Strategy_Base& strategy) {
if (dynamic_cast<const Manual_Render_Scene_2D_Strategy*>(&strategy)) return Gallery_Frame_Mode::Manual;
if (dynamic_cast<const Playback_Render_Scene_2D_Strategy*>(&strategy)) return Gallery_Frame_Mode::Playback;
if (dynamic_cast<const Manual_Render_Scene_2D_Strategy*>(&strategy))
return Gallery_Frame_Mode::Manual;
if (dynamic_cast<const Playback_Render_Scene_2D_Strategy*>(&strategy))
return Gallery_Frame_Mode::Playback;
return Gallery_Frame_Mode::Low_Latency;
}
Time_Of_Day current_time_of_day() {
constexpr std::int64_t day_ms = 24LL * 60LL * 60LL * 1000LL;
const auto now = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
std::chrono::system_clock::now().time_since_epoch())
.count();
return {now % day_ms};
}
class Gallery_Scene2D final : public Gallery_Scene_Interface {
public:
Gallery_Scene2D(std::string case_id, Gallery_Frame_Mode mode) : case_id_(std::move(case_id)) {
Gallery_Scene2D(std::string case_id, Gallery_Frame_Mode mode)
: case_id_(std::move(case_id)) {
rebuild(mode);
}
~Gallery_Scene2D() override {
if (scene_) scene_->deactivate_view();
}
const std::string& case_id() const noexcept override {
return case_id_;
}
const std::string& case_id() const noexcept override { return case_id_; }
nlohmann::json controls() const override {
return {
{"resources", nlohmann::json::array()},
{"observers", nlohmann::json::array()},
{"render_plan", gallery_render_plan_json(*scene_)},
{
"performance_capture",
gallery_performance_capture_json(*scene_)
}
};
return {{"resources", nlohmann::json::array()},
{"observers", nlohmann::json::array()},
{"render_plan", gallery_render_plan_json(*scene_)},
{"performance_capture",
gallery_performance_capture_json(*scene_)}};
}
Gallery_Frame_Mode frame_mode() const noexcept override {
return strategy_mode(*scene_->frame_control_strategy());
}
void reset_monitoring() override {
render_count_ = 0;
}
void reset_monitoring() override { render_count_ = 0; }
void set_client_metrics(Gallery_Client_Performance metrics) noexcept override {
client_ = metrics;
constexpr double maximum_interval_ms =
static_cast<double>(std::numeric_limits<std::uint64_t>::max()) /
1'000'000.0;
static_cast<double>(std::numeric_limits<std::uint64_t>::max()) /
1'000'000.0;
if (metrics.display_interval_latest_ms <= 0.0 ||
metrics.display_interval_latest_ms > maximum_interval_ms)
return;
metrics.display_interval_latest_ms > maximum_interval_ms) return;
const auto observed_interval_ns = static_cast<std::uint64_t>(
metrics.display_interval_latest_ms * 1'000'000.0);
if (observed_interval_ns == 0) return;
@@ -112,10 +120,12 @@ public:
Frame_Consumer_Feedback{observed_interval_ns}));
}
}
adminive::Update_Result apply_patch(
std::string_view, const nlohmann::json&) override {
return {false, "no editable resource matches the requested target"};
}
void resize(int width, int height) override {
const Size requested{std::max(1, width), std::max(1, height)};
if (requested == viewport_) return;
@@ -123,23 +133,30 @@ public:
viewport_ = requested;
rebuild(mode);
}
void dispatch(const Gallery_Input_Event& event) override {
std::visit([this](const auto& value) {
using T = std::decay_t<decltype(value)>;
if constexpr (std::same_as<T, Gallery_View_Input>) {
if (value.type == Gallery_View_Input_Type::Show) scene_->activate_view();
else if (value.type == Gallery_View_Input_Type::Hide) scene_->deactivate_view();
if (value.type == Gallery_View_Input_Type::Show)
scene_->activate_view();
else if (value.type == Gallery_View_Input_Type::Hide)
scene_->deactivate_view();
}
}, event);
}
bool request_frame() override {
update_samples();
const auto result = scene_->render_frame();
if (!result || *result != Plot_Render_Status::rendered) return false;
if (scene_->wait_for_render() != Scene_Render_Result::none) return false;
if (!result || *result != Plot_Render_Status::rendered)
return false;
if (scene_->wait_for_render() != Scene_Render_Result::none)
return false;
++render_count_;
return true;
}
std::optional<std::string> encode_latest_pixels() override {
std::string result;
scene_->with_frame([&](Image_View image) {
@@ -148,10 +165,12 @@ public:
if (result.empty()) return std::nullopt;
return result;
}
void record_pixel_response(std::chrono::steady_clock::time_point,
std::chrono::steady_clock::time_point,
std::chrono::steady_clock::time_point,
std::size_t) override {}
std::string action(const Gallery_Action_Request& request,
bool& recognized) override {
recognized = true;
@@ -160,15 +179,16 @@ public:
request.id == "frame_strategy_playback") {
const auto next = request.id == "frame_strategy_manual"
? Gallery_Frame_Mode::Manual
: request.id == "frame_strategy_playback"
? Gallery_Frame_Mode::Playback
: Gallery_Frame_Mode::Low_Latency;
: request.id == "frame_strategy_playback"
? Gallery_Frame_Mode::Playback
: Gallery_Frame_Mode::Low_Latency;
const auto operation = scene_->edit_renderables(
[strategy = make_strategy(next)](
Scene_2D_Base::Renderable_Editor& editor) mutable {
editor.replace_frame_control_strategy(std::move(strategy));
});
if (operation.wait() != Scene_Edit_Error::none) return "frame strategy replacement rejected";
if (operation.wait() != Scene_Edit_Error::none)
return "frame strategy replacement rejected";
return "frame strategy replaced";
}
if (request.id == "capture_next_frame") {
@@ -186,7 +206,9 @@ public:
recognized = false;
return {};
}
void record_action_notice_if_empty(std::string_view) override {}
std::string telemetry_json() const override {
const auto observer = scene_->observer();
auto kernel_observer = observer_json(observer);
@@ -197,182 +219,168 @@ public:
{"kernel_observer", std::move(kernel_observer)},
{"performance", {{"successful_render_count", render_count_}}},
{"client_performance", {{"transport_fps", client_.transport_fps}}},
{"performance_capture", gallery_performance_capture_json(*scene_)}
}
.dump();
{"performance_capture", gallery_performance_capture_json(*scene_)}}
.dump();
}
private:
template <class Product, class... Arguments>
static renderive_Owner<Product> build(
typename Product::Builder& builder, Arguments&&... arguments) {
return static_cast<Renderable_Builder<Product, typename Product::State>&>(
builder)
.build(std::forward<Arguments>(arguments)...);
}
void rebuild(Gallery_Frame_Mode mode) {
Render_Scene_2D::State scene_state;
scene_state.viewport = viewport_;
scene_state.background = Color{3, 8, 14, 255};
scene_ = Render_Scene_2D::Builder{scene_state}.build(
make_strategy(mode));
{
auto attach = scene_->attach_builder();
const auto root = scene_->root_renderable();
const bool numeric_domain =
scene_ = Render_Scene_2D::Builder(make_strategy(mode))
.set<&Render_Scene_2D::Properties::viewport>(viewport_)
.set<&Render_Scene_2D::Properties::background>(Color{3, 8, 14, 255})
.build();
const auto root = scene_->root_renderable();
const bool numeric_domain =
case_id_ == "sweep_spectrum" || case_id_ == "constellation";
const bool time_domain = case_id_ == "frequency_trace";
const int left = 62;
const int top = 18;
const auto width = static_cast<std::size_t>(
std::max(1, viewport_.width - 86));
const auto height = static_cast<std::size_t>(
std::max(1, viewport_.height - 64));
if (time_domain || case_id_ == "waterfall" || case_id_ == "axis_lab") {
Time_Axis::State state;
state.orientation = time_domain || case_id_ == "axis_lab"
? Orientation::Horizontal
: Orientation::Vertical;
state.visible_count = 80;
state.x = left;
state.y = state.orientation == Orientation::Horizontal
? top + static_cast<int>(height)
: top;
state.pixel_length =
state.orientation == Orientation::Horizontal ? width : height;
state.color = Color{69, 221, 190, 255};
Time_Axis::Builder builder{state, &attach};
time_axis_ = build<Time_Axis>(builder, root);
time_axis_->set_object_name("Time Axis");
}
if (!time_domain && !numeric_domain) {
Frequency_Axis::State state;
state.orientation = Orientation::Horizontal;
state.coordinates = Range{88'000'000.0, 108'000'000.0};
state.x = left;
state.y = top + static_cast<int>(height);
state.pixel_length = width;
state.color = Color{118, 145, 184, 255};
state.wheel = true;
state.drag = true;
Frequency_Axis::Builder builder{state, &attach};
frequency_axis_ = build<Frequency_Axis>(builder, root);
frequency_axis_->set_object_name("Frequency Axis");
}
else if (numeric_domain) {
Axis::State state;
state.orientation = Orientation::Horizontal;
state.coordinates = case_id_ == "constellation"
? Range{-1.25, 1.25}
: Range{0.0, 300.0};
state.x = left;
state.y = top + static_cast<int>(height);
state.pixel_length = width;
state.color = Color{118, 145, 184, 255};
state.wheel = true;
state.drag = true;
Axis::Builder builder{state, &attach};
domain_axis_ = build<Axis>(builder, root);
domain_axis_->set_object_name("Domain Axis");
}
if (case_id_ != "waterfall") {
Axis::State state;
state.orientation = Orientation::Vertical;
state.coordinates = case_id_ == "constellation"
? Range{-1.25, 1.25}
: time_domain
? Range{-1.0, 1.0}
: Range{-120.0, -20.0};
state.x = left;
state.y = top;
state.pixel_length = height;
state.color = Color{118, 145, 184, 255};
Axis::Builder builder{state, &attach};
value_axis_ = build<Axis>(builder, root);
value_axis_->set_object_name("Value Axis");
}
if (case_id_ == "spectrum" || case_id_ == "selection_overlay") {
Spectrum::State state;
state.frequency_range = {88'000'000.0, 108'000'000.0};
state.frequency_point_size = 512;
state.center_frequency = 98'000'000.0;
state.max_hold_visible = true;
state.current_pen = {Color{53, 230, 178, 255}, 2.0};
state.max_pen = {Color{255, 209, 102, 255}, 1.2};
state.min_pen = {Color{122, 162, 255, 255}, 1.2};
Spectrum::Builder builder{state, &attach};
spectrum_ = build<Spectrum>(
builder, root, frequency_axis_, value_axis_);
spectrum_->set_object_name("Spectrum");
if (case_id_ == "selection_overlay") {
Selection_Rectangle_Overlay::State overlay_state;
overlay_state.selection_brush =
{Color{23, 59, 102, 90}, Brush_Style::Solid};
Selection_Rectangle_Overlay::Builder overlay_builder{
overlay_state, &attach
};
selection_ = build<Selection_Rectangle_Overlay>(
overlay_builder, root, frequency_axis_, value_axis_);
selection_->set_object_name("Selection Overlay");
}
}
else if (case_id_ == "waterfall") {
Waterfall::State state;
state.frequency_range = {88'000'000.0, 108'000'000.0};
state.power_range = {-120.0, -20.0};
state.frequency_bin_count = 256;
Waterfall::Builder builder{state, &attach};
waterfall_ = build<Waterfall>(
builder, root, frequency_axis_, time_axis_);
waterfall_->set_object_name("Waterfall");
}
else if (case_id_ == "afterglow") {
Afterglow::State state;
state.frequency_range = {88'000'000.0, 108'000'000.0};
state.power_range = {-120.0, -20.0};
state.frequency_point_size = 192;
state.power_point_size = 96;
state.attenuation_rate = 0.18;
Afterglow::Builder builder{state, &attach};
afterglow_ = build<Afterglow>(
builder, root, frequency_axis_, value_axis_);
// Afterglow::Builder(frequency_axis_, value_axis_).set_paint_rely({root}).set_render_rely({root}).build();
afterglow_->set_object_name("Afterglow");
}
else if (case_id_ == "sweep_spectrum") {
Sweep_Spectrum::State state;
state.frequency_range = {0.0, 300.0};
state.bins_per_block = static_cast<int>(sweep_bins_per_block);
state.block_count = static_cast<int>(sweep_block_count);
Sweep_Spectrum::Builder builder{state, &attach};
sweep_ = build<Sweep_Spectrum>(
builder, root, domain_axis_, value_axis_);
sweep_->set_object_name("Sweep Spectrum");
}
else if (case_id_ == "frequency_trace") {
Frequency_Trace::State state;
state.pen = {Color{53, 230, 178, 255}, 2.0};
Frequency_Trace::Builder builder{state, &attach};
trace_ = build<Frequency_Trace>(
builder, root, time_axis_, value_axis_);
trace_->set_object_name("Frequency Trace");
}
else if (case_id_ == "constellation") {
Constellation_Diagram::State state;
state.i_range = {-1.25, 1.25};
state.q_range = {-1.25, 1.25};
state.point_color = Color{73, 230, 195, 255};
state.anchor_color = Color{255, 209, 102, 255};
Constellation_Diagram::Builder builder{state, &attach};
constellation_ = build<Constellation_Diagram>(
builder, root, domain_axis_, value_axis_);
constellation_->set_object_name("Constellation Diagram");
const bool time_domain = case_id_ == "frequency_trace";
const int left = 62;
const int top = 18;
const auto width = static_cast<std::size_t>(
std::max(1, viewport_.width - 86));
const auto height = static_cast<std::size_t>(
std::max(1, viewport_.height - 64));
if (time_domain || case_id_ == "waterfall" || case_id_ == "axis_lab") {
const auto orientation = time_domain || case_id_ == "axis_lab"
? Orientation::Horizontal
: Orientation::Vertical;
time_axis_ = Time_Axis::Builder{}
.set<&Time_Axis::Properties::orientation>(orientation)
.set<&Time_Axis::Properties::visible_count>(80)
.set<&Time_Axis::Properties::x>(left)
.set<&Time_Axis::Properties::y>(
orientation == Orientation::Horizontal
? top + static_cast<int>(height)
: top)
.set<&Time_Axis::Properties::pixel_length>(
orientation == Orientation::Horizontal ? width : height)
.set<&Time_Axis::Properties::color>(Color{69, 221, 190, 255})
.set_paint_rely({root})
.set_render_rely({root})
.build();
time_axis_->set_object_name("Time Axis");
}
if (!time_domain && !numeric_domain) {
frequency_axis_ = Frequency_Axis::Builder{}
.set<&Frequency_Axis::Properties::orientation>(Orientation::Horizontal)
.set<&Frequency_Axis::Properties::coordinates>(Range{88'000'000.0, 108'000'000.0})
.set<&Frequency_Axis::Properties::x>(left)
.set<&Frequency_Axis::Properties::y>(top + static_cast<int>(height))
.set<&Frequency_Axis::Properties::pixel_length>(width)
.set<&Frequency_Axis::Properties::color>(Color{118, 145, 184, 255})
.set<&Frequency_Axis::Properties::wheel>(true)
.set<&Frequency_Axis::Properties::drag>(true)
.set_paint_rely({root})
.set_render_rely({root})
.build();
frequency_axis_->set_object_name("Frequency Axis");
} else if (numeric_domain) {
domain_axis_ = Axis::Builder{}
.set<&Axis::Properties::orientation>(Orientation::Horizontal)
.set<&Axis::Properties::coordinates>(
case_id_ == "constellation" ? Range{-1.25, 1.25} : Range{0.0, 300.0})
.set<&Axis::Properties::x>(left)
.set<&Axis::Properties::y>(top + static_cast<int>(height))
.set<&Axis::Properties::pixel_length>(width)
.set<&Axis::Properties::color>(Color{118, 145, 184, 255})
.set<&Axis::Properties::wheel>(true)
.set<&Axis::Properties::drag>(true)
.set_paint_rely({root})
.set_render_rely({root})
.build();
domain_axis_->set_object_name("Domain Axis");
}
if (case_id_ != "waterfall") {
value_axis_ = Axis::Builder{}
.set<&Axis::Properties::orientation>(Orientation::Vertical)
.set<&Axis::Properties::coordinates>(
case_id_ == "constellation"
? Range{-1.25, 1.25}
: time_domain ? Range{-1.0, 1.0} : Range{-120.0, -20.0})
.set<&Axis::Properties::x>(left)
.set<&Axis::Properties::y>(top)
.set<&Axis::Properties::pixel_length>(height)
.set<&Axis::Properties::color>(Color{118, 145, 184, 255})
.set_paint_rely({root})
.set_render_rely({root})
.build();
value_axis_->set_object_name("Value Axis");
}
if (case_id_ == "spectrum" || case_id_ == "selection_overlay") {
spectrum_ = Spectrum::Builder(frequency_axis_, value_axis_)
.set<&Spectrum::Properties::frequency_range>(Range{88'000'000.0, 108'000'000.0})
.set<&Spectrum::Properties::frequency_point_size>(512)
.set<&Spectrum::Properties::center_frequency>(98'000'000.0)
.set<&Spectrum::Properties::max_hold_visible>(true)
.set<&Spectrum::Properties::current_pen>(Pen{Color{53, 230, 178, 255}, 2.0})
.set<&Spectrum::Properties::max_pen>(Pen{Color{255, 209, 102, 255}, 1.2})
.set<&Spectrum::Properties::min_pen>(Pen{Color{122, 162, 255, 255}, 1.2})
.set_paint_rely({root})
.set_render_rely({root})
.build();
spectrum_->set_object_name("Spectrum");
if (case_id_ == "selection_overlay") {
selection_ = Selection_Rectangle_Overlay::Builder(frequency_axis_, value_axis_)
.set<&Selection_Rectangle_Overlay::Properties::selection_brush>(
Brush{Color{23, 59, 102, 90}, Brush_Style::Solid})
.set_paint_rely({root})
.set_render_rely({root})
.build();
selection_->set_object_name("Selection Overlay");
}
} else if (case_id_ == "waterfall") {
waterfall_ = Waterfall::Builder(frequency_axis_, time_axis_)
.set<&Waterfall::Properties::frequency_range>(Range{88'000'000.0, 108'000'000.0})
.set<&Waterfall::Properties::power_range>(Range{-120.0, -20.0})
.set<&Waterfall::Properties::frequency_bin_count>(256)
.set_paint_rely({root})
.set_render_rely({root})
.build();
waterfall_->set_object_name("Waterfall");
} else if (case_id_ == "afterglow") {
afterglow_ = Afterglow::Builder(frequency_axis_, value_axis_)
.set<&Afterglow::Properties::frequency_range>(Range{88'000'000.0, 108'000'000.0})
.set<&Afterglow::Properties::power_range>(Range{-120.0, -20.0})
.set<&Afterglow::Properties::frequency_point_size>(192)
.set<&Afterglow::Properties::power_point_size>(96)
.set<&Afterglow::Properties::attenuation_rate>(0.18)
.set_paint_rely({root})
.set_render_rely({root})
.build();
afterglow_->set_object_name("Afterglow");
} else if (case_id_ == "sweep_spectrum") {
sweep_ = Sweep_Spectrum::Builder(domain_axis_, value_axis_)
.set<&Sweep_Spectrum::Properties::frequency_range>(Range{0.0, 300.0})
.set<&Sweep_Spectrum::Properties::bins_per_block>(static_cast<int>(sweep_bins_per_block))
.set<&Sweep_Spectrum::Properties::block_count>(static_cast<int>(sweep_block_count))
.set_paint_rely({root})
.set_render_rely({root})
.build();
sweep_->set_object_name("Sweep Spectrum");
} else if (case_id_ == "frequency_trace") {
trace_ = Frequency_Trace::Builder(time_axis_, value_axis_)
.set<&Frequency_Trace::Properties::pen>(Pen{Color{53, 230, 178, 255}, 2.0})
.set_paint_rely({root})
.set_render_rely({root})
.build();
trace_->set_object_name("Frequency Trace");
} else if (case_id_ == "constellation") {
constellation_ = Constellation_Diagram::Builder(domain_axis_, value_axis_)
.set<&Constellation_Diagram::Properties::i_range>(Range{-1.25, 1.25})
.set<&Constellation_Diagram::Properties::q_range>(Range{-1.25, 1.25})
.set<&Constellation_Diagram::Properties::point_color>(Color{73, 230, 195, 255})
.set<&Constellation_Diagram::Properties::anchor_color>(Color{255, 209, 102, 255})
.set_paint_rely({root})
.set_render_rely({root})
.build();
constellation_->set_object_name("Constellation Diagram");
}
scene_->activate_view();
}
void update_samples() {
const std::size_t count =
waterfall_ ? 256U : afterglow_ ? 192U : sweep_ ? sweep_bins_per_block * sweep_block_count : 512U;
waterfall_ ? 256U :
afterglow_ ? 192U :
sweep_ ? sweep_bins_per_block * sweep_block_count : 512U;
std::vector<double> values(count);
const std::uint64_t sample_sequence = sample_sequence_++;
const std::uint64_t time_sequence = sweep_
@@ -405,19 +413,20 @@ private:
if (trace_) trace_->append_sample(current_time_of_day(), std::sin(time));
if (constellation_) {
constexpr std::size_t symbol_count =
static_cast<std::size_t>(Constellation_Diagram_Type::Psk8);
static_cast<std::size_t>(Constellation_Diagram_Type::Psk8);
const std::size_t symbol_index = sample_sequence % symbol_count;
const double phase = 2.0 * std::numbers::pi *
static_cast<double>(symbol_index) /
static_cast<double>(symbol_count);
static_cast<double>(symbol_index) /
static_cast<double>(symbol_count);
const double i_noise =
std::sin(time * 17.0 + static_cast<double>(symbol_index) * 0.73) * 0.04;
std::sin(time * 17.0 + static_cast<double>(symbol_index) * 0.73) * 0.04;
const double q_noise =
std::cos(time * 19.0 + static_cast<double>(symbol_index) * 1.11) * 0.04;
std::cos(time * 19.0 + static_cast<double>(symbol_index) * 1.11) * 0.04;
constellation_->append_point(
{std::cos(phase) + i_noise, std::sin(phase) + q_noise});
}
}
std::string case_id_;
Size viewport_{560, 320};
std::unique_ptr<Render_Scene_2D> scene_;
@@ -437,6 +446,7 @@ private:
std::uint64_t sample_sequence_{};
};
} // namespace
std::unique_ptr<Gallery_Scene_Interface> make_gallery_scene_2d(
std::uint64_t, std::string case_id, Gallery_Frame_Mode mode) {
return std::make_unique<Gallery_Scene2D>(std::move(case_id), mode);
+5 -6
View File
@@ -208,13 +208,12 @@ private:
{{-0.55F, -0.25F, 0.0F}, {255, 80, 80, 255}, 24.0F},
{{0.0F, 0.45F, 0.0F}, {80, 255, 120, 255}, 28.0F},
{{0.55F, -0.1F, 0.0F}, {80, 140, 255, 255}, 26.0F}};
visual_ = Point_Visual::Builder{}.build(std::move(points));
visual_ = Point_Visual::Builder(std::move(points)).build();
visual_->set_object_name(case_id_ + " Points");
Render_Scene_3D::State state;
state.viewport = extent_;
state.visual_family = visual_family(case_id_);
scene_ = Render_Scene_3D::Builder{state}.build(
visual_, make_strategy(mode));
scene_ = Render_Scene_3D::Builder(visual_, make_strategy(mode))
.set<&Render_Scene_3D::Properties::viewport>(extent_)
.set<&Render_Scene_3D::Properties::visual_family>(visual_family(case_id_))
.build();
}
std::string case_id_;