四个方向 分块渲染
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
#include "Waterfall.h"
|
||||
#include "Heatmap_Utils.h"
|
||||
#include "Plottable_Real_Time_Data.h"
|
||||
#include "../renderable/Render_Partition.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
@@ -18,6 +20,17 @@ struct Waterfall_Interaction {
|
||||
};
|
||||
using Waterfall_History = Plottable_History_Real_Time_Data<Waterfall_Row, std::deque<Waterfall_Row>>;
|
||||
using Waterfall_Interaction_State = Double_State_Strategy<Waterfall_Interaction_Base, Waterfall_Interaction>;
|
||||
struct Waterfall_Render_Frame {
|
||||
Adaptive_Render_Partitioner partitioner;
|
||||
Axis_Raster_Layout layout;
|
||||
Frequency_Columns columns;
|
||||
std::vector<Pixel> pixels;
|
||||
int source_width{};
|
||||
int source_height{};
|
||||
int active_partitions{1};
|
||||
std::size_t work_size{};
|
||||
bool valid{};
|
||||
};
|
||||
}
|
||||
struct Waterfall_Control::Impl {
|
||||
Impl(Waterfall_Control& owner, std::shared_ptr<Frequency_Axis> frequency, std::shared_ptr<Time_Axis> time)
|
||||
@@ -26,6 +39,7 @@ struct Waterfall_Control::Impl {
|
||||
std::shared_ptr<Time_Axis> time_axis;
|
||||
Waterfall_History rows;
|
||||
Waterfall_Interaction_State interaction;
|
||||
Waterfall_Render_Frame render_frame;
|
||||
};
|
||||
Waterfall_Control::Waterfall_Control(Plot_Core& plot, const Waterfall_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Time_Axis> time_axis)
|
||||
: Plottable_State(plot, properties), impl_(std::make_unique<Impl>(*this, std::move(frequency_axis), std::move(time_axis))) {}
|
||||
@@ -82,35 +96,95 @@ void Waterfall_Control::publish() {
|
||||
publish_properties();
|
||||
impl_->interaction.publish();
|
||||
}
|
||||
void Waterfall_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
|
||||
void Waterfall_Control::build_paint_task_graph(Renderable_Task_Graph& graph) {
|
||||
const auto prepare = graph.emplace([this](const Scene_Render_Context&) {
|
||||
prepare_render_frame(render_state_view());
|
||||
}, "prepare waterfall");
|
||||
std::array<Renderable_Task_Graph::Task, maximum_render_partitions> partitions;
|
||||
for (int index = 0; index < maximum_render_partitions; ++index) {
|
||||
partitions[static_cast<std::size_t>(index)] = graph.emplace(
|
||||
[this, index](const Scene_Render_Context&) {
|
||||
render_partition(render_state_view(), index);
|
||||
},
|
||||
"raster waterfall partition");
|
||||
graph.precede(prepare, partitions[static_cast<std::size_t>(index)]);
|
||||
}
|
||||
const auto compose = add_paint_task(
|
||||
graph, "compose waterfall",
|
||||
[this](Painter& painter, const Render_State_View& view) { paint(painter, view); });
|
||||
for (const auto partition : partitions)
|
||||
graph.precede(partition, compose);
|
||||
}
|
||||
|
||||
void Waterfall_Control::prepare_render_frame(const Render_State_View& view) {
|
||||
const auto& state = render_properties(view);
|
||||
const auto& rows = view.get(impl_->rows);
|
||||
const auto& interaction = view.get(impl_->interaction);
|
||||
if(rows.empty())
|
||||
auto& output = impl_->render_frame;
|
||||
output.valid = false;
|
||||
output.work_size = 0;
|
||||
if (rows.empty())
|
||||
return;
|
||||
const int source_width = std::min(state.frequency_bin_count.get(), static_cast<int>(std::min_element(rows.begin(), rows.end(), [](const auto& left, const auto& right) { return left.values.size() < right.values.size(); })->values.size()));
|
||||
const int height = static_cast<int>(rows.size());
|
||||
if(source_width <= 0 || height <= 0)
|
||||
if (source_width <= 0 || height <= 0)
|
||||
return;
|
||||
const Axis_Transform horizontal = impl_->frequency_axis->transform(view);
|
||||
const auto columns = frequency_columns(state.frequency_range, horizontal.coordinate_range, source_width, state.visible_range_only);
|
||||
if(!columns)
|
||||
const Axis_Transform frequency_axis = impl_->frequency_axis->transform(view);
|
||||
const Axis_Transform time_axis = impl_->time_axis->transform(view);
|
||||
const auto columns = frequency_columns(state.frequency_range, frequency_axis.coordinate_range,
|
||||
source_width, state.visible_range_only);
|
||||
if (!columns)
|
||||
return;
|
||||
const int width = columns->last - columns->first + 1;
|
||||
std::vector<Pixel> pixels(static_cast<std::size_t>(width) * height);
|
||||
for(int y = 0; y < height; ++y) {
|
||||
const Range time_range = rows.size() == 1
|
||||
? time_axis.coordinate_range
|
||||
: Range{static_cast<double>(rows.front().tick),
|
||||
static_cast<double>(rows.back().tick)};
|
||||
const auto layout = axis_raster_layout(frequency_axis, time_axis, columns->range,
|
||||
time_range, width, height);
|
||||
if (!layout.valid())
|
||||
return;
|
||||
output.layout = layout;
|
||||
output.columns = *columns;
|
||||
output.source_width = width;
|
||||
output.source_height = height;
|
||||
output.work_size = static_cast<std::size_t>(width) * height;
|
||||
output.pixels.resize(output.work_size);
|
||||
output.active_partitions =
|
||||
output.partitioner.begin(state.partition_count.get(), output.work_size);
|
||||
output.valid = true;
|
||||
}
|
||||
|
||||
void Waterfall_Control::render_partition(const Render_State_View& view, int partition_index) {
|
||||
auto& output = impl_->render_frame;
|
||||
if (!output.valid || partition_index >= output.active_partitions)
|
||||
return;
|
||||
const auto& state = render_properties(view);
|
||||
const auto& rows = view.get(impl_->rows);
|
||||
const auto range = render_partition_range(output.work_size, partition_index,
|
||||
output.active_partitions);
|
||||
for (std::size_t cell = range.first; cell < range.last; ++cell) {
|
||||
const int y = static_cast<int>(cell / static_cast<std::size_t>(output.source_width));
|
||||
const int x = static_cast<int>(cell % static_cast<std::size_t>(output.source_width));
|
||||
const auto& row = rows[static_cast<std::size_t>(y)].values;
|
||||
for(int x = 0; x < width; ++x)
|
||||
pixels[static_cast<std::size_t>(y) * width + x] = state.color_map.at_normalized(normalized_value(row[static_cast<std::size_t>(columns->first + x)], state.power_range));
|
||||
output.pixels[output.layout.index(x, y, output.source_width, output.source_height)] =
|
||||
state.color_map.at_normalized(normalized_value(
|
||||
row[static_cast<std::size_t>(output.columns.first + x)], state.power_range));
|
||||
}
|
||||
const Axis_Transform vertical = impl_->time_axis->transform(view);
|
||||
const Range time_range{static_cast<double>(rows.front().tick), static_cast<double>(rows.back().tick)};
|
||||
RectF target = mapped_rect(horizontal, vertical, columns->range, time_range);
|
||||
if(target.height < 1.0)
|
||||
target.height = std::max(1.0, impl_->time_axis->transform(view).pixel_length);
|
||||
painter.heatmap(target, width, height, pixels, state.interpolation_mode);
|
||||
if(state.tooltip_enabled && interaction.tooltip.active && target.contains(interaction.tooltip.position)) {
|
||||
const double frequency = horizontal.pixel_to_coord(interaction.tooltip.position.x);
|
||||
}
|
||||
|
||||
void Waterfall_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
const auto& state = render_properties(view);
|
||||
const auto& interaction = view.get(impl_->interaction);
|
||||
auto& output = impl_->render_frame;
|
||||
if (!output.valid)
|
||||
return;
|
||||
painter.heatmap(output.layout.target, output.layout.width, output.layout.height,
|
||||
output.pixels, state.interpolation_mode);
|
||||
output.partitioner.finish(output.work_size);
|
||||
if(state.tooltip_enabled && interaction.tooltip.active && output.layout.target.contains(interaction.tooltip.position)) {
|
||||
const Axis_Transform frequency_axis = impl_->frequency_axis->transform(view);
|
||||
const double frequency = frequency_axis.point_to_coord(interaction.tooltip.position);
|
||||
std::ostringstream text;
|
||||
text << std::fixed << std::setprecision(2) << frequency << " Hz";
|
||||
const RectF box{interaction.tooltip.position.x + 8.0, interaction.tooltip.position.y + 8.0, 110.0, 24.0};
|
||||
|
||||
Reference in New Issue
Block a user