/* SPDX-FileCopyrightText: 2023 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once /** \file * \ingroup bli * * Generic algorithms for finding the largest and smallest elements in a span. */ #include #include "BLI_bounds_types.hh" #include "BLI_index_mask.hh" #include "BLI_math_vector.hh" #include "BLI_task.hh" namespace blender { namespace bounds { template [[nodiscard]] inline Bounds merge(const Bounds &a, const Bounds &b) { return {math::min(a.min, b.min), math::max(a.max, b.max)}; } template [[nodiscard]] inline std::optional> merge(const std::optional> &a, const std::optional> &b) { if (a.has_value() && b.has_value()) { return merge(*a, *b); } if (a.has_value()) { return a; } if (b.has_value()) { return b; } return std::nullopt; } /** * Find the smallest and largest values element-wise in the span. */ template [[nodiscard]] inline std::optional> min_max(const Span values) { if (values.is_empty()) { return std::nullopt; } const Bounds init{values.first(), values.first()}; return threading::parallel_reduce( values.index_range(), 1024, init, [&](const IndexRange range, const Bounds &init) { Bounds result = init; for (const int i : range) { math::min_max(values[i], result.min, result.max); } return result; }, [](const Bounds &a, const Bounds &b) { return merge(a, b); }); } template [[nodiscard]] inline std::optional> min_max(const IndexMask &mask, const Span values) { if (values.is_empty() || mask.is_empty()) { return std::nullopt; } if (mask.size() == values.size()) { /* To avoid mask slice/lookup. */ return min_max(values); } const Bounds init{values.first(), values.first()}; return threading::parallel_reduce( mask.index_range(), 1024, init, [&](const IndexRange range, const Bounds &init) { Bounds result = init; mask.slice(range).foreach_index_optimized( [&](const int i) { math::min_max(values[i], result.min, result.max); }); return result; }, [](const Bounds &a, const Bounds &b) { return merge(a, b); }); } /** * Find the smallest and largest values element-wise in the span, adding the radius to each element * first. The template type T is expected to have an addition operator implemented with RadiusT. */ template [[nodiscard]] inline std::optional> min_max_with_radii(const Span values, const Span radii) { BLI_assert(values.size() == radii.size()); if (values.is_empty()) { return std::nullopt; } const Bounds init{values.first(), values.first()}; return threading::parallel_reduce( values.index_range(), 1024, init, [&](const IndexRange range, const Bounds &init) { Bounds result = init; for (const int i : range) { result.min = math::min(values[i] - radii[i], result.min); result.max = math::max(values[i] + radii[i], result.max); } return result; }, [](const Bounds &a, const Bounds &b) { return merge(a, b); }); } } // namespace bounds namespace detail { template [[nodiscard]] inline bool less_or_equal_than(const VecBase &a, const VecBase &b) { for (int i = 0; i < Size; i++) { if (a[i] <= b[i]) { return true; } } return false; } } // namespace detail template inline bool Bounds::is_empty() const { if constexpr (std::is_integral::value || std::is_floating_point::value) { return this->max <= this->min; } else { return detail::less_or_equal_than(this->max, this->min); } } template inline T Bounds::center() const { return math::midpoint(this->min, this->max); } template inline T Bounds::size() const { return math::abs(max - min); } template inline void Bounds::translate(const T &offset) { this->min += offset; this->max += offset; } template inline void Bounds::scale_from_center(const T &scale) { const T center = this->center(); const T new_half_size = this->size() / T(2) * scale; this->min = center - new_half_size; this->max = center + new_half_size; } template inline void Bounds::resize(const T &new_size) { this->min = this->center() - (new_size / T(2)); this->max = this->min + new_size; } template inline void Bounds::recenter(const T &new_center) { const T offset = new_center - this->center(); this->translate(offset); } template template inline void Bounds::pad(const PaddingT &padding) { this->min = this->min - padding; this->max = this->max + padding; } } // namespace blender