Files
Aethera/mcp/core/runtime/Renderable_Adapter.ipp
T
2026-08-28 10:11:40 +08:00

326 lines
18 KiB
C++

#pragma once
#include <mcp/core/Render_Types.hpp>
#include <nlohmann/json.hpp>
#include <render_2D/plottable/Plottables.hpp>
#include <render_3D/Render_3D.hpp>
#include <deque>
#include <limits>
#include <ranges>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
namespace aethera::web::detail {
struct Renderable_Field_Role_Category {};
template <Renderable_Field_Role Role>
struct Renderable_Field_Role_Attribute {
using attribute_category = Renderable_Field_Role_Category;
static constexpr bool single_valued = true;
static constexpr bool inheritable = false;
static constexpr auto value = Role;
};
template <auto Member, structive::Fixed_String Key, structive::Fixed_String Description,
structive::Fixed_String Editor>
struct Prop_Field {
static constexpr auto member = Member;
static constexpr auto key = Key;
static constexpr auto description = Description;
static constexpr auto editor = Editor;
static constexpr auto role = Renderable_Field_Role::prop;
};
template <typename Tag, auto Member, structive::Fixed_String Key, structive::Fixed_String Description>
struct State_Field {
using tag_type = Tag;
static constexpr auto member = Member;
static constexpr auto key = Key;
static constexpr auto description = Description;
static constexpr auto role = Renderable_Field_Role::state;
};
template <typename Object, typename... Fields>
struct Renderable_Adapter final : public structive::Property_Object<Renderable_Adapter<Object, Fields...>, structive::No_Lock_Policy> {
public:
explicit Renderable_Adapter(Object& value) : object(&value) {}
void identify(std::string_view id) {
if constexpr (std::derived_from<Object, aethera::Renderable>)
double_buffer::detail::Internal_Access::layer<aethera::Renderable>(object)
.diagnostic_component_id = id;
}
private:
template <typename Adapter, typename Field>
friend struct Renderable_Field_Accessor;
Object* object;
};
template <typename Adapter, typename Field>
struct Renderable_Field_Accessor;
template <typename Object, typename... Fields, auto Member, structive::Fixed_String Key,
structive::Fixed_String Description, structive::Fixed_String Editor>
struct Renderable_Field_Accessor<Renderable_Adapter<Object, Fields...>, Prop_Field<Member, Key, Description, Editor>> {
using object_type = Renderable_Adapter<Object, Fields...>;
using value_type = typename structive::Member_Pointer_Traits<decltype(Member)>::value_type;
using storage_identity = void;
using dependency_spec = structive::No_Property_Dependencies;
static constexpr auto description = Description;
static constexpr auto editor = Editor;
static constexpr bool readable = true;
static constexpr bool writable = true;
static constexpr bool synchronized_view_read = false;
static constexpr bool trusted_object_access = true;
[[nodiscard]] value_type read(const object_type& adapter) const {
return adapter.object->template get<Member>();
}
void write(object_type& adapter, value_type value) const {
adapter.object->template set<Member>(std::move(value));
}
};
template <typename Object, typename... Fields, typename Tag, auto Member, structive::Fixed_String Key, structive::Fixed_String Description>
struct Renderable_Field_Accessor<Renderable_Adapter<Object, Fields...>, State_Field<Tag, Member, Key, Description>> {
using object_type = Renderable_Adapter<Object, Fields...>;
using value_type = typename structive::Member_Pointer_Traits<decltype(Member)>::value_type;
using storage_identity = void;
using dependency_spec = structive::No_Property_Dependencies;
static constexpr auto description = Description;
static constexpr bool readable = true;
static constexpr bool writable = false;
static constexpr bool synchronized_view_read = false;
static constexpr bool trusted_object_access = true;
[[nodiscard]] value_type read(const object_type& adapter) const {
value_type result{};
adapter.object->template access_state<Tag>([&](const auto& state) {
result = state.*Member;
});
return result;
}
};
template <typename Adapter, typename Field>
constexpr auto renderable_field_descriptor() {
using Accessor = Renderable_Field_Accessor<Adapter, Field>;
using Role = Renderable_Field_Role_Attribute<Field::role>;
using Key = structive::Key_Attribute<Field::key>;
return structive::Property_Descriptor<Accessor, Key, Role>{{}, {Key{}, Role{}}};
}
}
namespace structive {
template <typename Object, typename... Fields>
struct Type_Descriptor<aethera::web::detail::Renderable_Adapter<Object, Fields...>> {
static auto get() {
using Adapter = aethera::web::detail::Renderable_Adapter<Object, Fields...>;
return object<Adapter>(synchronization(sync_all_unsynchronized),
aethera::web::detail::renderable_field_descriptor<Adapter, Fields>()...);
}
};
}
namespace aethera::web::detail {
template <typename Value>
[[nodiscard]] constexpr std::string_view renderable_editor(
mcp::Type_Tag<Value>) noexcept {
return mcp::protocol_editor<Value>();
}
[[nodiscard]] constexpr std::string_view renderable_editor(
mcp::Type_Tag<Color>) noexcept { return "color"; }
[[nodiscard]] constexpr std::string_view renderable_editor(
mcp::Type_Tag<render_3d::Linear_Color>) noexcept { return "color"; }
[[nodiscard]] constexpr std::string_view renderable_editor(
mcp::Type_Tag<render_2d::Point_F>) noexcept { return "point2"; }
[[nodiscard]] constexpr std::string_view renderable_editor(
mcp::Type_Tag<render_2d::Size>) noexcept { return "size"; }
[[nodiscard]] constexpr std::string_view renderable_editor(
mcp::Type_Tag<render_3d::Extent>) noexcept { return "size"; }
[[nodiscard]] constexpr std::string_view renderable_editor(
mcp::Type_Tag<render_2d::Rect_F>) noexcept { return "rect"; }
[[nodiscard]] constexpr std::string_view renderable_editor(
mcp::Type_Tag<render_2d::Axis_Range>) noexcept { return "range"; }
[[nodiscard]] constexpr std::string_view renderable_editor(
mcp::Type_Tag<render_2d::Plot_Partition_Grid>) noexcept {
return "partition-grid";
}
[[nodiscard]] constexpr std::string_view renderable_editor(
mcp::Type_Tag<render_2d::Pen>) noexcept { return "pen"; }
[[nodiscard]] constexpr std::string_view renderable_editor(
mcp::Type_Tag<render_2d::Brush>) noexcept { return "brush"; }
[[nodiscard]] constexpr std::string_view renderable_editor(
mcp::Type_Tag<render_2d::Font>) noexcept { return "font"; }
[[nodiscard]] constexpr std::string_view renderable_editor(
mcp::Type_Tag<render_2d::Color_Map>) noexcept { return "color-map"; }
[[nodiscard]] constexpr std::string_view renderable_editor(
mcp::Type_Tag<render_3d::Vec3>) noexcept { return "vector3"; }
[[nodiscard]] constexpr std::string_view renderable_editor(
mcp::Type_Tag<render_3d::Matrix4>) noexcept { return "matrix4"; }
[[nodiscard]] constexpr std::string_view renderable_editor(
mcp::Type_Tag<std::vector<render_3d::Marker>>) noexcept {
return "marker-list";
}
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", "启用视图"},
{"initial_view", "初始相机视图"}, {"projection", "投影方式"},
{"controller", "Datoviz 相机模式"}, {"turntable_control", "Turntable 控制"},
{"arcball_control", "Arcball 控制"}, {"fly_control", "Fly 控制"},
{"panzoom_control", "Panzoom 控制"}, {"vertical_field_of_view_degrees", "垂直视场角"},
{"near_plane", "近裁剪面"}, {"far_plane", "远裁剪面"},
{"x_axis", "X 坐标轴"}, {"y_axis", "Y 坐标轴"}, {"z_axis", "Z 坐标轴"},
{"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_grid", "分区网格"},
{"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", "已选区域"}, {"cache_enabled", "启用绘制缓存"},
{"transform", "空间变换"}, {"visible", "是否可见"},
{"depth_test", "深度测试"}, {"items", "项目数据"},
{"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 fields = nlohmann::json::array();
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 bool editable = field.template attribute<Renderable_Field_Role_Category>().value == Renderable_Field_Role::prop;
const auto field_label = protocol_field_label(field.key());
std::string_view editor = renderable_editor(mcp::Type_Tag<Value>{});
if constexpr (requires { Field::accessor_type::editor; }) {
if (!Field::accessor_type::editor.view().empty()) editor = Field::accessor_type::editor.view();
}
nlohmann::json item{
{"key", field.key()}, {"label", field_label}, {"editor", editor},
{"editable", editable},
{"description", std::string(field_label) + "。协议字段:" + std::string(field.key()) + ""},
{"technical_description", std::string(Field::accessor_type::description.view())}
};
if (editable) item["value"] = mcp::encode_protocol_value(field.accessor.read(adapter));
if constexpr (std::same_as<Value, render_3d::Linear_Color>) item["color_channel_scale"] = "normalized";
else if constexpr (std::same_as<Value, Color>) item["color_channel_scale"] = "byte";
if (editable) {
if constexpr (std::is_enum_v<Value>) {
item["options"] = nlohmann::json::array();
for (const auto enum_value : magic_enum::enum_values<Value>()) {
const auto name = magic_enum::enum_name(enum_value);
item["options"].push_back({{"value", name}, {"label", name}});
}
}
}
fields.push_back(std::move(item));
});
return {
{"id", id}, {"label", label}, {"kind", kind},
{"fields", std::move(fields)}
};
}
template <typename Adapter>
nlohmann::json adapter_values(const Adapter& adapter, Renderable_Field_Role role) {
nlohmann::json result = nlohmann::json::object();
structive::type_descriptor<Adapter>().for_each_property([&](auto, const auto& field) {
if (field.template attribute<Renderable_Field_Role_Category>().value == role)
result[field.key()] = mcp::encode_protocol_value(field.accessor.read(adapter));
});
return result;
}
template <typename Adapter>
nlohmann::json write_adapter_prop(Adapter& adapter, std::string_view key, const nlohmann::json& input) {
nlohmann::json result{{"success", false}, {"key", key}};
const bool found = structive::visit_schema_property(structive::type_descriptor<Adapter>(), key, [&](auto, const auto& field) {
using Field = std::remove_cvref_t<decltype(field)>;
using Value = typename Field::value_type;
if constexpr (!Field::writable) {
result["error"] = "property is read-only";
}
else {
try {
Value value{};
mcp::decode_protocol_value(value, input);
const auto status = adapter.runtime_write(key, typeid(Value), &value);
if (status != structive::Runtime_Access_Result::ok) {
result["error"] = "property write was rejected";
return;
}
result["success"] = true;
result["value"] = mcp::encode_protocol_value(field.accessor.read(adapter));
}
catch (const std::exception& error) {
result["error"] = error.what();
}
}
});
if (!found) result["error"] = "unknown property";
return result;
}
template <typename Adapter>
Renderable_Descriptor_Model<Adapter>::Renderable_Descriptor_Model(
std::string id, std::string label, std::string kind, Adapter value) : component_id(std::move(id)), component_label(std::move(label)), component_kind(std::move(kind)), adapter(std::move(value)) {
adapter.identify(component_id);
}
template <typename Adapter>
std::string_view Renderable_Descriptor_Model<Adapter>::id() const noexcept {
return component_id;
}
template <typename Adapter>
nlohmann::json Renderable_Descriptor_Model<Adapter>::schema() const {
return adapter_schema(adapter, component_id, component_label, component_kind);
}
template <typename Adapter>
nlohmann::json Renderable_Descriptor_Model<Adapter>::state() const {
return {{"protocol", "aethera.component.state"}, {"version", 1},
{"component", component_id},
{"state", adapter_values(adapter, Renderable_Field_Role::state)}};
}
template <typename Adapter>
nlohmann::json Renderable_Descriptor_Model<Adapter>::values() const {
return {{"component", component_id}, {"label", component_label},
{"kind", component_kind},
{"prop", adapter_values(adapter, Renderable_Field_Role::prop)},
{"state", adapter_values(adapter, Renderable_Field_Role::state)}};
}
template <typename Adapter>
nlohmann::json Renderable_Descriptor_Model<Adapter>::write_prop(std::string_view key, const nlohmann::json& value) {
return write_adapter_prop(adapter, key, value);
}
template <typename Adapter>
std::unique_ptr<Renderable_Descriptor> make_renderable_descriptor(
std::string id, std::string label, std::string kind, Adapter adapter) {
return std::make_unique < Renderable_Descriptor_Model<Adapter> > (
std::move(id), std::move(label), std::move(kind), std::move(adapter));
}
}