Files
Renderive/web_server/app/Gallery_Properties_Adminive.h
T
2026-08-11 17:06:17 +08:00

424 lines
25 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include "Gallery_Properties.h"
#include "adminive/adminive.hpp"
#include "adminive/adapters/nlohmann_json.hpp"
#include <array>
#include <optional>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
namespace renderive::web::gallery_adminive {
template <class Enum>
struct Enum_Metadata;
template <>
struct Enum_Metadata<Gallery_Cache_Mode> {
static constexpr std::array values{
std::pair{Gallery_Cache_Mode::Direct, std::string_view{"Direct"}},
std::pair{Gallery_Cache_Mode::Local_Pixel, std::string_view{"Local_Pixel"}}
};
};
template <>
struct Enum_Metadata<Gallery_Axis_Orientation> {
static constexpr std::array values{
std::pair{Gallery_Axis_Orientation::Horizontal, std::string_view{"Horizontal"}},
std::pair{Gallery_Axis_Orientation::Vertical, std::string_view{"Vertical"}}
};
};
template <>
struct Enum_Metadata<Gallery_Decimal_Separator> {
static constexpr std::array values{
std::pair{Gallery_Decimal_Separator::Dot, std::string_view{"."}},
std::pair{Gallery_Decimal_Separator::Comma, std::string_view{","}}
};
};
template <>
struct Enum_Metadata<Gallery_Line_Interpolation> {
static constexpr std::array values{
std::pair{Gallery_Line_Interpolation::Nearest_Sample, std::string_view{"Nearest_Sample"}},
std::pair{Gallery_Line_Interpolation::Linear_Value, std::string_view{"Linear_Value"}},
std::pair{Gallery_Line_Interpolation::Linear_Power_Domain, std::string_view{"Linear_Power_Domain"}},
std::pair{Gallery_Line_Interpolation::Step_Left, std::string_view{"Step_Left"}},
std::pair{Gallery_Line_Interpolation::Step_Right, std::string_view{"Step_Right"}},
std::pair{Gallery_Line_Interpolation::Cubic_Value, std::string_view{"Cubic_Value"}}
};
};
template <>
struct Enum_Metadata<Gallery_Image_Interpolation> {
static constexpr std::array values{
std::pair{Gallery_Image_Interpolation::Nearest, std::string_view{"Nearest"}},
std::pair{Gallery_Image_Interpolation::Bilinear, std::string_view{"Bilinear"}},
std::pair{Gallery_Image_Interpolation::Bicubic, std::string_view{"Bicubic"}}
};
};
template <>
struct Enum_Metadata<Gallery_Color_Map> {
static constexpr std::array values{
std::pair{Gallery_Color_Map::Spectrum, std::string_view{"Spectrum"}},
std::pair{Gallery_Color_Map::Ember, std::string_view{"Ember"}},
std::pair{Gallery_Color_Map::Grayscale, std::string_view{"Grayscale"}}
};
};
template <>
struct Enum_Metadata<Gallery_Constellation_Type> {
static constexpr std::array values{
std::pair{Gallery_Constellation_Type::PSK4, std::string_view{"PSK4"}},
std::pair{Gallery_Constellation_Type::PSK8, std::string_view{"PSK8"}},
std::pair{Gallery_Constellation_Type::PSK16, std::string_view{"PSK16"}}
};
};
template <class Enum>
struct Enum_Adapter {
static std::vector<Enum> values() {
std::vector<Enum> result;
result.reserve(Enum_Metadata<Enum>::values.size());
for(const auto& [value, name] : Enum_Metadata<Enum>::values)
result.push_back(value);
return result;
}
static std::string_view name(Enum value) {
for(const auto& [candidate, name] : Enum_Metadata<Enum>::values) {
if(candidate == value)
return name;
}
return {};
}
static std::optional<Enum> cast(std::string_view name) {
for(const auto& [value, candidate] : Enum_Metadata<Enum>::values) {
if(candidate == name)
return value;
}
return std::nullopt;
}
};
template <auto Member>
auto boolean_property(std::string name, std::string label, std::string api) {
return adminive::field<Member>(std::move(name), std::move(label)).editable().boolean_input().description(std::move(api));
}
template <auto Member>
auto number_property(std::string name, std::string label, std::string api) {
return adminive::field<Member>(std::move(name), std::move(label)).editable().number_input().description(std::move(api));
}
template <auto Member>
auto text_property(std::string name, std::string label, std::string api) {
return adminive::field<Member>(std::move(name), std::move(label)).editable().text_input().description(std::move(api));
}
template <auto Member>
auto color_property(std::string name, std::string label, std::string api) {
return adminive::field<Member>(std::move(name), std::move(label)).editable().color_input().description(std::move(api));
}
template <auto Member>
auto select_property(std::string name, std::string label, std::string api) {
return adminive::field<Member>(std::move(name), std::move(label)).editable().select_input().description(std::move(api));
}
inline auto common_fields() {
using T = Gallery_Common_Properties;
return std::tuple{
color_property<&T::background_color>("background_color", "背景颜色", "Plot_Core::set_background_color"),
boolean_property<&T::performance_overlay>("performance_overlay", "性能叠层", "set_performance_overlay_enabled"),
boolean_property<&T::renderable_visible>("renderable_visible", "控件可见", "Renderable::set_visible"),
select_property<&T::cache_mode>("cache_mode", "缓存模式", "Renderable::set_cache_mode"),
text_property<&T::object_name>("object_name", "对象名称", "Renderable::set_object_name")
};
}
inline auto low_latency_fields() {
using T = Gallery_Low_Latency_Properties;
return std::tuple{
boolean_property<&T::frequency_limit_enabled>("frequency_limit_enabled", "启用频率上限", "Plot_Core::set_max_render_fps / clear_max_render_fps"),
number_property<&T::max_render_fps>("max_render_fps", "最大渲染 FPS", "Plot_Core::set_max_render_fps"),
boolean_property<&T::consumer_feedback_enabled>("consumer_feedback_enabled", "启用消费者反馈", "Plot_Core::set_consumer_feedback / clear_consumer_feedback"),
boolean_property<&T::consumer_pixel_feedback_enabled>("consumer_pixel_feedback_enabled", "启用像素响应反馈", "Gallery_Plot_Session::set_client_metrics"),
boolean_property<&T::consumer_presentation_feedback_enabled>("consumer_presentation_feedback_enabled", "启用浏览器呈现反馈", "Gallery_Plot_Session::set_client_metrics"),
boolean_property<&T::consumer_manual_feedback_enabled>("consumer_manual_feedback_enabled", "启用手动消费者反馈", "Gallery_Plot_Session::apply_consumer_feedback"),
number_property<&T::consumer_manual_fps>("consumer_manual_fps", "手动消费者 FPS", "Gallery_Plot_Session::apply_consumer_feedback")
};
}
inline auto axis_fields() {
using T = Gallery_Axis_Properties;
return std::tuple{
select_property<&T::axis_orientation>("axis_orientation", "主轴方向", "Abs_Axis::set_orientation"),
number_property<&T::axis_x_offset>("axis_x_offset", "X 偏移", "Abs_Axis::set_x"),
number_property<&T::axis_y_offset>("axis_y_offset", "Y 偏移", "Abs_Axis::set_y"),
number_property<&T::axis_length_percent>("axis_length_percent", "轴长百分比", "Abs_Axis::set_pixel_length"),
number_property<&T::tick_length>("tick_length", "主刻度长度", "Abs_Axis::set_tick_length"),
number_property<&T::sub_tick_length>("sub_tick_length", "次刻度长度", "Abs_Axis::set_sub_tick_length"),
color_property<&T::axis_color>("axis_color", "轴颜色", "Abs_Axis::set_color"),
select_property<&T::decimal_separator>("decimal_separator", "小数点", "Abs_Axis::set_locale"),
text_property<&T::unit_text>("unit_text", "单位文本", "Abs_Axis::set_unit_text"),
number_property<&T::unit_font_size>("unit_font_size", "单位字号", "Abs_Axis::set_unit_text_font"),
number_property<&T::unit_font_weight>("unit_font_weight", "单位字重", "Abs_Axis::set_unit_text_font"),
boolean_property<&T::unit_font_italic>("unit_font_italic", "单位斜体", "Abs_Axis::set_unit_text_font"),
color_property<&T::unit_pen_color>("unit_pen_color", "单位文字颜色", "Abs_Axis::set_unit_text_pen"),
color_property<&T::unit_background_color>("unit_background_color", "单位背景颜色", "Abs_Axis::set_unit_text_background_brush"),
number_property<&T::label_rotation>("label_rotation", "标签旋转角度", "Abs_Axis::set_label_rotation_degrees"),
number_property<&T::coord_origin>("coord_origin", "坐标起点", "Axis::set_coord_range"),
number_property<&T::coord_target>("coord_target", "坐标终点", "Axis::set_coord_range"),
number_property<&T::label_precision>("label_precision", "标签精度", "Axis::set_label_precision"),
boolean_property<&T::use_wheel>("use_wheel", "启用滚轮缩放", "Axis::set_use_wheel"),
boolean_property<&T::use_drag>("use_drag", "启用拖拽平移", "Axis::set_use_drag")
};
}
inline auto time_axis_fields() {
using T = Gallery_Time_Axis_Properties;
return std::tuple{
number_property<&T::time_visible_count>("time_visible_count", "可见时间点", "Time_Axis::set_visible_time_point_count"),
number_property<&T::time_label_spacing>("time_label_spacing", "时间标签间距", "Time_Axis::set_tick_label_spacing_px"),
text_property<&T::time_format>("time_format", "时间格式", "Time_Axis::set_time_format"),
number_property<&T::time_font_size>("time_font_size", "时间字体", "Time_Axis::set_font"),
boolean_property<&T::time_newest_at_start>("time_newest_at_start", "最新数据位于轴起点", "Time_Axis::set_newest_at_axis_start")
};
}
inline auto hover_fields() {
using T = Gallery_Hover_Properties;
return std::tuple{
boolean_property<&T::hover_enabled>("hover_enabled", "悬浮信息", "Hover_Tooltip_Mixin::set_use_hover_info"),
number_property<&T::hover_font_size>("hover_font_size", "悬浮字体", "Hover_Tooltip_Mixin::set_hover_tooltip_font"),
color_property<&T::hover_text_color>("hover_text_color", "悬浮文字", "Hover_Tooltip_Mixin::set_tooltip_text_pen"),
color_property<&T::hover_background>("hover_background", "悬浮背景", "Hover_Tooltip_Mixin::set_hover_tooltip_background_brush")
};
}
inline auto spectrum_fields() {
using T = Gallery_Spectrum_Properties;
return std::tuple{
number_property<&T::frequency_point_size>("frequency_point_size", "频率点数", "Spectrum::set_frequency_point_size"),
number_property<&T::frequency_origin>("frequency_origin", "数据频率起点", "Spectrum::set_frequency_range"),
number_property<&T::frequency_target>("frequency_target", "数据频率终点", "Spectrum::set_frequency_range"),
number_property<&T::center_frequency>("center_frequency", "中心频率", "Spectrum::set_center_frequency"),
number_property<&T::sweep_origin>("sweep_origin", "扫频区起点", "Spectrum::set_sweep_frequency_range"),
number_property<&T::sweep_target>("sweep_target", "扫频区终点", "Spectrum::set_sweep_frequency_range"),
boolean_property<&T::max_hold_visible>("max_hold_visible", "最大保持线", "Spectrum::set_max_hold_visible"),
boolean_property<&T::min_hold_visible>("min_hold_visible", "最小保持线", "Spectrum::set_min_hold_visible"),
boolean_property<&T::max_marker_visible>("max_marker_visible", "峰值 Marker", "Spectrum::set_max_marker_visible"),
boolean_property<&T::use_min_marker>("use_min_marker", "最小值 Marker", "Spectrum::set_use_min_marker"),
boolean_property<&T::sweep_region_visible>("sweep_region_visible", "扫频区域", "Spectrum::set_sweep_region_visible"),
boolean_property<&T::visible_range_only>("visible_range_only", "仅绘制可见范围", "Spectrum::set_visible_range_only"),
select_property<&T::line_interpolation>("line_interpolation", "线插值模式", "Spectrum::set_interpolation_mode"),
color_property<&T::max_brush>("max_brush", "最大保持填充", "Spectrum::set_max_brush"),
color_property<&T::current_brush>("current_brush", "当前曲线填充", "Spectrum::set_current_brush"),
color_property<&T::min_brush>("min_brush", "最小保持填充", "Spectrum::set_min_brush"),
color_property<&T::max_pen>("max_pen", "最大保持线颜色", "Spectrum::set_max_pen"),
color_property<&T::current_pen>("current_pen", "当前曲线颜色", "Spectrum::set_current_pen"),
color_property<&T::min_pen>("min_pen", "最小保持线颜色", "Spectrum::set_min_pen"),
color_property<&T::selected_marker_pen>("selected_marker_pen", "选中 Marker", "Spectrum::set_selected_marker_pen"),
color_property<&T::marker_pen>("marker_pen", "Marker 颜色", "Spectrum::set_marker_pen"),
color_property<&T::middle_frequency_pen>("middle_frequency_pen", "中心频率线", "Spectrum::set_middle_frequency_pen"),
color_property<&T::sweep_region_brush>("sweep_region_brush", "扫频区填充", "Spectrum::set_sweep_region_brush")
};
}
template <class T>
struct Property_Fields;
template <>
struct Property_Fields<Gallery_Axis_Lab_Properties> {
static auto get() {
return std::tuple_cat(common_fields(), axis_fields(), time_axis_fields());
}
};
template <>
struct Property_Fields<Gallery_Spectrum_Properties> {
static auto get() {
return std::tuple_cat(common_fields(), axis_fields(), spectrum_fields(), hover_fields());
}
};
template <>
struct Property_Fields<Gallery_Selection_Overlay_Properties> {
static auto get() {
using T = Gallery_Selection_Overlay_Properties;
return std::tuple_cat(Property_Fields<Gallery_Spectrum_Properties>::get(), std::tuple{
number_property<&T::selection_font_size>("selection_font_size", "框选标签字体", "Selection_Rectangle_Overlay::set_label_font"),
color_property<&T::selection_label_pen>("selection_label_pen", "框选标签颜色", "Selection_Rectangle_Overlay::set_label_pen"),
color_property<&T::selection_brush>("selection_brush", "框选填充", "Selection_Rectangle_Overlay::set_selection_brush"),
color_property<&T::selection_border>("selection_border", "框选边框", "Selection_Rectangle_Overlay::set_selection_border_pen")
});
}
};
template <>
struct Property_Fields<Gallery_Waterfall_Properties> {
static auto get() {
using T = Gallery_Waterfall_Properties;
return std::tuple_cat(common_fields(), axis_fields(), time_axis_fields(), std::tuple{
number_property<&T::frequency_origin>("frequency_origin", "频率起点", "Waterfall::set_frequency_range"),
number_property<&T::frequency_target>("frequency_target", "频率终点", "Waterfall::set_frequency_range"),
number_property<&T::power_origin>("power_origin", "功率起点", "Waterfall::set_power_range"),
number_property<&T::power_target>("power_target", "功率终点", "Waterfall::set_power_range"),
number_property<&T::frequency_bin_count>("frequency_bin_count", "频率 Bin", "Waterfall::set_frequency_bin_count"),
boolean_property<&T::visible_range_only>("visible_range_only", "仅绘制可见范围", "Waterfall::set_visible_range_only"),
select_property<&T::image_interpolation>("image_interpolation", "图像插值(三模式)", "Waterfall::set_interpolation_mode"),
select_property<&T::color_map>("color_map", "颜色映射(重建 Builder", "Waterfall::Builder::set_color_map / Color_Map::set_colors")
}, hover_fields());
}
};
template <>
struct Property_Fields<Gallery_Afterglow_Properties> {
static auto get() {
using T = Gallery_Afterglow_Properties;
return std::tuple_cat(common_fields(), axis_fields(), std::tuple{
number_property<&T::frequency_origin>("frequency_origin", "频率起点", "Afterglow::set_frequency_range"),
number_property<&T::frequency_target>("frequency_target", "频率终点", "Afterglow::set_frequency_range"),
number_property<&T::power_origin>("power_origin", "功率起点", "Afterglow::set_power_range"),
number_property<&T::power_target>("power_target", "功率终点", "Afterglow::set_power_range"),
number_property<&T::frequency_point_size>("frequency_point_size", "频率格点", "Afterglow::set_frequency_point_size"),
number_property<&T::power_point_size>("power_point_size", "功率格点", "Afterglow::set_power_point_size"),
boolean_property<&T::interpolate_power>("interpolate_power", "功率 Bin 插值", "Afterglow::set_interpolate"),
number_property<&T::attenuation_rate>("attenuation_rate", "衰减率", "Afterglow::set_attenuation_rate"),
select_property<&T::color_map>("color_map", "颜色映射(重建 Builder", "Afterglow::Builder::set_color_map / Color_Map::set_colors")
});
}
};
template <>
struct Property_Fields<Gallery_Sweep_Spectrum_Properties> {
static auto get() {
using T = Gallery_Sweep_Spectrum_Properties;
return std::tuple_cat(common_fields(), axis_fields(), std::tuple{
number_property<&T::frequency_origin>("frequency_origin", "扫频起点", "Sweep_Spectrum::set_frequency_range"),
number_property<&T::frequency_target>("frequency_target", "扫频终点", "Sweep_Spectrum::set_frequency_range"),
number_property<&T::bins_per_block>("bins_per_block", "每块 Bin", "Sweep_Spectrum::set_bins_per_block"),
number_property<&T::block_count>("block_count", "块数量", "Sweep_Spectrum::set_block_count"),
color_property<&T::sweep_pen>("sweep_pen", "扫频曲线", "Sweep_Spectrum::set_pen"),
color_property<&T::current_frequency_pen>("current_frequency_pen", "当前频率游标", "Sweep_Spectrum::set_cur_frequency_pen"),
boolean_property<&T::visible_range_only>("visible_range_only", "仅绘制可见范围", "Sweep_Spectrum::set_visible_range_only"),
select_property<&T::line_interpolation>("line_interpolation", "线插值模式", "Sweep_Spectrum::set_interpolation_mode")
});
}
};
template <>
struct Property_Fields<Gallery_Frequency_Trace_Properties> {
static auto get() {
using T = Gallery_Frequency_Trace_Properties;
return std::tuple_cat(common_fields(), axis_fields(), time_axis_fields(), std::tuple{
color_property<&T::trace_pen>("trace_pen", "轨迹颜色(重建 Builder", "Frequency_Trace::Builder::set_pen"),
number_property<&T::trace_pen_width>("trace_pen_width", "轨迹宽度(重建 Builder", "Frequency_Trace::Builder::set_pen"),
number_property<&T::trace_value_min>("trace_value_min", "数值下限", "Axis::set_coord_range"),
number_property<&T::trace_value_max>("trace_value_max", "数值上限", "Axis::set_coord_range")
});
}
};
template <>
struct Property_Fields<Gallery_Constellation_Properties> {
static auto get() {
using T = Gallery_Constellation_Properties;
return std::tuple_cat(common_fields(), axis_fields(), std::tuple{
number_property<&T::i_origin>("i_origin", "I 起点", "Constellation_Diagram::set_i_range"),
number_property<&T::i_target>("i_target", "I 终点", "Constellation_Diagram::set_i_range"),
number_property<&T::q_origin>("q_origin", "Q 起点", "Constellation_Diagram::set_q_range"),
number_property<&T::q_target>("q_target", "Q 终点", "Constellation_Diagram::set_q_range"),
color_property<&T::point_color>("point_color", "采样点颜色", "Constellation_Diagram::set_point_color"),
color_property<&T::anchor_color>("anchor_color", "锚点颜色", "Constellation_Diagram::set_anchor_color"),
number_property<&T::point_lifetime_ms>("point_lifetime_ms", "采样点寿命", "Constellation_Diagram::set_point_lifetime_ms"),
select_property<&T::constellation_type>("constellation_type", "星座模式(重建 Builder", "Constellation_Diagram::Builder::set_type"),
number_property<&T::phase_offset>("phase_offset", "相位偏移(重建 Builder", "Constellation_Diagram::Builder::set_phase_offset_radians")
});
}
};
template <class Properties>
struct Property_Fields<Gallery_Low_Latency_Case_Properties<Properties>> {
static auto get() {
return std::tuple_cat(Property_Fields<Properties>::get(), low_latency_fields());
}
};
struct Property_Validator {
template <class T>
void operator()(const T& value, adminive::Validation_Context& context) const {
if constexpr(requires { value.coord_origin; value.coord_target; })
validate_pair(context, "coord_origin", "coord_target", value.coord_origin, value.coord_target);
if constexpr(requires { value.frequency_origin; value.frequency_target; })
validate_pair(context, "frequency_origin", "frequency_target", value.frequency_origin, value.frequency_target);
if constexpr(requires { value.power_origin; value.power_target; })
validate_pair(context, "power_origin", "power_target", value.power_origin, value.power_target);
if constexpr(requires { value.sweep_origin; value.sweep_target; })
validate_pair(context, "sweep_origin", "sweep_target", value.sweep_origin, value.sweep_target);
if constexpr(requires { value.trace_value_min; value.trace_value_max; })
validate_pair(context, "trace_value_min", "trace_value_max", value.trace_value_min, value.trace_value_max);
if constexpr(requires { value.i_origin; value.i_target; })
validate_pair(context, "i_origin", "i_target", value.i_origin, value.i_target);
if constexpr(requires { value.q_origin; value.q_target; })
validate_pair(context, "q_origin", "q_target", value.q_origin, value.q_target);
}
private:
template <class Origin, class Target>
static void validate_pair(adminive::Validation_Context& context, std::string_view origin, std::string_view target, const Origin& origin_value, const Target& target_value) {
if(static_cast<double>(origin_value) == static_cast<double>(target_value)) {
context.error(std::string(origin), std::string(origin) + "" + std::string(target) + " 不能相等");
context.error(std::string(target), std::string(origin) + "" + std::string(target) + " 不能相等");
}
}
};
template <class T>
auto property_descriptor() {
auto fields = Property_Fields<T>::get();
return std::apply([](auto... field) {
return adminive::object<T>("renderive_gallery_properties", "Renderive 控件属性", std::move(field)...).validator(Property_Validator{});
}, std::move(fields));
}
}
namespace adminive {
template <class T, T Minimum, T Maximum, T Step, class Json>
struct Value_Adapter<renderive::web::Gallery_Number<T, Minimum, Maximum, Step>, Json> {
using Storage = renderive::web::Gallery_Number<T, Minimum, Maximum, Step>;
using value_type = T;
static constexpr T minimum = Minimum;
static constexpr T maximum = Maximum;
static constexpr T multiple_of = Step;
static T read(const Storage& value) noexcept {
return value.value();
}
static void write(Storage& target, T value) {
target = value;
}
};
template <class Json>
struct Value_Adapter<renderive::web::Gallery_Text, Json> {
using value_type = std::string;
static const std::string& read(const renderive::web::Gallery_Text& value) noexcept {
return value.value();
}
static void write(renderive::web::Gallery_Text& target, std::string value) {
target = std::move(value);
}
};
template <class Json>
struct Value_Adapter<renderive::web::Gallery_Color, Json> {
using value_type = std::string;
static const std::string& read(const renderive::web::Gallery_Color& value) noexcept {
return value.value();
}
static void write(renderive::web::Gallery_Color& target, std::string value) {
target = std::move(value);
}
};
#define RENDERIVE_GALLERY_ENUM_ADAPTER(Type) \
template <> \
struct Enum_Adapter<renderive::web::Type> : renderive::web::gallery_adminive::Enum_Adapter<renderive::web::Type> {}
RENDERIVE_GALLERY_ENUM_ADAPTER(Gallery_Cache_Mode);
RENDERIVE_GALLERY_ENUM_ADAPTER(Gallery_Axis_Orientation);
RENDERIVE_GALLERY_ENUM_ADAPTER(Gallery_Decimal_Separator);
RENDERIVE_GALLERY_ENUM_ADAPTER(Gallery_Line_Interpolation);
RENDERIVE_GALLERY_ENUM_ADAPTER(Gallery_Image_Interpolation);
RENDERIVE_GALLERY_ENUM_ADAPTER(Gallery_Color_Map);
RENDERIVE_GALLERY_ENUM_ADAPTER(Gallery_Constellation_Type);
#undef RENDERIVE_GALLERY_ENUM_ADAPTER
#define RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR(Type) \
template <> \
struct Type_Descriptor<renderive::web::Type> { \
static auto get() { \
return renderive::web::gallery_adminive::property_descriptor<renderive::web::Type>(); \
} \
}
RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR(Gallery_Axis_Lab_Properties);
RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR(Gallery_Spectrum_Properties);
RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR(Gallery_Selection_Overlay_Properties);
RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR(Gallery_Waterfall_Properties);
RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR(Gallery_Afterglow_Properties);
RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR(Gallery_Sweep_Spectrum_Properties);
RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR(Gallery_Frequency_Trace_Properties);
RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR(Gallery_Constellation_Properties);
#undef RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR
template <class Properties>
struct Type_Descriptor<renderive::web::Gallery_Low_Latency_Case_Properties<Properties>> {
static auto get() {
using T = renderive::web::Gallery_Low_Latency_Case_Properties<Properties>;
return renderive::web::gallery_adminive::property_descriptor<T>();
}
};
}