还是一堆bug的状态
This commit is contained in:
@@ -0,0 +1,441 @@
|
||||
#include "Curve.h"
|
||||
#include "Curve_Utils_p.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <memory_resource>
|
||||
#include <system_error>
|
||||
#include "../base/Memory.h"
|
||||
#include "../render/Canvas.h"
|
||||
namespace renderive {
|
||||
enum class Curve_Input_Mode : std::uint8_t {
|
||||
None,
|
||||
Samples,
|
||||
Points
|
||||
};
|
||||
struct Curve_Input_Data : Input_Data {};
|
||||
struct Curve_Render_State : Render_State, Curve_Prop {};
|
||||
|
||||
struct Curve_Input_Block {
|
||||
std::uint64_t sequence{};
|
||||
Curve_Input_Mode mode = Curve_Input_Mode::None;
|
||||
std::pmr::vector<double> samples{memory_resource(Memory_Domain::Curve)};
|
||||
std::pmr::vector<PointF> points{memory_resource(Memory_Domain::Curve)};
|
||||
std::shared_ptr<Update_State> completion_state;
|
||||
|
||||
static std::shared_ptr<Curve_Input_Block> samples_block(std::span<const double> values, std::shared_ptr<Update_State> state = {}) {
|
||||
auto block = std::make_shared<Curve_Input_Block>();
|
||||
block->mode = Curve_Input_Mode::Samples;
|
||||
block->samples.assign(values.begin(), values.end());
|
||||
block->completion_state = std::move(state);
|
||||
return block;
|
||||
}
|
||||
|
||||
static std::shared_ptr<Curve_Input_Block> samples_block(std::pmr::vector<double>&& values, std::shared_ptr<Update_State> state = {}) {
|
||||
auto block = std::make_shared<Curve_Input_Block>();
|
||||
block->mode = Curve_Input_Mode::Samples;
|
||||
block->samples = std::move(values);
|
||||
block->completion_state = std::move(state);
|
||||
return block;
|
||||
}
|
||||
|
||||
static std::shared_ptr<Curve_Input_Block> points_block(std::span<const PointF> values, std::shared_ptr<Update_State> state = {}) {
|
||||
auto block = std::make_shared<Curve_Input_Block>();
|
||||
block->mode = Curve_Input_Mode::Points;
|
||||
block->points.assign(values.begin(), values.end());
|
||||
block->completion_state = std::move(state);
|
||||
return block;
|
||||
}
|
||||
|
||||
static std::shared_ptr<Curve_Input_Block> points_block(std::pmr::vector<PointF>&& values, std::shared_ptr<Update_State> state = {}) {
|
||||
auto block = std::make_shared<Curve_Input_Block>();
|
||||
block->mode = Curve_Input_Mode::Points;
|
||||
block->points = std::move(values);
|
||||
block->completion_state = std::move(state);
|
||||
return block;
|
||||
}
|
||||
};
|
||||
|
||||
struct Curve_Axis_Geometry_Key {
|
||||
Orientation orientation = Orientation::Horizontal;
|
||||
Range coord_range{0.0, 1.0};
|
||||
Range pixel_range{0.0, 1.0};
|
||||
|
||||
bool operator==(const Curve_Axis_Geometry_Key& other) const {
|
||||
return orientation == other.orientation && coord_range == other.coord_range && pixel_range == other.pixel_range;
|
||||
}
|
||||
};
|
||||
|
||||
struct Curve_Geometry_Key {
|
||||
std::uint64_t input_generation{};
|
||||
std::uint64_t state_version{};
|
||||
Curve_Input_Mode mode = Curve_Input_Mode::None;
|
||||
Line_Sampling_Mode sample_mode = Line_Sampling_Mode::Pixel_Peak_Envelope;
|
||||
Line_Interpolation_Mode interpolation_mode = Line_Interpolation_Mode::Linear_Display;
|
||||
bool visible_range_only{};
|
||||
Range data_range{0.0, 1.0};
|
||||
std::size_t sample_count{};
|
||||
std::size_t point_count{};
|
||||
Curve_Axis_Geometry_Key domain;
|
||||
Curve_Axis_Geometry_Key value;
|
||||
|
||||
bool operator==(const Curve_Geometry_Key& other) const {
|
||||
return input_generation == other.input_generation &&
|
||||
state_version == other.state_version &&
|
||||
mode == other.mode &&
|
||||
sample_mode == other.sample_mode &&
|
||||
interpolation_mode == other.interpolation_mode &&
|
||||
visible_range_only == other.visible_range_only &&
|
||||
data_range == other.data_range &&
|
||||
sample_count == other.sample_count &&
|
||||
point_count == other.point_count &&
|
||||
domain == other.domain &&
|
||||
value == other.value;
|
||||
}
|
||||
};
|
||||
|
||||
struct Curve_Geometry_Result {
|
||||
Curve_Geometry_Result() : points(memory_resource(Memory_Domain::Curve)), update_states(memory_resource(Memory_Domain::Update_Completion)) {}
|
||||
Axis_Mapping_2D mapping;
|
||||
std::pmr::vector<PointF> points;
|
||||
std::pmr::vector<std::shared_ptr<Update_State>> update_states;
|
||||
};
|
||||
|
||||
struct Curve_Geometry_Request {
|
||||
Curve_Geometry_Request() : samples(memory_resource(Memory_Domain::Curve)), data_points(memory_resource(Memory_Domain::Curve)), update_states(memory_resource(Memory_Domain::Update_Completion)) {}
|
||||
Curve_Input_Mode mode = Curve_Input_Mode::None;
|
||||
Axis_Mapping_2D mapping;
|
||||
Range data_range{0.0, 1.0};
|
||||
bool visible_range_only{};
|
||||
Line_Interpolation_Mode interpolation_mode = Line_Interpolation_Mode::Linear_Display;
|
||||
Line_Sampling_Mode sample_mode = Line_Sampling_Mode::Pixel_Peak_Envelope;
|
||||
std::pmr::vector<double> samples;
|
||||
std::pmr::vector<PointF> data_points;
|
||||
std::pmr::vector<std::shared_ptr<Update_State>> update_states;
|
||||
};
|
||||
|
||||
static Curve_Axis_Geometry_Key curve_axis_key(const Axis_Frame_Snapshot& axis) {
|
||||
return {axis.orientation, axis.coord_range, axis.pixel_range};
|
||||
}
|
||||
|
||||
static void complete_curve_update_states(Curve_Geometry_Request& request, std::error_code error, Update_Outcome outcome) {
|
||||
for (auto& state : request.update_states) {
|
||||
if (state)
|
||||
state->complete_all(error, outcome);
|
||||
}
|
||||
request.update_states.clear();
|
||||
}
|
||||
|
||||
static void complete_curve_result_states(Curve_Geometry_Result& result, std::error_code error, Update_Outcome outcome) {
|
||||
for (const auto& state : result.update_states) {
|
||||
if (state)
|
||||
state->complete_all(error, outcome);
|
||||
}
|
||||
result.update_states.clear();
|
||||
}
|
||||
|
||||
static void build_curve_geometry(Curve_Geometry_Result& result, const Curve_Geometry_Request& request) {
|
||||
std::pmr::vector<PointF>& points = result.points;
|
||||
if (request.mode == Curve_Input_Mode::Points) {
|
||||
points.reserve(request.data_points.size());
|
||||
for (std::size_t i = 0; i < request.data_points.size(); ++i)
|
||||
points.push_back(request.mapping.map(request.data_points[i]));
|
||||
return;
|
||||
}
|
||||
if (request.mode != Curve_Input_Mode::Samples || request.samples.size() < 2 || request.data_range.length() == 0.0)
|
||||
return;
|
||||
if (request.sample_mode == Line_Sampling_Mode::Pixel_Peak_Envelope) {
|
||||
points = Curve_Utils::create_peak_bucket_points(request.mapping, request.data_range, static_cast<int>(request.samples.size()), static_cast<int>(request.samples.size()), request.visible_range_only, [&request](int i) {
|
||||
return request.samples[static_cast<std::size_t>(i)];
|
||||
}, memory_resource(Memory_Domain::Curve));
|
||||
return;
|
||||
}
|
||||
Line_Sample_Window window = line_sample_window(request.mapping.domain, request.data_range, static_cast<int>(request.samples.size()), static_cast<int>(request.samples.size()), request.visible_range_only);
|
||||
if (!window)
|
||||
return;
|
||||
points.reserve(static_cast<std::size_t>(std::max(0, window.sample_count)));
|
||||
double denominator = static_cast<double>(std::max(window.sample_count - 1, 1));
|
||||
for (int i = 0; i < window.sample_count; ++i) {
|
||||
double index = window.first_index + (window.last_index - window.first_index) * static_cast<double>(i) / denominator;
|
||||
double coord = source_index_to_coord(request.data_range, static_cast<int>(request.samples.size()), index);
|
||||
double value = interpolate_line_value(index, static_cast<int>(request.samples.size()), request.interpolation_mode, [&request](int sample_index) {
|
||||
return request.samples[static_cast<std::size_t>(sample_index)];
|
||||
});
|
||||
points.push_back(request.mapping.map(coord, value));
|
||||
}
|
||||
}
|
||||
|
||||
struct Curve_Private : Typed_Render_Data<Curve, Curve_Render_State, Curve_Input_Data> {
|
||||
static constexpr std::size_t Input_Queue_Capacity = 8;
|
||||
std::pmr::vector<double> samples{memory_resource(Memory_Domain::Curve)};
|
||||
std::pmr::vector<PointF> data_points{memory_resource(Memory_Domain::Curve)};
|
||||
Curve_Input_Mode mode = Curve_Input_Mode::None;
|
||||
std::array<std::atomic<std::shared_ptr<Curve_Input_Block>>, Input_Queue_Capacity> input_slots;
|
||||
std::atomic<std::uint64_t> input_write_sequence{0};
|
||||
std::atomic<std::uint64_t> input_consumed_sequence{0};
|
||||
std::uint64_t input_read_sequence{};
|
||||
std::pmr::vector<std::shared_ptr<Update_State>> pending_geometry_update_states{memory_resource(Memory_Domain::Update_Completion)};
|
||||
Curve_Geometry_Result geometry_result;
|
||||
bool has_geometry_result{};
|
||||
Curve_Geometry_Key submitted_geometry_key;
|
||||
bool has_submitted_geometry_key{};
|
||||
std::uint64_t input_generation{};
|
||||
|
||||
~Curve_Private() override {
|
||||
complete_curve_result_states(geometry_result, std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
|
||||
for (auto& slot : input_slots) {
|
||||
auto block = slot.exchange({}, std::memory_order_acq_rel);
|
||||
if (block && block->completion_state)
|
||||
block->completion_state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
|
||||
}
|
||||
for (auto& state : pending_geometry_update_states) {
|
||||
if (state)
|
||||
state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
|
||||
}
|
||||
pending_geometry_update_states.clear();
|
||||
}
|
||||
|
||||
void enqueue_input(std::shared_ptr<Curve_Input_Block> block) {
|
||||
if (!block)
|
||||
return;
|
||||
std::uint64_t sequence = input_write_sequence.fetch_add(1, std::memory_order_acq_rel);
|
||||
block->sequence = sequence;
|
||||
auto replaced = input_slots[sequence % Input_Queue_Capacity].exchange(block, std::memory_order_acq_rel);
|
||||
if (replaced && replaced->sequence >= input_consumed_sequence.load(std::memory_order_acquire) && replaced->completion_state)
|
||||
replaced->completion_state->complete_all({}, Update_Outcome::Superseded);
|
||||
if (block->completion_state)
|
||||
block->completion_state->complete_until(Update_Stage::Input_Released);
|
||||
if (q())
|
||||
q()->mark_render_dirty();
|
||||
}
|
||||
|
||||
void consume_input_queue() {
|
||||
std::uint64_t write_sequence = input_write_sequence.load(std::memory_order_acquire);
|
||||
std::uint64_t first_sequence = write_sequence > Input_Queue_Capacity ? write_sequence - Input_Queue_Capacity : 0;
|
||||
input_read_sequence = std::max(input_read_sequence, first_sequence);
|
||||
std::shared_ptr<Curve_Input_Block> selected;
|
||||
for (std::uint64_t sequence = input_read_sequence; sequence < write_sequence; ++sequence) {
|
||||
auto block = input_slots[sequence % Input_Queue_Capacity].exchange({}, std::memory_order_acq_rel);
|
||||
if (!block || block->sequence != sequence)
|
||||
continue;
|
||||
if (selected && selected->completion_state)
|
||||
selected->completion_state->complete_all({}, Update_Outcome::Superseded);
|
||||
selected = std::move(block);
|
||||
input_consumed_sequence.store(sequence + 1, std::memory_order_release);
|
||||
}
|
||||
input_read_sequence = write_sequence;
|
||||
if (!selected)
|
||||
return;
|
||||
mode = selected->mode;
|
||||
samples = std::move(selected->samples);
|
||||
data_points = std::move(selected->points);
|
||||
++input_generation;
|
||||
if (selected->completion_state) {
|
||||
selected->completion_state->complete_until(Update_Stage::Committed);
|
||||
pending_geometry_update_states.push_back(std::move(selected->completion_state));
|
||||
}
|
||||
}
|
||||
|
||||
void prepare_data(const Plot_Render_Snapshot& snapshot) override {
|
||||
(void)snapshot;
|
||||
sync_state_pipeline();
|
||||
consume_input_queue();
|
||||
submit_geometry_if_needed();
|
||||
}
|
||||
void submit_geometry_if_needed() {
|
||||
Curve_Render_State* s = render_state();
|
||||
auto domain_axis = s->domain_axis.lock();
|
||||
auto value_axis = s->value_axis.lock();
|
||||
if (!domain_axis || !value_axis) {
|
||||
for (auto& state : pending_geometry_update_states) {
|
||||
if (state)
|
||||
state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
|
||||
}
|
||||
pending_geometry_update_states.clear();
|
||||
return;
|
||||
}
|
||||
Axis_Mapping_2D mapping = Axis_Render_Access::mapping(domain_axis.get(), value_axis.get());
|
||||
Curve_Geometry_Key key;
|
||||
key.input_generation = input_generation;
|
||||
key.state_version = s->version;
|
||||
key.mode = mode;
|
||||
key.sample_mode = s->sample_mode;
|
||||
key.interpolation_mode = s->interpolation_mode;
|
||||
key.visible_range_only = s->visible_range_only;
|
||||
key.data_range = s->data_range;
|
||||
key.sample_count = samples.size();
|
||||
key.point_count = data_points.size();
|
||||
key.domain = curve_axis_key(mapping.domain);
|
||||
key.value = curve_axis_key(mapping.value);
|
||||
if (has_submitted_geometry_key && key == submitted_geometry_key)
|
||||
return;
|
||||
submitted_geometry_key = key;
|
||||
has_submitted_geometry_key = true;
|
||||
auto request = std::make_shared<Curve_Geometry_Request>();
|
||||
request->mode = mode;
|
||||
request->mapping = mapping;
|
||||
request->data_range = s->data_range;
|
||||
request->visible_range_only = s->visible_range_only;
|
||||
request->interpolation_mode = s->interpolation_mode;
|
||||
request->sample_mode = s->sample_mode;
|
||||
request->samples = samples;
|
||||
request->data_points = data_points;
|
||||
request->update_states = std::move(pending_geometry_update_states);
|
||||
pending_geometry_update_states = std::pmr::vector<std::shared_ptr<Update_State>>(memory_resource(Memory_Domain::Update_Completion));
|
||||
geometry_result = Curve_Geometry_Result();
|
||||
geometry_result.mapping = request->mapping;
|
||||
build_curve_geometry(geometry_result, *request);
|
||||
geometry_result.update_states = std::move(request->update_states);
|
||||
has_geometry_result = true;
|
||||
}
|
||||
void draw(Canvas& canvas, const Plot_Render_Snapshot& snapshot) override {
|
||||
Curve_Render_State* s = render_state();
|
||||
if (!has_geometry_result)
|
||||
return;
|
||||
if (snapshot.frame_update_states) {
|
||||
for (const auto& state : geometry_result.update_states)
|
||||
snapshot.frame_update_states->push_back(state);
|
||||
geometry_result.update_states.clear();
|
||||
}
|
||||
const auto& points = geometry_result.points;
|
||||
if (points.empty())
|
||||
return;
|
||||
canvas.save();
|
||||
canvas.set_antialias(s->antialias);
|
||||
Curve_Utils::draw_polyline(&canvas, points, s->pen);
|
||||
Curve_Utils::draw_fill_to_value(&canvas, points, geometry_result.mapping, geometry_result.mapping.value.coord_range.target, s->fill_brush);
|
||||
canvas.restore();
|
||||
}
|
||||
};
|
||||
RENDERIVE_DEFINE_RENDERABLE_BINDING(Curve, Curve_Private, Curve_Render_State, Curve_Input_Data, "Curve")
|
||||
void Curve::give_samples(std::span<const double> values) {
|
||||
if (!ok())
|
||||
return;
|
||||
d()->enqueue_input(Curve_Input_Block::samples_block(values));
|
||||
}
|
||||
void Curve::give_samples(std::pmr::vector<double>&& values) {
|
||||
if (!ok())
|
||||
return;
|
||||
d()->enqueue_input(Curve_Input_Block::samples_block(std::move(values)));
|
||||
}
|
||||
Update_Ticket Curve::submit_samples(std::span<const double> values) {
|
||||
auto state = make_update_state();
|
||||
if (!ok()) {
|
||||
state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
|
||||
return Update_Ticket(state);
|
||||
}
|
||||
d()->enqueue_input(Curve_Input_Block::samples_block(values, state));
|
||||
return Update_Ticket(state);
|
||||
}
|
||||
Update_Ticket Curve::submit_samples(std::pmr::vector<double>&& values) {
|
||||
auto state = make_update_state();
|
||||
if (!ok()) {
|
||||
state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
|
||||
return Update_Ticket(state);
|
||||
}
|
||||
d()->enqueue_input(Curve_Input_Block::samples_block(std::move(values), state));
|
||||
return Update_Ticket(state);
|
||||
}
|
||||
void Curve::give_points(std::span<const PointF> points) {
|
||||
if (!ok())
|
||||
return;
|
||||
d()->enqueue_input(Curve_Input_Block::points_block(points));
|
||||
}
|
||||
void Curve::give_points(std::pmr::vector<PointF>&& points) {
|
||||
if (!ok())
|
||||
return;
|
||||
d()->enqueue_input(Curve_Input_Block::points_block(std::move(points)));
|
||||
}
|
||||
Update_Ticket Curve::submit_points(std::span<const PointF> points) {
|
||||
auto state = make_update_state();
|
||||
if (!ok()) {
|
||||
state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
|
||||
return Update_Ticket(state);
|
||||
}
|
||||
d()->enqueue_input(Curve_Input_Block::points_block(points, state));
|
||||
return Update_Ticket(state);
|
||||
}
|
||||
Update_Ticket Curve::submit_points(std::pmr::vector<PointF>&& points) {
|
||||
auto state = make_update_state();
|
||||
if (!ok()) {
|
||||
state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
|
||||
return Update_Ticket(state);
|
||||
}
|
||||
d()->enqueue_input(Curve_Input_Block::points_block(std::move(points), state));
|
||||
return Update_Ticket(state);
|
||||
}
|
||||
RENDERIVE_DEFINE_WEAK_STATE_PROP(Curve, Curve_Render_State, Abs_Axis, domain_axis)
|
||||
RENDERIVE_DEFINE_WEAK_STATE_PROP(Curve, Curve_Render_State, Abs_Axis, value_axis)
|
||||
Range Curve::data_range() {
|
||||
return d()->edit_state_value(&Curve_Render_State::data_range);
|
||||
}
|
||||
void Curve::set_data_range(Range value) {
|
||||
if (value.length() == 0.0)
|
||||
return;
|
||||
d()->set_state_value(&Curve_Render_State::data_range, value);
|
||||
}
|
||||
RENDERIVE_DEFINE_STATE_PROP(Curve, Curve_Render_State, Pen, pen)
|
||||
RENDERIVE_DEFINE_STATE_PROP(Curve, Curve_Render_State, Brush, fill_brush)
|
||||
RENDERIVE_DEFINE_STATE_PROP(Curve, Curve_Render_State, bool, visible_range_only)
|
||||
RENDERIVE_DEFINE_STATE_PROP(Curve, Curve_Render_State, bool, antialias)
|
||||
RENDERIVE_DEFINE_STATE_PROP(Curve, Curve_Render_State, Line_Interpolation_Mode, interpolation_mode)
|
||||
RENDERIVE_DEFINE_STATE_PROP(Curve, Curve_Render_State, Line_Sampling_Mode, sample_mode)
|
||||
Curve::Builder::Builder(std::shared_ptr<Renderable> parent, std::shared_ptr<Abs_Axis> domain_axis, std::shared_ptr<Abs_Axis> value_axis) {
|
||||
ASSERT(parent, "Curve build error! parent == nullptr");
|
||||
ASSERT(domain_axis && value_axis, "Curve build error! axis == nullptr");
|
||||
ASSERT(domain_axis->attached_to_plot(), "Curve build error! domain_axis->plot == nullptr");
|
||||
ASSERT(domain_axis->shares_plot_with(*value_axis), "Curve build error! domain_axis->plot != value_axis->plot");
|
||||
ASSERT(parent->shares_plot_with(*domain_axis), "Curve build error! parent->plot != axis->plot");
|
||||
ASSERT(domain_axis->orientation() != value_axis->orientation(), "Curve build error! axes must be orthogonal");
|
||||
this->parent = parent;
|
||||
this->domain_axis = domain_axis;
|
||||
this->value_axis = value_axis;
|
||||
}
|
||||
std::shared_ptr<Curve> Curve::Builder::build() {
|
||||
auto ret = renderive::make_shared<Curve>();
|
||||
ret->init(parent);
|
||||
Curve_Private* pd = ret->d();
|
||||
Curve_Render_State* sc = reinterpret_cast<Curve_Render_State*>(pd->state(State_Edit));
|
||||
PROP_R(std::weak_ptr<Abs_Axis>, domain_axis)
|
||||
PROP_R(std::weak_ptr<Abs_Axis>, value_axis)
|
||||
PROP_R(Range, data_range)
|
||||
PROP_R(Pen, pen)
|
||||
PROP_R(Brush, fill_brush)
|
||||
PROP_R(bool, visible_range_only)
|
||||
PROP_R(bool, antialias)
|
||||
PROP_R(Line_Interpolation_Mode, interpolation_mode)
|
||||
PROP_R(Line_Sampling_Mode, sample_mode)
|
||||
if (sc->data_range.length() == 0.0)
|
||||
sc->data_range = {0.0, 1.0};
|
||||
return ret;
|
||||
}
|
||||
void Curve_Utils::draw_polyline(Canvas* canvas, std::span<const PointF> points, const Pen& pen) {
|
||||
if (points.empty() || pen.style == Line_Style::None)
|
||||
return;
|
||||
canvas->set_pen(pen);
|
||||
canvas->draw_polyline(points);
|
||||
}
|
||||
void Curve_Utils::draw_fill_to_value(Canvas* canvas, std::span<const PointF> points, const Axis_Mapping_2D& mapping, double baseline_value, const Brush& brush) {
|
||||
if (points.empty() || brush.style == Brush_Style::None)
|
||||
return;
|
||||
double first_domain = mapping.domain.pixel_to_coord(mapping.domain_pixel(points.front()));
|
||||
double last_domain = mapping.domain.pixel_to_coord(mapping.domain_pixel(points.back()));
|
||||
std::pmr::vector<PointF> fill_points(points.size() + 2, frame_memory_resource());
|
||||
fill_points.front() = mapping.map(first_domain, baseline_value);
|
||||
fill_points.back() = mapping.map(last_domain, baseline_value);
|
||||
for (int i = 0; i < points.size(); ++i)
|
||||
fill_points[i + 1] = points[i];
|
||||
canvas->set_brush(brush);
|
||||
canvas->fill_polygon(std::span<const PointF>(fill_points.data(), fill_points.size()));
|
||||
}
|
||||
void Curve_Utils::append_peak_point(std::pmr::vector<PointF>& points, const Axis_Mapping_2D& mapping, const Range& data_range, int total_count, int index, double value) {
|
||||
if (index < 0)
|
||||
return;
|
||||
PointF point = mapping.map(source_index_to_coord(data_range, total_count, index), value);
|
||||
if (!points.empty() && points.back().x == point.x && points.back().y == point.y)
|
||||
return;
|
||||
points.push_back(point);
|
||||
}
|
||||
} // namespace renderive
|
||||
Reference in New Issue
Block a user