diff --git a/kernel/src/kernel/renderable.ipp b/kernel/src/kernel/renderable.ipp index 2580446..7b7fa9c 100644 --- a/kernel/src/kernel/renderable.ipp +++ b/kernel/src/kernel/renderable.ipp @@ -79,6 +79,7 @@ inline void Renderable::Private::after_prepare_data(Attached auto*) {} template void Renderable::Private::bind_private_crtp(Object* object) { Prev_Private::bind_private_crtp(object); + if constexpr (!No_Prepare_Renderable) object->template mark_dirty(); auto& data = static_cast(*this); if constexpr (Event_Renderable) { data.event_run = [](Root* root, const Event& event) { diff --git a/render_2D/render_2D/base/Renderable_2D.hpp b/render_2D/render_2D/base/Renderable_2D.hpp index 236ad57..31e031d 100644 --- a/render_2D/render_2D/base/Renderable_2D.hpp +++ b/render_2D/render_2D/base/Renderable_2D.hpp @@ -1,6 +1,8 @@ #pragma once #include "../render/Blend2D_Cache.hpp" #include +#include +#include namespace aethera::render_2d { /* 二维 Renderable 是否拥有可跨帧复用的完整颜色缓存。 */ enum class Renderable_2D_Cache { diff --git a/render_2D/render_2D/base/Renderable_2D.ipp b/render_2D/render_2D/base/Renderable_2D.ipp index c014dc3..06f2c35 100644 --- a/render_2D/render_2D/base/Renderable_2D.ipp +++ b/render_2D/render_2D/base/Renderable_2D.ipp @@ -2,10 +2,12 @@ #include namespace aethera::render_2d { struct Renderable_2D::Private : Prev_Private { + using Scene_Attach = std::function(Root*)>; using Cache_Access = Blend2D_Cache* (*)(Root*); Cache_Access pending_cache{}; /* 非空时返回最终对象本轮可写的缓存物理对象。 */ Blend2D_Cache* paint_target{}; /* 仅在 Scene Paint 阶段有效的非拥有绘制目标。 */ Blend2D_Cache* valid_cache{}; /* 缓存根最近一次完整重绘产生的权威物理缓存。 */ + Scene_Attach scene_attach{}; /* Scene Builder 在构造期执行的所属场景及拓扑绑定。 */ /* Paint 实现使用:返回 Scene 为本轮指定的目标;未进入 Paint 阶段属于契约错误。 */ [[nodiscard]] Blend2D_Cache& paint_surface(); /* CRTP 覆盖:识别最终类型是否选择了二维颜色缓存。 */ @@ -24,6 +26,7 @@ inline Blend2D_Cache& Renderable_2D::Private::pai template void Renderable_2D::Private::bind_private_crtp(Object* object) { Prev_Private::bind_private_crtp(object); + object->template mark_dirty(); } template void Renderable_2D::Private::bind_private_crtp(Object* object) { diff --git a/render_2D/render_2D/plottable/Afterglow.hpp b/render_2D/render_2D/plottable/Afterglow.hpp index cf22c76..013af81 100644 --- a/render_2D/render_2D/plottable/Afterglow.hpp +++ b/render_2D/render_2D/plottable/Afterglow.hpp @@ -33,10 +33,9 @@ struct Afterglow : Def> { struct Private; template struct Builder : Prev_Builder { using Base = Prev_Builder; - Builder(Scene_Object* scene, Frequency_Object* frequency_axis, Power_Object* power_axis); + Builder(Frequency_Object* frequency_axis, Power_Object* power_axis); [[nodiscard]] std::expected, Dependency_Graph_Error> build(); private: - Scene_Object* scene{}; /* 不拥有的所属 Scene;生命周期必须覆盖 Afterglow。 */ Frequency_Object* frequency_axis{}; /* 不拥有的频率轴;生命周期必须覆盖 Afterglow。 */ Power_Object* power_axis{}; /* 不拥有的功率轴;生命周期必须覆盖 Afterglow。 */ }; diff --git a/render_2D/render_2D/plottable/Afterglow.ipp b/render_2D/render_2D/plottable/Afterglow.ipp index 8b65d9c..6b62cc7 100644 --- a/render_2D/render_2D/plottable/Afterglow.ipp +++ b/render_2D/render_2D/plottable/Afterglow.ipp @@ -27,7 +27,7 @@ struct Afterglow::Private : Prev_Private { const Dispatch* dispatch{}; /* 最终类型的公开薄壳分派表。 */ /* CRTP 覆盖:绑定 Renderable 能力和最终 Afterglow 分派表。 */ template void bind_private_crtp(Object* object); - void bind_sources(Scene_Object* scene_value, Frequency_Object* frequency_axis_value, Power_Object* power_axis_value); + void bind_sources(Frequency_Object* frequency_axis_value, Power_Object* power_axis_value); template [[nodiscard]] static const Dispatch& dispatch_for(); /* CRTP 覆盖:构建累加、归一化和着色三阶段 Prepare 子图。 */ template [[nodiscard]] tf::Taskflow build_prepare_graph(Object* object, const Prop& state); @@ -40,9 +40,16 @@ struct Afterglow::Private : Prev_Private { template void after_prop_set(Object* object, Member Owner::* member, Prop_Access states); template void before_advance(Object* object, Prop_Type* pending_prop, State_Access pending_states, const Prop_Type* current_prop, State_Access current_states); }; -template Afterglow::Builder::Builder(Scene_Object* scene_value, Frequency_Object* frequency_axis_value, Power_Object* power_axis_value) : Base(), scene(scene_value), frequency_axis(frequency_axis_value), power_axis(power_axis_value) {} +template Afterglow::Builder::Builder(Frequency_Object* frequency_axis_value, Power_Object* power_axis_value) : Base(), frequency_axis(frequency_axis_value), power_axis(power_axis_value) {} template -std::expected, Dependency_Graph_Error> Afterglow::Builder::build() { auto result = Base::build(); if (!result) return std::unexpected(result.error()); auto plot = std::move(result).value(); static_cast(*plot->d).bind_sources(scene, frequency_axis, power_axis); auto graph_result = scene->template edit_dependency_graph([&](auto& prepare, auto& paint, auto& cache) { prepare.add_dependency(plot.get(), scene); prepare.add_dependency(plot.get(), frequency_axis); prepare.add_dependency(plot.get(), power_axis); paint.add_dependency(frequency_axis, plot.get()); paint.add_dependency(power_axis, plot.get()); cache.template add_prop_dependency<&Render_Scene_2D::Prop::viewport>(plot.get(), scene); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(plot.get(), frequency_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(plot.get(), frequency_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(plot.get(), frequency_axis); cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(plot.get(), frequency_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(plot.get(), power_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(plot.get(), power_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(plot.get(), power_axis); cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(plot.get(), power_axis); }); if (!graph_result) return std::unexpected(graph_result.error()); return std::move(plot); } +std::expected, Dependency_Graph_Error> Afterglow::Builder::build() { + auto result = Base::build(); if (!result) return std::unexpected(result.error()); auto plot = std::move(result).value(); + auto& private_data = static_cast(*plot->d); private_data.bind_sources(frequency_axis, power_axis); + private_data.scene_attach = [object = plot.get()](Root* root) -> std::expected { + auto* scene = static_cast(root); auto& data = static_cast(*object->d); data.scene = scene; auto* frequency_axis = data.frequency_axis; auto* power_axis = data.power_axis; + return scene->template edit_dependency_graph([&](auto& prepare, auto& paint, auto& cache) { prepare.add_dependency(object, scene); prepare.add_dependency(object, frequency_axis); prepare.add_dependency(object, power_axis); paint.add_dependency(frequency_axis, object); paint.add_dependency(power_axis, object); cache.template add_prop_dependency<&Render_Scene_2D::Prop::viewport>(object, scene); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(object, frequency_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(object, frequency_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(object, frequency_axis); cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(object, frequency_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(object, power_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(object, power_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(object, power_axis); cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(object, power_axis); }); + }; return plot; +} template void Afterglow::append_spectrum(const Values& values) { append_spectrum(std::span(std::data(values), std::size(values))); } template bool Afterglow::Private::should_rebuild_prepare_graph(Object*, const Prop& state) { const std::size_t available = state.spectra.empty() ? 0 : state.spectra.back().size(); const std::size_t columns = state.frequency_point_size ? std::min(state.frequency_point_size, available) : available; return graph_partition_count != detail::curve_partition_count(state.partition_mode, state.partition_count, std::max(1, columns)); } template @@ -61,5 +68,5 @@ template void Aftergl template const Afterglow::Private::Dispatch& Afterglow::Private::dispatch_for() { static const Dispatch value{[](Root* root, std::span values) { auto* object = static_cast(root); object->template update_prop<&Prop::spectra>([values](Prop_Access props) { auto& state = props.template get(); state.spectra.emplace_back(values.begin(), values.end()); constexpr std::size_t history_limit = 64; while (state.spectra.size() > history_limit) state.spectra.erase(state.spectra.begin()); }); }, [](const Root* root) { return static_cast(root)->template read_prop().spectra.size(); }, [](const Root* root) { const auto& spectra = static_cast(root)->template read_prop().spectra; return spectra.empty() ? 0 : spectra.back().size(); }, [](const Root* root) { const auto& data = static_cast(*static_cast(root)->d); return data.prepared.valid ? data.prepared.pixels.size() : 0; }}; return value; } template void Afterglow::Private::bind_private_crtp(Object* object) { Prev_Private::bind_private_crtp(object); dispatch = &dispatch_for(); } -inline void Afterglow::Private::bind_sources(Scene_Object* scene_value, Frequency_Object* frequency_axis_value, Power_Object* power_axis_value) { scene = scene_value; frequency_axis = frequency_axis_value; power_axis = power_axis_value; } +inline void Afterglow::Private::bind_sources(Frequency_Object* frequency_axis_value, Power_Object* power_axis_value) { frequency_axis = frequency_axis_value; power_axis = power_axis_value; } } diff --git a/render_2D/render_2D/plottable/Constellation_Diagram.hpp b/render_2D/render_2D/plottable/Constellation_Diagram.hpp index b159913..f88b5b7 100644 --- a/render_2D/render_2D/plottable/Constellation_Diagram.hpp +++ b/render_2D/render_2D/plottable/Constellation_Diagram.hpp @@ -29,10 +29,9 @@ struct Constellation_Diagram : Def struct Builder : Prev_Builder { using Base = Prev_Builder; - Builder(Scene_Object* scene, Axis_Object* i_axis, Axis_Object* q_axis); + Builder(Axis_Object* i_axis, Axis_Object* q_axis); [[nodiscard]] std::expected, Dependency_Graph_Error> build(); private: - Scene_Object* scene{}; /* 不拥有的所属 Scene;生命周期必须覆盖星座图。 */ Axis_Object* i_axis{}; /* 不拥有的同相分量轴;生命周期必须覆盖星座图。 */ Axis_Object* q_axis{}; /* 不拥有的正交分量轴;生命周期必须覆盖星座图。 */ }; diff --git a/render_2D/render_2D/plottable/Constellation_Diagram.ipp b/render_2D/render_2D/plottable/Constellation_Diagram.ipp index 718bc63..29ecf7c 100644 --- a/render_2D/render_2D/plottable/Constellation_Diagram.ipp +++ b/render_2D/render_2D/plottable/Constellation_Diagram.ipp @@ -23,7 +23,7 @@ struct Constellation_Diagram::Private : Prev_Private { const Dispatch* dispatch{}; /* 最终类型的公开薄壳分派表。 */ /* CRTP 覆盖:绑定 Renderable 能力和最终 Constellation 分派表。 */ template void bind_private_crtp(Object* object); - void bind_sources(Scene_Object* scene_value, Axis_Object* i_axis_value, Axis_Object* q_axis_value); + void bind_sources(Axis_Object* i_axis_value, Axis_Object* q_axis_value); template [[nodiscard]] static const Dispatch& dispatch_for(); /* CRTP 覆盖:从当前点集和轴状态准备画布坐标。 */ template void prepare_data(Object* object); @@ -33,9 +33,9 @@ struct Constellation_Diagram::Private : Prev_Private { template void after_prop_set(Object* object, Member Owner::* member, Prop_Access states); template void before_advance(Object* object, Prop_Type* pending_prop, State_Access pending_states, const Prop_Type* current_prop, State_Access current_states); }; -template Constellation_Diagram::Builder::Builder(Scene_Object* scene_value, Axis_Object* i_axis_value, Axis_Object* q_axis_value) : Base(), scene(scene_value), i_axis(i_axis_value), q_axis(q_axis_value) {} +template Constellation_Diagram::Builder::Builder(Axis_Object* i_axis_value, Axis_Object* q_axis_value) : Base(), i_axis(i_axis_value), q_axis(q_axis_value) {} template -std::expected, Dependency_Graph_Error> Constellation_Diagram::Builder::build() { auto result = Base::build(); if (!result) return std::unexpected(result.error()); auto plot = std::move(result).value(); static_cast(*plot->d).bind_sources(scene, i_axis, q_axis); auto graph_result = scene->template edit_dependency_graph([&](auto& prepare, auto& paint, auto& cache) { prepare.add_dependency(plot.get(), scene); prepare.add_dependency(plot.get(), i_axis); prepare.add_dependency(plot.get(), q_axis); paint.add_dependency(i_axis, plot.get()); paint.add_dependency(q_axis, plot.get()); cache.template add_prop_dependency<&Render_Scene_2D::Prop::viewport>(plot.get(), scene); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(plot.get(), i_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(plot.get(), i_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(plot.get(), i_axis); cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(plot.get(), i_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(plot.get(), q_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(plot.get(), q_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(plot.get(), q_axis); cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(plot.get(), q_axis); }); if (!graph_result) return std::unexpected(graph_result.error()); return std::move(plot); } +std::expected, Dependency_Graph_Error> Constellation_Diagram::Builder::build() { auto result = Base::build(); if (!result) return std::unexpected(result.error()); auto plot = std::move(result).value(); auto& private_data = static_cast(*plot->d); private_data.bind_sources(i_axis, q_axis); private_data.scene_attach = [object = plot.get()](Root* root) -> std::expected { auto* scene = static_cast(root); auto& data = static_cast(*object->d); data.scene = scene; auto* i_axis = data.i_axis; auto* q_axis = data.q_axis; return scene->template edit_dependency_graph([&](auto& prepare, auto& paint, auto& cache) { prepare.add_dependency(object, scene); prepare.add_dependency(object, i_axis); prepare.add_dependency(object, q_axis); paint.add_dependency(i_axis, object); paint.add_dependency(q_axis, object); cache.template add_prop_dependency<&Render_Scene_2D::Prop::viewport>(object, scene); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(object, i_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(object, i_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(object, i_axis); cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(object, i_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(object, q_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(object, q_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(object, q_axis); cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(object, q_axis); }); }; return plot; } template void Constellation_Diagram::Private::prepare_data(Object* object) { const auto& state = object->template read_prop(); const auto& i_layout = i_axis->template read_prop(); const auto& q_layout = q_axis->template read_prop(); prepared = {}; prepared.canvas = scene->template read_prop().viewport; if (prepared.canvas.empty() || i_layout.orientation == q_layout.orientation) return; const auto current = monotonic_milliseconds(); for (const auto& value : state.points) if (current - value.submitted_at_ms <= state.point_lifetime_ms) prepared.points.push_back(detail::map_plot_point(i_axis, value.point.x, q_axis, value.point.y, i_layout.orientation)); const int count = static_cast(state.type); const Plot_Coordinate center_i = state.i_range.center(); const Plot_Coordinate center_q = state.q_range.center(); const Plot_Coordinate radius = std::min(state.i_range.size(), state.q_range.size()) * 0.4; for (int index = 0; index < count; ++index) { const Plot_Ratio angle = state.phase_offset_radians + 2.0 * std::numbers::pi * index / count; prepared.anchors.push_back(detail::map_plot_point(i_axis, center_i + std::cos(angle) * radius, q_axis, center_q + std::sin(angle) * radius, i_layout.orientation)); } prepared.valid = true; object->template mark_dirty(); } template void Constellation_Diagram::Private::paint(Object* object) { const auto& state = object->template read_prop(); auto& cache = this->paint_surface(); if (!prepared.valid) return; detail::Painter painter(cache, prepared.canvas); for (const auto& anchor : prepared.anchors) painter.circle(anchor, 4.0, Pen{state.anchor_color}, Brush{state.anchor_color, Brush_Style::solid}); for (const auto& point : prepared.points) painter.circle(point, 2.0, Pen{state.point_color}, Brush{state.point_color, Brush_Style::solid}); } @@ -44,5 +44,5 @@ template void Constel template const Constellation_Diagram::Private::Dispatch& Constellation_Diagram::Private::dispatch_for() { static const Dispatch value{[](Root* root, Point_F point) { auto* object = static_cast(root); const auto submitted = monotonic_milliseconds(); object->template update_prop<&Prop::points>([=](Prop_Access props) { auto& state = props.template get(); state.points.erase(std::remove_if(state.points.begin(), state.points.end(), [=](const auto& value) { return submitted - value.submitted_at_ms > state.point_lifetime_ms; }), state.points.end()); state.points.push_back({point, submitted}); }); }, [](const Root* root) { return static_cast(root)->template read_prop().points.size(); }, [](Root* root) { auto* object = static_cast(root); auto& data = static_cast(*object->d); const auto& state = object->template read_prop(); const Plot_Coordinate size = std::max(state.i_range.size(), state.q_range.size()); const Plot_Coordinate i_center = state.i_range.center(); const Plot_Coordinate q_center = state.q_range.center(); data.i_axis->template set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{i_center - size * 0.5, i_center + size * 0.5}); data.q_axis->template set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{q_center - size * 0.5, q_center + size * 0.5}); }}; return value; } template void Constellation_Diagram::Private::bind_private_crtp(Object* object) { Prev_Private::bind_private_crtp(object); dispatch = &dispatch_for(); } -inline void Constellation_Diagram::Private::bind_sources(Scene_Object* scene_value, Axis_Object* i_axis_value, Axis_Object* q_axis_value) { scene = scene_value; i_axis = i_axis_value; q_axis = q_axis_value; } +inline void Constellation_Diagram::Private::bind_sources(Axis_Object* i_axis_value, Axis_Object* q_axis_value) { i_axis = i_axis_value; q_axis = q_axis_value; } } diff --git a/render_2D/render_2D/plottable/Frequency_Trace.hpp b/render_2D/render_2D/plottable/Frequency_Trace.hpp index fd15ef5..30169ad 100644 --- a/render_2D/render_2D/plottable/Frequency_Trace.hpp +++ b/render_2D/render_2D/plottable/Frequency_Trace.hpp @@ -32,10 +32,9 @@ struct Frequency_Trace : Def struct Builder : Prev_Builder { using Base = Prev_Builder; - Builder(Scene_Object* scene, Time_Object* time_axis, Value_Object* value_axis); + Builder(Time_Object* time_axis, Value_Object* value_axis); [[nodiscard]] std::expected, Dependency_Graph_Error> build(); private: - Scene_Object* scene{}; /* 不拥有的所属 Scene。 */ Time_Object* time_axis{}; /* 不拥有的时间轴。 */ Value_Object* value_axis{}; /* 不拥有的数值轴。 */ }; diff --git a/render_2D/render_2D/plottable/Frequency_Trace.ipp b/render_2D/render_2D/plottable/Frequency_Trace.ipp index 3aafbc2..758b189 100644 --- a/render_2D/render_2D/plottable/Frequency_Trace.ipp +++ b/render_2D/render_2D/plottable/Frequency_Trace.ipp @@ -26,7 +26,7 @@ struct Frequency_Trace::Private : Prev_Private { const Dispatch* dispatch{}; /* 最终类型公开薄壳分派表。 */ /* CRTP 覆盖:绑定 Renderable 能力和最终 Frequency_Trace 分派表。 */ template void bind_private_crtp(Object* object); - void bind_sources(Scene_Object* scene_value, Time_Object* time_axis_value, Value_Object* value_axis_value); + void bind_sources(Time_Object* time_axis_value, Value_Object* value_axis_value); template [[nodiscard]] static const Dispatch& dispatch_for(); /* CRTP 覆盖:按当前样本规模构建分块 Prepare 子图。 */ template [[nodiscard]] tf::Taskflow build_prepare_graph(Object* object, const Prop& state); @@ -42,20 +42,11 @@ struct Frequency_Trace::Private : Prev_Private { template void before_advance(Object* object, Prop_Type* pending_prop, State_Access pending_states, const Prop_Type* current_prop, State_Access current_states); }; template -Frequency_Trace::Builder::Builder(Scene_Object* scene_value, Time_Object* time_axis_value, Value_Object* value_axis_value) : Base(), scene(scene_value), time_axis(time_axis_value), value_axis(value_axis_value) {} +Frequency_Trace::Builder::Builder(Time_Object* time_axis_value, Value_Object* value_axis_value) : Base(), time_axis(time_axis_value), value_axis(value_axis_value) {} template std::expected, Dependency_Graph_Error> Frequency_Trace::Builder::build() { - auto result = Base::build(); if (!result) return std::unexpected(result.error()); auto trace = std::move(result).value(); - static_cast(*trace->d).bind_sources(scene, time_axis, value_axis); - auto graph_result = scene->template edit_dependency_graph([&](auto& prepare, auto& paint, auto& cache) { - prepare.add_dependency(trace.get(), scene); prepare.add_dependency(trace.get(), time_axis); prepare.add_dependency(trace.get(), value_axis); - paint.add_dependency(time_axis, trace.get()); paint.add_dependency(value_axis, trace.get()); - cache.template add_prop_dependency<&Render_Scene_2D::Prop::viewport>(trace.get(), scene); - cache.template add_prop_dependency<&Abs_Axis::Prop::position>(trace.get(), time_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(trace.get(), time_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(trace.get(), time_axis); - cache.template add_prop_dependency<&Time_Axis::Prop::visible_count>(trace.get(), time_axis); cache.template add_prop_dependency<&Time_Axis::Prop::newest_at_start>(trace.get(), time_axis); cache.template add_dependency<&Time_Axis::State::next_tick>(trace.get(), time_axis); - cache.template add_prop_dependency<&Abs_Axis::Prop::position>(trace.get(), value_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(trace.get(), value_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(trace.get(), value_axis); cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(trace.get(), value_axis); - }); - if (!graph_result) return std::unexpected(graph_result.error()); return std::move(trace); + auto result = Base::build(); if (!result) return std::unexpected(result.error()); auto trace = std::move(result).value(); auto& private_data = static_cast(*trace->d); private_data.bind_sources(time_axis, value_axis); + private_data.scene_attach = [object = trace.get()](Root* root) -> std::expected { auto* scene = static_cast(root); auto& data = static_cast(*object->d); data.scene = scene; auto* time_axis = data.time_axis; auto* value_axis = data.value_axis; return scene->template edit_dependency_graph([&](auto& prepare, auto& paint, auto& cache) { prepare.add_dependency(object, scene); prepare.add_dependency(object, time_axis); prepare.add_dependency(object, value_axis); paint.add_dependency(time_axis, object); paint.add_dependency(value_axis, object); cache.template add_prop_dependency<&Render_Scene_2D::Prop::viewport>(object, scene); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(object, time_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(object, time_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(object, time_axis); cache.template add_prop_dependency<&Time_Axis::Prop::visible_count>(object, time_axis); cache.template add_prop_dependency<&Time_Axis::Prop::newest_at_start>(object, time_axis); cache.template add_dependency<&Time_Axis::State::next_tick>(object, time_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(object, value_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(object, value_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(object, value_axis); cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(object, value_axis); }); }; return trace; } template bool Frequency_Trace::Private::should_rebuild_prepare_graph(Object* object, const Prop& state) { return graph_partition_count != detail::curve_partition_count(state.partition_mode, state.partition_count, state.samples.size()); } @@ -101,5 +92,5 @@ const Frequency_Trace::Private::Dispatch& Frequency_Trace::Private::dispatch_for } template void Frequency_Trace::Private::bind_private_crtp(Object* object) { Prev_Private::bind_private_crtp(object); dispatch = &dispatch_for(); } -inline void Frequency_Trace::Private::bind_sources(Scene_Object* scene_value, Time_Object* time_axis_value, Value_Object* value_axis_value) { scene = scene_value; time_axis = time_axis_value; value_axis = value_axis_value; } +inline void Frequency_Trace::Private::bind_sources(Time_Object* time_axis_value, Value_Object* value_axis_value) { time_axis = time_axis_value; value_axis = value_axis_value; } } diff --git a/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.hpp b/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.hpp index f1122a2..3fa2a45 100644 --- a/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.hpp +++ b/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.hpp @@ -26,10 +26,9 @@ struct Selection_Rectangle_Overlay : Def struct Builder : Prev_Builder { using Base = Prev_Builder; - Builder(Scene_Object* scene, Axis_Object* horizontal_axis, Axis_Object* vertical_axis); + Builder(Axis_Object* horizontal_axis, Axis_Object* vertical_axis); [[nodiscard]] std::expected, Dependency_Graph_Error> build(); private: - Scene_Object* scene{}; /* 不拥有的所属 Scene。 */ Axis_Object* horizontal_axis{}; /* 不拥有的水平坐标轴。 */ Axis_Object* vertical_axis{}; /* 不拥有的垂直坐标轴。 */ }; diff --git a/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.ipp b/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.ipp index cebce8c..2a37fe1 100644 --- a/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.ipp +++ b/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.ipp @@ -19,7 +19,7 @@ struct Selection_Rectangle_Overlay::Private : Prev_Private { const Dispatch* dispatch{}; /* 最终类型公开薄壳分派表。 */ /* CRTP 覆盖:绑定 Paint-only、事件能力和最终 Overlay 分派表。 */ template void bind_private_crtp(Object* object); - void bind_sources(Scene_Object* scene_value, Axis_Object* horizontal_axis_value, Axis_Object* vertical_axis_value); + void bind_sources(Axis_Object* horizontal_axis_value, Axis_Object* vertical_axis_value); template [[nodiscard]] static const Dispatch& dispatch_for(); /* CRTP 覆盖:无需 Prepare 子图,直接绘制选择矩形。 */ template void paint(Object* object); @@ -30,31 +30,17 @@ struct Selection_Rectangle_Overlay::Private : Prev_Private { template void before_advance(Object* object, Prop_Type* pending_prop, State_Access pending_states, const Prop_Type* current_prop, State_Access current_states); }; template -Selection_Rectangle_Overlay::Builder::Builder(Scene_Object* scene_value, Axis_Object* horizontal_axis_value, Axis_Object* vertical_axis_value) : Base(), scene(scene_value), horizontal_axis(horizontal_axis_value), vertical_axis(vertical_axis_value) {} +Selection_Rectangle_Overlay::Builder::Builder(Axis_Object* horizontal_axis_value, Axis_Object* vertical_axis_value) : Base(), horizontal_axis(horizontal_axis_value), vertical_axis(vertical_axis_value) {} template std::expected, Dependency_Graph_Error> Selection_Rectangle_Overlay::Builder::build() { auto result = Base::build(); if (!result) return std::unexpected(result.error()); auto overlay = std::move(result).value(); - static_cast(*overlay->d).bind_sources(scene, horizontal_axis, vertical_axis); - auto graph_result = scene->template edit_dependency_graph([&](auto& prepare, auto& paint, auto& cache) { - prepare.add_dependency(overlay.get(), scene); - prepare.add_dependency(overlay.get(), horizontal_axis); - prepare.add_dependency(overlay.get(), vertical_axis); - paint.add_dependency(horizontal_axis, overlay.get()); - paint.add_dependency(vertical_axis, overlay.get()); - cache.template add_prop_dependency<&Render_Scene_2D::Prop::viewport>(overlay.get(), scene); - cache.template add_prop_dependency<&Abs_Axis::Prop::position>(overlay.get(), horizontal_axis); - cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(overlay.get(), horizontal_axis); - cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(overlay.get(), horizontal_axis); - cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(overlay.get(), horizontal_axis); - cache.template add_prop_dependency<&Abs_Axis::Prop::position>(overlay.get(), vertical_axis); - cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(overlay.get(), vertical_axis); - cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(overlay.get(), vertical_axis); - cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(overlay.get(), vertical_axis); - }); - if (!graph_result) return std::unexpected(graph_result.error()); - return std::move(overlay); + auto& private_data = static_cast(*overlay->d); private_data.bind_sources(horizontal_axis, vertical_axis); + private_data.scene_attach = [object = overlay.get()](Root* root) -> std::expected { + auto* scene = static_cast(root); auto& data = static_cast(*object->d); data.scene = scene; auto* horizontal_axis = data.horizontal_axis; auto* vertical_axis = data.vertical_axis; + return scene->template edit_dependency_graph([&](auto& prepare, auto& paint, auto& cache) { prepare.add_dependency(object, scene); prepare.add_dependency(object, horizontal_axis); prepare.add_dependency(object, vertical_axis); paint.add_dependency(horizontal_axis, object); paint.add_dependency(vertical_axis, object); cache.template add_prop_dependency<&Render_Scene_2D::Prop::viewport>(object, scene); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(object, horizontal_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(object, horizontal_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(object, horizontal_axis); cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(object, horizontal_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(object, vertical_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(object, vertical_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(object, vertical_axis); cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(object, vertical_axis); }); + }; return overlay; } template void Selection_Rectangle_Overlay::Private::paint(Object* object) { @@ -101,5 +87,5 @@ const Selection_Rectangle_Overlay::Private::Dispatch& Selection_Rectangle_Overla } template void Selection_Rectangle_Overlay::Private::bind_private_crtp(Object* object) { Prev_Private::bind_private_crtp(object); dispatch = &dispatch_for(); } -inline void Selection_Rectangle_Overlay::Private::bind_sources(Scene_Object* scene_value, Axis_Object* horizontal_axis_value, Axis_Object* vertical_axis_value) { scene = scene_value; horizontal_axis = horizontal_axis_value; vertical_axis = vertical_axis_value; } +inline void Selection_Rectangle_Overlay::Private::bind_sources(Axis_Object* horizontal_axis_value, Axis_Object* vertical_axis_value) { horizontal_axis = horizontal_axis_value; vertical_axis = vertical_axis_value; } } diff --git a/render_2D/render_2D/plottable/Spectrum.hpp b/render_2D/render_2D/plottable/Spectrum.hpp index 662391e..501b0a9 100644 --- a/render_2D/render_2D/plottable/Spectrum.hpp +++ b/render_2D/render_2D/plottable/Spectrum.hpp @@ -65,10 +65,9 @@ struct Spectrum : Def, Tag template struct Builder : Prev_Builder { using Base = Prev_Builder; - Builder(Scene_Object* scene, Frequency_Object* frequency_axis, Power_Object* power_axis); + Builder(Frequency_Object* frequency_axis, Power_Object* power_axis); [[nodiscard]] std::expected, Dependency_Graph_Error> build(); private: - Scene_Object* scene{}; /* 不拥有的所属二维 Scene;生命周期必须覆盖 Spectrum。 */ Frequency_Object* frequency_axis{}; /* 不拥有的频率轴;生命周期必须覆盖 Spectrum。 */ Power_Object* power_axis{}; /* 不拥有的功率轴;生命周期必须覆盖 Spectrum。 */ }; diff --git a/render_2D/render_2D/plottable/Spectrum.ipp b/render_2D/render_2D/plottable/Spectrum.ipp index b627028..e2c3c45 100644 --- a/render_2D/render_2D/plottable/Spectrum.ipp +++ b/render_2D/render_2D/plottable/Spectrum.ipp @@ -67,7 +67,7 @@ struct Spectrum::Private : Prev_Private { /* CRTP 覆盖:绑定 Renderable 机制和 Spectrum 公开薄壳分派;派生 Private 必须先调用此实现。 */ template void bind_private_crtp(Object* object); /* Builder 内部绑定 Scene 与两根轴;三个来源都必须比 Spectrum 生命周期更长。 */ - void bind_render_sources(Scene_Object* scene_value, Frequency_Object* frequency_axis_value, Power_Object* power_axis_value); + void bind_render_sources(Frequency_Object* frequency_axis_value, Power_Object* power_axis_value); template [[nodiscard]] static const Dispatch& dispatch_for(); template void update_samples(Object* object, std::span values); template void add_marker(Object* object, Spectrum_Frequency frequency); @@ -95,31 +95,38 @@ struct Spectrum::Private : Prev_Private { [[nodiscard]] static std::expected power_at(const Prop& state, const Spectrum_Frame& frame, Spectrum_Frequency frequency); }; template -Spectrum::Builder::Builder(Scene_Object* scene_value, Frequency_Object* frequency_axis_value, Power_Object* power_axis_value) : Base(), scene(scene_value), frequency_axis(frequency_axis_value), power_axis(power_axis_value) {} +Spectrum::Builder::Builder(Frequency_Object* frequency_axis_value, Power_Object* power_axis_value) : Base(), frequency_axis(frequency_axis_value), power_axis(power_axis_value) {} template std::expected, Dependency_Graph_Error> Spectrum::Builder::build() { auto result = Base::build(); if (!result) return std::unexpected(result.error()); auto spectrum = std::move(result).value(); - static_cast(*spectrum->d).bind_render_sources(scene, frequency_axis, power_axis); - auto dependency_result = scene->template edit_dependency_graph([&](auto& prepare, auto& paint, auto& cache) { - prepare.add_dependency(spectrum.get(), scene); - prepare.add_dependency(spectrum.get(), frequency_axis); - prepare.add_dependency(spectrum.get(), power_axis); - paint.add_dependency(frequency_axis, spectrum.get()); - paint.add_dependency(power_axis, spectrum.get()); - cache.template add_prop_dependency<&Render_Scene_2D::Prop::viewport>(spectrum.get(), scene); - cache.template add_prop_dependency<&Abs_Axis::Prop::position>(spectrum.get(), frequency_axis); - cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(spectrum.get(), frequency_axis); - cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(spectrum.get(), frequency_axis); - cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(spectrum.get(), frequency_axis); - cache.template add_prop_dependency<&Abs_Axis::Prop::position>(spectrum.get(), power_axis); - cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(spectrum.get(), power_axis); - cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(spectrum.get(), power_axis); - cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(spectrum.get(), power_axis); - }); - if (!dependency_result) return std::unexpected(dependency_result.error()); - return std::move(spectrum); + auto& private_data = static_cast(*spectrum->d); + private_data.bind_render_sources(frequency_axis, power_axis); + private_data.scene_attach = [object = spectrum.get()](Root* root) -> std::expected { + auto* scene = static_cast(root); + auto& data = static_cast(*object->d); + data.scene = scene; + auto* frequency_axis = data.frequency_axis; + auto* power_axis = data.power_axis; + return scene->template edit_dependency_graph([&](auto& prepare, auto& paint, auto& cache) { + prepare.add_dependency(object, scene); + prepare.add_dependency(object, frequency_axis); + prepare.add_dependency(object, power_axis); + paint.add_dependency(frequency_axis, object); + paint.add_dependency(power_axis, object); + cache.template add_prop_dependency<&Render_Scene_2D::Prop::viewport>(object, scene); + cache.template add_prop_dependency<&Abs_Axis::Prop::position>(object, frequency_axis); + cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(object, frequency_axis); + cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(object, frequency_axis); + cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(object, frequency_axis); + cache.template add_prop_dependency<&Abs_Axis::Prop::position>(object, power_axis); + cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(object, power_axis); + cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(object, power_axis); + cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(object, power_axis); + }); + }; + return spectrum; } template void Spectrum::update_samples(const Values& values) { @@ -353,8 +360,7 @@ void Spectrum::Private::bind_private_crtp(Object* object) { Prev_Private::bind_private_crtp(object); dispatch = &Private::dispatch_for(); } -inline void Spectrum::Private::bind_render_sources(Scene_Object* scene_value, Frequency_Object* frequency_axis_value, Power_Object* power_axis_value) { - scene = scene_value; +inline void Spectrum::Private::bind_render_sources(Frequency_Object* frequency_axis_value, Power_Object* power_axis_value) { frequency_axis = frequency_axis_value; power_axis = power_axis_value; } diff --git a/render_2D/render_2D/plottable/Sweep_Spectrum.hpp b/render_2D/render_2D/plottable/Sweep_Spectrum.hpp index 4d8ca15..9165d7c 100644 --- a/render_2D/render_2D/plottable/Sweep_Spectrum.hpp +++ b/render_2D/render_2D/plottable/Sweep_Spectrum.hpp @@ -36,10 +36,9 @@ struct Sweep_Spectrum : Def struct Builder : Prev_Builder { using Base = Prev_Builder; - Builder(Scene_Object* scene, Frequency_Object* frequency_axis, Power_Object* power_axis); + Builder(Frequency_Object* frequency_axis, Power_Object* power_axis); [[nodiscard]] std::expected, Dependency_Graph_Error> build(); private: - Scene_Object* scene{}; /* 不拥有的所属 Scene。 */ Frequency_Object* frequency_axis{}; /* 不拥有的频率轴。 */ Power_Object* power_axis{}; /* 不拥有的功率轴。 */ }; diff --git a/render_2D/render_2D/plottable/Sweep_Spectrum.ipp b/render_2D/render_2D/plottable/Sweep_Spectrum.ipp index cf48d85..694cdb9 100644 --- a/render_2D/render_2D/plottable/Sweep_Spectrum.ipp +++ b/render_2D/render_2D/plottable/Sweep_Spectrum.ipp @@ -26,7 +26,7 @@ struct Sweep_Spectrum::Private : Prev_Private { const Dispatch* dispatch{}; /* 最终类型公开薄壳分派表。 */ /* CRTP 覆盖:绑定 Renderable 能力和最终 Sweep_Spectrum 分派表。 */ template void bind_private_crtp(Object* object); - void bind_sources(Scene_Object* scene_value, Frequency_Object* frequency_axis_value, Power_Object* power_axis_value); + void bind_sources(Frequency_Object* frequency_axis_value, Power_Object* power_axis_value); template [[nodiscard]] static const Dispatch& dispatch_for(); /* CRTP 覆盖:按扫描点规模构建分块 Prepare 子图。 */ template [[nodiscard]] tf::Taskflow build_prepare_graph(Object* object, const Prop& state); @@ -41,12 +41,11 @@ struct Sweep_Spectrum::Private : Prev_Private { template void before_advance(Object* object, Prop_Type* pending_prop, State_Access pending_states, const Prop_Type* current_prop, State_Access current_states); }; template -Sweep_Spectrum::Builder::Builder(Scene_Object* scene_value, Frequency_Object* frequency_axis_value, Power_Object* power_axis_value) : Base(), scene(scene_value), frequency_axis(frequency_axis_value), power_axis(power_axis_value) {} +Sweep_Spectrum::Builder::Builder(Frequency_Object* frequency_axis_value, Power_Object* power_axis_value) : Base(), frequency_axis(frequency_axis_value), power_axis(power_axis_value) {} template std::expected, Dependency_Graph_Error> Sweep_Spectrum::Builder::build() { - auto result = Base::build(); if (!result) return std::unexpected(result.error()); auto sweep = std::move(result).value(); static_cast(*sweep->d).bind_sources(scene, frequency_axis, power_axis); - auto graph_result = scene->template edit_dependency_graph([&](auto& prepare, auto& paint, auto& cache) { prepare.add_dependency(sweep.get(), scene); prepare.add_dependency(sweep.get(), frequency_axis); prepare.add_dependency(sweep.get(), power_axis); paint.add_dependency(frequency_axis, sweep.get()); paint.add_dependency(power_axis, sweep.get()); cache.template add_prop_dependency<&Render_Scene_2D::Prop::viewport>(sweep.get(), scene); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(sweep.get(), frequency_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(sweep.get(), frequency_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(sweep.get(), frequency_axis); cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(sweep.get(), frequency_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(sweep.get(), power_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(sweep.get(), power_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(sweep.get(), power_axis); cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(sweep.get(), power_axis); }); - if (!graph_result) return std::unexpected(graph_result.error()); return std::move(sweep); + auto result = Base::build(); if (!result) return std::unexpected(result.error()); auto sweep = std::move(result).value(); auto& private_data = static_cast(*sweep->d); private_data.bind_sources(frequency_axis, power_axis); + private_data.scene_attach = [object = sweep.get()](Root* root) -> std::expected { auto* scene = static_cast(root); auto& data = static_cast(*object->d); data.scene = scene; auto* frequency_axis = data.frequency_axis; auto* power_axis = data.power_axis; return scene->template edit_dependency_graph([&](auto& prepare, auto& paint, auto& cache) { prepare.add_dependency(object, scene); prepare.add_dependency(object, frequency_axis); prepare.add_dependency(object, power_axis); paint.add_dependency(frequency_axis, object); paint.add_dependency(power_axis, object); cache.template add_prop_dependency<&Render_Scene_2D::Prop::viewport>(object, scene); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(object, frequency_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(object, frequency_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(object, frequency_axis); cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(object, frequency_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(object, power_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(object, power_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(object, power_axis); cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(object, power_axis); }); }; return sweep; } template void Sweep_Spectrum::append_block(const Values& values) { append_block(std::span(std::data(values), std::size(values))); } template @@ -82,5 +81,5 @@ const Sweep_Spectrum::Private::Dispatch& Sweep_Spectrum::Private::dispatch_for() }; return value; } template void Sweep_Spectrum::Private::bind_private_crtp(Object* object) { Prev_Private::bind_private_crtp(object); dispatch = &dispatch_for(); } -inline void Sweep_Spectrum::Private::bind_sources(Scene_Object* scene_value, Frequency_Object* frequency_axis_value, Power_Object* power_axis_value) { scene = scene_value; frequency_axis = frequency_axis_value; power_axis = power_axis_value; } +inline void Sweep_Spectrum::Private::bind_sources(Frequency_Object* frequency_axis_value, Power_Object* power_axis_value) { frequency_axis = frequency_axis_value; power_axis = power_axis_value; } } diff --git a/render_2D/render_2D/plottable/Waterfall.hpp b/render_2D/render_2D/plottable/Waterfall.hpp index 07933c4..08bb377 100644 --- a/render_2D/render_2D/plottable/Waterfall.hpp +++ b/render_2D/render_2D/plottable/Waterfall.hpp @@ -38,10 +38,9 @@ struct Waterfall : Def> { struct Private; template struct Builder : Prev_Builder { using Base = Prev_Builder; - Builder(Scene_Object* scene, Frequency_Object* frequency_axis, Time_Object* time_axis); + Builder(Frequency_Object* frequency_axis, Time_Object* time_axis); [[nodiscard]] std::expected, Dependency_Graph_Error> build(); private: - Scene_Object* scene{}; /* 不拥有的所属 Scene;生命周期必须覆盖 Waterfall。 */ Frequency_Object* frequency_axis{}; /* 不拥有的频率轴;生命周期必须覆盖 Waterfall。 */ Time_Object* time_axis{}; /* 不拥有的时间轴;生命周期必须覆盖 Waterfall。 */ }; diff --git a/render_2D/render_2D/plottable/Waterfall.ipp b/render_2D/render_2D/plottable/Waterfall.ipp index 162b93a..5aeb143 100644 --- a/render_2D/render_2D/plottable/Waterfall.ipp +++ b/render_2D/render_2D/plottable/Waterfall.ipp @@ -33,7 +33,7 @@ struct Waterfall::Private : Prev_Private { const Dispatch* dispatch{}; /* 最终类型的公开薄壳分派表。 */ /* CRTP 覆盖:绑定 Renderable、事件能力和最终 Waterfall 分派表。 */ template void bind_private_crtp(Object* object); - void bind_sources(Scene_Object* scene_value, Frequency_Object* frequency_axis_value, Time_Object* time_axis_value); + void bind_sources(Frequency_Object* frequency_axis_value, Time_Object* time_axis_value); template [[nodiscard]] static const Dispatch& dispatch_for(); /* CRTP 覆盖:按当前色块工作量构建分块 Prepare 子图。 */ template [[nodiscard]] tf::Taskflow build_prepare_graph(Object* object, const Prop& state); @@ -48,9 +48,16 @@ struct Waterfall::Private : Prev_Private { template void after_prop_set(Object* object, Member Owner::* member, Prop_Access states); template void before_advance(Object* object, Prop_Type* pending_prop, State_Access pending_states, const Prop_Type* current_prop, State_Access current_states); }; -template Waterfall::Builder::Builder(Scene_Object* scene_value, Frequency_Object* frequency_axis_value, Time_Object* time_axis_value) : Base(), scene(scene_value), frequency_axis(frequency_axis_value), time_axis(time_axis_value) {} +template Waterfall::Builder::Builder(Frequency_Object* frequency_axis_value, Time_Object* time_axis_value) : Base(), frequency_axis(frequency_axis_value), time_axis(time_axis_value) {} template -std::expected, Dependency_Graph_Error> Waterfall::Builder::build() { auto result = Base::build(); if (!result) return std::unexpected(result.error()); auto plot = std::move(result).value(); static_cast(*plot->d).bind_sources(scene, frequency_axis, time_axis); auto graph_result = scene->template edit_dependency_graph([&](auto& prepare, auto& paint, auto& cache) { prepare.add_dependency(plot.get(), scene); prepare.add_dependency(plot.get(), frequency_axis); prepare.add_dependency(plot.get(), time_axis); paint.add_dependency(frequency_axis, plot.get()); paint.add_dependency(time_axis, plot.get()); cache.template add_prop_dependency<&Render_Scene_2D::Prop::viewport>(plot.get(), scene); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(plot.get(), frequency_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(plot.get(), frequency_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(plot.get(), frequency_axis); cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(plot.get(), frequency_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(plot.get(), time_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(plot.get(), time_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(plot.get(), time_axis); cache.template add_prop_dependency<&Time_Axis::Prop::visible_count>(plot.get(), time_axis); cache.template add_prop_dependency<&Time_Axis::Prop::newest_at_start>(plot.get(), time_axis); cache.template add_dependency<&Time_Axis::State::next_tick>(plot.get(), time_axis); }); if (!graph_result) return std::unexpected(graph_result.error()); return std::move(plot); } +std::expected, Dependency_Graph_Error> Waterfall::Builder::build() { + auto result = Base::build(); if (!result) return std::unexpected(result.error()); auto plot = std::move(result).value(); + auto& private_data = static_cast(*plot->d); private_data.bind_sources(frequency_axis, time_axis); + private_data.scene_attach = [object = plot.get()](Root* root) -> std::expected { + auto* scene = static_cast(root); auto& data = static_cast(*object->d); data.scene = scene; auto* frequency_axis = data.frequency_axis; auto* time_axis = data.time_axis; + return scene->template edit_dependency_graph([&](auto& prepare, auto& paint, auto& cache) { prepare.add_dependency(object, scene); prepare.add_dependency(object, frequency_axis); prepare.add_dependency(object, time_axis); paint.add_dependency(frequency_axis, object); paint.add_dependency(time_axis, object); cache.template add_prop_dependency<&Render_Scene_2D::Prop::viewport>(object, scene); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(object, frequency_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(object, frequency_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(object, frequency_axis); cache.template add_prop_dependency<&Numeric_Axis::Prop::coordinate_range>(object, frequency_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::position>(object, time_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::pixel_length>(object, time_axis); cache.template add_prop_dependency<&Abs_Axis::Prop::orientation>(object, time_axis); cache.template add_prop_dependency<&Time_Axis::Prop::visible_count>(object, time_axis); cache.template add_prop_dependency<&Time_Axis::Prop::newest_at_start>(object, time_axis); cache.template add_dependency<&Time_Axis::State::next_tick>(object, time_axis); }); + }; return plot; +} template void Waterfall::append_row(Plot_Time_Tick tick, const Values& values) { append_row(tick, std::span(std::data(values), std::size(values))); } template void Waterfall::append_row(Time_Of_Day time, const Values& values) { append_row(time, std::span(std::data(values), std::size(values))); } template bool Waterfall::Private::should_rebuild_prepare_graph(Object*, const Prop& state) { const std::size_t cells = state.rows.size() * (state.frequency_bin_count ? state.frequency_bin_count : state.rows.empty() ? 1 : state.rows.back().values.size()); return graph_partition_count != detail::curve_partition_count(state.partition_mode, state.partition_count, cells); } @@ -67,5 +74,5 @@ template void Waterfa template const Waterfall::Private::Dispatch& Waterfall::Private::dispatch_for() { static const Dispatch value{[](Root* root, Plot_Time_Tick tick, std::span values) { auto* object = static_cast(root); auto& data = static_cast(*object->d); const std::size_t row_limit = static_cast(std::max(2, data.time_axis->template read_prop().visible_count)); object->template update_prop<&Prop::rows>([=](Prop_Access props) { auto& state = props.template get(); state.rows.push_back({tick, {values.begin(), values.end()}}); while (state.rows.size() > row_limit) state.rows.erase(state.rows.begin()); }); }, [](Root* root, Time_Of_Day time, std::span values) { auto* object = static_cast(root); auto& data = static_cast(*object->d); data.dispatch->append(root, data.time_axis->append_time(time), values); }, [](const Root* root) { return static_cast(root)->template read_prop().rows.size(); }, [](const Root* root) { const auto& rows = static_cast(root)->template read_prop().rows; std::size_t count{}; for (const auto& row : rows) count += row.values.size(); return count; }, [](const Root* root) { const auto& data = static_cast(*static_cast(root)->d); return data.prepared.valid ? data.prepared.pixels.size() : 0; }}; return value; } template void Waterfall::Private::bind_private_crtp(Object* object) { Prev_Private::bind_private_crtp(object); dispatch = &dispatch_for(); } -inline void Waterfall::Private::bind_sources(Scene_Object* scene_value, Frequency_Object* frequency_axis_value, Time_Object* time_axis_value) { scene = scene_value; frequency_axis = frequency_axis_value; time_axis = time_axis_value; } +inline void Waterfall::Private::bind_sources(Frequency_Object* frequency_axis_value, Time_Object* time_axis_value) { frequency_axis = frequency_axis_value; time_axis = time_axis_value; } } diff --git a/render_2D/render_2D/scene/Render_Scene_2D.hpp b/render_2D/render_2D/scene/Render_Scene_2D.hpp index 88e715d..dcc513a 100644 --- a/render_2D/render_2D/scene/Render_Scene_2D.hpp +++ b/render_2D/render_2D/scene/Render_Scene_2D.hpp @@ -22,6 +22,17 @@ struct Render_Scene_2D : Def + struct Builder : Prev_Builder { + using Base = Prev_Builder; + Builder(); + template + requires std::derived_from + Builder& add_renderable(Renderable_Object* renderable); + [[nodiscard]] std::expected, Dependency_Graph_Error> build(); + private: + std::vector(Object*)>> attachments{}; /* 仅在 build 期间绑定已构造 Renderable。 */ + }; using Frame_Callback = std::function; /* 合成一帧;执行期间的重复调用合并为一次后继帧。 */ void render(); diff --git a/render_2D/render_2D/scene/Render_Scene_2D.ipp b/render_2D/render_2D/scene/Render_Scene_2D.ipp index 28c371a..8a625da 100644 --- a/render_2D/render_2D/scene/Render_Scene_2D.ipp +++ b/render_2D/render_2D/scene/Render_Scene_2D.ipp @@ -4,6 +4,32 @@ #include #include namespace aethera::render_2d { +template +Render_Scene_2D::Builder::Builder() : Base() {} +template +template + requires std::derived_from +Render_Scene_2D::Builder& Render_Scene_2D::Builder::add_renderable(Renderable_Object* renderable) { + attachments.emplace_back([renderable](Object* scene) { + auto& data = Base::private_access(renderable).template get(); + if (!data.scene_attach) + throw std::logic_error("2D renderable has no scene attachment contract"); + auto attach = std::move(data.scene_attach); + return attach(scene); + }); + return *this; +} +template +std::expected, Dependency_Graph_Error> Render_Scene_2D::Builder::build() { + auto result = Base::build(); + if (!result) return std::unexpected(result.error()); + auto scene = std::move(result).value(); + for (auto& attach : attachments) { + auto attach_result = attach(scene.get()); + if (!attach_result) return std::unexpected(attach_result.error()); + } + return scene; +} struct Render_Scene_2D::Private : Prev_Private { using Render_Run = void (*)(Root*); using Callback_Run = void (*)(Root*, Frame_Callback); diff --git a/web_server/src/Plot.cpp b/web_server/src/Plot.cpp index 521d8cd..288af49 100644 --- a/web_server/src/Plot.cpp +++ b/web_server/src/Plot.cpp @@ -179,20 +179,19 @@ void resize_axes(Size viewport, Axes*... axes) { std::shared_ptr make_spectrum_plot(asio::any_io_executor executor) { constexpr Size canvas{720, 420}; - auto scene = *Scene_2D::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) - .build(); auto frequency = *frequency_axis_builder(canvas).build(); auto vertical = *numeric_axis_builder( Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-110.0, 0.0}, canvas).build(); - auto spectrum = *Impl::Builder(scene.get(), frequency.get(), vertical.get()) - .set(&Spectrum::Prop::frequency_range, Axis_Range{0.0, 100.0}) - .set(&Spectrum::Prop::max_hold_visible, true) - .build(); - spectrum->mark_dirty(); - spectrum->mark_dirty(); + Impl::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); + auto spectrum = *spectrum_builder.build(); + 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); + 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(event.width), static_cast(event.height)}, frequency, vertical); std::array samples{}; @@ -238,18 +237,17 @@ std::shared_ptr make_spectrum_plot(asio::any_io_executor executor) { std::shared_ptr make_frequency_trace_plot(asio::any_io_executor executor) { constexpr Size canvas{720, 420}; - auto scene = *Scene_2D::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) - .build(); auto time = *time_axis_builder( Axis_Orientation::horizontal, {64.0, 370.0}, 620.0, canvas).build(); auto vertical = *numeric_axis_builder( Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-1.2, 1.2}, canvas).build(); - auto trace = *Impl::Builder(scene.get(), time.get(), vertical.get()).build(); - trace->mark_dirty(); - trace->mark_dirty(); + auto trace = *Impl::Builder(time.get(), vertical.get()).build(); + 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); + scene_builder.add_renderable(trace.get()); + auto scene = *scene_builder.build(); auto update = [raw = trace.get(), time = time.get(), vertical = vertical.get(), tick = std::uint64_t{}](const Plot_Event& event) mutable { resize_axes({static_cast(event.width), static_cast(event.height)}, time, vertical); raw->append_sample(tick++, std::sin(event.time_milliseconds * 0.0025) * 0.8 @@ -268,19 +266,18 @@ std::shared_ptr make_frequency_trace_plot(asio::any_io_executor executor) std::shared_ptr make_sweep_spectrum_plot(asio::any_io_executor executor) { constexpr Size canvas{720, 420}; - auto scene = *Scene_2D::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) - .build(); auto frequency = *frequency_axis_builder(canvas).build(); auto vertical = *numeric_axis_builder( Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-110.0, 0.0}, canvas).build(); - auto sweep = *Impl::Builder(scene.get(), frequency.get(), vertical.get()) - .set(&Sweep_Spectrum::Prop::frequency_range, Axis_Range{0.0, 100.0}) - .build(); - sweep->mark_dirty(); - sweep->mark_dirty(); + Impl::Builder sweep_builder(frequency.get(), vertical.get()); + sweep_builder.set(&Sweep_Spectrum::Prop::frequency_range, Axis_Range{0.0, 100.0}); + auto sweep = *sweep_builder.build(); + 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); + scene_builder.add_renderable(sweep.get()); + auto scene = *scene_builder.build(); auto update = [raw = sweep.get(), frequency = frequency.get(), vertical = vertical.get()](const Plot_Event& event) { resize_axes({static_cast(event.width), static_cast(event.height)}, frequency, vertical); std::array values{}; @@ -308,21 +305,20 @@ std::shared_ptr make_sweep_spectrum_plot(asio::any_io_executor executor) { std::shared_ptr make_afterglow_plot(asio::any_io_executor executor) { constexpr Size canvas{720, 420}; - auto scene = *Scene_2D::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) - .build(); auto frequency = *frequency_axis_builder(canvas).build(); auto vertical = *numeric_axis_builder( Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-110.0, 0.0}, canvas).build(); - auto afterglow = *Impl::Builder(scene.get(), frequency.get(), vertical.get()) - .set(&Afterglow::Prop::frequency_range, Axis_Range{0.0, 100.0}) + Impl::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}) - .set(&Afterglow::Prop::power_point_size, 96) - .build(); - afterglow->mark_dirty(); - afterglow->mark_dirty(); + .set(&Afterglow::Prop::power_point_size, 96); + auto afterglow = *afterglow_builder.build(); + 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); + 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(event.width), static_cast(event.height)}, frequency, vertical); std::array values{}; @@ -352,20 +348,19 @@ std::shared_ptr make_afterglow_plot(asio::any_io_executor executor) { std::shared_ptr make_waterfall_plot(asio::any_io_executor executor) { constexpr Size canvas{720, 420}; - auto scene = *Scene_2D::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) - .build(); auto frequency = *frequency_axis_builder(canvas).build(); auto time = *time_axis_builder( Axis_Orientation::vertical, {64.0, 370.0}, -320.0, canvas).build(); - auto waterfall = *Impl::Builder(scene.get(), frequency.get(), time.get()) - .set(&Waterfall::Prop::frequency_range, Axis_Range{0.0, 100.0}) - .set(&Waterfall::Prop::power_range, Axis_Range{-110.0, 0.0}) - .build(); - waterfall->mark_dirty(); - waterfall->mark_dirty(); + Impl::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}); + auto waterfall = *waterfall_builder.build(); + 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); + scene_builder.add_renderable(waterfall.get()); + auto scene = *scene_builder.build(); auto update = [raw = waterfall.get(), frequency = frequency.get(), time = time.get(), tick = std::uint64_t{}](const Plot_Event& event) mutable { resize_axes({static_cast(event.width), static_cast(event.height)}, frequency, time); std::array values{}; @@ -398,21 +393,20 @@ std::shared_ptr make_waterfall_plot(asio::any_io_executor executor) { std::shared_ptr make_constellation_plot(asio::any_io_executor executor) { constexpr Size canvas{720, 420}; - auto scene = *Scene_2D::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) - .build(); auto horizontal = *numeric_axis_builder( Axis_Orientation::horizontal, {64.0, 370.0}, 620.0, {-1.2, 1.2}, canvas).build(); auto vertical = *numeric_axis_builder( Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {-1.2, 1.2}, canvas).build(); - auto constellation = *Impl::Builder(scene.get(), horizontal.get(), vertical.get()) - .set(&Constellation_Diagram::Prop::i_range, Axis_Range{-1.2, 1.2}) - .set(&Constellation_Diagram::Prop::q_range, Axis_Range{-1.2, 1.2}) - .build(); - constellation->mark_dirty(); - constellation->mark_dirty(); + Impl::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}); + auto constellation = *constellation_builder.build(); + 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); + 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(event.width), static_cast(event.height)}, horizontal, vertical); const double phase = event.time_milliseconds * 0.003; @@ -435,18 +429,17 @@ std::shared_ptr make_constellation_plot(asio::any_io_executor executor) { std::shared_ptr make_selection_overlay_plot(asio::any_io_executor executor) { constexpr Size canvas{720, 420}; - auto scene = *Scene_2D::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) - .build(); auto horizontal = *numeric_axis_builder( Axis_Orientation::horizontal, {64.0, 370.0}, 620.0, {0.0, 100.0}, canvas).build(); auto vertical = *numeric_axis_builder( Axis_Orientation::vertical, {64.0, 370.0}, -320.0, {0.0, 100.0}, canvas).build(); - auto selection = *Impl::Builder(scene.get(), horizontal.get(), vertical.get()).build(); - selection->mark_dirty(); - selection->mark_dirty(); + auto selection = *Impl::Builder(horizontal.get(), vertical.get()).build(); + 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); + 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(event.width), static_cast(event.height)}, horizontal, vertical); };