功能比较完善的一版

This commit is contained in:
2026-08-21 19:53:00 +08:00
parent ade34e18f2
commit ae8d2f7272
15 changed files with 403 additions and 154 deletions
+44 -69
View File
@@ -97,7 +97,7 @@ public:
nlohmann::json schema() const override {
nlohmann::json components = nlohmann::json::array();
for (const auto& descriptor : descriptors) components.push_back(descriptor->schema());
return {{"protocol", "aethera.plot.inspector"}, {"version", 1}, {"components", std::move(components)}};
return {{"protocol", "aethera.plot.inspector"}, {"version", 2}, {"components", std::move(components)}};
}
nlohmann::json write_prop(std::string_view component, std::string_view key, const nlohmann::json& value) override {
const auto found = std::ranges::find_if(descriptors, [&](const auto& item) { return item->id() == component; });
@@ -145,6 +145,7 @@ std::unique_ptr<detail::Renderable_Descriptor> make_scene_component(Scene_Object
using Definition = typename Scene_Object::Attached_Object;
using Prop = typename Definition::Prop;
using Adapter = detail::Renderable_Adapter<Scene_Object,
detail::Prop_Field<&Prop::viewport, "viewport", "Final scene viewport in physical pixels.">,
detail::Prop_Field<&Prop::background, "background", "Scene clear color.">,
detail::Prop_Field<&Prop::view_active, "view_active", "Whether the scene publishes rendered frames.">,
detail::State_Field<Scene::Base_Tag, &Scene::State::taskflow_rebuilt, "taskflow_rebuilt", "Whether the scene task graph was rebuilt.">,
@@ -154,7 +155,7 @@ std::unique_ptr<detail::Renderable_Descriptor> make_scene_component(Scene_Object
detail::State_Field<Scene::Base_Tag, &Scene::State::taskflow_max_predecessors, "taskflow_max_predecessors", "Maximum direct predecessors of a task.">,
detail::State_Field<Scene::Base_Tag, &Scene::State::taskflow_max_successors, "taskflow_max_successors", "Maximum direct successors of a task.">,
detail::State_Field<Scene::Base_Tag, &Scene::State::taskflow_execution_time_ns, "taskflow_execution_time_ns", "Scene graph execution time in nanoseconds.">>;
return detail::make_renderable_descriptor("scene", "Scene", "scene", Adapter{scene});
return detail::make_renderable_descriptor("scene", "场景", "scene", Adapter{scene});
}
template <>
@@ -169,33 +170,14 @@ std::unique_ptr<detail::Renderable_Descriptor> make_scene_component<Scene_3D>(Sc
detail::State_Field<Scene::Base_Tag, &Scene::State::taskflow_max_predecessors, "taskflow_max_predecessors", "Maximum direct predecessors of a task.">,
detail::State_Field<Scene::Base_Tag, &Scene::State::taskflow_max_successors, "taskflow_max_successors", "Maximum direct successors of a task.">,
detail::State_Field<Scene::Base_Tag, &Scene::State::taskflow_execution_time_ns, "taskflow_execution_time_ns", "Scene graph execution time in nanoseconds.">>;
return detail::make_renderable_descriptor("scene", "Scene", "scene", Adapter{scene});
return detail::make_renderable_descriptor("scene", "场景", "scene", Adapter{scene});
}
template <typename Axis_Object>
std::unique_ptr<detail::Renderable_Descriptor> make_axis_component(
std::string id, std::string label, Axis_Object& axis) {
using Adapter = detail::Renderable_Adapter<Axis_Object,
Prop_Field<&Abs_Axis::Prop::position, "position", "Axis origin in viewport pixels.">,
Prop_Field<&Abs_Axis::Prop::canvas_size, "canvas_size", "Axis canvas extent.">,
Prop_Field<&Abs_Axis::Prop::pixel_length, "pixel_length", "Signed axis length in pixels.">,
Prop_Field<&Abs_Axis::Prop::orientation, "orientation", "Axis orientation.">,
Prop_Field<&Abs_Axis::Prop::tick_length, "tick_length", "Major tick length.">,
Prop_Field<&Abs_Axis::Prop::sub_tick_length, "sub_tick_length", "Minor tick length.">,
Prop_Field<&Abs_Axis::Prop::axis_pen, "axis_pen", "Axis line and tick style.">,
Prop_Field<&Abs_Axis::Prop::unit_text, "unit_text", "Axis unit label.">,
Prop_Field<&Abs_Axis::Prop::unit_text_font, "unit_text_font", "Axis label font.">,
Prop_Field<&Abs_Axis::Prop::unit_text_pen, "unit_text_pen", "Axis label foreground.">,
Prop_Field<&Abs_Axis::Prop::unit_text_background_brush, "unit_text_background_brush", "Axis label background.">,
Prop_Field<&Abs_Axis::Prop::label_rotation_degrees, "label_rotation_degrees", "Tick label rotation.">,
Prop_Field<&Numeric_Axis::Prop::coordinate_range, "coordinate_range", "Visible coordinate range.">,
Prop_Field<&Numeric_Axis::Prop::precision, "precision", "Maximum decimal precision.">,
Prop_Field<&Numeric_Axis::Prop::locale, "locale", "Numeric label locale.">,
Prop_Field<&Numeric_Axis::Prop::wheel_enabled, "wheel_enabled", "Allows wheel zoom.">,
Prop_Field<&Numeric_Axis::Prop::drag_enabled, "drag_enabled", "Allows pointer drag panning.">>;
return make_renderable_component<Axis_Object,
Prop_Field<&Abs_Axis::Prop::position, "position", "Axis origin in viewport pixels.">,
Prop_Field<&Abs_Axis::Prop::canvas_size, "canvas_size", "Axis canvas extent.">,
Prop_Field<&Abs_Axis::Prop::pixel_length, "pixel_length", "Signed axis length in pixels.">,
Prop_Field<&Abs_Axis::Prop::orientation, "orientation", "Axis orientation.">,
Prop_Field<&Abs_Axis::Prop::tick_length, "tick_length", "Major tick length.">,
@@ -219,7 +201,6 @@ std::unique_ptr<detail::Renderable_Descriptor> make_axis_component<Time_Axis_Obj
std::string id, std::string label, Time_Axis_Object& axis) {
return make_renderable_component<Time_Axis_Object,
Prop_Field<&Abs_Axis::Prop::position, "position", "Axis origin in viewport pixels.">,
Prop_Field<&Abs_Axis::Prop::canvas_size, "canvas_size", "Axis canvas extent.">,
Prop_Field<&Abs_Axis::Prop::pixel_length, "pixel_length", "Signed axis length in pixels.">,
Prop_Field<&Abs_Axis::Prop::orientation, "orientation", "Axis orientation.">,
Prop_Field<&Abs_Axis::Prop::tick_length, "tick_length", "Major tick length.">,
@@ -251,7 +232,7 @@ std::unique_ptr<Plot::Scene_View> make_scene_view(
using State = typename Definition::State;
std::vector<std::unique_ptr<detail::Renderable_Descriptor>> components;
components.push_back(make_scene_component(scene));
components.push_back(make_renderable_component<Object, Fields...>("plot", "Plot Renderable", "renderable", object));
components.push_back(make_renderable_component<Object, Fields...>("plot", "主绘图组件", "renderable", object));
std::size_t axis_index{};
const auto append_owned = [&](const auto& owned) {
using Owned = std::remove_cvref_t<decltype(owned)>;
@@ -259,7 +240,7 @@ std::unique_ptr<Plot::Scene_View> make_scene_view(
|| std::same_as<typename Owned::element_type, Numeric_Axis_Object>
|| std::same_as<typename Owned::element_type, Time_Axis_Object>) {
const auto id = axis_index++ == 0 ? "axis-x" : "axis-y";
components.push_back(make_axis_component(id, id == std::string_view{"axis-x"} ? "Horizontal Axis" : "Vertical Axis", *owned));
components.push_back(make_axis_component(id, id == std::string_view{"axis-x"} ? "横向坐标轴" : "纵向坐标轴", *owned));
}
};
(append_owned(owned_objects), ...);
@@ -269,51 +250,46 @@ std::unique_ptr<Plot::Scene_View> make_scene_view(
std::forward<Owned_Objects>(owned_objects)...);
}
Frequency_Axis_Object::Builder frequency_axis_builder(Size canvas) {
Frequency_Axis_Object::Builder frequency_axis_builder() {
Frequency_Axis_Object::Builder builder;
builder
.set(&Abs_Axis::Prop::orientation, Axis_Orientation::horizontal)
.set(&Abs_Axis::Prop::position, Point_F{64.0, 370.0})
.set(&Abs_Axis::Prop::pixel_length, 620.0)
.set(&Abs_Axis::Prop::canvas_size, canvas)
.set(&Numeric_Axis::Prop::coordinate_range, Axis_Range{0.0, 100.0});
return builder;
}
Numeric_Axis_Object::Builder numeric_axis_builder(
Axis_Orientation orientation, Point_F position, Axis_Pixel_Length length,
Axis_Range range, Size canvas) {
Axis_Range range) {
Numeric_Axis_Object::Builder builder;
builder
.set(&Abs_Axis::Prop::orientation, orientation)
.set(&Abs_Axis::Prop::position, position)
.set(&Abs_Axis::Prop::pixel_length, length)
.set(&Abs_Axis::Prop::canvas_size, canvas)
.set(&Numeric_Axis::Prop::coordinate_range, range);
return builder;
}
Time_Axis_Object::Builder time_axis_builder(
Axis_Orientation orientation, Point_F position, Axis_Pixel_Length length, Size canvas) {
Axis_Orientation orientation, Point_F position, Axis_Pixel_Length length) {
Time_Axis_Object::Builder builder;
builder
.set(&Abs_Axis::Prop::orientation, orientation)
.set(&Abs_Axis::Prop::position, position)
.set(&Abs_Axis::Prop::pixel_length, length)
.set(&Abs_Axis::Prop::canvas_size, canvas);
.set(&Abs_Axis::Prop::pixel_length, length);
return builder;
}
template <typename... Axes>
void resize_axes(Size viewport, Axes*... axes) {
void resize_axes(Scene_2D* scene, Size viewport, Axes*... axes) {
const Size previous_viewport = scene->template read_prop<Render_Scene_2D::Base_Tag>().viewport;
if (previous_viewport == viewport || previous_viewport.empty()) return;
const auto resize_axis = [&](auto* axis) {
const auto layout = axis->template read_prop<Abs_Axis::Base_Tag>();
if (layout.canvas_size == viewport) return;
const double horizontal_scale = layout.canvas_size.width > 0
? static_cast<double>(viewport.width) / layout.canvas_size.width : 1.0;
const double vertical_scale = layout.canvas_size.height > 0
? static_cast<double>(viewport.height) / layout.canvas_size.height : 1.0;
axis->template set<&Abs_Axis::Prop::canvas_size>(viewport);
const double horizontal_scale = static_cast<double>(viewport.width) / previous_viewport.width;
const double vertical_scale = static_cast<double>(viewport.height) / previous_viewport.height;
axis->template set<&Abs_Axis::Prop::position>(Point_F{
layout.position.x * horizontal_scale,
layout.position.y * vertical_scale});
@@ -373,9 +349,9 @@ void dispatch_plot_input(Scene_Object& scene, const Plot_Input_Event& input) {
std::shared_ptr<Plot> make_spectrum_plot(asio::any_io_executor executor) {
constexpr Size canvas{720, 420};
auto frequency = *frequency_axis_builder(canvas).build();
auto frequency = *frequency_axis_builder().build();
auto vertical = *numeric_axis_builder(
Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-110.0, 0.0}, canvas).build();
Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-110.0, 0.0}).build();
Impl<Spectrum>::Builder spectrum_builder(frequency.get(), vertical.get());
spectrum_builder.set(&Spectrum::Prop::frequency_range, Axis_Range{0.0, 100.0})
.set(&Spectrum::Prop::max_hold_visible, true);
@@ -386,8 +362,8 @@ std::shared_ptr<Plot> make_spectrum_plot(asio::any_io_executor executor) {
.set(&Render_Scene_2D::Prop::view_active, true);
scene_builder.add_renderable(spectrum.get());
auto scene = *scene_builder.build();
auto update = [raw = spectrum.get(), frequency = frequency.get(), vertical = vertical.get()](const Plot_Event& event) {
resize_axes({static_cast<int>(event.width), static_cast<int>(event.height)}, frequency, vertical);
auto update = [scene = scene.get(), raw = spectrum.get(), frequency = frequency.get(), vertical = vertical.get()](const Plot_Event& event) {
resize_axes(scene, {static_cast<int>(event.width), static_cast<int>(event.height)}, frequency, vertical);
if (event.input) return;
std::array<double, 256> samples{};
for (std::size_t i = 0; i < samples.size(); ++i) {
@@ -433,9 +409,9 @@ std::shared_ptr<Plot> make_spectrum_plot(asio::any_io_executor executor) {
std::shared_ptr<Plot> make_frequency_trace_plot(asio::any_io_executor executor) {
constexpr Size canvas{720, 420};
auto time = *time_axis_builder(
Axis_Orientation::horizontal, {64.0, 370.0}, 620.0, canvas).build();
Axis_Orientation::horizontal, {64.0, 370.0}, 620.0).build();
auto vertical = *numeric_axis_builder(
Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-1.2, 1.2}, canvas).build();
Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-1.2, 1.2}).build();
auto trace = *Impl<Frequency_Trace>::Builder(time.get(), vertical.get()).build();
Scene_2D::Builder scene_builder;
scene_builder.set(&Render_Scene_2D::Prop::viewport, canvas)
@@ -443,8 +419,8 @@ std::shared_ptr<Plot> make_frequency_trace_plot(asio::any_io_executor executor)
.set(&Render_Scene_2D::Prop::view_active, true);
scene_builder.add_renderable(trace.get());
auto scene = *scene_builder.build();
auto update = [raw = trace.get(), time = time.get(), vertical = vertical.get()](const Plot_Event& event) {
resize_axes({static_cast<int>(event.width), static_cast<int>(event.height)}, time, vertical);
auto update = [scene = scene.get(), raw = trace.get(), time = time.get(), vertical = vertical.get()](const Plot_Event& event) {
resize_axes(scene, {static_cast<int>(event.width), static_cast<int>(event.height)}, time, vertical);
if (event.input) return;
constexpr double day_milliseconds = 86'400'000.0;
raw->append_sample(Time_Of_Day{static_cast<std::int64_t>(
@@ -465,9 +441,9 @@ std::shared_ptr<Plot> make_frequency_trace_plot(asio::any_io_executor executor)
std::shared_ptr<Plot> make_sweep_spectrum_plot(asio::any_io_executor executor) {
constexpr Size canvas{720, 420};
auto frequency = *frequency_axis_builder(canvas).build();
auto frequency = *frequency_axis_builder().build();
auto vertical = *numeric_axis_builder(
Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-110.0, 0.0}, canvas).build();
Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-110.0, 0.0}).build();
Impl<Sweep_Spectrum>::Builder sweep_builder(frequency.get(), vertical.get());
sweep_builder.set(&Sweep_Spectrum::Prop::frequency_range, Axis_Range{0.0, 100.0})
.set(&Sweep_Spectrum::Prop::bins_per_block, std::size_t{8})
@@ -479,8 +455,8 @@ std::shared_ptr<Plot> make_sweep_spectrum_plot(asio::any_io_executor executor) {
.set(&Render_Scene_2D::Prop::view_active, true);
scene_builder.add_renderable(sweep.get());
auto scene = *scene_builder.build();
auto update = [raw = sweep.get(), frequency = frequency.get(), vertical = vertical.get(), block_index = std::size_t{}](const Plot_Event& event) mutable {
resize_axes({static_cast<int>(event.width), static_cast<int>(event.height)}, frequency, vertical);
auto update = [scene = scene.get(), raw = sweep.get(), frequency = frequency.get(), vertical = vertical.get(), block_index = std::size_t{}](const Plot_Event& event) mutable {
resize_axes(scene, {static_cast<int>(event.width), static_cast<int>(event.height)}, frequency, vertical);
if (event.input) return;
const auto& state = raw->template read_prop<Sweep_Spectrum::Base_Tag>();
const std::size_t block_count = std::max<std::size_t>(1, state.block_count);
@@ -517,9 +493,9 @@ std::shared_ptr<Plot> make_sweep_spectrum_plot(asio::any_io_executor executor) {
std::shared_ptr<Plot> make_afterglow_plot(asio::any_io_executor executor) {
constexpr Size canvas{720, 420};
auto frequency = *frequency_axis_builder(canvas).build();
auto frequency = *frequency_axis_builder().build();
auto vertical = *numeric_axis_builder(
Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-110.0, 0.0}, canvas).build();
Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-110.0, 0.0}).build();
Impl<Afterglow>::Builder afterglow_builder(frequency.get(), vertical.get());
afterglow_builder.set(&Afterglow::Prop::frequency_range, Axis_Range{0.0, 100.0})
.set(&Afterglow::Prop::power_range, Axis_Range{-110.0, 0.0})
@@ -531,8 +507,8 @@ std::shared_ptr<Plot> make_afterglow_plot(asio::any_io_executor executor) {
.set(&Render_Scene_2D::Prop::view_active, true);
scene_builder.add_renderable(afterglow.get());
auto scene = *scene_builder.build();
auto update = [raw = afterglow.get(), frequency = frequency.get(), vertical = vertical.get()](const Plot_Event& event) {
resize_axes({static_cast<int>(event.width), static_cast<int>(event.height)}, frequency, vertical);
auto update = [scene = scene.get(), raw = afterglow.get(), frequency = frequency.get(), vertical = vertical.get()](const Plot_Event& event) {
resize_axes(scene, {static_cast<int>(event.width), static_cast<int>(event.height)}, frequency, vertical);
if (event.input) return;
std::array<double, 192> values{};
for (std::size_t i = 0; i < values.size(); ++i)
@@ -561,9 +537,9 @@ std::shared_ptr<Plot> make_afterglow_plot(asio::any_io_executor executor) {
std::shared_ptr<Plot> make_waterfall_plot(asio::any_io_executor executor) {
constexpr Size canvas{720, 420};
auto frequency = *frequency_axis_builder(canvas).build();
auto frequency = *frequency_axis_builder().build();
auto time = *time_axis_builder(
Axis_Orientation::vertical, {64.0, 370.0}, -320.0, canvas).build();
Axis_Orientation::vertical, {64.0, 370.0}, -320.0).build();
Impl<Waterfall>::Builder waterfall_builder(frequency.get(), time.get());
waterfall_builder.set(&Waterfall::Prop::frequency_range, Axis_Range{0.0, 100.0})
.set(&Waterfall::Prop::power_range, Axis_Range{-110.0, 0.0});
@@ -574,8 +550,8 @@ std::shared_ptr<Plot> make_waterfall_plot(asio::any_io_executor executor) {
.set(&Render_Scene_2D::Prop::view_active, true);
scene_builder.add_renderable(waterfall.get());
auto scene = *scene_builder.build();
auto update = [raw = waterfall.get(), frequency = frequency.get(), time = time.get()](const Plot_Event& event) {
resize_axes({static_cast<int>(event.width), static_cast<int>(event.height)}, frequency, time);
auto update = [scene = scene.get(), raw = waterfall.get(), frequency = frequency.get(), time = time.get()](const Plot_Event& event) {
resize_axes(scene, {static_cast<int>(event.width), static_cast<int>(event.height)}, frequency, time);
if (event.input) return;
std::array<double, 192> values{};
for (std::size_t i = 0; i < values.size(); ++i)
@@ -610,9 +586,9 @@ std::shared_ptr<Plot> make_waterfall_plot(asio::any_io_executor executor) {
std::shared_ptr<Plot> make_constellation_plot(asio::any_io_executor executor) {
constexpr Size canvas{720, 420};
auto horizontal = *numeric_axis_builder(
Axis_Orientation::horizontal, {64.0, 370.0}, 620.0, {-1.2, 1.2}, canvas).build();
Axis_Orientation::horizontal, {64.0, 370.0}, 620.0, {-1.2, 1.2}).build();
auto vertical = *numeric_axis_builder(
Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-1.2, 1.2}, canvas).build();
Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-1.2, 1.2}).build();
Impl<Constellation_Diagram>::Builder constellation_builder(horizontal.get(), vertical.get());
constellation_builder.set(&Constellation_Diagram::Prop::i_range, Axis_Range{-1.2, 1.2})
.set(&Constellation_Diagram::Prop::q_range, Axis_Range{-1.2, 1.2});
@@ -623,8 +599,8 @@ std::shared_ptr<Plot> make_constellation_plot(asio::any_io_executor executor) {
.set(&Render_Scene_2D::Prop::view_active, true);
scene_builder.add_renderable(constellation.get());
auto scene = *scene_builder.build();
auto update = [raw = constellation.get(), horizontal = horizontal.get(), vertical = vertical.get()](const Plot_Event& event) {
resize_axes({static_cast<int>(event.width), static_cast<int>(event.height)}, horizontal, vertical);
auto update = [scene = scene.get(), raw = constellation.get(), horizontal = horizontal.get(), vertical = vertical.get()](const Plot_Event& event) {
resize_axes(scene, {static_cast<int>(event.width), static_cast<int>(event.height)}, horizontal, vertical);
if (event.input) return;
const auto& state = raw->template read_prop<Constellation_Diagram::Base_Tag>();
const int anchor_count = static_cast<int>(state.type);
@@ -658,9 +634,9 @@ std::shared_ptr<Plot> make_constellation_plot(asio::any_io_executor executor) {
std::shared_ptr<Plot> make_selection_overlay_plot(asio::any_io_executor executor) {
constexpr Size canvas{720, 420};
auto horizontal = *numeric_axis_builder(
Axis_Orientation::horizontal, {64.0, 370.0}, 620.0, {0.0, 100.0}, canvas).build();
Axis_Orientation::horizontal, {64.0, 370.0}, 620.0, {0.0, 100.0}).build();
auto vertical = *numeric_axis_builder(
Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {0.0, 100.0}, canvas).build();
Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {0.0, 100.0}).build();
auto selection = *Impl<Selection_Rectangle_Overlay>::Builder(horizontal.get(), vertical.get()).build();
Scene_2D::Builder scene_builder;
scene_builder.set(&Render_Scene_2D::Prop::viewport, canvas)
@@ -668,8 +644,8 @@ std::shared_ptr<Plot> make_selection_overlay_plot(asio::any_io_executor executor
.set(&Render_Scene_2D::Prop::view_active, true);
scene_builder.add_renderable(selection.get());
auto scene = *scene_builder.build();
auto update = [horizontal = horizontal.get(), vertical = vertical.get()](const Plot_Event& event) {
resize_axes({static_cast<int>(event.width), static_cast<int>(event.height)}, horizontal, vertical);
auto update = [scene = scene.get(), horizontal = horizontal.get(), vertical = vertical.get()](const Plot_Event& event) {
resize_axes(scene, {static_cast<int>(event.width), static_cast<int>(event.height)}, horizontal, vertical);
};
auto view = make_scene_view<
Prop_Field<&Selection_Rectangle_Overlay::Prop::label_font, "label_font", "Font used for labels attached to selected regions.">,
@@ -787,7 +763,6 @@ void Plot::attach(const void* owner, Frame_Handler handler) {
std::lock_guard lock(d->handlers_mutex);
d->handlers.insert_or_assign(owner, std::move(handler));
}
submit({});
}
void Plot::detach(const void* owner) {
@@ -835,7 +810,7 @@ std::shared_ptr<Plot> make_datoviz_point_plot(asio::any_io_executor executor) {
detail::State_Field<Point_Visual::Base_Tag, &Point_Visual::State::prepared_revision, "prepared_revision", "Property revision represented by the currently prepared GPU data.">>;
std::vector<std::unique_ptr<detail::Renderable_Descriptor>> components;
components.push_back(make_scene_component(*scene));
components.push_back(detail::make_renderable_descriptor("plot", "Point Visual", "visual", Adapter{*visual}));
components.push_back(detail::make_renderable_descriptor("plot", "主绘图组件", "visual", Adapter{*visual}));
auto view = std::make_unique<Scene_View_Model<decltype(visual)>>(
std::move(components),
[](const Plot_Event&) {}, std::move(visual));
+71 -7
View File
@@ -224,18 +224,82 @@ constexpr std::string_view protocol_editor() {
return "json";
}
inline std::string_view protocol_field_label(std::string_view key) {
static constexpr std::pair<std::string_view, std::string_view> labels[] = {
{"position", "位置"}, {"viewport", "视口尺寸"}, {"background", "背景颜色"},
{"clear_color", "清屏颜色"}, {"view_active", "启用视图"},
{"pixel_length", "轴线长度"}, {"orientation", "轴线方向"}, {"tick_length", "主刻度长度"},
{"sub_tick_length", "次刻度长度"}, {"axis_pen", "轴线画笔"}, {"unit_text", "单位文字"},
{"unit_text_font", "单位字体"}, {"unit_text_pen", "单位文字画笔"},
{"unit_text_background_brush", "单位文字背景"}, {"label_rotation_degrees", "标签旋转角度"},
{"coordinate_range", "坐标范围"}, {"precision", "小数精度"}, {"locale", "数字区域格式"},
{"wheel_enabled", "允许滚轮缩放"}, {"drag_enabled", "允许拖动平移"},
{"visible_count", "可见数量"}, {"tick_label_spacing_px", "刻度文字间距"},
{"estimated_label_width_px", "预计标签宽度"}, {"format", "时间格式"},
{"newest_at_start", "最新数据置于起点"}, {"center_frequency", "中心频率"},
{"partition_count", "分区数量"}, {"partition_mode", "分区模式"},
{"interpolation_mode", "插值模式"}, {"visible_range_only", "仅处理可见范围"},
{"frequency_range", "频率范围"}, {"power_range", "功率范围"}, {"i_range", "同相范围"},
{"q_range", "正交范围"}, {"phase_offset_radians", "相位偏移"}, {"pen", "画笔"},
{"samples", "样本数据"}, {"rows", "瀑布图行数据"}, {"color_map", "颜色映射"},
{"tooltip_enabled", "启用提示框"}, {"tooltip_font", "提示框字体"},
{"tooltip_text_pen", "提示文字画笔"}, {"tooltip_background_brush", "提示框背景"},
{"frequency_bin_count", "频率箱数量"}, {"frequency_point_size", "频率网格数量"},
{"power_point_size", "功率网格数量"}, {"attenuation_rate", "衰减速率"},
{"interpolate", "启用插值"}, {"spectra", "频谱历史"}, {"blocks", "扫频块数据"},
{"bins_per_block", "每块频点数"}, {"block_count", "扫频块数量"},
{"max_hold_visible", "显示最大保持"}, {"min_hold_visible", "显示最小保持"},
{"max_marker_visible", "显示最大标记"}, {"min_marker_visible", "显示最小标记"},
{"sweep_region_visible", "显示扫频区域"}, {"sweep_frequency_range", "扫频范围"},
{"max_brush", "最大保持画刷"}, {"current_brush", "当前曲线画刷"},
{"min_brush", "最小保持画刷"}, {"max_pen", "最大保持画笔"},
{"current_pen", "当前曲线画笔"}, {"min_pen", "最小保持画笔"},
{"selected_marker_pen", "选中标记画笔"}, {"marker_pen", "标记画笔"},
{"middle_frequency_pen", "中心频率画笔"}, {"sweep_region_brush", "扫频区域画刷"},
{"current_frequency_pen", "当前频率指示线"}, {"custom_markers", "自定义标记"},
{"selected_marker", "当前选中标记"}, {"point_lifetime_ms", "点保留时间"},
{"type", "星座类型"}, {"point_color", "数据点颜色"}, {"anchor_color", "参考点颜色"},
{"points", "星座点数据"}, {"label_font", "标签字体"}, {"label_pen", "标签画笔"},
{"selection_brush", "选区画刷"}, {"selection_border_pen", "选区边框画笔"},
{"selected_regions", "已选区域"}, {"transform", "空间变换"}, {"visible", "是否可见"},
{"depth_test", "深度测试"}, {"items", "项目数据"},
{"prepare_dirty", "准备阶段待更新"}, {"paint_dirty", "绘制阶段待更新"},
{"prepare_executed", "准备阶段已执行"}, {"paint_executed", "绘制阶段已执行"},
{"prepare_graph_rebuilt", "准备任务图已重建"}, {"paint_graph_rebuilt", "绘制任务图已重建"},
{"prepare_task_count", "准备任务数量"}, {"paint_task_count", "绘制任务数量"},
{"prepare_execution_time_ns", "准备耗时"}, {"paint_execution_time_ns", "绘制耗时"},
{"taskflow_rebuilt", "场景任务图已重建"}, {"renderable_count", "可渲染对象数量"},
{"taskflow_task_count", "场景任务数量"}, {"taskflow_dependency_count", "任务依赖数量"},
{"taskflow_max_predecessors", "最大前驱数量"}, {"taskflow_max_successors", "最大后继数量"},
{"taskflow_execution_time_ns", "场景执行耗时"}, {"next_tick", "下一时间刻度"},
{"sample_count", "样本数量"}, {"rendered_point_count", "已绘制点数量"},
{"selectable_marker_count", "可选标记数量"}, {"stored_block_count", "已保存数据块数量"},
{"stored_point_count", "已保存数据点数量"}, {"history_count", "历史帧数量"},
{"latest_spectrum_point_count", "最新频谱点数量"}, {"rendered_cell_count", "已绘制单元数量"},
{"row_count", "瀑布图行数量"}, {"point_count", "数据点数量"},
{"selected_region_count", "已选区域数量"}, {"item_count", "项目数量"},
{"prepared_item_count", "已准备项目数量"}, {"prepared_revision", "已准备修订号"}
};
const auto found = std::ranges::find_if(labels, [key](const auto& entry) { return entry.first == key; });
return found == std::end(labels) ? key : found->second;
}
template <typename Adapter>
nlohmann::json adapter_schema(const Adapter& adapter, std::string_view id, std::string_view label, std::string_view kind) {
nlohmann::json properties = nlohmann::json::array();
nlohmann::json fields = nlohmann::json::array();
nlohmann::json state = nlohmann::json::object();
structive::type_descriptor<Adapter>().for_each_property([&](auto, const auto& field) {
using Field = std::remove_cvref_t<decltype(field)>;
using Value = typename Field::value_type;
const auto value = field.accessor.read(adapter);
if (field.template attribute<Renderable_Field_Role_Category>().value == Renderable_Field_Role::prop) {
nlohmann::json item{{"key", field.key()}, {"editor", protocol_editor<Value>()},
{"description", std::string(Field::accessor_type::description.view())},
{"value", encode_protocol_value(value)}};
const bool editable = field.template attribute<Renderable_Field_Role_Category>().value == Renderable_Field_Role::prop;
const auto field_label = protocol_field_label(field.key());
nlohmann::json item{{"key", field.key()}, {"label", field_label}, {"editor", protocol_editor<Value>()},
{"editable", editable},
{"description", std::string(field_label) + "。协议字段:" + std::string(field.key()) + "。"},
{"technical_description", std::string(Field::accessor_type::description.view())},
{"value", encode_protocol_value(value)}};
if (editable) {
if constexpr (std::is_enum_v<Value>) {
item["options"] = nlohmann::json::array();
for (const auto enum_value : magic_enum::enum_values<Value>()) {
@@ -243,13 +307,13 @@ nlohmann::json adapter_schema(const Adapter& adapter, std::string_view id, std::
item["options"].push_back({{"value", name}, {"label", name}});
}
}
properties.push_back(std::move(item));
} else {
state[field.key()] = encode_protocol_value(value);
}
fields.push_back(std::move(item));
});
return {{"id", id}, {"label", label}, {"kind", kind},
{"properties", std::move(properties)}, {"state", std::move(state)}};
{"fields", std::move(fields)}, {"state", std::move(state)}};
}
template <typename Adapter>