四个方向 分块渲染
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
#include "Spectrum.h"
|
||||
#include "Curve_Sampling.h"
|
||||
#include "Plottable_Real_Time_Data.h"
|
||||
#include "../render/Blend2D_Cache.h"
|
||||
#include "../renderable/Render_Partition.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
@@ -20,23 +23,56 @@ struct Spectrum_Interaction {
|
||||
Hover_Tooltip_Runtime tooltip;
|
||||
};
|
||||
using Spectrum_Interaction_State = Double_State_Strategy<Spectrum_Interaction_Base, Spectrum_Interaction>;
|
||||
RectF axes_rect(const Axis_Transform& horizontal, const Axis_Transform& vertical) {
|
||||
const double x1 = horizontal.coord_to_pixel(horizontal.coordinate_range.origin);
|
||||
const double x2 = horizontal.coord_to_pixel(horizontal.coordinate_range.target);
|
||||
const double y1 = vertical.coord_to_pixel(vertical.coordinate_range.origin);
|
||||
const double y2 = vertical.coord_to_pixel(vertical.coordinate_range.target);
|
||||
return {std::min(x1, x2), std::min(y1, y2), std::abs(x2 - x1), std::abs(y2 - y1)};
|
||||
struct Spectrum_Render_Frame {
|
||||
Adaptive_Render_Partitioner partitioner;
|
||||
std::array<Blend2D_Color_Cache, maximum_render_partitions> layers;
|
||||
int active_partitions{1};
|
||||
std::size_t work_size{};
|
||||
bool valid{};
|
||||
};
|
||||
|
||||
struct Curve_Partition {
|
||||
std::span<const double> values;
|
||||
Range domain;
|
||||
Range clip_domain;
|
||||
};
|
||||
|
||||
Curve_Partition curve_partition(std::span<const double> values, Range domain,
|
||||
int partition_index, int partition_count) {
|
||||
if (values.size() < 2 || partition_count <= 0)
|
||||
return {};
|
||||
const std::size_t segment_count = values.size() - 1;
|
||||
const auto core = render_partition_range(segment_count, partition_index, partition_count);
|
||||
if (core.first == core.last)
|
||||
return {};
|
||||
const std::size_t first = core.first == 0 ? 0 : core.first - 1;
|
||||
const std::size_t last = std::min(segment_count, core.last + 1);
|
||||
const auto coordinate = [domain, segment_count](std::size_t index) {
|
||||
return domain.origin + domain.length() * static_cast<double>(index) /
|
||||
static_cast<double>(segment_count);
|
||||
};
|
||||
return {
|
||||
values.subspan(first, last - first + 1),
|
||||
{coordinate(first), coordinate(last)},
|
||||
{coordinate(core.first), coordinate(core.last)}
|
||||
};
|
||||
}
|
||||
void draw_curve(Painter& painter, std::span<const double> values, Range domain, const Axis_Transform& x_axis, const Axis_Transform& y_axis, bool visible_only, Line_Interpolation_Mode interpolation, const Pen& pen, const Brush& brush) {
|
||||
auto points = curve_points(values, domain, x_axis, y_axis, visible_only, interpolation);
|
||||
void draw_curve(Painter& painter, std::span<const double> values, Range domain,
|
||||
const Axis_Transform& frequency_axis, const Axis_Transform& power_axis,
|
||||
bool visible_only, Line_Interpolation_Mode interpolation,
|
||||
const Pen& pen, const Brush& brush) {
|
||||
auto points = curve_points(values, domain, frequency_axis, power_axis,
|
||||
visible_only, interpolation);
|
||||
if(points.size() < 2)
|
||||
return;
|
||||
if(brush.enabled()) {
|
||||
std::vector<PointF> polygon;
|
||||
polygon.reserve(points.size() + 2);
|
||||
polygon.push_back({points.front().x, y_axis.coord_to_pixel(y_axis.coordinate_range.target)});
|
||||
polygon.push_back(mapped_point(frequency_axis, domain.origin, power_axis,
|
||||
power_axis.coordinate_range.target));
|
||||
polygon.insert(polygon.end(), points.begin(), points.end());
|
||||
polygon.push_back({points.back().x, y_axis.coord_to_pixel(y_axis.coordinate_range.target)});
|
||||
polygon.push_back(mapped_point(frequency_axis, domain.target, power_axis,
|
||||
power_axis.coordinate_range.target));
|
||||
painter.polygon(polygon, Pen{.style = Line_Style::None}, brush);
|
||||
}
|
||||
painter.polyline(points, pen);
|
||||
@@ -62,6 +98,7 @@ struct Spectrum_Control::Impl {
|
||||
Plottable_Latest_Real_Time_Data<Spectrum_Frame> frame;
|
||||
Spectrum_Interaction_State interaction;
|
||||
std::mutex frame_update_mutex;
|
||||
Spectrum_Render_Frame render_frame;
|
||||
};
|
||||
Spectrum_Control::Spectrum_Control(Plot_Core& plot, const Spectrum_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Axis> power_axis)
|
||||
: Plottable_State(plot, properties), impl_(std::make_unique<Impl>(*this, std::move(frequency_axis), std::move(power_axis))) {}
|
||||
@@ -205,32 +242,116 @@ void Spectrum_Control::publish() {
|
||||
publish_properties();
|
||||
impl_->interaction.publish();
|
||||
}
|
||||
|
||||
void Spectrum_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 spectrum");
|
||||
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);
|
||||
},
|
||||
"paint spectrum partition");
|
||||
graph.precede(prepare, partitions[static_cast<std::size_t>(index)]);
|
||||
}
|
||||
const auto compose = add_paint_task(
|
||||
graph, "compose spectrum",
|
||||
[this](Painter& painter, const Render_State_View& view) { paint(painter, view); });
|
||||
for (const auto task : partitions)
|
||||
graph.precede(task, compose);
|
||||
}
|
||||
|
||||
void Spectrum_Control::prepare_render_frame(const Render_State_View& view) {
|
||||
const auto& state = render_properties(view);
|
||||
const auto& published_frame = view.get(impl_->frame);
|
||||
auto& output = impl_->render_frame;
|
||||
const Axis_Transform frequency_axis = impl_->frequency_axis->transform(view);
|
||||
const Axis_Transform power_axis = impl_->power_axis->transform(view);
|
||||
output.valid = axes_are_orthogonal(frequency_axis, power_axis);
|
||||
output.work_size = published_frame ? published_frame->samples.size() : 0;
|
||||
output.active_partitions =
|
||||
output.partitioner.begin(state.partition_count.get(), output.work_size);
|
||||
for (int index = 0; index < output.active_partitions; ++index)
|
||||
output.layers[static_cast<std::size_t>(index)].clear();
|
||||
}
|
||||
|
||||
void Spectrum_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& published_frame = view.get(impl_->frame);
|
||||
if (!published_frame)
|
||||
return;
|
||||
const Spectrum_Frame& frame = *published_frame;
|
||||
const Axis_Transform frequency_axis = impl_->frequency_axis->transform(view);
|
||||
const Axis_Transform power_axis = impl_->power_axis->transform(view);
|
||||
const auto current = curve_partition(frame.samples, state.frequency_range,
|
||||
partition_index, output.active_partitions);
|
||||
if (current.values.empty())
|
||||
return;
|
||||
|
||||
Painter painter(output.layers[static_cast<std::size_t>(partition_index)], viewport_size());
|
||||
if (!painter)
|
||||
return;
|
||||
painter.clip(mapped_rect(frequency_axis, power_axis, current.clip_domain,
|
||||
power_axis.coordinate_range));
|
||||
if (state.max_hold_visible) {
|
||||
const auto maximum = curve_partition(frame.maxima, state.frequency_range,
|
||||
partition_index, output.active_partitions);
|
||||
draw_curve(painter, maximum.values, maximum.domain, frequency_axis, power_axis,
|
||||
state.visible_range_only, state.interpolation_mode,
|
||||
state.max_pen, state.max_brush);
|
||||
}
|
||||
if (state.min_hold_visible) {
|
||||
const auto minimum = curve_partition(frame.minima, state.frequency_range,
|
||||
partition_index, output.active_partitions);
|
||||
draw_curve(painter, minimum.values, minimum.domain, frequency_axis, power_axis,
|
||||
state.visible_range_only, state.interpolation_mode,
|
||||
state.min_pen, state.min_brush);
|
||||
}
|
||||
draw_curve(painter, current.values, current.domain, frequency_axis, power_axis,
|
||||
state.visible_range_only, state.interpolation_mode,
|
||||
state.current_pen, state.current_brush);
|
||||
}
|
||||
|
||||
void Spectrum_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
const auto& state = render_properties(view);
|
||||
const auto& published_frame = view.get(impl_->frame);
|
||||
const Spectrum_Frame empty_frame;
|
||||
const Spectrum_Frame& frame = published_frame ? *published_frame : empty_frame;
|
||||
const auto& interaction = view.get(impl_->interaction);
|
||||
const Axis_Transform horizontal = impl_->frequency_axis->transform(view);
|
||||
const Axis_Transform vertical = impl_->power_axis->transform(view);
|
||||
const RectF content = axes_rect(horizontal, vertical);
|
||||
const Axis_Transform frequency_axis = impl_->frequency_axis->transform(view);
|
||||
const Axis_Transform power_axis = impl_->power_axis->transform(view);
|
||||
const RectF content = mapped_rect(frequency_axis, power_axis,
|
||||
frequency_axis.coordinate_range,
|
||||
power_axis.coordinate_range);
|
||||
if (content.empty())
|
||||
return;
|
||||
auto& output = impl_->render_frame;
|
||||
if(state.sweep_region_visible) {
|
||||
const double first = horizontal.coord_to_pixel(state.sweep_frequency_range.origin);
|
||||
const double last = horizontal.coord_to_pixel(state.sweep_frequency_range.target);
|
||||
painter.rect({std::min(first, last), content.y, std::abs(last - first), content.height}, Pen{.style = Line_Style::None}, state.sweep_region_brush);
|
||||
painter.rect(mapped_rect(frequency_axis, power_axis, state.sweep_frequency_range,
|
||||
power_axis.coordinate_range),
|
||||
Pen{.style = Line_Style::None}, state.sweep_region_brush);
|
||||
}
|
||||
if(state.max_hold_visible)
|
||||
draw_curve(painter, frame.maxima, state.frequency_range, horizontal, vertical, state.visible_range_only, state.interpolation_mode, state.max_pen, state.max_brush);
|
||||
if(state.min_hold_visible)
|
||||
draw_curve(painter, frame.minima, state.frequency_range, horizontal, vertical, state.visible_range_only, state.interpolation_mode, state.min_pen, state.min_brush);
|
||||
draw_curve(painter, frame.samples, state.frequency_range, horizontal, vertical, state.visible_range_only, state.interpolation_mode, state.current_pen, state.current_brush);
|
||||
for (int index = 0; output.valid && index < output.active_partitions; ++index)
|
||||
painter.composite(output.layers[static_cast<std::size_t>(index)]);
|
||||
if(state.middle_frequency_pen.enabled()) {
|
||||
const double x = horizontal.coord_to_pixel(state.center_frequency);
|
||||
painter.line({x, content.y}, {x, content.bottom()}, state.middle_frequency_pen);
|
||||
painter.line(mapped_point(frequency_axis, state.center_frequency, power_axis,
|
||||
power_axis.coordinate_range.origin),
|
||||
mapped_point(frequency_axis, state.center_frequency, power_axis,
|
||||
power_axis.coordinate_range.target),
|
||||
state.middle_frequency_pen);
|
||||
}
|
||||
for(std::size_t index = 0; index < interaction.markers.size(); ++index) {
|
||||
const double x = horizontal.coord_to_pixel(interaction.markers[index]);
|
||||
painter.line({x, content.y}, {x, content.bottom()}, static_cast<int>(index) == interaction.selected_marker ? state.selected_marker_pen : state.marker_pen);
|
||||
painter.line(mapped_point(frequency_axis, interaction.markers[index], power_axis,
|
||||
power_axis.coordinate_range.origin),
|
||||
mapped_point(frequency_axis, interaction.markers[index], power_axis,
|
||||
power_axis.coordinate_range.target),
|
||||
static_cast<int>(index) == interaction.selected_marker ? state.selected_marker_pen : state.marker_pen);
|
||||
}
|
||||
if(!frame.samples.empty() && (state.max_marker_visible || state.use_min_marker)) {
|
||||
const auto draw_extreme = [&](bool maximum) {
|
||||
@@ -238,7 +359,7 @@ void Spectrum_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
const std::size_t index = static_cast<std::size_t>(std::distance(frame.samples.begin(), iterator));
|
||||
const double denominator = frame.samples.size() > 1 ? frame.samples.size() - 1.0 : 1.0;
|
||||
const double frequency = state.frequency_range.origin + state.frequency_range.length() * index / denominator;
|
||||
const PointF point{horizontal.coord_to_pixel(frequency), vertical.coord_to_pixel(*iterator)};
|
||||
const PointF point = mapped_point(frequency_axis, frequency, power_axis, *iterator);
|
||||
const Pen& pen = maximum ? state.max_pen : state.min_pen;
|
||||
painter.circle(point, 3.0, pen, Brush{pen.color, Brush_Style::Solid});
|
||||
};
|
||||
@@ -248,7 +369,7 @@ void Spectrum_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
draw_extreme(false);
|
||||
}
|
||||
if(state.tooltip_enabled && interaction.tooltip.active && content.contains(interaction.tooltip.position)) {
|
||||
const double frequency = horizontal.pixel_to_coord(interaction.tooltip.position.x);
|
||||
const double frequency = frequency_axis.point_to_coord(interaction.tooltip.position);
|
||||
bool ok{};
|
||||
const double power = spectrum_power_at(state, frame, frequency, ok);
|
||||
if(ok) {
|
||||
@@ -259,6 +380,7 @@ void Spectrum_Control::paint(Painter& painter, const Render_State_View& view) {
|
||||
painter.text({box.x + 4.0, box.y + 3.0}, text.str(), state.tooltip_font, state.tooltip_text_pen);
|
||||
}
|
||||
}
|
||||
output.partitioner.finish(output.work_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user