From 9192b5953cb702e3874c1cd43b1bdc915c8eeeac Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Wed, 12 Aug 2026 02:02:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E5=AE=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web_server/app/Gallery_Controls.h | 15 +- web_server/app/Gallery_Plot_Session.cpp | 24 +- web_server/app/Gallery_Protocol.cpp | 81 +- web_server/app/Gallery_Protocol.h | 4 - web_server/app/Gallery_Renderable_Types.h | 144 ++++ web_server/app/Gallery_Renderables.h | 708 ++++++++++++++---- web_server/app/Gallery_Renderables_Adminive.h | 539 ------------- .../app/Gallery_Session_Control_Adminive.h | 4 +- web_server/tests/Web_Bridge_Tests.cpp | 2 +- 9 files changed, 758 insertions(+), 763 deletions(-) create mode 100644 web_server/app/Gallery_Renderable_Types.h delete mode 100644 web_server/app/Gallery_Renderables_Adminive.h diff --git a/web_server/app/Gallery_Controls.h b/web_server/app/Gallery_Controls.h index becabf0..c420993 100644 --- a/web_server/app/Gallery_Controls.h +++ b/web_server/app/Gallery_Controls.h @@ -1,6 +1,6 @@ #pragma once -#include "Gallery_Renderables_Adminive.h" +#include "Gallery_Renderables.h" #include @@ -17,6 +17,18 @@ class Gallery_Controls final { public: using Json = nlohmann::json; + template + void add(Object& object) { + const auto descriptor = adminive::Type_Descriptor::get(); + add(descriptor.name(), descriptor.label(), object); + } + + template + void add(std::string id, Object& object) { + add(std::move(id), adminive::Type_Descriptor::get().label(), object); + } + +private: template void add(std::string id, std::string title, Object& object) { using Model = adminive::Object_Model_Type; @@ -37,6 +49,7 @@ public: }); } +public: [[nodiscard]] Json resources() const { Json result = Json::array(); for (const auto& entry : entries_) { diff --git a/web_server/app/Gallery_Plot_Session.cpp b/web_server/app/Gallery_Plot_Session.cpp index 0ba85d2..002e187 100644 --- a/web_server/app/Gallery_Plot_Session.cpp +++ b/web_server/app/Gallery_Plot_Session.cpp @@ -1176,29 +1176,29 @@ private: } } void register_controls() { - controls_.add("scene", "Scene", *session_control_); + controls_.add(*session_control_); if (frequency_domain_axis_) - controls_.add("frequency_axis", "Frequency axis", *frequency_domain_axis_); + controls_.add(*frequency_domain_axis_); if (numeric_domain_axis_) - controls_.add("domain_axis", "Domain axis", *numeric_domain_axis_); + controls_.add("domain_axis", *numeric_domain_axis_); if (value_axis_) - controls_.add("value_axis", "Value axis", *value_axis_); + controls_.add("value_axis", *value_axis_); if (time_axis_) - controls_.add("time_axis", "Time axis", *time_axis_); + controls_.add(*time_axis_); if (spectrum_) - controls_.add("spectrum", "Spectrum", *spectrum_); + controls_.add(*spectrum_); if (waterfall_) - controls_.add("waterfall", "Waterfall", *waterfall_); + controls_.add(*waterfall_); if (afterglow_) - controls_.add("afterglow", "Afterglow", *afterglow_); + controls_.add(*afterglow_); if (sweep_) - controls_.add("sweep_spectrum", "Sweep spectrum", *sweep_); + controls_.add(*sweep_); if (trace_) - controls_.add("frequency_trace", "Frequency trace", *trace_); + controls_.add(*trace_); if (selection_) - controls_.add("selection_overlay", "Selection overlay", *selection_); + controls_.add(*selection_); if (constellation_) - controls_.add("constellation", "Constellation", *constellation_); + controls_.add(*constellation_); } std::vector spectrum_values(int count, Range power_range = {-120.0, -20.0}) const { count = std::max(count, 2); diff --git a/web_server/app/Gallery_Protocol.cpp b/web_server/app/Gallery_Protocol.cpp index 03965cf..f1377d2 100644 --- a/web_server/app/Gallery_Protocol.cpp +++ b/web_server/app/Gallery_Protocol.cpp @@ -1,5 +1,5 @@ #include "Gallery_Protocol.h" -#include "Gallery_Renderables_Adminive.h" +#include "Gallery_Renderables.h" #include "Gallery_Session_Control_Adminive.h" #include "adminive/adminive.hpp" @@ -489,17 +489,19 @@ std::string Gallery_Protocol::catalog_json() { Json entry = adminive::to_frontend_json(item); entry["control_count_by_mode"] = Json::object(); entry["action_count_by_mode"] = Json::object(); + const std::size_t controls = gallery_control_count() + + gallery_renderable_control_count(item.id); for (const auto mode : frame_modes) { const std::string key(frame_mode_id(mode)); - const std::size_t mode_controls = control_count(item.id, mode); - const std::size_t mode_actions = action_count(item.id, mode); - entry["control_count_by_mode"][key] = mode_controls; - entry["action_count_by_mode"][key] = mode_actions; - controls_total += mode_controls; - actions_total += mode_actions; + const std::size_t actions = gallery_detail::actions(item.id, mode).size(); + entry["control_count_by_mode"][key] = controls; + entry["action_count_by_mode"][key] = actions; + controls_total += controls; + actions_total += actions; } - entry["control_count"] = control_count(item.id, Gallery_Frame_Mode::Low_Latency); - entry["action_count"] = action_count(item.id, Gallery_Frame_Mode::Low_Latency); + entry["control_count"] = controls; + entry["action_count"] = + gallery_detail::actions(item.id, Gallery_Frame_Mode::Low_Latency).size(); result["cases"].push_back(std::move(entry)); } result["coverage"] = {{"case_count", gallery_detail::cases().size()}, @@ -642,67 +644,6 @@ std::optional Gallery_Protocol::control_patch_req } } -std::size_t Gallery_Protocol::control_count(std::string_view case_id, - Gallery_Frame_Mode frame_mode) { - const auto count = [](const auto& self, const Json& fields) -> std::size_t { - std::size_t result{}; - for (const auto& field : fields) { - if (field.value("editable", false) && !field.contains("children")) - ++result; - if (field.contains("children")) - result += self(self, field.at("children")); - } - return result; - }; - const auto type_count = [&count]() { - return count(count, adminive::to_descriptor_json().at("fields")); - }; - std::size_t result = type_count.template operator()(); - if (case_id == "axis_lab") - return result + type_count.template operator()() + - type_count.template operator()(); - if (case_id == "spectrum" || case_id == "selection_overlay") { - result += type_count.template operator()() + - type_count.template operator()() + - type_count.template operator()(); - if (case_id == "selection_overlay") - result += type_count.template operator()(); - return result; - } - if (case_id == "waterfall") - return result + type_count.template operator()() + - type_count.template operator()() + - type_count.template operator()(); - if (case_id == "afterglow") - return result + type_count.template operator()() + - type_count.template operator()() + - type_count.template operator()(); - if (case_id == "sweep_spectrum") - return result + 2 * type_count.template operator()() + - type_count.template operator()(); - if (case_id == "frequency_trace") - return result + type_count.template operator()() + - type_count.template operator()() + - type_count.template operator()(); - if (case_id == "constellation") - return result + 2 * type_count.template operator()() + - type_count.template operator()(); - return result; -} - -std::size_t Gallery_Protocol::control_count(std::string_view case_id) { - return control_count(case_id, Gallery_Frame_Mode::Low_Latency); -} - -std::size_t Gallery_Protocol::action_count(std::string_view case_id, - Gallery_Frame_Mode frame_mode) { - return gallery_detail::actions(case_id, frame_mode).size(); -} - -std::size_t Gallery_Protocol::action_count(std::string_view case_id) { - return action_count(case_id, Gallery_Frame_Mode::Low_Latency); -} - bool Gallery_Protocol::action_available(std::string_view case_id, Gallery_Frame_Mode frame_mode, std::string_view action_id) { diff --git a/web_server/app/Gallery_Protocol.h b/web_server/app/Gallery_Protocol.h index 621135d..015c91a 100644 --- a/web_server/app/Gallery_Protocol.h +++ b/web_server/app/Gallery_Protocol.h @@ -42,10 +42,6 @@ public: [[nodiscard]] static std::optional action_request(std::string_view message); [[nodiscard]] static std::optional control_patch_request( std::string_view message); - [[nodiscard]] static std::size_t control_count(std::string_view case_id); - [[nodiscard]] static std::size_t control_count(std::string_view case_id, Gallery_Frame_Mode frame_mode); - [[nodiscard]] static std::size_t action_count(std::string_view case_id); - [[nodiscard]] static std::size_t action_count(std::string_view case_id, Gallery_Frame_Mode frame_mode); [[nodiscard]] static bool action_available(std::string_view case_id, Gallery_Frame_Mode frame_mode, std::string_view action_id); }; } // namespace renderive::web diff --git a/web_server/app/Gallery_Renderable_Types.h b/web_server/app/Gallery_Renderable_Types.h new file mode 100644 index 0000000..2d075f7 --- /dev/null +++ b/web_server/app/Gallery_Renderable_Types.h @@ -0,0 +1,144 @@ +#pragma once + +#include "render_2D/axis/Axis.h" +#include "render_2D/axis/Frequency_Axis.h" +#include "render_2D/axis/Time_Axis.h" +#include "render_2D/plottable/Afterglow.h" +#include "render_2D/plottable/Constellation_Diagram.h" +#include "render_2D/plottable/Frequency_Trace.h" +#include "render_2D/plottable/Selection_Rectangle_Overlay.h" +#include "render_2D/plottable/Spectrum.h" +#include "render_2D/plottable/Sweep_Spectrum.h" +#include "render_2D/plottable/Waterfall.h" +#include + +#include +#include + +namespace renderive::web { + +template +struct Gallery_Property_Accessor { + using object_type = Object; + using Properties = typename Object::Properties; + using value_type = std::remove_cvref_t().*Member)>; + using storage_identity = void; + using dependency_spec = structive::No_Property_Dependencies; + static constexpr bool readable = true; + static constexpr bool writable = true; + static constexpr bool synchronized_view_read = false; + static constexpr bool trusted_object_access = true; + + value_type read(const Object& object) const { + return object.template adminive_read(); + } + void write(Object& object, value_type value) const { + object.template adminive_write(std::move(value)); + } +}; + +template +class Gallery_Plottable : public Base { +public: + using Properties = typename Base::Properties; + using Base::Base; + +private: + template + auto adminive_read() const { + return this->template property_value(); + } + template + void adminive_write(Value&& value) { + this->template set(std::forward(value)); + } + template + friend struct Gallery_Property_Accessor; +}; + +class Gallery_Spectrum final : public Gallery_Plottable { +public: + using Gallery_Plottable::Gallery_Plottable; + using Builder = Renderable_Builder; +}; + +class Gallery_Waterfall final : public Gallery_Plottable { +public: + using Gallery_Plottable::Gallery_Plottable; + using Builder = Renderable_Builder; +}; + +class Gallery_Afterglow final : public Gallery_Plottable { +public: + using Gallery_Plottable::Gallery_Plottable; + using Builder = Renderable_Builder; +}; + +class Gallery_Sweep_Spectrum final : public Gallery_Plottable { +public: + using Gallery_Plottable::Gallery_Plottable; + using Builder = Renderable_Builder; +}; + +class Gallery_Frequency_Trace final : public Gallery_Plottable { +public: + using Gallery_Plottable::Gallery_Plottable; + using Builder = Renderable_Builder; +}; + +class Gallery_Selection_Rectangle_Overlay final + : public Gallery_Plottable { +public: + using Gallery_Plottable::Gallery_Plottable; + using Builder = Renderable_Builder; +}; + +class Gallery_Constellation_Diagram final + : public Gallery_Plottable { +public: + using Gallery_Plottable::Gallery_Plottable; + using Builder = Renderable_Builder; +}; + +template +class Gallery_Axis_Base : public Base { +public: + using Properties = Properties_Type; + using Base::Base; + +private: + template + auto adminive_read() const { + return this->read([](const auto& state) { + return state.*Member; + }); + } + template + void adminive_write(Value&& value) { + this->template set(std::forward(value)); + } + template + friend struct Gallery_Property_Accessor; +}; + +class Gallery_Axis final : public Gallery_Axis_Base { +public: + using Gallery_Axis_Base::Gallery_Axis_Base; + using Builder = detail::Axis_Renderable_Builder; +}; + +class Gallery_Frequency_Axis final + : public Gallery_Axis_Base { +public: + using Gallery_Axis_Base::Gallery_Axis_Base; + using Builder = detail::Axis_Renderable_Builder; +}; + +class Gallery_Time_Axis final + : public Gallery_Axis_Base { +public: + using Gallery_Axis_Base::Gallery_Axis_Base; + using Builder = detail::Axis_Renderable_Builder; +}; + +} // namespace renderive::web diff --git a/web_server/app/Gallery_Renderables.h b/web_server/app/Gallery_Renderables.h index 2d075f7..0d14665 100644 --- a/web_server/app/Gallery_Renderables.h +++ b/web_server/app/Gallery_Renderables.h @@ -1,144 +1,584 @@ #pragma once -#include "render_2D/axis/Axis.h" -#include "render_2D/axis/Frequency_Axis.h" -#include "render_2D/axis/Time_Axis.h" -#include "render_2D/plottable/Afterglow.h" -#include "render_2D/plottable/Constellation_Diagram.h" -#include "render_2D/plottable/Frequency_Trace.h" -#include "render_2D/plottable/Selection_Rectangle_Overlay.h" -#include "render_2D/plottable/Spectrum.h" -#include "render_2D/plottable/Sweep_Spectrum.h" -#include "render_2D/plottable/Waterfall.h" -#include +#include "Gallery_Renderable_Types.h" -#include +#include "adminive/adminive.hpp" +#include "adminive/adapters/nlohmann_json.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include #include +#include + +namespace renderive::web::gallery_adminive { + +enum class Number_Locale_Choice { Point, Comma }; +enum class Color_Map_Choice { Spectrum, Ember, Grayscale }; + +template +struct Enum_Metadata; + +#define RENDERIVE_ENUM_METADATA(Type, ...) \ + template <> struct Enum_Metadata { static constexpr auto values = std::array{__VA_ARGS__}; } + +RENDERIVE_ENUM_METADATA(Orientation, + std::pair{Orientation::Horizontal, std::string_view{"Horizontal"}}, + std::pair{Orientation::Vertical, std::string_view{"Vertical"}}); +RENDERIVE_ENUM_METADATA(Renderable_Cache_Mode, + std::pair{Renderable_Cache_Mode::Direct, std::string_view{"Direct"}}, + std::pair{Renderable_Cache_Mode::Local_Pixel, std::string_view{"Local_Pixel"}}); +RENDERIVE_ENUM_METADATA(Line_Interpolation_Mode, + std::pair{Line_Interpolation_Mode::Nearest_Sample, std::string_view{"Nearest_Sample"}}, + std::pair{Line_Interpolation_Mode::Linear_Value, std::string_view{"Linear_Value"}}, + std::pair{Line_Interpolation_Mode::Linear_Power_Domain, std::string_view{"Linear_Power_Domain"}}, + std::pair{Line_Interpolation_Mode::Step_Left, std::string_view{"Step_Left"}}, + std::pair{Line_Interpolation_Mode::Step_Right, std::string_view{"Step_Right"}}, + std::pair{Line_Interpolation_Mode::Cubic_Value, std::string_view{"Cubic_Value"}}); +RENDERIVE_ENUM_METADATA(Image_Interpolation_Mode, + std::pair{Image_Interpolation_Mode::Nearest, std::string_view{"Nearest"}}, + std::pair{Image_Interpolation_Mode::Bilinear, std::string_view{"Bilinear"}}, + std::pair{Image_Interpolation_Mode::Bicubic, std::string_view{"Bicubic"}}); +RENDERIVE_ENUM_METADATA(Constellation_Diagram_Type, + std::pair{Constellation_Diagram_Type::Psk4, std::string_view{"PSK4"}}, + std::pair{Constellation_Diagram_Type::Psk8, std::string_view{"PSK8"}}, + std::pair{Constellation_Diagram_Type::Psk16, std::string_view{"PSK16"}}); +RENDERIVE_ENUM_METADATA(Line_Style, + std::pair{Line_Style::None, std::string_view{"None"}}, + std::pair{Line_Style::Solid, std::string_view{"Solid"}}, + std::pair{Line_Style::Dash, std::string_view{"Dash"}}, + std::pair{Line_Style::Dot, std::string_view{"Dot"}}); +RENDERIVE_ENUM_METADATA(Line_Cap, + std::pair{Line_Cap::Butt, std::string_view{"Butt"}}, + std::pair{Line_Cap::Square, std::string_view{"Square"}}, + std::pair{Line_Cap::Round, std::string_view{"Round"}}); +RENDERIVE_ENUM_METADATA(Line_Join, + std::pair{Line_Join::Miter, std::string_view{"Miter"}}, + std::pair{Line_Join::Bevel, std::string_view{"Bevel"}}, + std::pair{Line_Join::Round, std::string_view{"Round"}}); +RENDERIVE_ENUM_METADATA(Brush_Style, + std::pair{Brush_Style::None, std::string_view{"None"}}, + std::pair{Brush_Style::Solid, std::string_view{"Solid"}}); +RENDERIVE_ENUM_METADATA(Number_Locale_Choice, + std::pair{Number_Locale_Choice::Point, std::string_view{"."}}, + std::pair{Number_Locale_Choice::Comma, std::string_view{","}}); +RENDERIVE_ENUM_METADATA(Color_Map_Choice, + std::pair{Color_Map_Choice::Spectrum, std::string_view{"Spectrum"}}, + std::pair{Color_Map_Choice::Ember, std::string_view{"Ember"}}, + std::pair{Color_Map_Choice::Grayscale, std::string_view{"Grayscale"}}); + +#undef RENDERIVE_ENUM_METADATA + +template +struct Actual_Enum_Adapter { + static std::vector values() { + std::vector result; + result.reserve(Enum_Metadata::values.size()); + for (const auto& [value, name] : Enum_Metadata::values) + result.push_back(value); + return result; + } + + static std::string_view name(Enum value) { + for (const auto& [candidate, name] : Enum_Metadata::values) { + if (candidate == value) + return name; + } + return {}; + } + + static std::optional cast(std::string_view name) { + for (const auto& [value, candidate] : Enum_Metadata::values) { + if (candidate == name) + return value; + } + return std::nullopt; + } +}; + +inline Color blend(Color first, Color second, double amount) { + const auto channel = [amount](std::uint8_t a, std::uint8_t b) { + return static_cast(std::clamp( + std::lround(a + (b - a) * amount), 0L, 255L)); + }; + return {channel(first.r, second.r), channel(first.g, second.g), + channel(first.b, second.b), channel(first.a, second.a)}; +} + +inline Color_Map color_map(std::string_view palette) { + std::array stops{ + Color{5, 10, 25, 255}, Color{20, 52, 112, 255}, Color{39, 196, 181, 255}, + Color{242, 201, 76, 255}, Color{255, 76, 106, 255} + }; + if (palette == "Ember") { + stops = {Color{10, 5, 8, 255}, Color{62, 18, 30, 255}, Color{153, 47, 39, 255}, + Color{244, 132, 48, 255}, Color{255, 238, 166, 255}}; + } else if (palette == "Grayscale") { + stops = {Color{0, 0, 0, 255}, Color{52, 52, 52, 255}, Color{112, 112, 112, 255}, + Color{188, 188, 188, 255}, Color{255, 255, 255, 255}}; + } + std::vector colors; + colors.reserve(256); + for (int index = 0; index < 256; ++index) { + const double position = index / 255.0 * (stops.size() - 1); + const auto first = static_cast(std::floor(position)); + const auto second = std::min(first + 1, stops.size() - 1); + colors.push_back(premultiply(blend(stops[first], stops[second], position - first))); + } + return Color_Map(std::move(colors)); +} + +inline std::string color_map_name(const Color_Map& value) { + for (const std::string_view name : {std::string_view{"Spectrum"}, + std::string_view{"Ember"}, + std::string_view{"Grayscale"}}) { + if (value.colors() == color_map(name).colors()) + return std::string(name); + } + return "Spectrum"; +} + +template +auto editable(std::string name, std::string label, adminive::Field_Control control) { + return adminive::field(std::move(name), std::move(label)) + .editable().control(control); +} + +template +auto number(std::string name, std::string label) { + return editable(std::move(name), std::move(label), adminive::Field_Control::number); +} + +template +auto boolean(std::string name, std::string label) { + return editable(std::move(name), std::move(label), adminive::Field_Control::boolean); +} + +template +auto select(std::string name, std::string label) { + return editable(std::move(name), std::move(label), adminive::Field_Control::select); +} + +template +auto color(std::string name, std::string label) { + return editable(std::move(name), std::move(label), adminive::Field_Control::color); +} + +template +auto property(std::string name, std::string label, adminive::Field_Control control) { + return adminive::field(std::move(name), std::move(label), + Gallery_Property_Accessor{}) + .editable() + .unsynchronized() + .control(control); +} + +template +auto readonly_property(std::string name, std::string label) { + return adminive::field(std::move(name), std::move(label), + Gallery_Property_Accessor{}); +} + +template +auto property_number(std::string name, std::string label) { + return property(std::move(name), std::move(label), + adminive::Field_Control::number); +} + +template +auto property_boolean(std::string name, std::string label) { + return property(std::move(name), std::move(label), + adminive::Field_Control::boolean); +} + +template +auto property_select(std::string name, std::string label) { + return property(std::move(name), std::move(label), + adminive::Field_Control::select); +} + +template +auto property_color(std::string name, std::string label) { + return property(std::move(name), std::move(label), + adminive::Field_Control::color); +} + +template +auto property_object(std::string name, std::string label) { + return property(std::move(name), std::move(label), + adminive::Field_Control::automatic); +} + +template +auto property_text(std::string name, std::string label) { + return property(std::move(name), std::move(label), + adminive::Field_Control::text); +} + +} // namespace renderive::web::gallery_adminive + +namespace adminive { + +template +struct Renderive_Validator_Metadata {}; + +template +struct Renderive_Validator_Metadata<::Range_Validator> { + static constexpr T minimum = Minimum; + static constexpr T maximum = Maximum; +}; + +template +struct Value_Adapter<::Validated_Value, Json> + : Renderive_Validator_Metadata { + using Storage = ::Validated_Value; + using value_type = T; + static const T& read(const Storage& value) noexcept { return value.get(); } + static void write(Storage& target, T value) { target = std::move(value); } +}; + +#define RENDERIVE_ENUM_ADAPTER(Type) \ + template <> struct Enum_Adapter \ + : renderive::web::gallery_adminive::Actual_Enum_Adapter {} + +RENDERIVE_ENUM_ADAPTER(Orientation); +RENDERIVE_ENUM_ADAPTER(Renderable_Cache_Mode); +RENDERIVE_ENUM_ADAPTER(Line_Interpolation_Mode); +RENDERIVE_ENUM_ADAPTER(Image_Interpolation_Mode); +RENDERIVE_ENUM_ADAPTER(Constellation_Diagram_Type); +RENDERIVE_ENUM_ADAPTER(Line_Style); +RENDERIVE_ENUM_ADAPTER(Line_Cap); +RENDERIVE_ENUM_ADAPTER(Line_Join); +RENDERIVE_ENUM_ADAPTER(Brush_Style); +#undef RENDERIVE_ENUM_ADAPTER + +template <> +struct Enum_Adapter + : renderive::web::gallery_adminive::Actual_Enum_Adapter< + renderive::web::gallery_adminive::Number_Locale_Choice> {}; + +template <> +struct Enum_Adapter + : renderive::web::gallery_adminive::Actual_Enum_Adapter< + renderive::web::gallery_adminive::Color_Map_Choice> {}; + +template +struct Value_Adapter { + using value_type = std::string; + static std::string read(renderive::Color value) { + char result[8]{}; + std::snprintf(result, sizeof(result), "#%02x%02x%02x", value.r, value.g, value.b); + return result; + } + static void write(renderive::Color& target, const std::string& value) { + if (value.size() != 7 || value.front() != '#' || + !std::all_of(value.begin() + 1, value.end(), [](unsigned char character) { + return std::isxdigit(character) != 0; + })) + throw std::invalid_argument("color must use #RRGGBB"); + const auto channel = [&value](std::size_t offset) { + return static_cast(std::stoul(value.substr(offset, 2), nullptr, 16)); + }; + target.r = channel(1); + target.g = channel(3); + target.b = channel(5); + } +}; + +template +struct Value_Adapter { + using Choice = renderive::web::gallery_adminive::Number_Locale_Choice; + using value_type = Choice; + static Choice read(renderive::Number_Locale value) { + return value.decimal_point == ',' ? Choice::Comma : Choice::Point; + } + static void write(renderive::Number_Locale& target, Choice value) { + target.decimal_point = value == Choice::Comma ? ',' : '.'; + } +}; + +template +struct Value_Adapter, Json> { + using Storage = renderive::Clamped_Property; + using value_type = T; + static constexpr T minimum = Minimum; + static constexpr T maximum = Maximum; + static T read(const Storage& value) noexcept { return value.get(); } + static void write(Storage& target, T value) { target = value; } +}; + +template +struct Value_Adapter { + using value_type = double; + static constexpr double minimum = 0.0; + static constexpr double maximum = 1.0; + static constexpr double multiple_of = 0.01; + static double read(const renderive::Unit_Interval& value) noexcept { return value.get(); } + static void write(renderive::Unit_Interval& target, double value) { target = value; } +}; + +template +struct Value_Adapter { + using Choice = renderive::web::gallery_adminive::Color_Map_Choice; + using value_type = Choice; + static Choice read(const renderive::Color_Map& value) { + const auto name = renderive::web::gallery_adminive::color_map_name(value); + if (name == "Ember") + return Choice::Ember; + if (name == "Grayscale") + return Choice::Grayscale; + return Choice::Spectrum; + } + static void write(renderive::Color_Map& target, Choice value) { + const std::string_view name = value == Choice::Ember ? "Ember" : + value == Choice::Grayscale ? "Grayscale" : "Spectrum"; + target = renderive::web::gallery_adminive::color_map(name); + } +}; + +template <> +struct Type_Descriptor { + static auto get() { + using T = renderive::Range; + using namespace renderive::web::gallery_adminive; + return adminive::object("range", "Range", + number<&T::origin>("origin", "Origin"), + number<&T::target>("target", "Target")); + } +}; + +template <> +struct Type_Descriptor { + static auto get() { + using T = renderive::Pen; + using namespace renderive::web::gallery_adminive; + return adminive::object("pen", "Pen", + color<&T::color>("color", "Color"), + number<&T::width>("width", "Width"), + select<&T::style>("style", "Style"), + select<&T::cap>("cap", "Cap"), + select<&T::join>("join", "Join")); + } +}; + +template <> +struct Type_Descriptor { + static auto get() { + using T = renderive::Brush; + using namespace renderive::web::gallery_adminive; + return adminive::object("brush", "Brush", + color<&T::color>("color", "Color"), + select<&T::style>("style", "Style")); + } +}; + +template <> +struct Type_Descriptor { + static auto get() { + using T = renderive::Font; + using namespace renderive::web::gallery_adminive; + return adminive::object("font", "Font", + number<&T::size>("size", "Size"), + number<&T::weight>("weight", "Weight"), + boolean<&T::italic>("italic", "Italic")); + } +}; + +#define RENDERIVE_AXIS_DESCRIPTOR(GalleryType, Name, Label) \ + template <> struct Type_Descriptor { \ + static auto get() { \ + using T = renderive::web::GalleryType; \ + using B = renderive::Axis_Base_Properties; \ + using P = renderive::Axis_Properties; \ + using namespace renderive::web::gallery_adminive; \ + return adminive::object(Name, Label, \ + readonly_property("x", "X"), \ + readonly_property("y", "Y"), \ + property_select("orientation", "Orientation"), \ + readonly_property("pixel_length", "Pixel length"), \ + property_number("tick_length", "Tick length"), \ + property_number("sub_tick_length", "Sub tick length"), \ + property_color("color", "Color"), \ + property_select("locale", "Decimal separator"), \ + property_text("unit_text", "Unit text"), \ + property_object("unit_text_font", "Unit font"), \ + property_object("unit_text_pen", "Unit pen"), \ + property_object("unit_text_background_brush", "Unit background"), \ + property_number("label_rotation_degrees", "Label rotation"), \ + property_object("coordinates", "Coordinates"), \ + property_number("precision", "Precision"), \ + property_boolean("wheel", "Wheel zoom"), \ + property_boolean("drag", "Drag pan")); \ + } \ + } + +RENDERIVE_AXIS_DESCRIPTOR(Gallery_Axis, "axis", "Axis"); +RENDERIVE_AXIS_DESCRIPTOR(Gallery_Frequency_Axis, "frequency_axis", "Frequency axis"); +#undef RENDERIVE_AXIS_DESCRIPTOR + +template <> +struct Type_Descriptor { + static auto get() { + using T = renderive::web::Gallery_Time_Axis; + using B = renderive::Axis_Base_Properties; + using P = renderive::Time_Axis_Properties; + using namespace renderive::web::gallery_adminive; + return adminive::object("time_axis", "Time axis", + readonly_property("x", "X"), + readonly_property("y", "Y"), + property_select("orientation", "Orientation"), + readonly_property("pixel_length", "Pixel length"), + property_number("tick_length", "Tick length"), + property_number("sub_tick_length", "Sub tick length"), + property_color("color", "Color"), + property_select("locale", "Decimal separator"), + property_text("unit_text", "Unit text"), + property_object("unit_text_font", "Unit font"), + property_object("unit_text_pen", "Unit pen"), + property_object("unit_text_background_brush", "Unit background"), + property_number("label_rotation_degrees", "Label rotation"), + property_number("visible_count", "Visible points"), + property_number("tick_label_spacing_px", "Label spacing"), + property_text("format", "Time format"), + property_boolean("newest_at_start", "Newest at start")); + } +}; + +#define RENDERIVE_GALLERY_DESCRIPTOR(GalleryType, PropertiesType, Name, Label, ...) \ + template <> struct Type_Descriptor { \ + static auto get() { \ + using T = renderive::web::GalleryType; \ + using P = renderive::PropertiesType; \ + using namespace renderive::web::gallery_adminive; \ + return adminive::object(Name, Label, __VA_ARGS__); \ + } \ + } + +RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Spectrum, Spectrum_Properties, "spectrum", "Spectrum", + property_number("frequency_point_size", "Frequency points"), + property_object("frequency_range", "Frequency range"), + property_number("center_frequency", "Center frequency"), + property_object("sweep_frequency_range", "Sweep range"), + property_boolean("max_hold_visible", "Max hold"), + property_boolean("min_hold_visible", "Min hold"), + property_boolean("max_marker_visible", "Max marker"), + property_boolean("use_min_marker", "Use min marker"), + property_boolean("sweep_region_visible", "Sweep region"), + property_boolean("visible_range_only", "Visible range only"), + property_select("interpolation_mode", "Interpolation"), + property_object("max_brush", "Max brush"), + property_object("current_brush", "Current brush"), + property_object("min_brush", "Min brush"), + property_object("max_pen", "Max pen"), + property_object("current_pen", "Current pen"), + property_object("min_pen", "Min pen"), + property_object("selected_marker_pen", "Selected marker pen"), + property_object("marker_pen", "Marker pen"), + property_object("middle_frequency_pen", "Middle frequency pen"), + property_object("sweep_region_brush", "Sweep region brush"), + property_boolean("tooltip_enabled", "Tooltip"), + property_object("tooltip_font", "Tooltip font"), + property_object("tooltip_text_pen", "Tooltip text pen"), + property_object("tooltip_background_brush", "Tooltip background")); + +RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Waterfall, Waterfall_Properties, "waterfall", "Waterfall", + property_object("frequency_range", "Frequency range"), + property_object("power_range", "Power range"), + property_number("frequency_bin_count", "Frequency bins"), + property_boolean("visible_range_only", "Visible range only"), + property_select("interpolation_mode", "Interpolation"), + property_select("color_map", "Color map"), + property_boolean("tooltip_enabled", "Tooltip"), + property_object("tooltip_font", "Tooltip font"), + property_object("tooltip_text_pen", "Tooltip text pen"), + property_object("tooltip_background_brush", "Tooltip background")); + +RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Afterglow, Afterglow_Properties, "afterglow", "Afterglow", + property_object("frequency_range", "Frequency range"), + property_object("power_range", "Power range"), + property_number("frequency_point_size", "Frequency points"), + property_number("power_point_size", "Power points"), + property_boolean("interpolate", "Interpolate power"), + property_number("attenuation_rate", "Attenuation"), + property_select("color_map", "Color map")); + +RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Sweep_Spectrum, Sweep_Spectrum_Properties, "sweep_spectrum", "Sweep spectrum", + property_object("frequency_range", "Frequency range"), + property_number("bins_per_block", "Bins per block"), + property_number("block_count", "Block count"), + property_object("pen", "Sweep pen"), + property_object("current_frequency_pen", "Current frequency pen"), + property_boolean("visible_range_only", "Visible range only"), + property_select("interpolation_mode", "Interpolation")); + +RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Frequency_Trace, Frequency_Trace_Properties, "frequency_trace", "Frequency trace", + property_object("pen", "Trace pen")); + +RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Selection_Rectangle_Overlay, Selection_Rectangle_Overlay_Properties, "selection_overlay", "Selection overlay", + property_object("label_font", "Label font"), + property_object("label_pen", "Label pen"), + property_object("selection_brush", "Selection brush"), + property_object("selection_border_pen", "Selection border")); + +RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Constellation_Diagram, Constellation_Diagram_Properties, "constellation", "Constellation diagram", + property_object("i_range", "I range"), + property_object("q_range", "Q range"), + property_color("point_color", "Point color"), + property_color("anchor_color", "Anchor color"), + property_number("point_lifetime_ms", "Point lifetime"), + property_select("type", "Constellation"), + property_number("phase_offset_radians", "Phase offset")); + +#undef RENDERIVE_GALLERY_DESCRIPTOR + +} // namespace adminive namespace renderive::web { -template -struct Gallery_Property_Accessor { - using object_type = Object; - using Properties = typename Object::Properties; - using value_type = std::remove_cvref_t().*Member)>; - using storage_identity = void; - using dependency_spec = structive::No_Property_Dependencies; - static constexpr bool readable = true; - static constexpr bool writable = true; - static constexpr bool synchronized_view_read = false; - static constexpr bool trusted_object_access = true; +template +std::size_t gallery_control_count() { + using Json = nlohmann::json; + const auto count_fields = [](const auto& self, const Json& fields) -> std::size_t { + std::size_t result{}; + for (const auto& field : fields) { + if (field.value("editable", false) && !field.contains("children")) + ++result; + if (field.contains("children")) + result += self(self, field.at("children")); + } + return result; + }; + const Json descriptor = adminive::to_descriptor_json(); + return count_fields(count_fields, descriptor.at("fields")); +} - value_type read(const Object& object) const { - return object.template adminive_read(); - } - void write(Object& object, value_type value) const { - object.template adminive_write(std::move(value)); - } -}; - -template -class Gallery_Plottable : public Base { -public: - using Properties = typename Base::Properties; - using Base::Base; - -private: - template - auto adminive_read() const { - return this->template property_value(); - } - template - void adminive_write(Value&& value) { - this->template set(std::forward(value)); - } - template - friend struct Gallery_Property_Accessor; -}; - -class Gallery_Spectrum final : public Gallery_Plottable { -public: - using Gallery_Plottable::Gallery_Plottable; - using Builder = Renderable_Builder; -}; - -class Gallery_Waterfall final : public Gallery_Plottable { -public: - using Gallery_Plottable::Gallery_Plottable; - using Builder = Renderable_Builder; -}; - -class Gallery_Afterglow final : public Gallery_Plottable { -public: - using Gallery_Plottable::Gallery_Plottable; - using Builder = Renderable_Builder; -}; - -class Gallery_Sweep_Spectrum final : public Gallery_Plottable { -public: - using Gallery_Plottable::Gallery_Plottable; - using Builder = Renderable_Builder; -}; - -class Gallery_Frequency_Trace final : public Gallery_Plottable { -public: - using Gallery_Plottable::Gallery_Plottable; - using Builder = Renderable_Builder; -}; - -class Gallery_Selection_Rectangle_Overlay final - : public Gallery_Plottable { -public: - using Gallery_Plottable::Gallery_Plottable; - using Builder = Renderable_Builder; -}; - -class Gallery_Constellation_Diagram final - : public Gallery_Plottable { -public: - using Gallery_Plottable::Gallery_Plottable; - using Builder = Renderable_Builder; -}; - -template -class Gallery_Axis_Base : public Base { -public: - using Properties = Properties_Type; - using Base::Base; - -private: - template - auto adminive_read() const { - return this->read([](const auto& state) { - return state.*Member; - }); - } - template - void adminive_write(Value&& value) { - this->template set(std::forward(value)); - } - template - friend struct Gallery_Property_Accessor; -}; - -class Gallery_Axis final : public Gallery_Axis_Base { -public: - using Gallery_Axis_Base::Gallery_Axis_Base; - using Builder = detail::Axis_Renderable_Builder; -}; - -class Gallery_Frequency_Axis final - : public Gallery_Axis_Base { -public: - using Gallery_Axis_Base::Gallery_Axis_Base; - using Builder = detail::Axis_Renderable_Builder; -}; - -class Gallery_Time_Axis final - : public Gallery_Axis_Base { -public: - using Gallery_Axis_Base::Gallery_Axis_Base; - using Builder = detail::Axis_Renderable_Builder; -}; +inline std::size_t gallery_renderable_control_count(std::string_view case_id) { + const auto axis = gallery_control_count(); + const auto frequency_axis = gallery_control_count(); + const auto time_axis = gallery_control_count(); + if (case_id == "axis_lab") + return frequency_axis + time_axis; + if (case_id == "spectrum") + return frequency_axis + axis + gallery_control_count(); + if (case_id == "selection_overlay") + return frequency_axis + axis + gallery_control_count() + + gallery_control_count(); + if (case_id == "waterfall") + return frequency_axis + time_axis + gallery_control_count(); + if (case_id == "afterglow") + return frequency_axis + axis + gallery_control_count(); + if (case_id == "sweep_spectrum") + return 2 * axis + gallery_control_count(); + if (case_id == "frequency_trace") + return time_axis + axis + gallery_control_count(); + if (case_id == "constellation") + return 2 * axis + gallery_control_count(); + return 0; +} } // namespace renderive::web diff --git a/web_server/app/Gallery_Renderables_Adminive.h b/web_server/app/Gallery_Renderables_Adminive.h deleted file mode 100644 index 5a0ef68..0000000 --- a/web_server/app/Gallery_Renderables_Adminive.h +++ /dev/null @@ -1,539 +0,0 @@ -#pragma once - -#include "Gallery_Renderables.h" - -#include "adminive/adminive.hpp" -#include "adminive/adapters/nlohmann_json.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace renderive::web::gallery_adminive { - -enum class Number_Locale_Choice { Point, Comma }; -enum class Color_Map_Choice { Spectrum, Ember, Grayscale }; - -template -struct Enum_Metadata; - -#define RENDERIVE_ENUM_METADATA(Type, ...) \ - template <> struct Enum_Metadata { static constexpr auto values = std::array{__VA_ARGS__}; } - -RENDERIVE_ENUM_METADATA(Orientation, - std::pair{Orientation::Horizontal, std::string_view{"Horizontal"}}, - std::pair{Orientation::Vertical, std::string_view{"Vertical"}}); -RENDERIVE_ENUM_METADATA(Renderable_Cache_Mode, - std::pair{Renderable_Cache_Mode::Direct, std::string_view{"Direct"}}, - std::pair{Renderable_Cache_Mode::Local_Pixel, std::string_view{"Local_Pixel"}}); -RENDERIVE_ENUM_METADATA(Line_Interpolation_Mode, - std::pair{Line_Interpolation_Mode::Nearest_Sample, std::string_view{"Nearest_Sample"}}, - std::pair{Line_Interpolation_Mode::Linear_Value, std::string_view{"Linear_Value"}}, - std::pair{Line_Interpolation_Mode::Linear_Power_Domain, std::string_view{"Linear_Power_Domain"}}, - std::pair{Line_Interpolation_Mode::Step_Left, std::string_view{"Step_Left"}}, - std::pair{Line_Interpolation_Mode::Step_Right, std::string_view{"Step_Right"}}, - std::pair{Line_Interpolation_Mode::Cubic_Value, std::string_view{"Cubic_Value"}}); -RENDERIVE_ENUM_METADATA(Image_Interpolation_Mode, - std::pair{Image_Interpolation_Mode::Nearest, std::string_view{"Nearest"}}, - std::pair{Image_Interpolation_Mode::Bilinear, std::string_view{"Bilinear"}}, - std::pair{Image_Interpolation_Mode::Bicubic, std::string_view{"Bicubic"}}); -RENDERIVE_ENUM_METADATA(Constellation_Diagram_Type, - std::pair{Constellation_Diagram_Type::Psk4, std::string_view{"PSK4"}}, - std::pair{Constellation_Diagram_Type::Psk8, std::string_view{"PSK8"}}, - std::pair{Constellation_Diagram_Type::Psk16, std::string_view{"PSK16"}}); -RENDERIVE_ENUM_METADATA(Line_Style, - std::pair{Line_Style::None, std::string_view{"None"}}, - std::pair{Line_Style::Solid, std::string_view{"Solid"}}, - std::pair{Line_Style::Dash, std::string_view{"Dash"}}, - std::pair{Line_Style::Dot, std::string_view{"Dot"}}); -RENDERIVE_ENUM_METADATA(Line_Cap, - std::pair{Line_Cap::Butt, std::string_view{"Butt"}}, - std::pair{Line_Cap::Square, std::string_view{"Square"}}, - std::pair{Line_Cap::Round, std::string_view{"Round"}}); -RENDERIVE_ENUM_METADATA(Line_Join, - std::pair{Line_Join::Miter, std::string_view{"Miter"}}, - std::pair{Line_Join::Bevel, std::string_view{"Bevel"}}, - std::pair{Line_Join::Round, std::string_view{"Round"}}); -RENDERIVE_ENUM_METADATA(Brush_Style, - std::pair{Brush_Style::None, std::string_view{"None"}}, - std::pair{Brush_Style::Solid, std::string_view{"Solid"}}); -RENDERIVE_ENUM_METADATA(Number_Locale_Choice, - std::pair{Number_Locale_Choice::Point, std::string_view{"."}}, - std::pair{Number_Locale_Choice::Comma, std::string_view{","}}); -RENDERIVE_ENUM_METADATA(Color_Map_Choice, - std::pair{Color_Map_Choice::Spectrum, std::string_view{"Spectrum"}}, - std::pair{Color_Map_Choice::Ember, std::string_view{"Ember"}}, - std::pair{Color_Map_Choice::Grayscale, std::string_view{"Grayscale"}}); - -#undef RENDERIVE_ENUM_METADATA - -template -struct Actual_Enum_Adapter { - static std::vector values() { - std::vector result; - result.reserve(Enum_Metadata::values.size()); - for (const auto& [value, name] : Enum_Metadata::values) - result.push_back(value); - return result; - } - - static std::string_view name(Enum value) { - for (const auto& [candidate, name] : Enum_Metadata::values) { - if (candidate == value) - return name; - } - return {}; - } - - static std::optional cast(std::string_view name) { - for (const auto& [value, candidate] : Enum_Metadata::values) { - if (candidate == name) - return value; - } - return std::nullopt; - } -}; - -inline Color blend(Color first, Color second, double amount) { - const auto channel = [amount](std::uint8_t a, std::uint8_t b) { - return static_cast(std::clamp( - std::lround(a + (b - a) * amount), 0L, 255L)); - }; - return {channel(first.r, second.r), channel(first.g, second.g), - channel(first.b, second.b), channel(first.a, second.a)}; -} - -inline Color_Map color_map(std::string_view palette) { - std::array stops{ - Color{5, 10, 25, 255}, Color{20, 52, 112, 255}, Color{39, 196, 181, 255}, - Color{242, 201, 76, 255}, Color{255, 76, 106, 255} - }; - if (palette == "Ember") { - stops = {Color{10, 5, 8, 255}, Color{62, 18, 30, 255}, Color{153, 47, 39, 255}, - Color{244, 132, 48, 255}, Color{255, 238, 166, 255}}; - } else if (palette == "Grayscale") { - stops = {Color{0, 0, 0, 255}, Color{52, 52, 52, 255}, Color{112, 112, 112, 255}, - Color{188, 188, 188, 255}, Color{255, 255, 255, 255}}; - } - std::vector colors; - colors.reserve(256); - for (int index = 0; index < 256; ++index) { - const double position = index / 255.0 * (stops.size() - 1); - const auto first = static_cast(std::floor(position)); - const auto second = std::min(first + 1, stops.size() - 1); - colors.push_back(premultiply(blend(stops[first], stops[second], position - first))); - } - return Color_Map(std::move(colors)); -} - -inline std::string color_map_name(const Color_Map& value) { - for (const std::string_view name : {std::string_view{"Spectrum"}, - std::string_view{"Ember"}, - std::string_view{"Grayscale"}}) { - if (value.colors() == color_map(name).colors()) - return std::string(name); - } - return "Spectrum"; -} - -template -auto editable(std::string name, std::string label, adminive::Field_Control control) { - return adminive::field(std::move(name), std::move(label)) - .editable().control(control); -} - -template -auto number(std::string name, std::string label) { - return editable(std::move(name), std::move(label), adminive::Field_Control::number); -} - -template -auto boolean(std::string name, std::string label) { - return editable(std::move(name), std::move(label), adminive::Field_Control::boolean); -} - -template -auto select(std::string name, std::string label) { - return editable(std::move(name), std::move(label), adminive::Field_Control::select); -} - -template -auto color(std::string name, std::string label) { - return editable(std::move(name), std::move(label), adminive::Field_Control::color); -} - -template -auto property(std::string name, std::string label, adminive::Field_Control control) { - return adminive::field(std::move(name), std::move(label), - Gallery_Property_Accessor{}) - .editable() - .unsynchronized() - .control(control); -} - -template -auto readonly_property(std::string name, std::string label) { - return adminive::field(std::move(name), std::move(label), - Gallery_Property_Accessor{}); -} - -template -auto property_number(std::string name, std::string label) { - return property(std::move(name), std::move(label), - adminive::Field_Control::number); -} - -template -auto property_boolean(std::string name, std::string label) { - return property(std::move(name), std::move(label), - adminive::Field_Control::boolean); -} - -template -auto property_select(std::string name, std::string label) { - return property(std::move(name), std::move(label), - adminive::Field_Control::select); -} - -template -auto property_color(std::string name, std::string label) { - return property(std::move(name), std::move(label), - adminive::Field_Control::color); -} - -template -auto property_object(std::string name, std::string label) { - return property(std::move(name), std::move(label), - adminive::Field_Control::automatic); -} - -template -auto property_text(std::string name, std::string label) { - return property(std::move(name), std::move(label), - adminive::Field_Control::text); -} - -} // namespace renderive::web::gallery_adminive - -namespace adminive { - -template -struct Renderive_Validator_Metadata {}; - -template -struct Renderive_Validator_Metadata<::Range_Validator> { - static constexpr T minimum = Minimum; - static constexpr T maximum = Maximum; -}; - -template -struct Value_Adapter<::Validated_Value, Json> - : Renderive_Validator_Metadata { - using Storage = ::Validated_Value; - using value_type = T; - static const T& read(const Storage& value) noexcept { return value.get(); } - static void write(Storage& target, T value) { target = std::move(value); } -}; - -#define RENDERIVE_ENUM_ADAPTER(Type) \ - template <> struct Enum_Adapter \ - : renderive::web::gallery_adminive::Actual_Enum_Adapter {} - -RENDERIVE_ENUM_ADAPTER(Orientation); -RENDERIVE_ENUM_ADAPTER(Renderable_Cache_Mode); -RENDERIVE_ENUM_ADAPTER(Line_Interpolation_Mode); -RENDERIVE_ENUM_ADAPTER(Image_Interpolation_Mode); -RENDERIVE_ENUM_ADAPTER(Constellation_Diagram_Type); -RENDERIVE_ENUM_ADAPTER(Line_Style); -RENDERIVE_ENUM_ADAPTER(Line_Cap); -RENDERIVE_ENUM_ADAPTER(Line_Join); -RENDERIVE_ENUM_ADAPTER(Brush_Style); -#undef RENDERIVE_ENUM_ADAPTER - -template <> -struct Enum_Adapter - : renderive::web::gallery_adminive::Actual_Enum_Adapter< - renderive::web::gallery_adminive::Number_Locale_Choice> {}; - -template <> -struct Enum_Adapter - : renderive::web::gallery_adminive::Actual_Enum_Adapter< - renderive::web::gallery_adminive::Color_Map_Choice> {}; - -template -struct Value_Adapter { - using value_type = std::string; - static std::string read(renderive::Color value) { - char result[8]{}; - std::snprintf(result, sizeof(result), "#%02x%02x%02x", value.r, value.g, value.b); - return result; - } - static void write(renderive::Color& target, const std::string& value) { - if (value.size() != 7 || value.front() != '#' || - !std::all_of(value.begin() + 1, value.end(), [](unsigned char character) { - return std::isxdigit(character) != 0; - })) - throw std::invalid_argument("color must use #RRGGBB"); - const auto channel = [&value](std::size_t offset) { - return static_cast(std::stoul(value.substr(offset, 2), nullptr, 16)); - }; - target.r = channel(1); - target.g = channel(3); - target.b = channel(5); - } -}; - -template -struct Value_Adapter { - using Choice = renderive::web::gallery_adminive::Number_Locale_Choice; - using value_type = Choice; - static Choice read(renderive::Number_Locale value) { - return value.decimal_point == ',' ? Choice::Comma : Choice::Point; - } - static void write(renderive::Number_Locale& target, Choice value) { - target.decimal_point = value == Choice::Comma ? ',' : '.'; - } -}; - -template -struct Value_Adapter, Json> { - using Storage = renderive::Clamped_Property; - using value_type = T; - static constexpr T minimum = Minimum; - static constexpr T maximum = Maximum; - static T read(const Storage& value) noexcept { return value.get(); } - static void write(Storage& target, T value) { target = value; } -}; - -template -struct Value_Adapter { - using value_type = double; - static constexpr double minimum = 0.0; - static constexpr double maximum = 1.0; - static constexpr double multiple_of = 0.01; - static double read(const renderive::Unit_Interval& value) noexcept { return value.get(); } - static void write(renderive::Unit_Interval& target, double value) { target = value; } -}; - -template -struct Value_Adapter { - using Choice = renderive::web::gallery_adminive::Color_Map_Choice; - using value_type = Choice; - static Choice read(const renderive::Color_Map& value) { - const auto name = renderive::web::gallery_adminive::color_map_name(value); - if (name == "Ember") - return Choice::Ember; - if (name == "Grayscale") - return Choice::Grayscale; - return Choice::Spectrum; - } - static void write(renderive::Color_Map& target, Choice value) { - const std::string_view name = value == Choice::Ember ? "Ember" : - value == Choice::Grayscale ? "Grayscale" : "Spectrum"; - target = renderive::web::gallery_adminive::color_map(name); - } -}; - -template <> -struct Type_Descriptor { - static auto get() { - using T = renderive::Range; - using namespace renderive::web::gallery_adminive; - return adminive::object("range", "Range", - number<&T::origin>("origin", "Origin"), - number<&T::target>("target", "Target")); - } -}; - -template <> -struct Type_Descriptor { - static auto get() { - using T = renderive::Pen; - using namespace renderive::web::gallery_adminive; - return adminive::object("pen", "Pen", - color<&T::color>("color", "Color"), - number<&T::width>("width", "Width"), - select<&T::style>("style", "Style"), - select<&T::cap>("cap", "Cap"), - select<&T::join>("join", "Join")); - } -}; - -template <> -struct Type_Descriptor { - static auto get() { - using T = renderive::Brush; - using namespace renderive::web::gallery_adminive; - return adminive::object("brush", "Brush", - color<&T::color>("color", "Color"), - select<&T::style>("style", "Style")); - } -}; - -template <> -struct Type_Descriptor { - static auto get() { - using T = renderive::Font; - using namespace renderive::web::gallery_adminive; - return adminive::object("font", "Font", - number<&T::size>("size", "Size"), - number<&T::weight>("weight", "Weight"), - boolean<&T::italic>("italic", "Italic")); - } -}; - -#define RENDERIVE_AXIS_DESCRIPTOR(GalleryType, Name, Label) \ - template <> struct Type_Descriptor { \ - static auto get() { \ - using T = renderive::web::GalleryType; \ - using B = renderive::Axis_Base_Properties; \ - using P = renderive::Axis_Properties; \ - using namespace renderive::web::gallery_adminive; \ - return adminive::object(Name, Label, \ - readonly_property("x", "X"), \ - readonly_property("y", "Y"), \ - property_select("orientation", "Orientation"), \ - readonly_property("pixel_length", "Pixel length"), \ - property_number("tick_length", "Tick length"), \ - property_number("sub_tick_length", "Sub tick length"), \ - property_color("color", "Color"), \ - property_select("locale", "Decimal separator"), \ - property_text("unit_text", "Unit text"), \ - property_object("unit_text_font", "Unit font"), \ - property_object("unit_text_pen", "Unit pen"), \ - property_object("unit_text_background_brush", "Unit background"), \ - property_number("label_rotation_degrees", "Label rotation"), \ - property_object("coordinates", "Coordinates"), \ - property_number("precision", "Precision"), \ - property_boolean("wheel", "Wheel zoom"), \ - property_boolean("drag", "Drag pan")); \ - } \ - } - -RENDERIVE_AXIS_DESCRIPTOR(Gallery_Axis, "gallery_axis", "Axis"); -RENDERIVE_AXIS_DESCRIPTOR(Gallery_Frequency_Axis, "gallery_frequency_axis", "Frequency axis"); -#undef RENDERIVE_AXIS_DESCRIPTOR - -template <> -struct Type_Descriptor { - static auto get() { - using T = renderive::web::Gallery_Time_Axis; - using B = renderive::Axis_Base_Properties; - using P = renderive::Time_Axis_Properties; - using namespace renderive::web::gallery_adminive; - return adminive::object("gallery_time_axis", "Time axis", - readonly_property("x", "X"), - readonly_property("y", "Y"), - property_select("orientation", "Orientation"), - readonly_property("pixel_length", "Pixel length"), - property_number("tick_length", "Tick length"), - property_number("sub_tick_length", "Sub tick length"), - property_color("color", "Color"), - property_select("locale", "Decimal separator"), - property_text("unit_text", "Unit text"), - property_object("unit_text_font", "Unit font"), - property_object("unit_text_pen", "Unit pen"), - property_object("unit_text_background_brush", "Unit background"), - property_number("label_rotation_degrees", "Label rotation"), - property_number("visible_count", "Visible points"), - property_number("tick_label_spacing_px", "Label spacing"), - property_text("format", "Time format"), - property_boolean("newest_at_start", "Newest at start")); - } -}; - -#define RENDERIVE_GALLERY_DESCRIPTOR(GalleryType, PropertiesType, Label, ...) \ - template <> struct Type_Descriptor { \ - static auto get() { \ - using T = renderive::web::GalleryType; \ - using P = renderive::PropertiesType; \ - using namespace renderive::web::gallery_adminive; \ - return adminive::object(#GalleryType, Label, __VA_ARGS__); \ - } \ - } - -RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Spectrum, Spectrum_Properties, "Spectrum", - property_number("frequency_point_size", "Frequency points"), - property_object("frequency_range", "Frequency range"), - property_number("center_frequency", "Center frequency"), - property_object("sweep_frequency_range", "Sweep range"), - property_boolean("max_hold_visible", "Max hold"), - property_boolean("min_hold_visible", "Min hold"), - property_boolean("max_marker_visible", "Max marker"), - property_boolean("use_min_marker", "Use min marker"), - property_boolean("sweep_region_visible", "Sweep region"), - property_boolean("visible_range_only", "Visible range only"), - property_select("interpolation_mode", "Interpolation"), - property_object("max_brush", "Max brush"), - property_object("current_brush", "Current brush"), - property_object("min_brush", "Min brush"), - property_object("max_pen", "Max pen"), - property_object("current_pen", "Current pen"), - property_object("min_pen", "Min pen"), - property_object("selected_marker_pen", "Selected marker pen"), - property_object("marker_pen", "Marker pen"), - property_object("middle_frequency_pen", "Middle frequency pen"), - property_object("sweep_region_brush", "Sweep region brush"), - property_boolean("tooltip_enabled", "Tooltip"), - property_object("tooltip_font", "Tooltip font"), - property_object("tooltip_text_pen", "Tooltip text pen"), - property_object("tooltip_background_brush", "Tooltip background")); - -RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Waterfall, Waterfall_Properties, "Waterfall", - property_object("frequency_range", "Frequency range"), - property_object("power_range", "Power range"), - property_number("frequency_bin_count", "Frequency bins"), - property_boolean("visible_range_only", "Visible range only"), - property_select("interpolation_mode", "Interpolation"), - property_select("color_map", "Color map"), - property_boolean("tooltip_enabled", "Tooltip"), - property_object("tooltip_font", "Tooltip font"), - property_object("tooltip_text_pen", "Tooltip text pen"), - property_object("tooltip_background_brush", "Tooltip background")); - -RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Afterglow, Afterglow_Properties, "Afterglow", - property_object("frequency_range", "Frequency range"), - property_object("power_range", "Power range"), - property_number("frequency_point_size", "Frequency points"), - property_number("power_point_size", "Power points"), - property_boolean("interpolate", "Interpolate power"), - property_number("attenuation_rate", "Attenuation"), - property_select("color_map", "Color map")); - -RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Sweep_Spectrum, Sweep_Spectrum_Properties, "Sweep spectrum", - property_object("frequency_range", "Frequency range"), - property_number("bins_per_block", "Bins per block"), - property_number("block_count", "Block count"), - property_object("pen", "Sweep pen"), - property_object("current_frequency_pen", "Current frequency pen"), - property_boolean("visible_range_only", "Visible range only"), - property_select("interpolation_mode", "Interpolation")); - -RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Frequency_Trace, Frequency_Trace_Properties, "Frequency trace", - property_object("pen", "Trace pen")); - -RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Selection_Rectangle_Overlay, Selection_Rectangle_Overlay_Properties, "Selection overlay", - property_object("label_font", "Label font"), - property_object("label_pen", "Label pen"), - property_object("selection_brush", "Selection brush"), - property_object("selection_border_pen", "Selection border")); - -RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Constellation_Diagram, Constellation_Diagram_Properties, "Constellation diagram", - property_object("i_range", "I range"), - property_object("q_range", "Q range"), - property_color("point_color", "Point color"), - property_color("anchor_color", "Anchor color"), - property_number("point_lifetime_ms", "Point lifetime"), - property_select("type", "Constellation"), - property_number("phase_offset_radians", "Phase offset")); - -#undef RENDERIVE_GALLERY_DESCRIPTOR - -} // namespace adminive diff --git a/web_server/app/Gallery_Session_Control_Adminive.h b/web_server/app/Gallery_Session_Control_Adminive.h index 975e45a..997f535 100644 --- a/web_server/app/Gallery_Session_Control_Adminive.h +++ b/web_server/app/Gallery_Session_Control_Adminive.h @@ -1,7 +1,7 @@ #pragma once #include "Gallery_Session_Control.h" -#include "Gallery_Renderables_Adminive.h" +#include "Gallery_Renderables.h" #include #include @@ -44,7 +44,7 @@ struct Type_Descriptor { using T = renderive::web::Gallery_Session_Control; using namespace renderive::web::gallery_adminive; const auto dirty = [](T& value) { value.plot_.notify_model_dirty(); }; - return adminive::object("gallery_session", "Scene", + return adminive::object("scene", "Scene", callback_property( "background_color", "Background", Field_Control::color, [](const T& value) { return value.plot_.background_color(); }, diff --git a/web_server/tests/Web_Bridge_Tests.cpp b/web_server/tests/Web_Bridge_Tests.cpp index 473a518..405bf22 100644 --- a/web_server/tests/Web_Bridge_Tests.cpp +++ b/web_server/tests/Web_Bridge_Tests.cpp @@ -293,7 +293,7 @@ TEST(RenderiveWebGallery, AdminiveResourcesPatchTheRealRenderableState) { }; const auto& spectrum = find_resource(response, "spectrum"); - EXPECT_EQ(spectrum.at("descriptor").at("name"), "Gallery_Spectrum"); + EXPECT_EQ(spectrum.at("descriptor").at("name"), "spectrum"); EXPECT_TRUE(spectrum.at("data").at("frequency_range").is_object()); EXPECT_EQ(spectrum.at("data").at("frequency_point_size"), 512);