#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