diff --git a/Kernel/src/renderive/base/property/Property_Builder.hpp b/Kernel/src/renderive/base/property/Property_Builder.hpp index e4a258b..ee4f54a 100644 --- a/Kernel/src/renderive/base/property/Property_Builder.hpp +++ b/Kernel/src/renderive/base/property/Property_Builder.hpp @@ -4,6 +4,8 @@ #include "renderive/base/memory/Memory_Resource.hpp" #include #include +#include +#include #include template Validator_Type = No_Property_Validator> 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 && std::default_initializable : properties{}, validator{} {} - explicit Property_Builder(Properties properties) requires std::default_initializable : 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 { + bind_constructor(); + } + template requires (sizeof...(Args) > 0) && std::default_initializable + explicit Property_Builder(Args&&... args) { + bind_constructor(std::forward(args)...); + } template Value> Self& set(Value&& value) { - properties.*Member = std::forward < Value > (value); + properties_.*Member = std::forward(value); return *this; } template Configure> Self& configure(Configure&& configure) { - std::invoke(std::forward < Configure > (configure), properties); + std::invoke(std::forward(configure), properties_); return *this; } const Properties& properties_value() const noexcept { - return properties; + return properties_; } - template requires Property_Constructible - std::shared_ptr build(Args&&... args) const { - validator(properties); - return std::make_shared(properties, std::forward(args)...); + [[nodiscard]] std::shared_ptr build() { + validator_(properties_); + auto construct = std::exchange(shared_constructor_, {}); + clear_constructors(); + return construct(properties_); } - template requires Property_Constructible - std::shared_ptr build_with_resource(std::pmr::memory_resource& memory_resource, Args&&... args) const { - validator(properties); - return std::allocate_shared(std::pmr::polymorphic_allocator(&memory_resource), properties, std::forward(args)...); + [[nodiscard]] std::shared_ptr 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 requires Property_Constructible - std::unique_ptr build_unique(Args&&... args) const { - validator(properties); - return std::make_unique(properties, std::forward(args)...); + [[nodiscard]] std::unique_ptr build_unique() { + validator_(properties_); + auto construct = std::exchange(unique_constructor_, {}); + clear_constructors(); + return construct(properties_); } - template requires Property_Constructible - Memory_Resource_Unique_Ptr build_unique_with_resource(std::pmr::memory_resource& memory_resource, Args&&... args) const { - validator(properties); - return make_memory_resource_unique(memory_resource, properties, std::forward(args)...); + [[nodiscard]] Memory_Resource_Unique_Ptr 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(const Properties&)>; + using Resource_Shared_Constructor = std::function(std::pmr::memory_resource&, const Properties&)>; + using Unique_Constructor = std::function(const Properties&)>; + using Resource_Unique_Constructor = std::function(std::pmr::memory_resource&, const Properties&)>; + template + void bind_constructor(Args&&... args) { + auto stored = std::make_shared...>>(std::forward(args)...); + shared_constructor_ = [stored](const Properties& properties) mutable { + return std::apply( + [&](auto&... values) { + return std::make_shared(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(std::pmr::polymorphic_allocator(&memory_resource), properties, std::move(values)...); + }, + *stored); + }; + unique_constructor_ = [stored](const Properties& properties) mutable { + return std::apply( + [&](auto&... values) { + return std::make_unique(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(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_; }; diff --git a/Kernel/src/renderive/renderable/Inheritance.hpp b/Kernel/src/renderive/renderable/Inheritance.hpp index d74ea3d..7f34cf2 100644 --- a/Kernel/src/renderive/renderable/Inheritance.hpp +++ b/Kernel/src/renderive/renderable/Inheritance.hpp @@ -34,6 +34,9 @@ template struct Builder : Parent_Next, Business_Builder { using Business_Builder::Business_Builder; + using Business_Builder::set; + using Business_Builder::configure; + using Business_Builder::build; }; template struct State_Root diff --git a/Kernel/src/renderive/renderable/base/Renderable_Base.hpp b/Kernel/src/renderive/renderable/base/Renderable_Base.hpp index 0f50ae6..c5dc76c 100644 --- a/Kernel/src/renderive/renderable/base/Renderable_Base.hpp +++ b/Kernel/src/renderive/renderable/base/Renderable_Base.hpp @@ -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 d_ptr; }; diff --git a/Kernel/src/renderive/scene/Scene_Builder.hpp b/Kernel/src/renderive/scene/Scene_Builder.hpp index ebf113f..ec40061 100644 --- a/Kernel/src/renderive/scene/Scene_Builder.hpp +++ b/Kernel/src/renderive/scene/Scene_Builder.hpp @@ -2,44 +2,51 @@ #include "renderive/base/property/Concepts.hpp" #include "renderive/base/property/Validators.hpp" #include "renderive/scene/Inheritance.hpp" -#include #include #include +#include +#include #include namespace renderive::scene { -template Validator_Type = - No_Property_Validator> +template Validator_Type = No_Property_Validator> 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 && - std::default_initializable - = default; - explicit Scene_Builder(Properties properties) requires std::default_initializable : properties_(std::move(properties)) {} - Scene_Builder(Properties properties, Validator validator) : properties_(std::move(properties)), - validator_(std::move(validator)) {} - template Value> + Scene_Builder() requires std::default_initializable : constructor_(make_constructor()) {} + template requires (sizeof...(Args) > 0) && std::default_initializable + explicit Scene_Builder(Args&&... args) : constructor_(make_constructor(std::forward(args)...)) {} + template Value> Self& set(Value&& value) { - properties_.*Member = std::forward < Value > (value); + properties_.*Member = std::forward(value); return *this; } template Configure> Self& configure(Configure&& configure) { - std::invoke(std::forward < Configure > (configure), properties_); + std::invoke(std::forward(configure), properties_); return *this; } - template - [[nodiscard]] std::unique_ptr build(Args&&... args) const { + [[nodiscard]] std::unique_ptr build() { validator_(properties_); - return scene_inheritance::Builder_Access::make( - properties_, std::forward(args)...); + auto construct = std::exchange(constructor_, {}); + return construct(properties_); } private: + using Constructor = std::function(const Properties&)>; + template + static Constructor make_constructor(Args&&... args) { + auto stored = std::make_shared...>>(std::forward(args)...); + return [stored = std::move(stored)](const Properties& properties) mutable { + return std::apply( + [&](auto&... values) { + return scene_inheritance::Builder_Access::make(properties, std::move(values)...); + }, + *stored); + }; + } Properties properties_{}; [[no_unique_address]] Validator validator_{}; + Constructor constructor_; }; -} // namespace renderive::scene +} diff --git a/Kernel/src/renderive/scene/base/Scene_Base.cpp b/Kernel/src/renderive/scene/base/Scene_Base.cpp index c75911d..bdfcf89 100644 --- a/Kernel/src/renderive/scene/base/Scene_Base.cpp +++ b/Kernel/src/renderive/scene/base/Scene_Base.cpp @@ -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(anchor->d_func().scene())); +} Scene_Base::Edit_Operation Scene_2D_Base::edit_renderables(Renderable_Edit edit) { if (!edit) { std::promise promise; diff --git a/Kernel/src/renderive/scene/base/Scene_Base.hpp b/Kernel/src/renderive/scene/base/Scene_Base.hpp index 92eafb0..1bfd2f6 100644 --- a/Kernel/src/renderive/scene/base/Scene_Base.hpp +++ b/Kernel/src/renderive/scene/base/Scene_Base.hpp @@ -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 {}; diff --git a/Kernel/tests/renderive/base/property/Property_Builder_Test.cpp b/Kernel/tests/renderive/base/property/Property_Builder_Test.cpp index 4b9da50..b8600c5 100644 --- a/Kernel/tests/renderive/base/property/Property_Builder_Test.cpp +++ b/Kernel/tests/renderive/base/property/Property_Builder_Test.cpp @@ -2,14 +2,19 @@ #include #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>); EXPECT_EQ(product->value, 12); EXPECT_EQ(product->id, 3); diff --git a/Kernel/tests/renderive/scene/Scene_Memory_Resource_Test.cpp b/Kernel/tests/renderive/scene/Scene_Memory_Resource_Test.cpp index cccfbc7..1059f9d 100644 --- a/Kernel/tests/renderive/scene/Scene_Memory_Resource_Test.cpp +++ b/Kernel/tests/renderive/scene/Scene_Memory_Resource_Test.cpp @@ -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 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{4} + .set<&Scene_Memory_Resource_Test_Properties::value>(3) + .build_with_resource(memory_resource); + auto unique_product = Property_Builder{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); diff --git a/Qt/tests/Qt_Bridge_Tests.cpp b/Qt/tests/Qt_Bridge_Tests.cpp index 6c035dd..b6daaa4 100644 --- a/Qt/tests/Qt_Bridge_Tests.cpp +++ b/Qt/tests/Qt_Bridge_Tests.cpp @@ -16,27 +16,30 @@ TEST(Renderive_Qt, WidgetLifecycleDrivesAndStopsKernelScene) { renderive_Owner x_axis; renderive_Owner y_axis; renderive_Owner 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{0.1, 0.3, 0.8, 0.5, 0.9, 0.4, 0.2, 0.7}); plot.show(); QTimer::singleShot(150, application, &QCoreApplication::quit); diff --git a/render_2D/render_2D/renderable/Renderable_Builder.h b/render_2D/render_2D/renderable/Renderable_Builder.h index d8e0e62..b3e5d68 100644 --- a/render_2D/render_2D/renderable/Renderable_Builder.h +++ b/render_2D/render_2D/renderable/Renderable_Builder.h @@ -2,32 +2,19 @@ #include "Renderable.h" #include #include -#include #include -#include #include #include +#include +#include +#include +#include #include +#include namespace renderive { class Abs_Axis; namespace detail { struct Paint_Overlay; -template requires std::derived_from && std::derived_from -void attach_renderable_dependency(::Scene_2D_Base::Attach_Builder& builder, const renderive_Owner& renderable, const renderive_Owner& axis) { - if (!axis) return; - builder.add_dependency_parent(renderable, axis); - if constexpr (std::derived_from) builder.add_display_parent(renderable, axis); - else builder.add_display_parent(axis, renderable); -} -template -void attach_renderable_dependency(::Scene_2D_Base::Attach_Builder&, const renderive_Owner&, const Value&) {} -template requires std::derived_from -void attach_renderable(::Scene_2D_Base::Attach_Builder& builder, const renderive_Owner& renderable, const renderive_Owner& 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 Validator_Type = No_Property_Validator> 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 && std::default_initializable = default; - explicit Renderable_Builder(::Scene_2D_Base::Attach_Builder* attach_builder) requires std::default_initializable && std::default_initializable : attach_builder_(attach_builder) {} - explicit Renderable_Builder(Properties properties, ::Scene_2D_Base::Attach_Builder* attach_builder = nullptr) requires std::default_initializable : 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 : constructor_(make_constructor()) {} + template requires (sizeof...(Args) > 0) && std::default_initializable + explicit Renderable_Builder(Args&&... args) { + (collect_constructor_rely(args), ...); + constructor_ = make_constructor(std::forward(args)...); + } template Value> Self& set(Value&& value) { - properties.*Member = std::forward(value); + properties_.*Member = std::forward(value); return *this; } template Configure> Self& configure(Configure&& configure) { - std::invoke(std::forward(configure), properties); + std::invoke(std::forward(configure), properties_); + return *this; + } + Self& set_paint_rely(std::initializer_list> rely) { + paint_rely_.assign(rely); + return *this; + } + Self& set_render_rely(std::initializer_list> rely) { + render_rely_.assign(rely); return *this; } const Properties& properties_value() const noexcept { - return properties; + return properties_; } - template requires std::derived_from - renderive_Owner build(const renderive_Owner& parent, Args&&... args) const { - if (!parent || !(valid_argument(args) && ...)) return {}; - validator(properties); - return inheritance::initialize( - renderive_Owner( - renderable_inheritance::Builder_Access::make( - properties, args...)), - [&](renderive_Owner& result) { - if (attach_builder_) - detail::attach_renderable(*attach_builder_, result, - parent, args...); - }); + [[nodiscard]] renderive_Owner 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) attach.add_display_parent(result, parent); + else attach.add_display_parent(parent, result); + } + return result; } private: - template - static bool valid_argument(const renderive_Owner& value) { - return static_cast(value); + using Constructor = std::function(const Properties&)>; + template + static Constructor make_constructor(Args&&... args) { + auto stored = std::make_shared...>>(std::forward(args)...); + return [stored = std::move(stored)](const Properties& properties) mutable { + return std::apply( + [&](auto&... values) { + return renderive_Owner(renderable_inheritance::Builder_Access::make(properties, std::move(values)...)); + }, + *stored); + }; + } + template requires std::derived_from + void collect_constructor_rely(const renderive_Owner& value) { + constructor_rely_.emplace_back(value); } template - static bool valid_argument(const T&) { + void collect_constructor_rely(const T&) {} + static bool valid_rely(const std::vector>& 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* 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> paint_rely_; + std::vector> render_rely_; + std::vector> constructor_rely_; }; } diff --git a/render_2D/tests/render_2D_Frame_Pipeline_Tests.cpp b/render_2D/tests/render_2D_Frame_Pipeline_Tests.cpp index 536cc7c..75eafd9 100644 --- a/render_2D/tests/render_2D_Frame_Pipeline_Tests.cpp +++ b/render_2D/tests/render_2D_Frame_Pipeline_Tests.cpp @@ -9,7 +9,11 @@ std::unique_ptr make_scene( Render_Scene_2D::State state = {}) { auto strategy = std::make_shared(); - 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) { diff --git a/render_2D/tests/render_2D_Integration_Tests.cpp b/render_2D/tests/render_2D_Integration_Tests.cpp index f771adf..c4a0bf0 100644 --- a/render_2D/tests/render_2D_Integration_Tests.cpp +++ b/render_2D/tests/render_2D_Integration_Tests.cpp @@ -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()); + auto scene = Render_Scene_2D::Builder( + std::make_shared()) + .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()); + auto scene = Render_Scene_2D::Builder( + std::make_shared()) + .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()); + auto scene = Render_Scene_2D::Builder( + std::make_shared()) + .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( diff --git a/render_3D/render_3D/renderable/Renderable_Builder.h b/render_3D/render_3D/renderable/Renderable_Builder.h index 13b67e0..959dd50 100644 --- a/render_3D/render_3D/renderable/Renderable_Builder.h +++ b/render_3D/render_3D/renderable/Renderable_Builder.h @@ -2,26 +2,23 @@ #include #include #include -#include +#include #include #include +#include +#include #include namespace renderive::render_3d { -template Validator_Type = - No_Property_Validator> +template Validator_Type = No_Property_Validator> 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 && - std::default_initializable - = default; - explicit Renderable_Builder(Properties properties) requires std::default_initializable : properties_(std::move(properties)) {} - Renderable_Builder(Properties properties, Validator validator) : properties_(std::move(properties)), validator_(std::move(validator)) {} - template Value> + Renderable_Builder() requires std::default_initializable : constructor_(make_constructor()) {} + template requires (sizeof...(Args) > 0) && std::default_initializable + explicit Renderable_Builder(Args&&... args) : constructor_(make_constructor(std::forward(args)...)) {} + template Value> Self& set(Value&& value) { properties_.*Member = std::forward(value); return *this; @@ -31,17 +28,27 @@ struct Renderable_Builder { std::invoke(std::forward(configure), properties_); return *this; } - template - std::shared_ptr build(Args&&... args) const { + [[nodiscard]] std::shared_ptr build() { validator_(properties_); - auto result = - ::renderive::renderable_inheritance::Builder_Access::make( - properties_, std::forward(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(const Properties&)>; + template + static Constructor make_constructor(Args&&... args) { + auto stored = std::make_shared...>>(std::forward(args)...); + return [stored = std::move(stored)](const Properties& properties) mutable { + return std::apply( + [&](auto&... values) { + return ::renderive::renderable_inheritance::Builder_Access::make(properties, std::move(values)...); + }, + *stored); + }; + } Properties properties_{}; [[no_unique_address]] Validator validator_{}; + Constructor constructor_; }; -} // namespace renderive::render_3d +} diff --git a/render_3D/tests/Datoviz_Visual_Family_Tests.cpp b/render_3D/tests/Datoviz_Visual_Family_Tests.cpp index 4d15fae..b3cd860 100644 --- a/render_3D/tests/Datoviz_Visual_Family_Tests.cpp +++ b/render_3D/tests/Datoviz_Visual_Family_Tests.cpp @@ -7,7 +7,7 @@ namespace { template void exercise(Item item) { - auto visual = typename Visual::Builder{}.build(std::vector{item}); + auto visual = typename Visual::Builder{std::vector{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(64)) .set<&Volume_State::field_width>(4U) .set<&Volume_State::field_height>(4U) .set<&Volume_State::field_depth>(4U) - .build(std::vector(64)); + .build(); ASSERT_TRUE(volume); EXPECT_EQ(volume->get<&Volume_State::field_depth>(), 4U); EXPECT_THROW((Volume_Visual::Builder{} diff --git a/render_3D/tests/Point_Render_Integration_Tests.cpp b/render_3D/tests/Point_Render_Integration_Tests.cpp index 458268f..052f7d6 100644 --- a/render_3D/tests/Point_Render_Integration_Tests.cpp +++ b/render_3D/tests/Point_Render_Integration_Tests.cpp @@ -9,13 +9,18 @@ namespace { std::unique_ptr make_scene( Render_Scene_3D::State state, const std::shared_ptr& visual) { - return Render_Scene_3D::Builder{state}.build( - visual, std::make_shared()); + return Render_Scene_3D::Builder( + visual, std::make_shared()) + .configure([&](Render_Scene_3D::Properties& properties) { + properties = state; + }) + .build(); } TEST(PointRenderIntegration, BuilderConstructsSceneFromStateAndStrategy) { - auto visual = Point_Visual::Builder{}.build(std::vector{ - {{0.0F, 0.0F, 0.0F}, {255, 0, 0, 255}, 8.0F}}); + auto visual = Point_Visual::Builder(std::vector{ + {{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 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{ - {{0.0F, 0.0F, 0.0F}, {255, 0, 0, 255}, 8.0F}}); + auto visual = Point_Visual::Builder(std::vector{ + {{0.0F, 0.0F, 0.0F}, {255, 0, 0, 255}, 8.0F}}) + .build(); Render_Scene_3D::State state; state.viewport = {64, 48}; try { diff --git a/render_3D/tests/Point_State_Tests.cpp b/render_3D/tests/Point_State_Tests.cpp index 804a596..698362e 100644 --- a/render_3D/tests/Point_State_Tests.cpp +++ b/render_3D/tests/Point_State_Tests.cpp @@ -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{{{1.0F, 2.0F, 3.0F}, {1, 2, 3, 255}, 7.0F}}); + auto visual = Point_Visual::Builder( + std::vector{{{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; diff --git a/web_server/app/Web_Plot_Session.cpp b/web_server/app/Web_Plot_Session.cpp index e6dc7a2..261b04e 100644 --- a/web_server/app/Web_Plot_Session.cpp +++ b/web_server/app/Web_Plot_Session.cpp @@ -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()); + scene = Render_Scene_2D::Builder( + std::make_shared()) + .set<&Render_Scene_2D::Properties::viewport>(viewport) + .build(); scene->activate_view(); } std::optional handle(const Web_Event& event) { diff --git a/web_server/app/render_2D/Gallery_Scene2D.cpp b/web_server/app/render_2D/Gallery_Scene2D.cpp index ba0c50a..e802a5d 100644 --- a/web_server/app/render_2D/Gallery_Scene2D.cpp +++ b/web_server/app/render_2D/Gallery_Scene2D.cpp @@ -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 #include #include @@ -22,7 +24,7 @@ #include #include #include -#include + namespace renderive::web { template <> struct Observer_Pfr_Adapter { @@ -40,12 +42,16 @@ constexpr std::size_t sweep_block_count = 8; std::shared_ptr make_strategy( Gallery_Frame_Mode mode) { switch (mode) { - case Gallery_Frame_Mode::Manual: return std::make_shared(); - case Gallery_Frame_Mode::Low_Latency: return std::make_shared(); - case Gallery_Frame_Mode::Playback: return std::make_shared(); + case Gallery_Frame_Mode::Manual: + return std::make_shared(); + case Gallery_Frame_Mode::Low_Latency: + return std::make_shared(); + case Gallery_Frame_Mode::Playback: + return std::make_shared(); } 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(&strategy)) return Gallery_Frame_Mode::Manual; - if (dynamic_cast(&strategy)) return Gallery_Frame_Mode::Playback; + if (dynamic_cast(&strategy)) + return Gallery_Frame_Mode::Manual; + if (dynamic_cast(&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::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(std::numeric_limits::max()) / - 1'000'000.0; + static_cast(std::numeric_limits::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( 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; if constexpr (std::same_as) { - 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 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 - static renderive_Owner build( - typename Product::Builder& builder, Arguments&&... arguments) { - return static_cast&>( - builder) - .build(std::forward(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::max(1, viewport_.width - 86)); - const auto height = static_cast( - 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(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(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(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(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(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(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(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( - 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( - 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( - 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( - 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(sweep_bins_per_block); - state.block_count = static_cast(sweep_block_count); - Sweep_Spectrum::Builder builder{state, &attach}; - sweep_ = build( - 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( - 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( - 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::max(1, viewport_.width - 86)); + const auto height = static_cast( + 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(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(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(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(sweep_bins_per_block)) + .set<&Sweep_Spectrum::Properties::block_count>(static_cast(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 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(Constellation_Diagram_Type::Psk8); + static_cast(Constellation_Diagram_Type::Psk8); const std::size_t symbol_index = sample_sequence % symbol_count; const double phase = 2.0 * std::numbers::pi * - static_cast(symbol_index) / - static_cast(symbol_count); + static_cast(symbol_index) / + static_cast(symbol_count); const double i_noise = - std::sin(time * 17.0 + static_cast(symbol_index) * 0.73) * 0.04; + std::sin(time * 17.0 + static_cast(symbol_index) * 0.73) * 0.04; const double q_noise = - std::cos(time * 19.0 + static_cast(symbol_index) * 1.11) * 0.04; + std::cos(time * 19.0 + static_cast(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 scene_; @@ -437,6 +446,7 @@ private: std::uint64_t sample_sequence_{}; }; } // namespace + std::unique_ptr make_gallery_scene_2d( std::uint64_t, std::string case_id, Gallery_Frame_Mode mode) { return std::make_unique(std::move(case_id), mode); diff --git a/web_server/app/render_3D/Gallery_Scene3D.cpp b/web_server/app/render_3D/Gallery_Scene3D.cpp index b2b69b7..3bdd2dd 100644 --- a/web_server/app/render_3D/Gallery_Scene3D.cpp +++ b/web_server/app/render_3D/Gallery_Scene3D.cpp @@ -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_;