Blender V4.3
simplify_curves.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_array_utils.hh"
6#include "BLI_stack.hh"
7
8#include "BKE_curves_utils.hh"
9
11
12namespace blender::geometry {
13
26template<typename T>
28 const Span<T> attribute_data,
29 const int64_t first_index,
30 const int64_t last_index,
31 const int64_t index)
32{
33 const float3 ray_dir = positions[last_index] - positions[first_index];
34 float lambda = 0.0f;
35 if (!math::is_zero(ray_dir)) {
36 lambda = math::dot(ray_dir, positions[index] - positions[first_index]) /
37 math::dot(ray_dir, ray_dir);
38 }
39 const T &from = attribute_data[first_index];
40 const T &to = attribute_data[last_index];
41 const T &value = attribute_data[index];
42 const T &interpolated = math::interpolate(from, to, lambda);
43 return math::distance(value, interpolated);
44}
45
49template<typename T>
50static void ramer_douglas_peucker(const IndexRange range,
51 const Span<float3> positions,
52 const float epsilon,
53 const Span<T> attribute_data,
54 MutableSpan<bool> points_to_delete)
55{
56 /* Mark all points to be kept. */
57 points_to_delete.slice(range).fill(false);
58
60 stack.push(range);
61 while (!stack.is_empty()) {
62 const IndexRange sub_range = stack.pop();
63 /* Skip ranges with less than 3 points. All points are kept. */
64 if (sub_range.size() < 3) {
65 continue;
66 }
67 const IndexRange inside_range = sub_range.drop_front(1).drop_back(1);
68 /* Compute the maximum distance and the corresponding index. */
69 float max_dist = -1.0f;
70 int max_index = -1;
71 for (const int64_t index : inside_range) {
72 const float dist = perpendicular_distance(
73 positions, attribute_data, sub_range.first(), sub_range.last(), index);
74 if (dist > max_dist) {
75 max_dist = dist;
76 max_index = index - sub_range.first();
77 }
78 }
79
80 if (max_dist > epsilon) {
81 /* Found point outside the epsilon-sized strip. The point at `max_index` will be kept, repeat
82 * the search on the left & right side. */
83 stack.push(sub_range.slice(0, max_index + 1));
84 stack.push(sub_range.slice(max_index, sub_range.size() - max_index));
85 }
86 else {
87 /* Points in `sub_range` are inside the epsilon-sized strip. Mark them to be deleted. */
88 points_to_delete.slice(inside_range).fill(true);
89 }
90 }
91}
92
93template<typename T>
94static void curve_simplify(const Span<float3> positions,
95 const bool cyclic,
96 const float epsilon,
97 const Span<T> attribute_data,
98 MutableSpan<bool> points_to_delete)
99{
100 const Vector<IndexRange> selection_ranges = array_utils::find_all_ranges(
101 points_to_delete.as_span(), true);
103 selection_ranges.index_range(), 512, [&](const IndexRange range_of_ranges) {
104 for (const IndexRange range : selection_ranges.as_span().slice(range_of_ranges)) {
105 ramer_douglas_peucker(range, positions, epsilon, attribute_data, points_to_delete);
106 }
107 });
108
109 /* For cyclic curves, handle the last segment separately. */
110 const int points_num = positions.size();
111 if (cyclic && points_num > 2) {
112 const float dist = perpendicular_distance(
113 positions, attribute_data, points_num - 2, 0, points_num - 1);
114 if (dist <= epsilon) {
115 points_to_delete[points_num - 1] = true;
116 }
117 }
118}
119
120void curve_simplify(const Span<float3> positions,
121 const bool cyclic,
122 const float epsilon,
123 const GSpan attribute_data,
124 MutableSpan<bool> points_to_delete)
125
126{
127 bke::attribute_math::convert_to_static_type(attribute_data.type(), [&](auto dummy) {
128 using T = decltype(dummy);
129 if constexpr (std::is_same_v<T, float> || std::is_same_v<T, float2> ||
130 std::is_same_v<T, float3>)
131 {
132 curve_simplify(positions, cyclic, epsilon, attribute_data.typed<T>(), points_to_delete);
133 }
134 });
135}
136
138 const IndexMask &curves_selection,
139 const OffsetIndices<int> points_by_curve,
140 const VArray<bool> &cyclic,
141 const float epsilon,
142 const GSpan attribute_data,
143 IndexMaskMemory &memory)
144{
145 Array<bool> points_to_delete(positions.size(), false);
146 if (epsilon <= 0.0f) {
147 return IndexMask::from_bools(points_to_delete, memory);
148 }
149 bke::curves::fill_points(
150 points_by_curve, curves_selection, true, points_to_delete.as_mutable_span());
151 curves_selection.foreach_index(GrainSize(512), [&](const int64_t curve_i) {
152 const IndexRange points = points_by_curve[curve_i];
153 bke::attribute_math::convert_to_static_type(attribute_data.type(), [&](auto dummy) {
154 using T = decltype(dummy);
155 if constexpr (std::is_same_v<T, float> || std::is_same_v<T, float2> ||
156 std::is_same_v<T, float3>)
157 {
158 curve_simplify(positions.slice(points),
159 cyclic[curve_i],
160 epsilon,
161 attribute_data.typed<T>().slice(points),
162 points_to_delete.as_mutable_span().slice(points));
163 }
164 });
165 });
166 return IndexMask::from_bools(points_to_delete, memory);
167}
168
169} // namespace blender::geometry
Low-level operations for curves.
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
const CPPType & type() const
constexpr int64_t first() const
constexpr IndexRange drop_back(int64_t n) const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr IndexRange slice(int64_t start, int64_t size) const
constexpr IndexRange drop_front(int64_t n) const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:574
constexpr void fill(const T &value) const
Definition BLI_span.hh:518
constexpr Span< T > as_span() const
Definition BLI_span.hh:662
bool is_empty() const
Definition BLI_stack.hh:308
void push(const T &value)
Definition BLI_stack.hh:213
IndexRange index_range() const
void foreach_index(Fn &&fn) const
Vector< IndexRange > find_all_ranges(const Span< T > span, const T &value)
float perpendicular_distance(const Span< float3 > positions, const Span< T > attribute_data, const int64_t first_index, const int64_t last_index, const int64_t index)
static void ramer_douglas_peucker(const IndexRange range, const Span< float3 > positions, const float epsilon, const Span< T > attribute_data, MutableSpan< bool > points_to_delete)
IndexMask simplify_curve_attribute(const Span< float3 > positions, const IndexMask &curves_selection, const OffsetIndices< int > points_by_curve, const VArray< bool > &cyclic, float epsilon, GSpan attribute_data, IndexMaskMemory &memory)
void curve_simplify(const Span< float3 > positions, const bool cyclic, const float epsilon, const GSpan attribute_data, MutableSpan< bool > points_to_delete)
T distance(const T &a, const T &b)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
bool is_zero(const T &a)
T interpolate(const T &a, const T &b, const FactorT &t)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
__int64 int64_t
Definition stdint.h:89