#pragma once #include #include #include #include #include #include #include #include "../Axis/Abs_Axis_p.h" #include "../base/Frame_Memory.h" #include "../plottable/Interpolation_p.h" #include "Curve.h" namespace renderive { struct Pixel_Envelope_Bucket { bool has_samples = false; int first_index = -1; int last_index = -1; int minimum_index = -1; int maximum_index = -1; double minimum_value = std::numeric_limits::max(); double maximum_value = std::numeric_limits::lowest(); }; class Curve_Utils { public: template static std::pmr::vector create_points(int count, Point_At point_at, std::pmr::memory_resource* resource = frame_memory_resource()) { std::pmr::vector points(resource); points.reserve(count); for (int i = 0; i < count; ++i) points.push_back(point_at(i)); return points; } template static std::pmr::vector build_resampled_points(Abs_Axis* domain_axis, Abs_Axis* value_axis, const Range& sample_domain_range, int source_sample_count, int available_sample_count, bool visible_range_only, Line_Interpolation_Mode mode, Value_At value_at) { return build_sample_line_points(Axis_Render_Access::mapping(domain_axis, value_axis), sample_domain_range, source_sample_count, available_sample_count, visible_range_only, mode, value_at, frame_memory_resource()); } template static std::pmr::vector build_resampled_points(const Axis_Mapping_2D& mapping, const Range& sample_domain_range, int source_sample_count, int available_sample_count, bool visible_range_only, Line_Interpolation_Mode mode, Value_At value_at, std::pmr::memory_resource* resource = frame_memory_resource()) { return build_sample_line_points(mapping, sample_domain_range, source_sample_count, available_sample_count, visible_range_only, mode, value_at, resource); } template static std::pmr::vector build_pixel_envelope_points(const Axis_Mapping_2D& mapping, const Range& sample_domain_range, int source_sample_count, int available_sample_count, bool visible_range_only, Value_At value_at, std::pmr::memory_resource* resource = frame_memory_resource()) { Visible_Sample_Window window = compute_visible_sample_window(mapping.domain, sample_domain_range, source_sample_count, available_sample_count, visible_range_only); if (!window) return {}; double first_index = std::clamp(std::min(window.first_index, window.last_index), 0.0, static_cast(available_sample_count - 1)); double last_index = std::clamp(std::max(window.first_index, window.last_index), first_index, static_cast(available_sample_count - 1)); int first_source = std::clamp(static_cast(std::ceil(first_index)), 0, available_sample_count - 1); int last_source = std::clamp(static_cast(std::floor(last_index)), first_source, available_sample_count - 1); double first_pixel = mapping.domain.coord_to_pixel(source_index_to_coordinate(sample_domain_range, source_sample_count, first_index)); double last_pixel = mapping.domain.coord_to_pixel(source_index_to_coordinate(sample_domain_range, source_sample_count, last_index)); int pixel_floor_min = static_cast(std::floor(std::min(first_pixel, last_pixel))); int pixel_floor_max = static_cast(std::floor(std::max(first_pixel, last_pixel))); int bucket_count = std::max(1, pixel_floor_max - pixel_floor_min + 1); bucket_count = std::min(bucket_count, mapping.domain.pixel_size() + 1); std::pmr::vector buckets(bucket_count, resource); for (int i = first_source; i <= last_source; ++i) { double value = value_at(i); if (!std::isfinite(value)) continue; double coord = source_index_to_coordinate(sample_domain_range, source_sample_count, i); double pixel = mapping.domain.coord_to_pixel(coord); int bucket_index = std::clamp(static_cast(std::floor(pixel)) - pixel_floor_min, 0, bucket_count - 1); Pixel_Envelope_Bucket& bucket = buckets[bucket_index]; if (!bucket.has_samples) { bucket.has_samples = true; bucket.first_index = i; bucket.minimum_index = i; bucket.maximum_index = i; bucket.minimum_value = value; bucket.maximum_value = value; } bucket.last_index = i; if (value < bucket.minimum_value) { bucket.minimum_value = value; bucket.minimum_index = i; } if (value > bucket.maximum_value) { bucket.maximum_value = value; bucket.maximum_index = i; } } std::pmr::vector points(resource); points.reserve(bucket_count * 4 + 2); double last_appended_index = -std::numeric_limits::infinity(); auto append_index = [&](double index) { if (std::abs(index - last_appended_index) <= 1e-9) return; double value = interpolate_sample_value(index, available_sample_count, Line_Interpolation_Mode::Linear_Value, value_at); if (!std::isfinite(value)) return; PointF point = mapping.map(source_index_to_coordinate(sample_domain_range, source_sample_count, index), value); if (!points.empty() && points.back().x == point.x && points.back().y == point.y) return; points.push_back(point); last_appended_index = index; }; append_index(first_index); int first_bucket = std::clamp(static_cast(std::floor(first_pixel)) - pixel_floor_min, 0, bucket_count - 1); int last_bucket = std::clamp(static_cast(std::floor(last_pixel)) - pixel_floor_min, 0, bucket_count - 1); bool bucket_forward = first_bucket <= last_bucket; for (int b = bucket_forward ? 0 : bucket_count - 1; bucket_forward ? b < bucket_count : b >= 0; bucket_forward ? ++b : --b) { const Pixel_Envelope_Bucket& bucket = buckets[b]; if (!bucket.has_samples) continue; std::array indexes{bucket.first_index, bucket.minimum_index, bucket.maximum_index, bucket.last_index}; std::sort(indexes.begin(), indexes.end()); auto end = std::unique(indexes.begin(), indexes.end()); for (auto it = indexes.begin(); it != end; ++it) append_index(static_cast(*it)); } append_index(last_index); return points; } template static std::pmr::vector build_pixel_envelope_points(Abs_Axis* domain_axis, Abs_Axis* value_axis, const Range& sample_domain_range, int source_sample_count, int available_sample_count, bool visible_range_only, Value_At value_at) { return build_pixel_envelope_points(Axis_Render_Access::mapping(domain_axis, value_axis), sample_domain_range, source_sample_count, available_sample_count, visible_range_only, value_at, frame_memory_resource()); } static void draw_polyline(Canvas* painter, std::span points, const Pen& pen); static void draw_fill_to_value(Canvas* painter, std::span points, const Axis_Mapping_2D& mapping, double baseline_value, const Brush& brush); private: static void append_envelope_point(std::pmr::vector& points, const Axis_Mapping_2D& mapping, const Range& sample_domain_range, int source_sample_count, int index, double value); }; } // namespace renderive