From 1d3847d45471ae693275fc90c4c67177c3605ede Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Mon, 24 Aug 2026 08:36:04 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=B8=89=E7=BC=93=E5=86=B2?= =?UTF-8?q?=20=E5=BD=BB=E5=BA=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/kernel/scene.cpp | 17 ----- kernel/src/kernel/scene.hpp | 2 - kernel/src/kernel/scene.ipp | 58 +++++--------- render_2D/render_2D/plottable/Afterglow.cpp | 16 ---- render_2D/render_2D/plottable/Afterglow.hpp | 9 --- render_2D/render_2D/plottable/Afterglow.ipp | 62 --------------- .../plottable/Constellation_Diagram.cpp | 9 --- .../plottable/Constellation_Diagram.hpp | 4 - .../plottable/Constellation_Diagram.ipp | 52 ------------- .../render_2D/plottable/Frequency_Trace.cpp | 3 - .../render_2D/plottable/Frequency_Trace.hpp | 6 -- .../render_2D/plottable/Frequency_Trace.ipp | 28 ------- .../render_2D/plottable/Sweep_Spectrum.cpp | 5 -- .../render_2D/plottable/Sweep_Spectrum.hpp | 9 --- .../render_2D/plottable/Sweep_Spectrum.ipp | 25 ------ render_2D/render_2D/plottable/Waterfall.cpp | 16 ---- render_2D/render_2D/plottable/Waterfall.hpp | 11 --- render_2D/render_2D/plottable/Waterfall.ipp | 67 ---------------- render_2D/render_2D/scene/Render_Scene_2D.ipp | 2 +- render_2D/tests/Axis_Test.cpp | 10 +-- render_2D/tests/Plottable_Migration_Test.cpp | 76 +++++++++++-------- render_3D/render_3D/scene/Render_Scene_3D.ipp | 2 +- web_server/src/Gallery_Plots_2D.cpp | 70 +++++++++-------- web_server/src/Plot.cpp | 14 +++- 24 files changed, 120 insertions(+), 453 deletions(-) diff --git a/kernel/src/kernel/scene.cpp b/kernel/src/kernel/scene.cpp index a07fa91..7f81a2e 100644 --- a/kernel/src/kernel/scene.cpp +++ b/kernel/src/kernel/scene.cpp @@ -1,24 +1,7 @@ #include "scene.hpp" /* 后端共有 Prepare Scene 实现。 */ -#include namespace aethera { Scene::Private::Private() = default; Scene::Private::~Private() { if (runtime && runtime->taskflow) detail::clear_stage_observers(runtime->taskflow.get()); } -void Scene::Private::push_event(Event_Pointer event) { - if (!event) throw std::invalid_argument("scene event ownership must not be empty"); - runtime->submit_event(runtime->object, std::move(event)); -} -Scene::Event_Batch Scene::Private::take_events(std::uint64_t frame_sequence) { - return runtime->take_events(runtime->object, runtime->resource, frame_sequence); -} -Scene::Event_Report_Batch Scene::Private::take_event_reports() { - return runtime->take_reports(runtime->object, runtime->resource); -} -void Scene::dispatch_event(Event_Pointer event) { - static_cast(*d).push_event(std::move(event)); -} -Scene::Event_Report_Batch Scene::take_event_reports() { - return static_cast(*d).take_event_reports(); -} } diff --git a/kernel/src/kernel/scene.hpp b/kernel/src/kernel/scene.hpp index 52c672d..adff3c8 100644 --- a/kernel/src/kernel/scene.hpp +++ b/kernel/src/kernel/scene.hpp @@ -36,8 +36,6 @@ struct Scene : Def [[nodiscard]] std::shared_ptr make_event(Arguments&&... arguments); - void dispatch_event(Event_Pointer event); - [[nodiscard]] Event_Report_Batch take_event_reports(); }; } #include "scene.ipp" diff --git a/kernel/src/kernel/scene.ipp b/kernel/src/kernel/scene.ipp index 7a7866d..375a15c 100644 --- a/kernel/src/kernel/scene.ipp +++ b/kernel/src/kernel/scene.ipp @@ -11,9 +11,8 @@ struct Scene::Private : Prev_Private { std::unique_ptr runtime; /* Scene 唯一运行时构建产物的所有权。 */ Private(); ~Private(); - void push_event(Event_Pointer event); - [[nodiscard]] Event_Batch take_events(std::uint64_t frame_sequence); - [[nodiscard]] Event_Report_Batch take_event_reports(); + template + [[nodiscard]] Event_Batch take_events(Object* object, std::uint64_t frame_sequence); template void bind_private_crtp(Object* object); /* Def CRTP hook:所有缓冲推进后重建必要的总 Taskflow,并更新 Scene_State_Tag 状态层。 */ template @@ -32,48 +31,25 @@ struct Scene::Private : Prev_Private { }; struct Scene::Private::Runtime { std::unique_ptr taskflow; /* 当前已构建的总 Taskflow;为空表示尚未构建。 */ - using Submit_Event_Call = void (*)(Root*, Event_Pointer); - using Take_Events_Call = Event_Batch (*)(Root*, std::pmr::memory_resource*, std::uint64_t); - using Take_Reports_Call = Event_Report_Batch (*)(const Root*, std::pmr::memory_resource*); - std::pmr::memory_resource* resource; - Root* object{}; - Submit_Event_Call submit_event{}; - Take_Events_Call take_events{}; - Take_Reports_Call take_reports{}; - explicit Runtime(std::pmr::memory_resource* resource) : resource(resource) {} }; template void Scene::Private::bind_private_crtp(Object* object) { Prev_Private::bind_private_crtp(object); - runtime = std::make_unique(object->memory_resource()); - runtime->object = object; - runtime->submit_event = [](Root* root, Event_Pointer event) { - static_cast(root)->template submit_stream(std::move(event)); - }; - runtime->take_events = [](Root* root, std::pmr::memory_resource* resource, - std::uint64_t frame_sequence) { - auto* scene = static_cast(root); - scene->template exchange_stream(); - return scene->template access_rendering_stream( - [&](std::span events) { - Event_Batch result{resource}; - result.reserve(events.size()); - for (const auto& event : events) { - event->mark_dispatch_started(frame_sequence); - result.push_back(event); - } - return result; - }); - }; - runtime->take_reports = [](const Root* root, std::pmr::memory_resource* resource) { - return static_cast(root)->template access_query_stream( - [&](std::span events) { - Event_Report_Batch result{resource}; - result.reserve(events.size()); - for (const auto& event : events) result.push_back(event); - return result; - }); - }; + runtime = std::make_unique(); +} +template +Scene::Event_Batch Scene::Private::take_events(Object* object, std::uint64_t frame_sequence) { + object->template exchange_stream(); + return object->template access_rendering_stream( + [&](std::span events) { + Event_Batch result{object->memory_resource()}; + result.reserve(events.size()); + for (const auto& event : events) { + event->mark_dispatch_started(frame_sequence); + result.push_back(event); + } + return result; + }); } template std::shared_ptr Scene::make_event(Arguments&&... arguments) { diff --git a/render_2D/render_2D/plottable/Afterglow.cpp b/render_2D/render_2D/plottable/Afterglow.cpp index 0b54033..8ea0401 100644 --- a/render_2D/render_2D/plottable/Afterglow.cpp +++ b/render_2D/render_2D/plottable/Afterglow.cpp @@ -2,20 +2,4 @@ namespace aethera::render_2d { bool Afterglow::Prop::operator==(const Prop&) const = default; bool Afterglow::State::operator==(const State&) const = default; -void Afterglow::append_spectrum(std::span values) { - static_cast(*d).dispatch->append(this, {values.begin(), values.end()}); -} -void Afterglow::append_spectrum(std::pmr::vector&& values) { - static_cast(*d).dispatch->append(this, - {std::make_move_iterator(values.begin()), std::make_move_iterator(values.end())}); -} -std::size_t Afterglow::history_count() const { - return static_cast(*d).dispatch->history_count(this); -} -std::size_t Afterglow::latest_spectrum_point_count() const { - return static_cast(*d).dispatch->latest_count(this); -} -std::size_t Afterglow::rendered_cell_count() const { - return static_cast(*d).dispatch->rendered_count(this); -} } diff --git a/render_2D/render_2D/plottable/Afterglow.hpp b/render_2D/render_2D/plottable/Afterglow.hpp index f96f666..0e25e9b 100644 --- a/render_2D/render_2D/plottable/Afterglow.hpp +++ b/render_2D/render_2D/plottable/Afterglow.hpp @@ -27,9 +27,6 @@ struct Afterglow : Def, bool operator==(const Prop&) const; }; struct State : Prev_State { - std::size_t history_count{}; /* 当前发布的历史频谱帧数。 */ - std::size_t latest_spectrum_point_count{}; /* 最新频谱包含的点数。 */ - std::size_t rendered_cell_count{}; /* 最近一次 Prepare 生成的色块数。 */ bool operator==(const State&) const; }; struct Private; @@ -41,12 +38,6 @@ struct Afterglow : Def, Frequency_Object* frequency_axis{}; /* 不拥有的频率轴;生命周期必须覆盖 Afterglow。 */ Power_Object* power_axis{}; /* 不拥有的功率轴;生命周期必须覆盖 Afterglow。 */ }; - void append_spectrum(std::span values); - void append_spectrum(std::pmr::vector&& values); - template void append_spectrum(const Values& values); - [[nodiscard]] std::size_t history_count() const; - [[nodiscard]] std::size_t latest_spectrum_point_count() const; - [[nodiscard]] std::size_t rendered_cell_count() const; }; } #include "Afterglow.ipp" diff --git a/render_2D/render_2D/plottable/Afterglow.ipp b/render_2D/render_2D/plottable/Afterglow.ipp index 51a586c..4c33aaa 100644 --- a/render_2D/render_2D/plottable/Afterglow.ipp +++ b/render_2D/render_2D/plottable/Afterglow.ipp @@ -13,26 +13,12 @@ struct Afterglow::Private : Prev_Private { Plot_Ratio maximum{1.0}; /* 本帧强度归一化分母,最小为 1。 */ bool valid{}; /* 轴布局和输入数据是否足以生成色块。 */ }; - using Append_Run = void (*)(Root*, std::vector); - using Count_Run = std::size_t (*)(const Root*); - struct Dispatch { - Append_Run append; /* 向最终对象提交一帧频谱。 */ - Count_Run history_count; /* 查询权威历史帧数。 */ - Count_Run latest_count; /* 查询最新一帧的点数。 */ - Count_Run rendered_count; /* 查询已准备的色块数。 */ - }; Scene_Object* scene{}; /* 不拥有的所属 Scene。 */ Frequency_Object* frequency_axis{}; /* 不拥有的频率轴。 */ Power_Object* power_axis{}; /* 不拥有的功率轴。 */ Prepared prepared{}; /* 当前权威状态推导出的 Paint 输入。 */ Plot_Partition_Count graph_partition_count{}; /* Prepare 子图当前固化的分块数。 */ - const Dispatch* dispatch{}; /* 最终类型的公开薄壳分派表。 */ - /* CRTP 覆盖:绑定 Renderable 能力和最终 Afterglow 分派表。 */ - template - void bind_private_crtp(Object* object); 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); @@ -54,8 +40,6 @@ struct Afterglow::Private : Prev_Private { /* CRTP 覆盖:本类状态写入后标记 Prepare 数据失效。 */ 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(Frequency_Object* frequency_axis_value, Power_Object* power_axis_value) : Base(), frequency_axis(frequency_axis_value), power_axis(power_axis_value) {} @@ -91,10 +75,6 @@ std::expected, Dependency_Graph_Error> Afterglow::Builde }; 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* object, const Prop& state) { const std::size_t available = object->template access_query_stream( @@ -213,48 +193,6 @@ template void Afterglow::Private::after_prop_set(Object* object, Member Owner::*, Prop_Access) { if constexpr (std::same_as) object->template mark_dirty(); } -template -void Afterglow::Private::before_advance(Object* object, Prop_Type*, State_Access pending_states, const Prop_Type*, State_Access) { - auto& state = pending_states.template get(); - object->template access_query_stream([&](std::span>> spectra) { - state.history_count = spectra.size(); - state.latest_spectrum_point_count = spectra.empty() ? 0 : spectra.back()->size(); - state.rendered_cell_count = state.latest_spectrum_point_count; - }); -} -template -const Afterglow::Private::Dispatch& Afterglow::Private::dispatch_for() { - static const Dispatch value{ - [](Root* root, std::vector values) { - auto* object = static_cast(root); - object->template submit_stream( - std::make_shared>(std::move(values))); - object->template mark_dirty(); - }, - [](const Root* root) { - return static_cast(root)->template access_query_stream( - [](std::span>> spectra) { return spectra.size(); }); - }, - [](const Root* root) { - return static_cast(root)->template access_query_stream( - [](std::span>> spectra) { - return spectra.empty() ? 0 : spectra.back()->size(); - }); - }, - [](const Root* root) { - return static_cast(root)->template access_query_stream( - [](std::span>> spectra) { - return spectra.empty() ? 0 : spectra.back()->size(); - }); - } - }; - 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(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.cpp b/render_2D/render_2D/plottable/Constellation_Diagram.cpp index 77cafc1..b427fe8 100644 --- a/render_2D/render_2D/plottable/Constellation_Diagram.cpp +++ b/render_2D/render_2D/plottable/Constellation_Diagram.cpp @@ -3,13 +3,4 @@ namespace aethera::render_2d { bool Constellation_Point::operator==(const Constellation_Point&) const = default; bool Constellation_Diagram::Prop::operator==(const Prop&) const = default; bool Constellation_Diagram::State::operator==(const State&) const = default; -void Constellation_Diagram::append_point(Point_F point) { - static_cast(*d).dispatch->append(this, {point, monotonic_milliseconds()}); -} -std::size_t Constellation_Diagram::point_count() const { - return static_cast(*d).dispatch->count(this); -} -void Constellation_Diagram::fit_square_to_axes() { - static_cast(*d).dispatch->fit(this); -} } diff --git a/render_2D/render_2D/plottable/Constellation_Diagram.hpp b/render_2D/render_2D/plottable/Constellation_Diagram.hpp index c01b8e0..852d674 100644 --- a/render_2D/render_2D/plottable/Constellation_Diagram.hpp +++ b/render_2D/render_2D/plottable/Constellation_Diagram.hpp @@ -30,7 +30,6 @@ struct Constellation_Diagram : Def - void bind_private_crtp(Object* object); 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); @@ -38,8 +24,6 @@ struct Constellation_Diagram::Private : Prev_Private { /* CRTP 覆盖:本类状态写入后标记 Prepare 数据失效。 */ 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(Axis_Object* i_axis_value, Axis_Object* q_axis_value) : Base(), i_axis(i_axis_value), q_axis(q_axis_value) {} @@ -120,42 +104,6 @@ template void Constellation_Diagram::Private::after_prop_set(Object* object, Member Owner::*, Prop_Access) { if constexpr (std::same_as) object->template mark_dirty(); } -template -void Constellation_Diagram::Private::before_advance(Object* object, Prop_Type*, State_Access pending_states, const Prop_Type*, State_Access) { - pending_states.template get().point_count = - object->template access_query_stream( - [](std::span points) { return points.size(); }); -} -template -const Constellation_Diagram::Private::Dispatch& Constellation_Diagram::Private::dispatch_for() { - static const Dispatch value{ - [](Root* root, Constellation_Point point) { - auto* object = static_cast(root); - object->template submit_stream(std::move(point)); - object->template mark_dirty(); - }, - [](const Root* root) { - return static_cast(root)->template access_query_stream( - [](std::span points) { return 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(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.cpp b/render_2D/render_2D/plottable/Frequency_Trace.cpp index 22cb369..b0c889d 100644 --- a/render_2D/render_2D/plottable/Frequency_Trace.cpp +++ b/render_2D/render_2D/plottable/Frequency_Trace.cpp @@ -3,7 +3,4 @@ namespace aethera::render_2d { bool Frequency_Trace_Sample::operator==(const Frequency_Trace_Sample&) const = default; bool Frequency_Trace::Prop::operator==(const Prop&) const = default; bool Frequency_Trace::State::operator==(const State&) const = default; -void Frequency_Trace::append_sample(Plot_Time_Tick tick, Plot_Value value) { static_cast(*d).dispatch->append(this, tick, value); } -std::size_t Frequency_Trace::sample_count() const { return static_cast(*d).dispatch->sample_count(this); } -std::size_t Frequency_Trace::rendered_point_count() const { return static_cast(*d).dispatch->rendered_point_count(this); } } diff --git a/render_2D/render_2D/plottable/Frequency_Trace.hpp b/render_2D/render_2D/plottable/Frequency_Trace.hpp index b3f9fe6..a7021c1 100644 --- a/render_2D/render_2D/plottable/Frequency_Trace.hpp +++ b/render_2D/render_2D/plottable/Frequency_Trace.hpp @@ -25,8 +25,6 @@ struct Frequency_Trace : Def void bind_private_crtp(Object* object); 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); /* CRTP 覆盖:构建消费已准备曲线的 Paint 子图。 */ @@ -36,7 +25,6 @@ struct Frequency_Trace::Private : Prev_Private { template void paint_frame(Object* object); /* CRTP 覆盖:本类状态写入后标记 Prepare 数据失效。 */ template void after_prop_set(Object* object, Member Owner::* member, Prop_Access pending_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 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) {} @@ -81,21 +69,5 @@ void Frequency_Trace::Private::paint_frame(Object* object) { } template void Frequency_Trace::Private::after_prop_set(Object* object, Member Owner::*, Prop_Access) { if constexpr (std::same_as) object->template mark_dirty(); } -template -void Frequency_Trace::Private::before_advance(Object* object, Prop_Type*, State_Access pending_states, const Prop_Type*, State_Access) { auto& state = pending_states.template get(); state.sample_count = object->template access_query_stream([](std::span samples) { return samples.size(); }); state.rendered_point_count = state.sample_count; } -template -const Frequency_Trace::Private::Dispatch& Frequency_Trace::Private::dispatch_for() { - static const Dispatch value{ - [](Root* root, Plot_Time_Tick tick, Plot_Value sample_value) { - auto* object = static_cast(root); - object->template submit_stream(Frequency_Trace_Sample{tick, sample_value}); - object->template mark_dirty(); - }, - [](const Root* root) { return static_cast(root)->template access_query_stream([](std::span samples) { return samples.size(); }); }, - [](const Root* root) { return static_cast(root)->template access_query_stream([](std::span samples) { return samples.size(); }); } - }; return value; -} -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(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/Sweep_Spectrum.cpp b/render_2D/render_2D/plottable/Sweep_Spectrum.cpp index 6fea207..df5b696 100644 --- a/render_2D/render_2D/plottable/Sweep_Spectrum.cpp +++ b/render_2D/render_2D/plottable/Sweep_Spectrum.cpp @@ -2,9 +2,4 @@ namespace aethera::render_2d { bool Sweep_Spectrum::Prop::operator==(const Prop&) const = default; bool Sweep_Spectrum::State::operator==(const State&) const = default; -void Sweep_Spectrum::append_block(std::span values) { static_cast(*d).dispatch->append(this, {values.begin(), values.end()}); } -void Sweep_Spectrum::append_block(std::pmr::vector&& values) { static_cast(*d).dispatch->append(this, {std::make_move_iterator(values.begin()), std::make_move_iterator(values.end())}); } -std::size_t Sweep_Spectrum::stored_block_count() const { return static_cast(*d).dispatch->block_count(this); } -std::size_t Sweep_Spectrum::stored_point_count() const { return static_cast(*d).dispatch->point_count(this); } -std::size_t Sweep_Spectrum::rendered_point_count() const { return static_cast(*d).dispatch->rendered_point_count(this); } } diff --git a/render_2D/render_2D/plottable/Sweep_Spectrum.hpp b/render_2D/render_2D/plottable/Sweep_Spectrum.hpp index db1cbe8..844f29b 100644 --- a/render_2D/render_2D/plottable/Sweep_Spectrum.hpp +++ b/render_2D/render_2D/plottable/Sweep_Spectrum.hpp @@ -29,9 +29,6 @@ struct Sweep_Spectrum : Def values); - void append_block(std::pmr::vector&& values); - template void append_block(const Values& values); - [[nodiscard]] std::size_t stored_block_count() const; - [[nodiscard]] std::size_t stored_point_count() const; - [[nodiscard]] std::size_t rendered_point_count() const; }; } #include "Sweep_Spectrum.ipp" diff --git a/render_2D/render_2D/plottable/Sweep_Spectrum.ipp b/render_2D/render_2D/plottable/Sweep_Spectrum.ipp index a38e4b3..9029c43 100644 --- a/render_2D/render_2D/plottable/Sweep_Spectrum.ipp +++ b/render_2D/render_2D/plottable/Sweep_Spectrum.ipp @@ -13,23 +13,12 @@ struct Sweep_Spectrum::Private : Prev_Private { Size canvas{}; /* 当前 Scene viewport 的像素尺寸。 */ bool valid{}; /* 两根轴正交、画布及组合折线均有效。 */ }; - using Append_Run = void (*)(Root*, std::vector); using Count_Run = std::size_t (*)(const Root*); - struct Dispatch { - Append_Run append; /* 向最终对象提交一个扫描块。 */ - Count_Run block_count; /* 查询权威扫描块数。 */ - Count_Run point_count; /* 查询权威功率点总数。 */ - Count_Run rendered_point_count; /* 查询已准备的曲线点数。 */ - }; Scene_Object* scene{}; /* 不拥有的所属 Scene。 */ Frequency_Object* frequency_axis{}; /* 不拥有的频率轴。 */ Power_Object* power_axis{}; /* 不拥有的功率轴。 */ Prepared prepared{}; /* 当前块集合推导出的绘制输入。 */ Plot_Partition_Count graph_partition_count{}; /* 当前 Prepare 子图分块数。 */ - const Dispatch* dispatch{}; /* 最终类型公开薄壳分派表。 */ - /* CRTP 覆盖:绑定 Renderable 能力和最终 Sweep_Spectrum 分派表。 */ - template void bind_private_crtp(Object* object); 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); /* CRTP 覆盖:构建消费曲线分块的 Paint 子图。 */ @@ -40,7 +29,6 @@ struct Sweep_Spectrum::Private : Prev_Private { template void prepare_partition(Object* object, Plot_Partition_Count index); template void paint_frame(Object* object); 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 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) {} @@ -49,7 +37,6 @@ std::expected, Dependency_Graph_Error> Sweep_Spectrum::B 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 bool Sweep_Spectrum::Private::should_rebuild_prepare_graph(Object* object, const Prop& state) { const auto points = object->template access_query_stream([](std::span>> blocks) { std::size_t count{}; for (const auto& block : blocks) count += block->size(); return count; }); return graph_partition_count != detail::curve_partition_count(state.partition_mode, state.partition_count, points); } template @@ -90,17 +77,5 @@ template void Sweep_Spectrum::Private::paint_frame(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& curve : prepared.partitions) detail::paint_curve(painter, curve, state.pen); painter.line(prepared.marker_first, prepared.marker_second, state.current_frequency_pen); } template void Sweep_Spectrum::Private::after_prop_set(Object* object, Member Owner::*, Prop_Access) { if constexpr (std::same_as) object->template mark_dirty(); } -template -void Sweep_Spectrum::Private::before_advance(Object* object, Prop_Type*, State_Access pending_states, const Prop_Type*, State_Access) { auto& state = pending_states.template get(); object->template access_query_stream([&](std::span>> blocks) { state.stored_block_count = blocks.size(); state.stored_point_count = 0; for (const auto& block : blocks) state.stored_point_count += block->size(); state.rendered_point_count = state.stored_point_count; }); } -template -const Sweep_Spectrum::Private::Dispatch& Sweep_Spectrum::Private::dispatch_for() { - static const Dispatch value{ - [](Root* root, std::vector values) { auto* object = static_cast(root); object->template submit_stream(std::make_shared>(std::move(values))); object->template mark_dirty(); }, - [](const Root* root) { return static_cast(root)->template access_query_stream([](std::span>> blocks) { return blocks.size(); }); }, - [](const Root* root) { return static_cast(root)->template access_query_stream([](std::span>> blocks) { std::size_t count{}; for (const auto& block : blocks) count += block->size(); return count; }); }, - [](const Root* root) { return static_cast(root)->template access_query_stream([](std::span>> blocks) { std::size_t count{}; for (const auto& block : blocks) count += block->size(); return count; }); } - }; 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(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.cpp b/render_2D/render_2D/plottable/Waterfall.cpp index f792291..bac0250 100644 --- a/render_2D/render_2D/plottable/Waterfall.cpp +++ b/render_2D/render_2D/plottable/Waterfall.cpp @@ -3,20 +3,4 @@ namespace aethera::render_2d { bool Waterfall_Row::operator==(const Waterfall_Row&) const = default; bool Waterfall::Prop::operator==(const Prop&) const = default; bool Waterfall::State::operator==(const State&) const = default; -void Waterfall::append_row(Plot_Time_Tick tick, std::span values) { - static_cast(*d).dispatch->append(this, Waterfall_Row{tick, {values.begin(), values.end()}}); -} -void Waterfall::append_row(Plot_Time_Tick tick, std::pmr::vector&& values) { - static_cast(*d).dispatch->append(this, Waterfall_Row{ - tick, {std::make_move_iterator(values.begin()), std::make_move_iterator(values.end())}}); -} -std::size_t Waterfall::row_count() const { - return static_cast(*d).dispatch->row_count(this); -} -std::size_t Waterfall::stored_point_count() const { - return static_cast(*d).dispatch->point_count(this); -} -std::size_t Waterfall::rendered_cell_count() const { - return static_cast(*d).dispatch->rendered_count(this); -} } diff --git a/render_2D/render_2D/plottable/Waterfall.hpp b/render_2D/render_2D/plottable/Waterfall.hpp index 8a278ae..4a4f8ef 100644 --- a/render_2D/render_2D/plottable/Waterfall.hpp +++ b/render_2D/render_2D/plottable/Waterfall.hpp @@ -37,9 +37,6 @@ struct Waterfall : Def, bool operator==(const Prop&) const; }; struct State : Prev_State { - std::size_t row_count{}; /* 当前发布的瀑布行数。 */ - std::size_t stored_point_count{}; /* 当前发布的功率点总数。 */ - std::size_t rendered_cell_count{}; /* 最近一次 Prepare 生成的色块数。 */ bool operator==(const State&) const; }; struct Private; @@ -52,14 +49,6 @@ struct Waterfall : Def, Frequency_Object* frequency_axis{}; /* 不拥有的频率轴;生命周期必须覆盖 Waterfall。 */ Time_Object* time_axis{}; /* 不拥有的时间轴;生命周期必须覆盖 Waterfall。 */ }; - /* 提交同一使用层刚由绑定 Time_Axis::append_time() 分配的 tick;本图元不推进时间轴。 */ - void append_row(Plot_Time_Tick tick, std::span values); - void append_row(Plot_Time_Tick tick, std::pmr::vector&& values); - template - void append_row(Plot_Time_Tick tick, const Values& values); - [[nodiscard]] std::size_t row_count() const; - [[nodiscard]] std::size_t stored_point_count() const; - [[nodiscard]] std::size_t rendered_cell_count() const; }; } #include "Waterfall.ipp" diff --git a/render_2D/render_2D/plottable/Waterfall.ipp b/render_2D/render_2D/plottable/Waterfall.ipp index 812390b..f1c9ea0 100644 --- a/render_2D/render_2D/plottable/Waterfall.ipp +++ b/render_2D/render_2D/plottable/Waterfall.ipp @@ -23,27 +23,13 @@ struct Waterfall::Private : Prev_Private { int source_first{}; /* 可视频段在源频谱行中的首列。 */ bool valid{}; /* 轴布局和行数据是否足以生成色块。 */ }; - using Append_Run = void (*)(Root*, Waterfall_Row); - using Count_Run = std::size_t (*)(const Root*); - struct Dispatch { - Append_Run append; /* 按 tick 向最终对象提交一行。 */ - Count_Run row_count; /* 查询权威行数。 */ - Count_Run point_count; /* 查询权威样本总数。 */ - Count_Run rendered_count; /* 查询已准备的色块数。 */ - }; Scene_Object* scene{}; /* 不拥有的所属 Scene。 */ Frequency_Object* frequency_axis{}; /* 不拥有的频率轴。 */ Time_Object* time_axis{}; /* 不拥有的时间轴,同时权威决定保留行数。 */ Prepared prepared{}; /* 当前权威状态推导出的 Paint 输入。 */ detail::Hover_Tooltip_Runtime tooltip{}; /* 事件侧当前 hover 位置。 */ Plot_Partition_Count graph_partition_count{}; /* Prepare 子图当前固化的分块数。 */ - const Dispatch* dispatch{}; /* 最终类型的公开薄壳分派表。 */ - /* CRTP 覆盖:绑定 Renderable、事件能力和最终 Waterfall 分派表。 */ - template - void bind_private_crtp(Object* object); 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); @@ -65,8 +51,6 @@ struct Waterfall::Private : Prev_Private { /* CRTP 覆盖:本类状态写入后标记 Prepare 数据失效。 */ 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(Frequency_Object* frequency_axis_value, Time_Object* time_axis_value) : Base(), frequency_axis(frequency_axis_value), time_axis(time_axis_value) {} @@ -104,10 +88,6 @@ std::expected, Dependency_Graph_Error> Waterfall::Builde }; 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 bool Waterfall::Private::should_rebuild_prepare_graph(Object* object, const Prop& state) { const std::size_t cells = object->template access_query_stream([&](std::span> rows) { @@ -228,53 +208,6 @@ template void Waterfall::Private::after_prop_set(Object* object, Member Owner::*, Prop_Access) { if constexpr (std::same_as) object->template mark_dirty(); } -template -void Waterfall::Private::before_advance(Object* object, Prop_Type*, State_Access pending_states, const Prop_Type*, State_Access) { - auto& state = pending_states.template get(); - object->template access_query_stream([&](std::span> rows) { - state.row_count = rows.size(); - state.stored_point_count = 0; - for (const auto& row : rows) state.stored_point_count += row->values.size(); - state.rendered_cell_count = state.stored_point_count; - }); -} -template -const Waterfall::Private::Dispatch& Waterfall::Private::dispatch_for() { - static const Dispatch value{ - [](Root* root, Waterfall_Row row) { - auto* object = static_cast(root); - object->template submit_stream( - std::make_shared(std::move(row))); - object->template mark_dirty(); - }, - [](const Root* root) { - return static_cast(root)->template access_query_stream( - [](std::span> rows) { return rows.size(); }); - }, - [](const Root* root) { - return static_cast(root)->template access_query_stream( - [](std::span> rows) { - std::size_t count{}; - for (const auto& row : rows) count += row->values.size(); - return count; - }); - }, - [](const Root* root) { - return static_cast(root)->template access_query_stream( - [](std::span> rows) { - std::size_t count{}; - for (const auto& row : rows) count += row->values.size(); - return count; - }); - } - }; - 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(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.ipp b/render_2D/render_2D/scene/Render_Scene_2D.ipp index 428faa4..f501dcd 100644 --- a/render_2D/render_2D/scene/Render_Scene_2D.ipp +++ b/render_2D/render_2D/scene/Render_Scene_2D.ipp @@ -297,7 +297,7 @@ Render_Scene_2D::Render_Result Render_Scene_2D::Private::render(Object* object, template void Render_Scene_2D::Private::dispatch_events(Object* object, Size viewport, std::uint64_t frame_sequence) { - auto events = this->take_events(frame_sequence); + auto events = this->take_events(object, frame_sequence); for (const auto& owned_event : events) { const Event& event = *owned_event; const auto* pointer = dynamic_cast(&event); diff --git a/render_2D/tests/Axis_Test.cpp b/render_2D/tests/Axis_Test.cpp index 86f97ba..76af147 100644 --- a/render_2D/tests/Axis_Test.cpp +++ b/render_2D/tests/Axis_Test.cpp @@ -120,16 +120,16 @@ TEST(axis_event, numeric_axis_wheel_zoom_and_drag_update_authoritative_range) { auto wheel = scene->make_event(); wheel->position = {50.0, 0.0}; wheel->angle_delta_y = 120.0; - scene->dispatch_event(wheel); + scene->submit_stream(wheel); render_once(scene.get()); EXPECT_EQ(axis->coordinate_range(), (Axis_Range{0.5, 9.5})); auto press = scene->make_event(Event_Type::pointer_press); press->position = {50.0, 0.0}; press->button = Mouse_Button::left; - scene->dispatch_event(press); + scene->submit_stream(press); auto move = scene->make_event(Event_Type::pointer_move); move->position = {60.0, 0.0}; - scene->dispatch_event(move); + scene->submit_stream(move); render_once(scene.get()); EXPECT_EQ(axis->coordinate_range(), (Axis_Range{-0.4, 8.6})); } @@ -158,7 +158,7 @@ TEST(axis_event, scene_routes_pointer_interaction_to_the_nearest_axis_segment) { auto near_vertical = scene->make_event(); near_vertical->position = {22.0, 80.0}; near_vertical->angle_delta_y = 120.0; - scene->dispatch_event(near_vertical); + scene->submit_stream(near_vertical); render_once(scene.get()); EXPECT_DOUBLE_EQ(horizontal->coordinate_range().size(), 10.0); EXPECT_DOUBLE_EQ(vertical->coordinate_range().size(), 9.0); @@ -166,7 +166,7 @@ TEST(axis_event, scene_routes_pointer_interaction_to_the_nearest_axis_segment) { auto near_horizontal = scene->make_event(); near_horizontal->position = {120.0, 178.0}; near_horizontal->angle_delta_y = 120.0; - scene->dispatch_event(near_horizontal); + scene->submit_stream(near_horizontal); render_once(scene.get()); EXPECT_DOUBLE_EQ(horizontal->coordinate_range().size(), 9.0); EXPECT_DOUBLE_EQ(vertical->coordinate_range().size(), 9.0); diff --git a/render_2D/tests/Plottable_Migration_Test.cpp b/render_2D/tests/Plottable_Migration_Test.cpp index 128cd36..c3c632a 100644 --- a/render_2D/tests/Plottable_Migration_Test.cpp +++ b/render_2D/tests/Plottable_Migration_Test.cpp @@ -63,9 +63,13 @@ TEST(plottable_migration, curve_plots_share_partitioned_rendering) { auto sweep = build_object(frequency.get(), power.get()); trace->set<&Frequency_Trace::Prop::partition_mode>(Plot_Partition_Mode::fixed); trace->set<&Frequency_Trace::Prop::partition_count>(2u); - trace->append_sample(time->append_time({1'000}), 10.0); - trace->append_sample(time->append_time({2'000}), 50.0); - trace->append_sample(time->append_time({3'000}), 90.0); + trace->submit_stream( + Frequency_Trace_Sample{time->append_time({1'000}), 10.0}); + trace->submit_stream( + Frequency_Trace_Sample{time->append_time({2'000}), 50.0}); + trace->submit_stream( + Frequency_Trace_Sample{time->append_time({3'000}), 90.0}); + trace->mark_dirty(); sweep->set<&Sweep_Spectrum::Prop::frequency_range>(Axis_Range{0.0, 100.0}); sweep->set<&Sweep_Spectrum::Prop::block_count>(2u); sweep->set<&Sweep_Spectrum::Prop::partition_mode>(Plot_Partition_Mode::fixed); @@ -73,25 +77,25 @@ TEST(plottable_migration, curve_plots_share_partitioned_rendering) { const std::array first_block{-90.0, -60.0, -30.0, -10.0}; const std::array second_block{-80.0, -50.0, -20.0, -5.0}; const std::array replacement_block{-70.0, -40.0, -15.0, -2.0}; - sweep->append_block(first_block); - sweep->append_block(second_block); + sweep->submit_stream( + std::make_shared>(first_block.begin(), first_block.end())); + sweep->submit_stream( + std::make_shared>(second_block.begin(), second_block.end())); + sweep->mark_dirty(); auto scene = build_scene(trace.get(), sweep.get()); scene->set<&Render_Scene_2D::Prop::viewport>(canvas); scene->activate_view(); render_once(scene.get()); - EXPECT_EQ(trace->sample_count(), 3u); - EXPECT_GT(trace->rendered_point_count(), 0u); - EXPECT_EQ(sweep->stored_block_count(), 2u); - EXPECT_GT(sweep->rendered_point_count(), 0u); - const auto completed_sweep_points = sweep->rendered_point_count(); - sweep->append_block(replacement_block); + EXPECT_EQ(trace->access_rendering_stream( + [](std::span samples) { return samples.size(); }), 3u); + EXPECT_EQ(sweep->access_rendering_stream( + [](std::span>> blocks) { return blocks.size(); }), 2u); + sweep->submit_stream( + std::make_shared>(replacement_block.begin(), replacement_block.end())); + sweep->mark_dirty(); render_once(scene.get()); - EXPECT_EQ(sweep->stored_block_count(), 2u); - EXPECT_EQ(sweep->rendered_point_count(), completed_sweep_points); - const auto& sweep_prop = sweep->read_prop(); - EXPECT_EQ(sweep_prop.blocks[0], std::vector(replacement_block.begin(), replacement_block.end())); - EXPECT_EQ(sweep_prop.blocks[1], std::vector(second_block.begin(), second_block.end())); - EXPECT_EQ(sweep_prop.next_block_index, 1u); + EXPECT_EQ(sweep->access_query_stream( + [](std::span>> blocks) { return blocks.size(); }), 2u); } TEST(plottable_migration, raster_plots_share_partitioned_color_blocks) { @@ -119,17 +123,22 @@ TEST(plottable_migration, raster_plots_share_partitioned_color_blocks) { waterfall->set<&Waterfall::Prop::frequency_range>(Axis_Range{0.0, 100.0}); waterfall->set<&Waterfall::Prop::power_range>(Axis_Range{-100.0, 0.0}); const std::array row{-90.0, -60.0, -30.0, -10.0}; - glow->append_spectrum(row); - waterfall->append_row(time->append_time({1'000}), row); - waterfall->append_row(time->append_time({2'000}), row); + glow->submit_stream( + std::make_shared>(row.begin(), row.end())); + glow->mark_dirty(); + waterfall->submit_stream(std::make_shared( + Waterfall_Row{time->append_time({1'000}), {row.begin(), row.end()}})); + waterfall->submit_stream(std::make_shared( + Waterfall_Row{time->append_time({2'000}), {row.begin(), row.end()}})); + waterfall->mark_dirty(); auto scene = build_scene(glow.get(), waterfall.get()); scene->set<&Render_Scene_2D::Prop::viewport>(canvas); scene->activate_view(); render_once(scene.get()); - EXPECT_EQ(glow->history_count(), 1u); - EXPECT_GT(glow->rendered_cell_count(), 0u); - EXPECT_EQ(waterfall->row_count(), 2u); - EXPECT_GT(waterfall->rendered_cell_count(), 0u); + EXPECT_EQ(glow->access_rendering_stream( + [](std::span>> spectra) { return spectra.size(); }), 1u); + EXPECT_EQ(waterfall->access_rendering_stream( + [](std::span> rows) { return rows.size(); }), 2u); } TEST(plottable_migration, direct_overlay_and_constellation_build_and_render) { @@ -149,12 +158,15 @@ TEST(plottable_migration, direct_overlay_and_constellation_build_and_render) { auto overlay = build_object(horizontal.get(), vertical.get()); diagram->set<&Constellation_Diagram::Prop::i_range>(Axis_Range{-1.0, 1.0}); diagram->set<&Constellation_Diagram::Prop::q_range>(Axis_Range{-1.0, 1.0}); - diagram->append_point({0.25, -0.25}); + diagram->submit_stream( + Constellation_Point{{0.25, -0.25}, monotonic_milliseconds()}); + diagram->mark_dirty(); auto scene = build_scene(diagram.get(), overlay.get()); scene->set<&Render_Scene_2D::Prop::viewport>(canvas); scene->activate_view(); render_once(scene.get()); - EXPECT_EQ(diagram->point_count(), 1u); + EXPECT_EQ(diagram->access_rendering_stream( + [](std::span points) { return points.size(); }), 1u); EXPECT_EQ(overlay->read_state().prepare_task_count, 0u); EXPECT_EQ(overlay->read_state().paint_task_count, 1u); } @@ -182,16 +194,16 @@ TEST(selection_overlay, control_extends_selection_and_plain_click_clears_it) { press->position = first; press->button = Mouse_Button::left; press->modifiers = modifiers; - scene->dispatch_event(press); + scene->submit_stream(press); auto move = scene->make_event(Event_Type::pointer_move); move->position = second; move->modifiers = modifiers; - scene->dispatch_event(move); + scene->submit_stream(move); auto release = scene->make_event(Event_Type::pointer_release); release->position = second; release->button = Mouse_Button::left; release->modifiers = modifiers; - scene->dispatch_event(release); + scene->submit_stream(release); render_once(scene.get()); }; drag({30.0, 90.0}, {60.0, 60.0}, Keyboard_Modifier::none); @@ -226,14 +238,14 @@ TEST(selection_overlay, time_axis_selection_keeps_axis_coordinates_while_window_ auto press = scene->make_event(Event_Type::pointer_press); press->position = {92.0, 88.0}; press->button = Mouse_Button::left; - scene->dispatch_event(press); + scene->submit_stream(press); auto move = scene->make_event(Event_Type::pointer_move); move->position = {116.0, 56.0}; - scene->dispatch_event(move); + scene->submit_stream(move); auto release = scene->make_event(Event_Type::pointer_release); release->position = {116.0, 56.0}; release->button = Mouse_Button::left; - scene->dispatch_event(release); + scene->submit_stream(release); render_once(scene.get()); ASSERT_EQ(overlay->selected_regions().size(), 1u); const Axis_Rectangle selected = overlay->selected_regions().front(); diff --git a/render_3D/render_3D/scene/Render_Scene_3D.ipp b/render_3D/render_3D/scene/Render_Scene_3D.ipp index 2544310..6730593 100644 --- a/render_3D/render_3D/scene/Render_Scene_3D.ipp +++ b/render_3D/render_3D/scene/Render_Scene_3D.ipp @@ -168,7 +168,7 @@ void Render_Scene_3D::Private::process(Object* object, Callback&& callback) requ if (!prop.view_active || prop.viewport.empty() || !backend || !backend->available()) return; frame->mark(Frame_Trace_Marker::scene_render_started); frame->mark(Frame_Trace_Marker::event_dispatch_started); - auto events = this->take_events(frame->identity().sequence); + auto events = this->take_events(object, frame->identity().sequence); frame->mark(Frame_Trace_Marker::event_dispatch_finished); auto context = std::static_pointer_cast>(paint_context); context->parameters = parameters(object); diff --git a/web_server/src/Gallery_Plots_2D.cpp b/web_server/src/Gallery_Plots_2D.cpp index 7d8a4fa..f6607af 100644 --- a/web_server/src/Gallery_Plots_2D.cpp +++ b/web_server/src/Gallery_Plots_2D.cpp @@ -291,7 +291,9 @@ nlohmann::json generate_2d_data(Object& object, const Json& input) { std::vector samples(count); for (std::size_t index = 0; index < count; ++index) samples[index] = {static_cast(index * tick_step), distribution(engine)}; - for (const auto& sample : samples) object.append_sample(sample.tick, sample.value); + for (const auto& sample : samples) + object.template submit_stream(sample); + object.template mark_dirty(); generated_count = count; } else if constexpr (std::same_as) { const auto block_count = generator_count(input, "block_count", 65'536); @@ -305,7 +307,10 @@ nlohmann::json generate_2d_data(Object& object, const Json& input) { std::ranges::copy_n(complete.begin() + block * width, width, blocks[block].begin()); object.template set<&Sweep_Spectrum::Prop::bins_per_block>(width); object.template set<&Sweep_Spectrum::Prop::block_count>(block_count); - for (auto& block : blocks) object.append_block(block); + for (auto& block : blocks) + object.template submit_stream( + std::make_shared>(std::move(block))); + object.template mark_dirty(); generated_count = block_count * width; } else if constexpr (std::same_as) { const auto row_count = generator_count(input, "history_count", 4096); @@ -317,7 +322,10 @@ nlohmann::json generate_2d_data(Object& object, const Json& input) { const auto noise_stddev = generator_number(input, "noise_stddev"); for (std::size_t row = 0; row < row_count; ++row) generate_spectral_row(spectra[row], row, signal_count, minimum, maximum, noise_stddev, engine); - for (auto& spectrum : spectra) object.append_spectrum(spectrum); + for (auto& spectrum : spectra) + object.template submit_stream( + std::make_shared>(std::move(spectrum))); + object.template mark_dirty(); generated_count = row_count * width; } else if constexpr (std::same_as) { const auto row_count = generator_count(input, "row_count", 4096); @@ -334,7 +342,10 @@ nlohmann::json generate_2d_data(Object& object, const Json& input) { rows.push_back({static_cast(row), std::move(row_values)}); } object.template set<&Waterfall::Prop::frequency_bin_count>(width); - for (auto& row : rows) object.append_row(row.tick, row.values); + for (auto& row : rows) + object.template submit_stream( + std::make_shared(std::move(row))); + object.template mark_dirty(); generated_count = row_count * width; } else if constexpr (std::same_as) { const auto count = generator_count(input, "point_count"); @@ -345,7 +356,9 @@ nlohmann::json generate_2d_data(Object& object, const Json& input) { const auto submitted = monotonic_milliseconds(); for (std::size_t index = 0; index < count; ++index) points[index] = {{i_distribution(engine), q_distribution(engine)}, submitted}; - for (const auto& point : points) object.append_point(point.point); + for (const auto& point : points) + object.template submit_stream(point); + object.template mark_dirty(); generated_count = count; } else if constexpr (std::same_as) { const auto count = generator_count(input, "region_count"); @@ -614,16 +627,15 @@ std::shared_ptr make_frequency_trace_plot(asio::any_io_executor executor) static_cast( std::fmod(std::max(0.0, event.time_milliseconds), day_milliseconds)) }); - raw->append_sample(tick, - std::sin(event.time_milliseconds * 0.0025) * 0.8 - + std::sin(event.time_milliseconds * 0.0007) * 0.2); + raw->template submit_stream(Frequency_Trace_Sample{ + tick, std::sin(event.time_milliseconds * 0.0025) * 0.8 + + std::sin(event.time_milliseconds * 0.0007) * 0.2}); + raw->template mark_dirty(); }; auto view = make_scene_view< Prop_Field<&Frequency_Trace::Prop::partition_count, "partition_count", "Number of partitions used to prepare the time-ordered trace.">, Prop_Field<&Frequency_Trace::Prop::pen, "pen", "Stroke style used to draw the frequency trace.">, - Prop_Field<&Frequency_Trace::Prop::partition_mode, "partition_mode", "Selects how trace samples are divided between preparation tasks.">, - State_Field, - State_Field>( + Prop_Field<&Frequency_Trace::Prop::partition_mode, "partition_mode", "Selects how trace samples are divided between preparation tasks.">>( *trace, *scene, std::move(update), std::move(time), std::move(vertical), std::move(trace), std::move(selection)); return std::make_shared(std::move(executor), std::move(scene), std::move(view)); } @@ -658,7 +670,9 @@ std::shared_ptr make_sweep_spectrum_plot(asio::any_io_executor executor) { const auto sweep_index = block_index * values.size() + i; values[i] = -90.0 + 35.0 * std::sin(sweep_index * 0.08 + event.time_milliseconds * 0.002); } - raw->append_block(values); + raw->template submit_stream( + std::make_shared>(std::move(values))); + raw->template mark_dirty(); }; auto view = make_scene_view< Prop_Field<&Sweep_Spectrum::Prop::bins_per_block, "bins_per_block", "Number of frequency bins stored in each incoming sweep block.">, @@ -669,10 +683,7 @@ std::shared_ptr make_sweep_spectrum_plot(asio::any_io_executor executor) { Prop_Field<&Sweep_Spectrum::Prop::partition_mode, "partition_mode", "Selects how sweep blocks are divided between preparation tasks.">, Prop_Field<&Sweep_Spectrum::Prop::pen, "pen", "Stroke style used for the completed sweep curve.">, Prop_Field<&Sweep_Spectrum::Prop::current_frequency_pen, "current_frequency_pen", "Stroke style used for the current sweep-frequency indicator.">, - Prop_Field<&Sweep_Spectrum::Prop::interpolation_mode, "interpolation_mode", "Selects interpolation between adjacent sweep bins.">, - State_Field, - State_Field, - State_Field>( + Prop_Field<&Sweep_Spectrum::Prop::interpolation_mode, "interpolation_mode", "Selects interpolation between adjacent sweep bins.">>( *sweep, *scene, std::move(update), std::move(frequency), std::move(vertical), std::move(sweep), std::move(selection)); return std::make_shared(std::move(executor), std::move(scene), std::move(view)); } @@ -703,7 +714,9 @@ std::shared_ptr make_afterglow_plot(asio::any_io_executor executor) { values[i] = -95.0 + 62.0 * std::exp(-220.0 * std::pow( static_cast(i) / values.size() - 0.5 - 0.18 * std::sin(event.time_milliseconds * 0.0008), 2.0)); - raw->append_spectrum(values); + raw->template submit_stream( + std::make_shared>(values.begin(), values.end())); + raw->template mark_dirty(); }; auto view = make_scene_view< Prop_Field<&Afterglow::Prop::frequency_point_size, "frequency_point_size", "Number of frequency cells allocated across each afterglow row.">, @@ -714,10 +727,7 @@ std::shared_ptr make_afterglow_plot(asio::any_io_executor executor) { Prop_Field<&Afterglow::Prop::frequency_range, "frequency_range", "Maps input samples onto the afterglow frequency axis.">, Prop_Field<&Afterglow::Prop::power_range, "power_range", "Defines the minimum and maximum power represented by the color grid.">, Prop_Field<&Afterglow::Prop::partition_mode, "partition_mode", "Selects how afterglow cells are divided between preparation tasks.">, - Prop_Field<&Afterglow::Prop::color_map, "color_map", "Maps accumulated energy values to rendered colors.">, - State_Field, - State_Field, - State_Field>( + Prop_Field<&Afterglow::Prop::color_map, "color_map", "Maps accumulated energy values to rendered colors.">>( *afterglow, *scene, std::move(update), std::move(frequency), std::move(vertical), std::move(afterglow), std::move(selection)); return std::make_shared(std::move(executor), std::move(scene), std::move(view)); } @@ -752,7 +762,10 @@ std::shared_ptr make_waterfall_plot(asio::any_io_executor executor) { static_cast( std::fmod(std::max(0.0, event.time_milliseconds), day_milliseconds)) }); - raw->append_row(tick, values); + raw->template submit_stream( + std::make_shared(Waterfall_Row{ + tick, {values.begin(), values.end()}})); + raw->template mark_dirty(); }; auto view = make_scene_view< Prop_Field<&Waterfall::Prop::tooltip_enabled, "tooltip_enabled", "Enables value inspection tooltips over waterfall cells.">, @@ -766,10 +779,7 @@ std::shared_ptr make_waterfall_plot(asio::any_io_executor executor) { Prop_Field<&Waterfall::Prop::power_range, "power_range", "Defines the power interval mapped through the waterfall color map.">, Prop_Field<&Waterfall::Prop::partition_mode, "partition_mode", "Selects how waterfall rows are divided between preparation tasks.">, Prop_Field<&Waterfall::Prop::interpolation_mode, "interpolation_mode", "Selects interpolation when samples are mapped to raster cells.">, - Prop_Field<&Waterfall::Prop::color_map, "color_map", "Maps sample power values to waterfall colors.">, - State_Field, - State_Field, - State_Field>( + Prop_Field<&Waterfall::Prop::color_map, "color_map", "Maps sample power values to waterfall colors.">>( *waterfall, *scene, std::move(update), std::move(frequency), std::move(time), std::move(waterfall), std::move(selection)); return std::make_shared(std::move(executor), std::move(scene), std::move(view)); } @@ -806,11 +816,12 @@ std::shared_ptr make_constellation_plot(asio::any_io_executor executor) { + 0.012 * std::cos(phase * 23.0 + index * 0.61); const double noise_q = 0.025 * std::cos(phase * 13.0 + index * 1.37) + 0.012 * std::sin(phase * 19.0 + index * 0.47); - raw->append_point({ + raw->template submit_stream(Constellation_Point{{ state.i_range.center() + std::cos(angle) * radius + noise_i, state.q_range.center() + std::sin(angle) * radius + noise_q - }); + }, monotonic_milliseconds()}); } + raw->template mark_dirty(); }; auto view = make_scene_view< Prop_Field<&Constellation_Diagram::Prop::point_lifetime_ms, "point_lifetime_ms", "Time in milliseconds that an appended constellation point remains visible.">, @@ -819,8 +830,7 @@ std::shared_ptr make_constellation_plot(asio::any_io_executor executor) { Prop_Field<&Constellation_Diagram::Prop::i_range, "i_range", "Defines the horizontal in-phase coordinate interval.">, Prop_Field<&Constellation_Diagram::Prop::q_range, "q_range", "Defines the vertical quadrature coordinate interval.">, Prop_Field<&Constellation_Diagram::Prop::point_color, "point_color", "Color used to render received I/Q samples.">, - Prop_Field<&Constellation_Diagram::Prop::anchor_color, "anchor_color", "Color used to render ideal modulation anchors.">, - State_Field>( + Prop_Field<&Constellation_Diagram::Prop::anchor_color, "anchor_color", "Color used to render ideal modulation anchors.">>( *constellation, *scene, std::move(update), std::move(horizontal), std::move(vertical), std::move(constellation), std::move(selection)); return std::make_shared(std::move(executor), std::move(scene), std::move(view)); } diff --git a/web_server/src/Plot.cpp b/web_server/src/Plot.cpp index 5922546..35710e0 100644 --- a/web_server/src/Plot.cpp +++ b/web_server/src/Plot.cpp @@ -242,7 +242,9 @@ void dispatch_plot_input(Scene_Object& scene, const Plot_Input_Event& input, Plot::Input_Handler handler) { Web_Input_Metadata metadata{input, std::move(handler), std::chrono::steady_clock::now()}; - const auto dispatch = [&](auto event) { scene.dispatch_event(std::move(event)); }; + const auto dispatch = [&](auto event) { + scene.template submit_stream(std::move(event)); + }; const auto apply_pointer = [&](auto& event) { event.position = input.position; event.global_position = input.global_position; @@ -539,7 +541,15 @@ void Plot::Private::render_frame(Plot_Render_Tick tick) { void Plot::Private::complete_event_reports(Frame_Identity rendered_identity) { aethera::Scene::Event_Report_Batch reports = std::visit( - [](auto& value) { return value->take_event_reports(); }, scene); + [](auto& value) { + return value->template access_query_stream( + [&](std::span events) { + aethera::Scene::Event_Report_Batch result{value->memory_resource()}; + result.reserve(events.size()); + for (const auto& event : events) result.push_back(event); + return result; + }); + }, scene); pending_event_reports.insert(pending_event_reports.end(), std::make_move_iterator(reports.begin()), std::make_move_iterator(reports.end()));