diff --git a/web_server/src/Plot.cpp b/web_server/src/Plot.cpp index 4e1374f..1e3a1ff 100644 --- a/web_server/src/Plot.cpp +++ b/web_server/src/Plot.cpp @@ -114,14 +114,18 @@ private: std::function update_scene; }; -#define AETHERA_PROP(Type, Name) detail::Prop_Field<&Type::Prop::Name, #Name> -#define AETHERA_STATE(Type, Name) detail::State_Field +template +using Prop_Field = detail::Prop_Field; -template +template +using State_Field = detail::State_Field; + +template std::unique_ptr make_scene_view( Object& object, std::vector> owners, std::function update) { + using Definition = typename Object::Attached_Object; using Tag = typename Definition::Base_Tag; using State = typename Definition::State; using Adapter = detail::Renderable_Adaptertemplate set<&Abs_Axis::Prop::canvas_size>(viewport), ...); } -template -std::shared_ptr build_2d_plot(asio::any_io_executor executor) { +std::unique_ptr make_scene_2d() { constexpr Size canvas{720, 420}; Scene_2D::Builder scene_builder; scene_builder .set(&Render_Scene_2D::Prop::viewport, canvas) .set(&Render_Scene_2D::Prop::background, Color{7, 13, 24, 255}) .set(&Render_Scene_2D::Prop::view_active, true); - auto scene = finish_build(std::move(scene_builder)); + return finish_build(std::move(scene_builder)); +} +} - if constexpr (std::same_as) { +std::shared_ptr make_spectrum_plot(asio::any_io_executor executor) { + constexpr Size canvas{720, 420}; + auto scene = make_scene_2d(); auto frequency = finish_shared(frequency_axis_builder(canvas)); auto vertical = finish_shared(numeric_axis_builder( Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-110.0, 0.0}, canvas)); @@ -214,23 +221,41 @@ std::shared_ptr build_2d_plot(asio::any_io_executor executor) { raw->update_samples(samples); }; std::vector> owners{frequency, vertical, object}; - auto view = make_scene_view(*object, std::move(owners), std::move(update)); + auto view = make_scene_view< + Prop_Field<&Spectrum::Prop::center_frequency, "center_frequency">, + Prop_Field<&Spectrum::Prop::partition_count, "partition_count">, + Prop_Field<&Spectrum::Prop::max_hold_visible, "max_hold_visible">, + Prop_Field<&Spectrum::Prop::min_hold_visible, "min_hold_visible">, + Prop_Field<&Spectrum::Prop::max_marker_visible, "max_marker_visible">, + Prop_Field<&Spectrum::Prop::min_marker_visible, "min_marker_visible">, + Prop_Field<&Spectrum::Prop::sweep_region_visible, "sweep_region_visible">, + Prop_Field<&Spectrum::Prop::visible_range_only, "visible_range_only">, + Prop_Field<&Spectrum::Prop::frequency_range, "frequency_range">, + Prop_Field<&Spectrum::Prop::sweep_frequency_range, "sweep_frequency_range">, + Prop_Field<&Spectrum::Prop::partition_mode, "partition_mode">, + Prop_Field<&Spectrum::Prop::interpolation_mode, "interpolation_mode">, + Prop_Field<&Spectrum::Prop::max_brush, "max_brush">, + Prop_Field<&Spectrum::Prop::current_brush, "current_brush">, + Prop_Field<&Spectrum::Prop::min_brush, "min_brush">, + Prop_Field<&Spectrum::Prop::max_pen, "max_pen">, + Prop_Field<&Spectrum::Prop::current_pen, "current_pen">, + Prop_Field<&Spectrum::Prop::min_pen, "min_pen">, + Prop_Field<&Spectrum::Prop::selected_marker_pen, "selected_marker_pen">, + Prop_Field<&Spectrum::Prop::marker_pen, "marker_pen">, + Prop_Field<&Spectrum::Prop::middle_frequency_pen, "middle_frequency_pen">, + Prop_Field<&Spectrum::Prop::sweep_region_brush, "sweep_region_brush">, + Prop_Field<&Spectrum::Prop::custom_markers, "custom_markers">, + Prop_Field<&Spectrum::Prop::selected_marker, "selected_marker">, + State_Field, + State_Field, + State_Field>( + *object, std::move(owners), std::move(update)); return std::make_shared(std::move(executor), std::move(scene), std::move(view)); - } else if constexpr (std::same_as) { +} + +std::shared_ptr make_frequency_trace_plot(asio::any_io_executor executor) { + constexpr Size canvas{720, 420}; + auto scene = make_scene_2d(); auto time = finish_shared(time_axis_builder( Axis_Orientation::horizontal, {64.0, 370.0}, 620.0, canvas)); auto vertical = finish_shared(numeric_axis_builder( @@ -246,13 +271,20 @@ std::shared_ptr build_2d_plot(asio::any_io_executor executor) { + std::sin(event.time_milliseconds * 0.0007) * 0.2); }; std::vector> owners{time, vertical, object, tick}; - auto view = make_scene_view( + auto view = make_scene_view< + Prop_Field<&Frequency_Trace::Prop::partition_count, "partition_count">, + Prop_Field<&Frequency_Trace::Prop::pen, "pen">, + Prop_Field<&Frequency_Trace::Prop::partition_mode, "partition_mode">, + Prop_Field<&Frequency_Trace::Prop::samples, "samples">, + State_Field, + State_Field>( *object, std::move(owners), std::move(update)); return std::make_shared(std::move(executor), std::move(scene), std::move(view)); - } else if constexpr (std::same_as) { +} + +std::shared_ptr make_sweep_spectrum_plot(asio::any_io_executor executor) { + constexpr Size canvas{720, 420}; + auto scene = make_scene_2d(); auto frequency = finish_shared(frequency_axis_builder(canvas)); auto vertical = finish_shared(numeric_axis_builder( Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-110.0, 0.0}, canvas)); @@ -269,16 +301,27 @@ std::shared_ptr build_2d_plot(asio::any_io_executor executor) { raw->append_block(values); }; std::vector> owners{frequency, vertical, object}; - auto view = make_scene_view(*object, std::move(owners), std::move(update)); + auto view = make_scene_view< + Prop_Field<&Sweep_Spectrum::Prop::bins_per_block, "bins_per_block">, + Prop_Field<&Sweep_Spectrum::Prop::block_count, "block_count">, + Prop_Field<&Sweep_Spectrum::Prop::partition_count, "partition_count">, + Prop_Field<&Sweep_Spectrum::Prop::visible_range_only, "visible_range_only">, + Prop_Field<&Sweep_Spectrum::Prop::frequency_range, "frequency_range">, + Prop_Field<&Sweep_Spectrum::Prop::partition_mode, "partition_mode">, + Prop_Field<&Sweep_Spectrum::Prop::pen, "pen">, + Prop_Field<&Sweep_Spectrum::Prop::current_frequency_pen, "current_frequency_pen">, + Prop_Field<&Sweep_Spectrum::Prop::interpolation_mode, "interpolation_mode">, + Prop_Field<&Sweep_Spectrum::Prop::blocks, "blocks">, + State_Field, + State_Field, + State_Field>( + *object, std::move(owners), std::move(update)); return std::make_shared(std::move(executor), std::move(scene), std::move(view)); - } else if constexpr (std::same_as) { +} + +std::shared_ptr make_afterglow_plot(asio::any_io_executor executor) { + constexpr Size canvas{720, 420}; + auto scene = make_scene_2d(); auto frequency = finish_shared(frequency_axis_builder(canvas)); auto vertical = finish_shared(numeric_axis_builder( Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-110.0, 0.0}, canvas)); @@ -300,16 +343,27 @@ std::shared_ptr build_2d_plot(asio::any_io_executor executor) { raw->append_spectrum(values); }; std::vector> owners{frequency, vertical, object}; - auto view = make_scene_view(*object, std::move(owners), std::move(update)); + auto view = make_scene_view< + Prop_Field<&Afterglow::Prop::frequency_point_size, "frequency_point_size">, + Prop_Field<&Afterglow::Prop::power_point_size, "power_point_size">, + Prop_Field<&Afterglow::Prop::partition_count, "partition_count">, + Prop_Field<&Afterglow::Prop::interpolate, "interpolate">, + Prop_Field<&Afterglow::Prop::attenuation_rate, "attenuation_rate">, + Prop_Field<&Afterglow::Prop::frequency_range, "frequency_range">, + Prop_Field<&Afterglow::Prop::power_range, "power_range">, + Prop_Field<&Afterglow::Prop::partition_mode, "partition_mode">, + Prop_Field<&Afterglow::Prop::color_map, "color_map">, + Prop_Field<&Afterglow::Prop::spectra, "spectra">, + State_Field, + State_Field, + State_Field>( + *object, std::move(owners), std::move(update)); return std::make_shared(std::move(executor), std::move(scene), std::move(view)); - } else if constexpr (std::same_as) { +} + +std::shared_ptr make_waterfall_plot(asio::any_io_executor executor) { + constexpr Size canvas{720, 420}; + auto scene = make_scene_2d(); auto frequency = finish_shared(frequency_axis_builder(canvas)); auto time = finish_shared(time_axis_builder( Axis_Orientation::vertical, {64.0, 370.0}, -320.0, canvas)); @@ -331,18 +385,30 @@ std::shared_ptr build_2d_plot(asio::any_io_executor executor) { raw->append_row((*tick)++, values); }; std::vector> owners{frequency, time, object, tick}; - auto view = make_scene_view( + auto view = make_scene_view< + Prop_Field<&Waterfall::Prop::tooltip_enabled, "tooltip_enabled">, + Prop_Field<&Waterfall::Prop::tooltip_font, "tooltip_font">, + Prop_Field<&Waterfall::Prop::tooltip_text_pen, "tooltip_text_pen">, + Prop_Field<&Waterfall::Prop::tooltip_background_brush, "tooltip_background_brush">, + Prop_Field<&Waterfall::Prop::frequency_bin_count, "frequency_bin_count">, + Prop_Field<&Waterfall::Prop::partition_count, "partition_count">, + Prop_Field<&Waterfall::Prop::visible_range_only, "visible_range_only">, + Prop_Field<&Waterfall::Prop::frequency_range, "frequency_range">, + Prop_Field<&Waterfall::Prop::power_range, "power_range">, + Prop_Field<&Waterfall::Prop::partition_mode, "partition_mode">, + Prop_Field<&Waterfall::Prop::interpolation_mode, "interpolation_mode">, + Prop_Field<&Waterfall::Prop::color_map, "color_map">, + Prop_Field<&Waterfall::Prop::rows, "rows">, + State_Field, + State_Field, + State_Field>( *object, std::move(owners), std::move(update)); return std::make_shared(std::move(executor), std::move(scene), std::move(view)); - } else if constexpr (std::same_as) { +} + +std::shared_ptr make_constellation_plot(asio::any_io_executor executor) { + constexpr Size canvas{720, 420}; + auto scene = make_scene_2d(); auto horizontal = finish_shared(numeric_axis_builder( Axis_Orientation::horizontal, {64.0, 370.0}, 620.0, {-1.2, 1.2}, canvas)); auto vertical = finish_shared(numeric_axis_builder( @@ -361,14 +427,23 @@ std::shared_ptr build_2d_plot(asio::any_io_executor executor) { std::sin(phase) * 0.82 + 0.04 * std::cos(phase * 5.0)}); }; std::vector> owners{horizontal, vertical, object}; - auto view = make_scene_view(*object, std::move(owners), std::move(update)); + auto view = make_scene_view< + Prop_Field<&Constellation_Diagram::Prop::point_lifetime_ms, "point_lifetime_ms">, + Prop_Field<&Constellation_Diagram::Prop::type, "type">, + Prop_Field<&Constellation_Diagram::Prop::phase_offset_radians, "phase_offset_radians">, + Prop_Field<&Constellation_Diagram::Prop::i_range, "i_range">, + Prop_Field<&Constellation_Diagram::Prop::q_range, "q_range">, + Prop_Field<&Constellation_Diagram::Prop::point_color, "point_color">, + Prop_Field<&Constellation_Diagram::Prop::anchor_color, "anchor_color">, + Prop_Field<&Constellation_Diagram::Prop::points, "points">, + State_Field>( + *object, std::move(owners), std::move(update)); return std::make_shared(std::move(executor), std::move(scene), std::move(view)); - } else if constexpr (std::same_as) { +} + +std::shared_ptr make_selection_overlay_plot(asio::any_io_executor executor) { + constexpr Size canvas{720, 420}; + auto scene = make_scene_2d(); auto horizontal = finish_shared(numeric_axis_builder( Axis_Orientation::horizontal, {64.0, 370.0}, 620.0, {0.0, 100.0}, canvas)); auto vertical = finish_shared(numeric_axis_builder( @@ -381,20 +456,17 @@ std::shared_ptr build_2d_plot(asio::any_io_executor executor) { resize_axes({static_cast(event.width), static_cast(event.height)}, horizontal, vertical); }; std::vector> owners{horizontal, vertical, object}; - auto view = make_scene_view( + auto view = make_scene_view< + Prop_Field<&Selection_Rectangle_Overlay::Prop::label_font, "label_font">, + Prop_Field<&Selection_Rectangle_Overlay::Prop::label_pen, "label_pen">, + Prop_Field<&Selection_Rectangle_Overlay::Prop::selection_brush, "selection_brush">, + Prop_Field<&Selection_Rectangle_Overlay::Prop::selection_border_pen, "selection_border_pen">, + Prop_Field<&Selection_Rectangle_Overlay::Prop::selected_regions, "selected_regions">, + State_Field>( *object, std::move(owners), std::move(update)); return std::make_shared(std::move(executor), std::move(scene), std::move(view)); - } -} - -#undef AETHERA_STATE -#undef AETHERA_PROP } struct Plot::Private { @@ -523,28 +595,6 @@ void Plot::async_write_prop(std::string key, nlohmann::json value, Json_Handler throw std::runtime_error("plot input queue is unavailable"); } -std::shared_ptr make_spectrum_plot(asio::any_io_executor executor) { - return build_2d_plot(std::move(executor)); -} -std::shared_ptr make_frequency_trace_plot(asio::any_io_executor executor) { - return build_2d_plot(std::move(executor)); -} -std::shared_ptr make_sweep_spectrum_plot(asio::any_io_executor executor) { - return build_2d_plot(std::move(executor)); -} -std::shared_ptr make_afterglow_plot(asio::any_io_executor executor) { - return build_2d_plot(std::move(executor)); -} -std::shared_ptr make_waterfall_plot(asio::any_io_executor executor) { - return build_2d_plot(std::move(executor)); -} -std::shared_ptr make_constellation_plot(asio::any_io_executor executor) { - return build_2d_plot(std::move(executor)); -} -std::shared_ptr make_selection_overlay_plot(asio::any_io_executor executor) { - return build_2d_plot(std::move(executor)); -} - std::shared_ptr make_datoviz_point_plot(asio::any_io_executor executor) { using Visual_Object = Impl; Visual_Object::Builder visual_builder; diff --git a/web_server/src/Renderable_Adapter.ipp b/web_server/src/Renderable_Adapter.ipp index cfd9158..13ce7ee 100644 --- a/web_server/src/Renderable_Adapter.ipp +++ b/web_server/src/Renderable_Adapter.ipp @@ -41,23 +41,32 @@ struct Value_Adapter, Json> { static void decode(value_type& target, const Json& value); }; -#define AETHERA_WEB_JSON_REFLECTION(Type) \ - template <> struct Reflection_Adapter : Boost_Pfr_Reflection_Adapter {}; \ - template <> struct Type_Descriptor { static auto get(); }; -AETHERA_WEB_JSON_REFLECTION(aethera::Color) -AETHERA_WEB_JSON_REFLECTION(aethera::render_2d::Point_F) -AETHERA_WEB_JSON_REFLECTION(aethera::render_2d::Rect_F) -AETHERA_WEB_JSON_REFLECTION(aethera::render_2d::Axis_Range) -AETHERA_WEB_JSON_REFLECTION(aethera::render_2d::Pen) -AETHERA_WEB_JSON_REFLECTION(aethera::render_2d::Brush) -AETHERA_WEB_JSON_REFLECTION(aethera::render_2d::Font) -AETHERA_WEB_JSON_REFLECTION(aethera::render_2d::Frequency_Trace_Sample) -AETHERA_WEB_JSON_REFLECTION(aethera::render_2d::Waterfall_Row) -AETHERA_WEB_JSON_REFLECTION(aethera::render_2d::Constellation_Point) -AETHERA_WEB_JSON_REFLECTION(aethera::render_3d::Vec3) -AETHERA_WEB_JSON_REFLECTION(aethera::render_3d::Matrix4) -AETHERA_WEB_JSON_REFLECTION(aethera::render_3d::Point) -#undef AETHERA_WEB_JSON_REFLECTION +template <> struct Reflection_Adapter : Boost_Pfr_Reflection_Adapter {}; +template <> struct Type_Descriptor { static auto get(); }; +template <> struct Reflection_Adapter : Boost_Pfr_Reflection_Adapter {}; +template <> struct Type_Descriptor { static auto get(); }; +template <> struct Reflection_Adapter : Boost_Pfr_Reflection_Adapter {}; +template <> struct Type_Descriptor { static auto get(); }; +template <> struct Reflection_Adapter : Boost_Pfr_Reflection_Adapter {}; +template <> struct Type_Descriptor { static auto get(); }; +template <> struct Reflection_Adapter : Boost_Pfr_Reflection_Adapter {}; +template <> struct Type_Descriptor { static auto get(); }; +template <> struct Reflection_Adapter : Boost_Pfr_Reflection_Adapter {}; +template <> struct Type_Descriptor { static auto get(); }; +template <> struct Reflection_Adapter : Boost_Pfr_Reflection_Adapter {}; +template <> struct Type_Descriptor { static auto get(); }; +template <> struct Reflection_Adapter : Boost_Pfr_Reflection_Adapter {}; +template <> struct Type_Descriptor { static auto get(); }; +template <> struct Reflection_Adapter : Boost_Pfr_Reflection_Adapter {}; +template <> struct Type_Descriptor { static auto get(); }; +template <> struct Reflection_Adapter : Boost_Pfr_Reflection_Adapter {}; +template <> struct Type_Descriptor { static auto get(); }; +template <> struct Reflection_Adapter : Boost_Pfr_Reflection_Adapter {}; +template <> struct Type_Descriptor { static auto get(); }; +template <> struct Reflection_Adapter : Boost_Pfr_Reflection_Adapter {}; +template <> struct Type_Descriptor { static auto get(); }; +template <> struct Reflection_Adapter : Boost_Pfr_Reflection_Adapter {}; +template <> struct Type_Descriptor { static auto get(); }; template <> struct Reflection_Adapter @@ -320,21 +329,46 @@ void Value_Adapter, Json>::decode(value_type& target, co target = std::move(updated); } -#define AETHERA_WEB_DEFINE_JSON_DESCRIPTOR(Type, Id, Label) \ - inline auto Type_Descriptor::get() { return reflected_object_with(Id, Label, Identity_Field_Customizer{}); } -AETHERA_WEB_DEFINE_JSON_DESCRIPTOR(aethera::Color, "color", "Color") -AETHERA_WEB_DEFINE_JSON_DESCRIPTOR(aethera::render_2d::Point_F, "point", "Point") -AETHERA_WEB_DEFINE_JSON_DESCRIPTOR(aethera::render_2d::Rect_F, "rectangle", "Rectangle") -AETHERA_WEB_DEFINE_JSON_DESCRIPTOR(aethera::render_2d::Axis_Range, "axis_range", "Axis range") -AETHERA_WEB_DEFINE_JSON_DESCRIPTOR(aethera::render_2d::Pen, "pen", "Pen") -AETHERA_WEB_DEFINE_JSON_DESCRIPTOR(aethera::render_2d::Brush, "brush", "Brush") -AETHERA_WEB_DEFINE_JSON_DESCRIPTOR(aethera::render_2d::Font, "font", "Font") -AETHERA_WEB_DEFINE_JSON_DESCRIPTOR(aethera::render_2d::Frequency_Trace_Sample, "frequency_trace_sample", "Frequency trace sample") -AETHERA_WEB_DEFINE_JSON_DESCRIPTOR(aethera::render_2d::Waterfall_Row, "waterfall_row", "Waterfall row") -AETHERA_WEB_DEFINE_JSON_DESCRIPTOR(aethera::render_2d::Constellation_Point, "constellation_point", "Constellation point") -AETHERA_WEB_DEFINE_JSON_DESCRIPTOR(aethera::render_2d::Color_Map, "color_map", "Color map") -AETHERA_WEB_DEFINE_JSON_DESCRIPTOR(aethera::render_3d::Vec3, "vec3", "3D vector") -AETHERA_WEB_DEFINE_JSON_DESCRIPTOR(aethera::render_3d::Matrix4, "matrix4", "Transform") -AETHERA_WEB_DEFINE_JSON_DESCRIPTOR(aethera::render_3d::Point, "point_3d", "3D point") -#undef AETHERA_WEB_DEFINE_JSON_DESCRIPTOR +inline auto Type_Descriptor::get() { + return reflected_object_with("color", "Color", Identity_Field_Customizer{}); +} +inline auto Type_Descriptor::get() { + return reflected_object_with("point", "Point", Identity_Field_Customizer{}); +} +inline auto Type_Descriptor::get() { + return reflected_object_with("rectangle", "Rectangle", Identity_Field_Customizer{}); +} +inline auto Type_Descriptor::get() { + return reflected_object_with("axis_range", "Axis range", Identity_Field_Customizer{}); +} +inline auto Type_Descriptor::get() { + return reflected_object_with("pen", "Pen", Identity_Field_Customizer{}); +} +inline auto Type_Descriptor::get() { + return reflected_object_with("brush", "Brush", Identity_Field_Customizer{}); +} +inline auto Type_Descriptor::get() { + return reflected_object_with("font", "Font", Identity_Field_Customizer{}); +} +inline auto Type_Descriptor::get() { + return reflected_object_with("frequency_trace_sample", "Frequency trace sample", Identity_Field_Customizer{}); +} +inline auto Type_Descriptor::get() { + return reflected_object_with("waterfall_row", "Waterfall row", Identity_Field_Customizer{}); +} +inline auto Type_Descriptor::get() { + return reflected_object_with("constellation_point", "Constellation point", Identity_Field_Customizer{}); +} +inline auto Type_Descriptor::get() { + return reflected_object_with("color_map", "Color map", Identity_Field_Customizer{}); +} +inline auto Type_Descriptor::get() { + return reflected_object_with("vec3", "3D vector", Identity_Field_Customizer{}); +} +inline auto Type_Descriptor::get() { + return reflected_object_with("matrix4", "Transform", Identity_Field_Customizer{}); +} +inline auto Type_Descriptor::get() { + return reflected_object_with("point_3d", "3D point", Identity_Field_Customizer{}); +} }