This commit is contained in:
2026-08-12 06:16:00 +08:00
parent 9192b5953c
commit b51833499f
6 changed files with 386 additions and 146 deletions
+172
View File
@@ -0,0 +1,172 @@
#pragma once
#include "renderive/scene/Scene2D_Context.hpp"
#include <structive/property/property.hpp>
#include <cstdint>
#include <algorithm>
#include <string>
#include <string_view>
#include <vector>
namespace renderive::web {
enum class Gallery_Frame_Mode : std::uint8_t {
Manual,
Low_Latency,
Playback
};
struct Gallery_Action_Category {};
struct Gallery_Action_Attribute {
using attribute_category = Gallery_Action_Category;
static constexpr bool single_valued = false;
static constexpr bool inheritable = false;
std::string_view id;
std::string_view label;
std::string_view api;
std::string_view group;
std::string_view description;
std::string_view argument_input;
std::string_view argument_label;
double argument_default{};
bool request_frame{};
};
constexpr Gallery_Action_Attribute gallery_action(
std::string_view id, std::string_view label, std::string_view api,
std::string_view group, std::string_view description = {},
std::string_view argument_input = {}, std::string_view argument_label = {},
double argument_default = 0.0, bool request_frame = false) {
return {id, label, api, group, description, argument_input, argument_label,
argument_default, request_frame};
}
struct Gallery_Action_Model {
std::string id;
std::string label;
std::string api;
std::string description;
std::string group;
std::string argument_input;
std::string argument_label;
double argument_default{};
bool request_frame{};
};
inline Gallery_Action_Model gallery_action_model(
const Gallery_Action_Attribute& action) {
return {std::string(action.id), std::string(action.label), std::string(action.api),
std::string(action.description), std::string(action.group),
std::string(action.argument_input), std::string(action.argument_label),
action.argument_default, action.request_frame};
}
template <structive::Property_Described_Object Object>
void append_gallery_actions(std::vector<Gallery_Action_Model>& target) {
structive::type_descriptor<Object>()
.template for_each_type_attribute<Gallery_Action_Category>(
[&target](const auto& action) {
const auto found = std::find_if(
target.begin(), target.end(),
[&action](const auto& existing) { return existing.id == action.id; });
if (found == target.end())
target.push_back(gallery_action_model(action));
});
}
} // namespace renderive::web
namespace structive {
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
struct Type_Descriptor<Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>> {
using T = Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>;
static auto get() {
using renderive::web::gallery_action;
return object<T>(type_metadata(
gallery_action("mode_prepare", "准备帧",
"Manual_Refresh_Strategy::acquire_painter / Plot_Core::prepare_frame",
"Manual_Refresh_Strategy"),
gallery_action("mode_refresh", "提交手动刷新",
"Manual_Refresh_Strategy::refresh / Plot_Core::refresh_manual_frame",
"Manual_Refresh_Strategy"),
gallery_action("mode_render", "渲染已刷新帧",
"Manual_Refresh_Strategy::acquire_renderer / Plot_Core::render_prepared_frame",
"Manual_Refresh_Strategy", {}, {}, {}, 0.0, true),
gallery_action("mode_discard", "丢弃待刷新帧",
"Plot_Core::discard_pending_frame",
"Manual_Refresh_Strategy"),
gallery_action("mode_cycle", "执行完整手动帧周期",
"Plot_Core::render_frame", "Manual_Refresh_Strategy",
{}, {}, {}, 0.0, true)));
}
};
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
struct Type_Descriptor<Low_Latency_Strategy<Scene_Frame, Mutex, Observer>> {
using T = Low_Latency_Strategy<Scene_Frame, Mutex, Observer>;
static auto get() {
using renderive::web::gallery_action;
return object<T>(type_metadata(
gallery_action("mode_prepare", "发布低延迟帧",
"Low_Latency_Strategy::acquire_painter / Plot_Core::prepare_frame",
"Low_Latency_Strategy"),
gallery_action("mode_render", "消费最新帧",
"Low_Latency_Strategy::acquire_renderer / Plot_Core::render_prepared_frame",
"Low_Latency_Strategy", {}, {}, {}, 0.0, true),
gallery_action("mode_discard", "丢弃陈旧帧",
"Plot_Core::discard_pending_frame", "Low_Latency_Strategy"),
gallery_action("mode_cycle", "执行低延迟帧周期",
"Plot_Core::render_frame", "Low_Latency_Strategy",
{}, {}, {}, 0.0, true)));
}
};
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
struct Type_Descriptor<Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>> {
using T = Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>;
static auto get() {
using renderive::web::gallery_action;
return object<T>(type_metadata(
gallery_action("mode_enqueue", "压入一帧",
"Flow_Refresh_Strategy::acquire_painter / Plot_Core::prepare_frame",
"Flow_Refresh_Strategy"),
gallery_action("mode_enqueue_burst", "批量压入回放队列",
"Flow_Refresh_Strategy::acquire_painter / Plot_Core::prepare_frame x N",
"Flow_Refresh_Strategy", {}, "number", "帧数", 8.0),
gallery_action("mode_dequeue", "消费队首帧",
"Flow_Refresh_Strategy::acquire_renderer / Plot_Core::render_prepared_frame",
"Flow_Refresh_Strategy", {}, {}, {}, 0.0, true),
gallery_action("mode_cycle", "执行回放帧周期",
"Plot_Core::render_frame", "Flow_Refresh_Strategy",
{}, {}, {}, 0.0, true)));
}
};
} // namespace structive
namespace renderive::web {
inline std::vector<Gallery_Action_Model> gallery_frame_actions(Gallery_Frame_Mode mode) {
std::vector<Gallery_Action_Model> result;
switch (mode) {
case Gallery_Frame_Mode::Manual:
append_gallery_actions<Manual_Refresh_Strategy<Scene2D_Frame_Data, std::mutex,
Observer_State<>>>(result);
break;
case Gallery_Frame_Mode::Low_Latency:
append_gallery_actions<Low_Latency_Strategy<Scene2D_Frame_Data, std::mutex,
Observer_State<>>>(result);
break;
case Gallery_Frame_Mode::Playback:
append_gallery_actions<Flow_Refresh_Strategy<Scene2D_Frame_Data, std::mutex,
Observer_State<>>>(result);
break;
}
return result;
}
} // namespace renderive::web
+21 -119
View File
@@ -31,21 +31,7 @@ struct Case_Model {
int preferred_height = 320;
};
struct Action_Model {
std::string id;
std::string label;
std::string api;
std::string description;
std::string group;
std::string argument_input;
std::string argument_label;
double argument_default{};
bool request_frame{};
};
struct Action_Definition {
Action_Model model;
};
using Action_Model = Gallery_Action_Model;
const std::vector<Case_Model>& cases() {
static const std::vector<Case_Model> value{
@@ -69,103 +55,6 @@ const std::vector<Case_Model>& cases() {
return value;
}
Action_Definition action(std::string id, std::string label, std::string api,
std::string group, std::string description = {},
std::string argument_input = {}, std::string argument_label = {},
double argument_default = 0) {
return {{std::move(id), std::move(label), std::move(api), std::move(description),
std::move(group), std::move(argument_input), std::move(argument_label),
argument_default}};
}
Action_Definition frame_action(std::string id, std::string label, std::string api,
std::string group) {
auto result = action(std::move(id), std::move(label), std::move(api), std::move(group));
result.model.request_frame = true;
return result;
}
std::vector<Action_Definition> actions(std::string_view case_id,
Gallery_Frame_Mode frame_mode) {
std::vector<Action_Definition> result{
action("reset", "恢复本图默认值", "Gallery_Scene::rebuild", "会话"),
action("toggle_view", "切换 View 生命周期", "Plot_Core::activate_view / deactivate_view", "会话"),
action("axis_probe", "读取坐标映射与刻度", "coord_to_pixel / pixel_to_coord / tick_step / sub_tick_count / tick_label", "Axis")
};
if (frame_mode == Gallery_Frame_Mode::Manual) {
result.push_back(action("mode_prepare", "准备帧", "Manual_Refresh_Strategy::acquire_painter / Plot_Core::prepare_frame",
"Manual_Refresh_Strategy"));
result.push_back(action("mode_refresh", "提交手动刷新", "Manual_Refresh_Strategy::refresh / Plot_Core::refresh_manual_frame",
"Manual_Refresh_Strategy"));
result.push_back(frame_action("mode_render", "渲染已刷新帧", "Manual_Refresh_Strategy::acquire_renderer / Plot_Core::render_prepared_frame",
"Manual_Refresh_Strategy"));
result.push_back(action("mode_discard", "丢弃待刷新帧", "Plot_Core::discard_pending_frame",
"Manual_Refresh_Strategy"));
result.push_back(frame_action("mode_cycle", "执行完整手动帧周期", "Plot_Core::render_frame",
"Manual_Refresh_Strategy"));
} else if (frame_mode == Gallery_Frame_Mode::Low_Latency) {
result.push_back(action("mode_prepare", "发布低延迟帧", "Low_Latency_Strategy::acquire_painter / Plot_Core::prepare_frame",
"Low_Latency_Strategy"));
result.push_back(frame_action("mode_render", "消费最新帧", "Low_Latency_Strategy::acquire_renderer / Plot_Core::render_prepared_frame",
"Low_Latency_Strategy"));
result.push_back(action("mode_discard", "丢弃陈旧帧", "Plot_Core::discard_pending_frame",
"Low_Latency_Strategy"));
result.push_back(frame_action("mode_cycle", "执行低延迟帧周期", "Plot_Core::render_frame",
"Low_Latency_Strategy"));
} else {
result.push_back(action("mode_enqueue", "压入一帧", "Flow_Refresh_Strategy::acquire_painter / Plot_Core::prepare_frame",
"Flow_Refresh_Strategy"));
result.push_back(action("mode_enqueue_burst", "批量压入回放队列", "Flow_Refresh_Strategy::acquire_painter / Plot_Core::prepare_frame × N",
"Flow_Refresh_Strategy", {}, "number", "帧数", 8));
result.push_back(frame_action("mode_dequeue", "消费队首帧", "Flow_Refresh_Strategy::acquire_renderer / Plot_Core::render_prepared_frame",
"Flow_Refresh_Strategy"));
result.push_back(frame_action("mode_cycle", "执行回放帧周期", "Plot_Core::render_frame",
"Flow_Refresh_Strategy"));
}
if (case_id == "axis_lab") {
result.push_back(action("append_time", "追加时间点", "Time_Axis::append_time / tick_to_time", "Time_Axis"));
result.push_back(action("read_data_shape", "读取轴采样点状态", "Abs_Axis::transform / Time_Axis::time_point_count", "观察者"));
} else if (case_id == "spectrum" || case_id == "selection_overlay") {
result.push_back(action("push_samples", "推送一帧样本", "Spectrum::update_samples", "Spectrum"));
result.push_back(action("power_at", "查询指定频率功率", "Spectrum::power_at", "Spectrum", {}, "number", "频率", 98e6));
result.push_back(action("add_marker", "添加点 Marker", "Spectrum::add_custom_marker", "Marker", {}, "number", "频率", 96e6));
result.push_back(action("add_line_marker", "添加线 Marker", "Spectrum::add_custom_line_marker", "Marker", {}, "number", "频率", 100e6));
result.push_back(action("remove_marker", "按频率删除 Marker", "Spectrum::remove_custom_marker", "Marker", {}, "number", "频率", 96e6));
result.push_back(action("remove_selected_marker", "删除选中 Marker", "Spectrum::remove_selected_marker", "Marker"));
result.push_back(action("clear_markers", "清空 Marker", "Spectrum::clear_custom_markers", "Marker"));
result.push_back(action("select_marker", "选择 Marker 索引", "Spectrum::set_selected_marker_index", "Marker", {}, "number", "索引", 0));
result.push_back(action("select_next_marker", "选择下一个 Marker", "Spectrum::select_next_marker", "Marker"));
result.push_back(action("select_previous_marker", "选择上一个 Marker", "Spectrum::select_previous_marker", "Marker"));
result.push_back(action("clear_marker_selection", "清除 Marker 选择", "Spectrum::clear_marker_selection", "Marker"));
result.push_back(action("set_marker_frequency", "修改选中 Marker 频率",
"Spectrum::set_marker_frequency / set_current_marker_frequency",
"Marker", {}, "number", "频率", 99e6));
result.push_back(action("read_data_shape", "读取输入与绘制点数", "Spectrum::sample_count / rendered_point_count", "观察者"));
if (case_id == "selection_overlay") {
result.push_back(action("clear_selection", "清空框选区域", "Selection_Rectangle_Overlay::clear_selected_regions", "Selection Overlay"));
}
} else if (case_id == "waterfall") {
result.push_back(action("append_row", "追加一行", "Waterfall::append_row", "Waterfall"));
result.push_back(action("append_tick_row", "按 tick 追加一行", "Time_Axis::append_time / Waterfall::append_row(int, span)", "Waterfall"));
result.push_back(action("read_data_shape", "读取行、点与渲染单元", "Waterfall::row_count / stored_point_count / rendered_cell_count", "观察者"));
} else if (case_id == "afterglow") {
result.push_back(action("append_spectrum", "追加一帧频谱", "Afterglow::append_spectrum", "Afterglow"));
result.push_back(action("read_data_shape", "读取历史帧与渲染网格", "Afterglow::history_count / latest_spectrum_point_count / rendered_cell_count", "观察者"));
} else if (case_id == "sweep_spectrum") {
result.push_back(action("append_block", "追加一个扫频块", "Sweep_Spectrum::append_block", "Sweep Spectrum"));
result.push_back(action("read_data_shape", "读取块与绘制点数", "Sweep_Spectrum::stored_block_count / stored_point_count / rendered_point_count", "观察者"));
} else if (case_id == "frequency_trace") {
result.push_back(action("append_sample", "追加一个样本", "Frequency_Trace::append_sample", "Frequency Trace"));
result.push_back(action("append_tick_sample", "按 tick 追加样本", "Time_Axis::append_time / Frequency_Trace::append_sample(int, double)", "Frequency Trace"));
result.push_back(action("read_data_shape", "读取轨迹点数", "Frequency_Trace::sample_count / rendered_point_count / Time_Axis::time_point_count", "观察者"));
} else if (case_id == "constellation") {
result.push_back(action("append_points", "追加一组 IQ 点", "Constellation_Diagram::append_point", "Constellation"));
result.push_back(action("fit_square", "按轴拟合正方形", "Constellation_Diagram::fit_square_to_axes", "Constellation"));
result.push_back(action("read_data_shape", "读取星座点与锚点数", "Constellation_Diagram::point_count", "观察者"));
}
return result;
}
const Case_Model* find_case(std::string_view id) {
const auto& all = cases();
const auto found = std::find_if(all.begin(), all.end(),
@@ -173,6 +62,17 @@ const Case_Model* find_case(std::string_view id) {
return found == all.end() ? nullptr : &*found;
}
std::vector<Action_Model> registered_actions(std::string_view case_id,
Gallery_Frame_Mode frame_mode) {
std::vector<Action_Model> result;
append_gallery_actions<Gallery_Session_Control>(result);
for (auto& action : gallery_frame_actions(frame_mode)) {
result.push_back(std::move(action));
}
append_gallery_renderable_actions(case_id, result);
return result;
}
Json parse_request(std::string_view message) {
return Json::parse(message.begin(), message.end());
}
@@ -493,7 +393,8 @@ std::string Gallery_Protocol::catalog_json() {
gallery_renderable_control_count(item.id);
for (const auto mode : frame_modes) {
const std::string key(frame_mode_id(mode));
const std::size_t actions = gallery_detail::actions(item.id, mode).size();
const std::size_t actions =
gallery_detail::registered_actions(item.id, mode).size();
entry["control_count_by_mode"][key] = controls;
entry["action_count_by_mode"][key] = actions;
controls_total += controls;
@@ -501,7 +402,8 @@ std::string Gallery_Protocol::catalog_json() {
}
entry["control_count"] = controls;
entry["action_count"] =
gallery_detail::actions(item.id, Gallery_Frame_Mode::Low_Latency).size();
gallery_detail::registered_actions(
item.id, Gallery_Frame_Mode::Low_Latency).size();
result["cases"].push_back(std::move(entry));
}
result["coverage"] = {{"case_count", gallery_detail::cases().size()},
@@ -541,9 +443,9 @@ std::string Gallery_Protocol::case_json_from_controls(
gallery_detail::action_view())},
{"data", Json::array()}
};
for (const auto& definition : gallery_detail::actions(case_id, frame_mode))
for (const auto& action : gallery_detail::registered_actions(case_id, frame_mode))
result["actions"]["data"].push_back(
adminive::to_frontend_json<Json>(definition.model));
adminive::to_frontend_json<Json>(action));
try {
result["telemetry"] = Json::parse(telemetry_json.begin(), telemetry_json.end());
} catch (const std::exception&) {
@@ -647,10 +549,10 @@ std::optional<Gallery_Control_Patch_Request> Gallery_Protocol::control_patch_req
bool Gallery_Protocol::action_available(std::string_view case_id,
Gallery_Frame_Mode frame_mode,
std::string_view action_id) {
const auto available = gallery_detail::actions(case_id, frame_mode);
const auto available = gallery_detail::registered_actions(case_id, frame_mode);
return std::any_of(available.begin(), available.end(),
[action_id](const gallery_detail::Action_Definition& item) {
return item.model.id == action_id;
[action_id](const gallery_detail::Action_Model& item) {
return item.id == action_id;
});
}
+2 -6
View File
@@ -1,15 +1,11 @@
#pragma once
#include <cstdint>
#include "Gallery_Actions.h"
#include <optional>
#include <string>
#include <string_view>
#include <variant>
namespace renderive::web {
enum class Gallery_Frame_Mode : std::uint8_t {
Manual,
Low_Latency,
Playback
};
struct Gallery_Open_Request {
std::string case_id;
Gallery_Frame_Mode frame_mode = Gallery_Frame_Mode::Low_Latency;
+151 -21
View File
@@ -1,5 +1,6 @@
#pragma once
#include "Gallery_Actions.h"
#include "Gallery_Renderable_Types.h"
#include "adminive/adminive.hpp"
@@ -538,6 +539,112 @@ RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Constellation_Diagram, Constellation_Diagra
} // namespace adminive
namespace renderive::web::gallery_action_registry {
constexpr auto axis_metadata() {
return structive::type_metadata(
gallery_action("axis_probe", "读取坐标映射与刻度",
"coord_to_pixel / pixel_to_coord / tick_step / sub_tick_count / tick_label",
"Axis"));
}
} // namespace renderive::web::gallery_action_registry
namespace structive {
#define RENDERIVE_TYPE_ACTIONS(Type, ...) \
template <> struct Type_Descriptor<renderive::web::Type> { \
static auto get() { \
using T = renderive::web::Type; \
using renderive::web::gallery_action; \
return object<T>(type_metadata(__VA_ARGS__)); \
} \
}
template <>
struct Type_Descriptor<renderive::web::Gallery_Axis> {
static auto get() {
return object<renderive::web::Gallery_Axis>(
renderive::web::gallery_action_registry::axis_metadata());
}
};
template <>
struct Type_Descriptor<renderive::web::Gallery_Frequency_Axis> {
static auto get() {
return object<renderive::web::Gallery_Frequency_Axis>(
renderive::web::gallery_action_registry::axis_metadata());
}
};
template <>
struct Type_Descriptor<renderive::web::Gallery_Time_Axis> {
static auto get() {
using T = renderive::web::Gallery_Time_Axis;
using renderive::web::gallery_action;
return object<T>(type_metadata(
gallery_action("axis_probe", "读取坐标映射与刻度",
"coord_to_pixel / pixel_to_coord / tick_step / sub_tick_count / tick_label",
"Axis"),
gallery_action("append_time", "追加时间点",
"Time_Axis::append_time / tick_to_time", "Time_Axis")));
}
};
RENDERIVE_TYPE_ACTIONS(Gallery_Spectrum,
gallery_action("push_samples", "推送一帧样本", "Spectrum::update_samples", "Spectrum"),
gallery_action("power_at", "查询指定频率功率", "Spectrum::power_at", "Spectrum",
{}, "number", "频率", 98e6),
gallery_action("add_marker", "添加点 Marker", "Spectrum::add_custom_marker", "Marker",
{}, "number", "频率", 96e6),
gallery_action("add_line_marker", "添加线 Marker", "Spectrum::add_custom_line_marker", "Marker",
{}, "number", "频率", 100e6),
gallery_action("remove_marker", "按频率删除 Marker", "Spectrum::remove_custom_marker", "Marker",
{}, "number", "频率", 96e6),
gallery_action("remove_selected_marker", "删除选中 Marker",
"Spectrum::remove_selected_marker", "Marker"),
gallery_action("clear_markers", "清空 Marker", "Spectrum::clear_custom_markers", "Marker"),
gallery_action("select_marker", "选择 Marker 索引", "Spectrum::set_selected_marker_index", "Marker",
{}, "number", "索引", 0.0),
gallery_action("select_next_marker", "选择下一个 Marker", "Spectrum::select_next_marker", "Marker"),
gallery_action("select_previous_marker", "选择上一个 Marker", "Spectrum::select_previous_marker", "Marker"),
gallery_action("clear_marker_selection", "清除 Marker 选择",
"Spectrum::clear_marker_selection", "Marker"),
gallery_action("set_marker_frequency", "修改选中 Marker 频率",
"Spectrum::set_marker_frequency / set_current_marker_frequency", "Marker",
{}, "number", "频率", 99e6));
RENDERIVE_TYPE_ACTIONS(Gallery_Waterfall,
gallery_action("append_row", "追加一行", "Waterfall::append_row", "Waterfall"),
gallery_action("append_tick_row", "按 tick 追加一行",
"Time_Axis::append_time / Waterfall::append_row(int, span)", "Waterfall"));
RENDERIVE_TYPE_ACTIONS(Gallery_Afterglow,
gallery_action("append_spectrum", "追加一帧频谱", "Afterglow::append_spectrum", "Afterglow"));
RENDERIVE_TYPE_ACTIONS(Gallery_Sweep_Spectrum,
gallery_action("append_block", "追加一个扫频块", "Sweep_Spectrum::append_block", "Sweep Spectrum"));
RENDERIVE_TYPE_ACTIONS(Gallery_Frequency_Trace,
gallery_action("append_sample", "追加一个样本", "Frequency_Trace::append_sample", "Frequency Trace"),
gallery_action("append_tick_sample", "按 tick 追加样本",
"Time_Axis::append_time / Frequency_Trace::append_sample(int, double)",
"Frequency Trace"));
RENDERIVE_TYPE_ACTIONS(Gallery_Selection_Rectangle_Overlay,
gallery_action("clear_selection", "清空框选区域",
"Selection_Rectangle_Overlay::clear_selected_regions", "Selection Overlay"));
RENDERIVE_TYPE_ACTIONS(Gallery_Constellation_Diagram,
gallery_action("append_points", "追加一组 IQ 点",
"Constellation_Diagram::append_point", "Constellation"),
gallery_action("fit_square", "按轴拟合正方形",
"Constellation_Diagram::fit_square_to_axes", "Constellation"));
#undef RENDERIVE_TYPE_ACTIONS
} // namespace structive
namespace renderive::web {
template <class Object>
@@ -557,28 +664,51 @@ std::size_t gallery_control_count() {
return count_fields(count_fields, descriptor.at("fields"));
}
struct Gallery_Case_Type_Registration {
std::string_view case_id;
std::size_t (*control_count)();
void (*append_actions)(std::vector<Gallery_Action_Model>&);
};
template <class... Objects>
Gallery_Case_Type_Registration gallery_case_types(std::string_view case_id) {
return {
case_id,
[] { return (std::size_t{} + ... + gallery_control_count<Objects>()); },
[](std::vector<Gallery_Action_Model>& actions) {
(append_gallery_actions<Objects>(actions), ...);
}
};
}
inline const Gallery_Case_Type_Registration* gallery_case_registration(
std::string_view case_id) {
static const std::array registrations{
gallery_case_types<Gallery_Frequency_Axis, Gallery_Time_Axis>("axis_lab"),
gallery_case_types<Gallery_Frequency_Axis, Gallery_Axis, Gallery_Spectrum>("spectrum"),
gallery_case_types<Gallery_Frequency_Axis, Gallery_Axis, Gallery_Afterglow>("afterglow"),
gallery_case_types<Gallery_Axis, Gallery_Axis, Gallery_Sweep_Spectrum>("sweep_spectrum"),
gallery_case_types<Gallery_Frequency_Axis, Gallery_Time_Axis, Gallery_Waterfall>("waterfall"),
gallery_case_types<Gallery_Time_Axis, Gallery_Axis, Gallery_Frequency_Trace>("frequency_trace"),
gallery_case_types<Gallery_Frequency_Axis, Gallery_Axis, Gallery_Spectrum,
Gallery_Selection_Rectangle_Overlay>("selection_overlay"),
gallery_case_types<Gallery_Axis, Gallery_Axis, Gallery_Constellation_Diagram>("constellation")
};
const auto found = std::find_if(
registrations.begin(), registrations.end(),
[case_id](const auto& registration) { return registration.case_id == case_id; });
return found == registrations.end() ? nullptr : &*found;
}
inline std::size_t gallery_renderable_control_count(std::string_view case_id) {
const auto axis = gallery_control_count<Gallery_Axis>();
const auto frequency_axis = gallery_control_count<Gallery_Frequency_Axis>();
const auto time_axis = gallery_control_count<Gallery_Time_Axis>();
if (case_id == "axis_lab")
return frequency_axis + time_axis;
if (case_id == "spectrum")
return frequency_axis + axis + gallery_control_count<Gallery_Spectrum>();
if (case_id == "selection_overlay")
return frequency_axis + axis + gallery_control_count<Gallery_Spectrum>() +
gallery_control_count<Gallery_Selection_Rectangle_Overlay>();
if (case_id == "waterfall")
return frequency_axis + time_axis + gallery_control_count<Gallery_Waterfall>();
if (case_id == "afterglow")
return frequency_axis + axis + gallery_control_count<Gallery_Afterglow>();
if (case_id == "sweep_spectrum")
return 2 * axis + gallery_control_count<Gallery_Sweep_Spectrum>();
if (case_id == "frequency_trace")
return time_axis + axis + gallery_control_count<Gallery_Frequency_Trace>();
if (case_id == "constellation")
return 2 * axis + gallery_control_count<Gallery_Constellation_Diagram>();
return 0;
const auto* registration = gallery_case_registration(case_id);
return registration ? registration->control_count() : 0;
}
inline void append_gallery_renderable_actions(
std::string_view case_id, std::vector<Gallery_Action_Model>& actions) {
if (const auto* registration = gallery_case_registration(case_id))
registration->append_actions(actions);
}
} // namespace renderive::web
@@ -120,3 +120,21 @@ static_assert(Described_Type<renderive::web::Gallery_Session_Control>);
static_assert(Direct_Described_Object<renderive::web::Gallery_Session_Control>);
} // namespace adminive
namespace structive {
template <>
struct Type_Descriptor<renderive::web::Gallery_Session_Control> {
static auto get() {
using T = renderive::web::Gallery_Session_Control;
using renderive::web::gallery_action;
return object<T>(type_metadata(
gallery_action("reset", "恢复本图默认值", "Gallery_Scene::rebuild", "Session"),
gallery_action("toggle_view", "切换 View 生命周期",
"Plot_Core::activate_view / deactivate_view", "Session"),
gallery_action("read_data_shape", "读取数据形状",
"Renderable data query / observer telemetry", "Observer")));
}
};
} // namespace structive
+22
View File
@@ -1,5 +1,6 @@
#include "../app/Gallery_Plot_Session.h"
#include "../app/Gallery_Protocol.h"
#include "../app/Gallery_Renderables.h"
#include "../app/Pixel_Frame.h"
#include "../app/Web_Event_Adapter.h"
#include "../app/Web_Plot_Session.h"
@@ -267,6 +268,27 @@ TEST(RenderiveWebGallery, CatalogOwnsDashboardFieldsAndDynamicBehavior) {
EXPECT_TRUE(action->at("request_frame"));
}
TEST(RenderiveWebGallery, ActionsComeFromStructiveTypeMetadata) {
using Spectrum_Schema = structive::type_descriptor_schema_t<Gallery_Spectrum>;
using Time_Axis_Schema = structive::type_descriptor_schema_t<Gallery_Time_Axis>;
using Manual_Strategy =
Manual_Refresh_Strategy<Scene2D_Frame_Data, std::mutex, Observer_State<>>;
using Manual_Schema = structive::type_descriptor_schema_t<Manual_Strategy>;
static_assert(Spectrum_Schema::template type_attribute_count<
Gallery_Action_Category> == 12);
static_assert(Time_Axis_Schema::template type_attribute_count<
Gallery_Action_Category> == 2);
static_assert(Manual_Schema::template type_attribute_count<
Gallery_Action_Category> == 5);
std::vector<Gallery_Action_Model> actions;
append_gallery_actions<Gallery_Spectrum>(actions);
EXPECT_EQ(actions.size(), 12U);
EXPECT_TRUE(std::any_of(actions.begin(), actions.end(), [](const auto& action) {
return action.id == "power_at" && action.argument_input == "number";
}));
}
TEST(RenderiveWebBridge, ComposesAdditionalEventDecoders) {
const auto event = Extended_Web_Event_Adapter::decode(
R"({"category":"event","type":"spatial_test","layer":7})");