Blender V5.0
curve_decimate.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "DNA_curve_types.h"
10
11#include "BLI_heap.h"
12#include "BLI_math_vector.h"
13#include "MEM_guardedalloc.h"
14
15#include "BKE_curve.hh"
16
17extern "C" {
18#include "curve_fit_nd.h"
19}
20
21#include <cstring>
22
23#include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */
24
25struct Knot {
27 uint point_index; /* Index in point array. */
28 uint knot_index; /* Index in knot array. */
29 float tan[2][3];
30 float handles[2];
31
35
36#ifndef NDEBUG
37 const float *co;
38#endif
39};
40
41struct Removal {
43 /* handles for prev/next knots */
44 float handles[2];
45};
46
47static float knot_remove_error_value(const float tan_l[3],
48 const float tan_r[3],
49 const float (*points)[3],
50 const uint points_len,
51 /* avoid having to re-calculate again */
52 float r_handle_factors[2])
53{
54 const uint dims = 3;
55 float error_sq = FLT_MAX;
56 uint error_sq_index;
57 float handle_factors[2][3];
58
59 curve_fit_cubic_to_points_single_fl(&points[0][0],
60 points_len,
61 nullptr,
62 dims,
63 0.0f,
64 tan_l,
65 tan_r,
66 handle_factors[0],
67 handle_factors[1],
68 &error_sq,
69 &error_sq_index);
70
71 sub_v3_v3(handle_factors[0], points[0]);
72 r_handle_factors[0] = dot_v3v3(tan_l, handle_factors[0]);
73
74 sub_v3_v3(handle_factors[1], points[points_len - 1]);
75 r_handle_factors[1] = dot_v3v3(tan_r, handle_factors[1]);
76
77 return error_sq;
78}
79
81 Heap *heap, const float (*points)[3], const uint points_len, Knot *k, const float error_sq_max)
82{
84 float handles[2];
85
86#ifndef NDEBUG
87 BLI_assert(equals_v3v3(points[k->prev->point_index], k->prev->co));
88 BLI_assert(equals_v3v3(points[k->next->point_index], k->next->co));
89#endif
90
91 const float (*points_offset)[3];
92 uint points_offset_len;
93
94 if (k->prev->point_index < k->next->point_index) {
95 points_offset = &points[k->prev->point_index];
96 points_offset_len = (k->next->point_index - k->prev->point_index) + 1;
97 }
98 else {
99 points_offset = &points[k->prev->point_index];
100 points_offset_len = ((k->next->point_index + points_len) - k->prev->point_index) + 1;
101 }
102
103 const float cost_sq = knot_remove_error_value(
104 k->prev->tan[1], k->next->tan[0], points_offset, points_offset_len, handles);
105
106 if (cost_sq < error_sq_max) {
107 Removal *r;
108 if (k->heap_node) {
109 r = static_cast<Removal *>(BLI_heap_node_ptr(k->heap_node));
110 }
111 else {
112 r = MEM_mallocN<Removal>(__func__);
113 r->knot_index = k->knot_index;
114 }
115
117
118 BLI_heap_insert_or_update(heap, &k->heap_node, cost_sq, r);
119 }
120 else {
121 if (k->heap_node) {
122 Removal *r;
123 r = static_cast<Removal *>(BLI_heap_node_ptr(k->heap_node));
124 BLI_heap_remove(heap, k->heap_node);
125
126 MEM_freeN(r);
127
128 k->heap_node = nullptr;
129 }
130 }
131}
132
133static void curve_decimate(const float (*points)[3],
134 const uint points_len,
135 Knot *knots,
136 const uint knots_len,
137 float error_sq_max,
138 const uint error_target_len)
139{
140 Heap *heap = BLI_heap_new_ex(knots_len);
141 for (uint i = 0; i < knots_len; i++) {
142 Knot *k = &knots[i];
143 if (k->can_remove) {
144 knot_remove_error_recalculate(heap, points, points_len, k, error_sq_max);
145 }
146 }
147
148 uint knots_len_remaining = knots_len;
149
150 while ((knots_len_remaining > error_target_len) && (BLI_heap_is_empty(heap) == false)) {
151 Knot *k;
152
153 {
154 Removal *r = static_cast<Removal *>(BLI_heap_pop_min(heap));
155 k = &knots[r->knot_index];
156 k->heap_node = nullptr;
157 k->prev->handles[1] = r->handles[0];
158 k->next->handles[0] = r->handles[1];
159 MEM_freeN(r);
160 }
161
162 Knot *k_prev = k->prev;
163 Knot *k_next = k->next;
164
165 /* remove ourselves */
166 k_next->prev = k_prev;
167 k_prev->next = k_next;
168
169 k->next = nullptr;
170 k->prev = nullptr;
171 k->is_removed = true;
172
173 if (k_prev->can_remove) {
174 knot_remove_error_recalculate(heap, points, points_len, k_prev, error_sq_max);
175 }
176
177 if (k_next->can_remove) {
178 knot_remove_error_recalculate(heap, points, points_len, k_next, error_sq_max);
179 }
180
181 knots_len_remaining -= 1;
182 }
183
185}
186
188 const uint bezt_array_len,
189 const uint resolu,
190 const bool is_cyclic,
191 const char flag_test,
192 const char flag_set,
193 const float error_sq_max,
194 const uint error_target_len)
195{
196 const uint bezt_array_last = bezt_array_len - 1;
197 const uint points_len = BKE_curve_calc_coords_axis_len(bezt_array_len, resolu, is_cyclic, true);
198
199 float (*points)[3] = MEM_malloc_arrayN<float[3]>(points_len * (is_cyclic ? 2 : 1), __func__);
200
202 bezt_array, bezt_array_len, resolu, is_cyclic, false, 0, sizeof(float[3]), &points[0][0]);
204 bezt_array, bezt_array_len, resolu, is_cyclic, false, 1, sizeof(float[3]), &points[0][1]);
206 bezt_array, bezt_array_len, resolu, is_cyclic, false, 2, sizeof(float[3]), &points[0][2]);
207
208 const uint knots_len = bezt_array_len;
209 Knot *knots = MEM_malloc_arrayN<Knot>(bezt_array_len, __func__);
210
211 if (is_cyclic) {
212 memcpy(points[points_len], points[0], sizeof(float[3]) * points_len);
213 }
214
215 for (uint i = 0; i < bezt_array_len; i++) {
216 knots[i].heap_node = nullptr;
217 knots[i].can_remove = (bezt_array[i].f2 & flag_test) != 0;
218 knots[i].is_removed = false;
219 knots[i].next = &knots[i + 1];
220 knots[i].prev = &knots[i - 1];
221 knots[i].point_index = i * resolu;
222 knots[i].knot_index = i;
223
224 sub_v3_v3v3(knots[i].tan[0], bezt_array[i].vec[0], bezt_array[i].vec[1]);
225 knots[i].handles[0] = normalize_v3(knots[i].tan[0]);
226
227 sub_v3_v3v3(knots[i].tan[1], bezt_array[i].vec[1], bezt_array[i].vec[2]);
228 knots[i].handles[1] = -normalize_v3(knots[i].tan[1]);
229
230#ifndef NDEBUG
231 knots[i].co = bezt_array[i].vec[1];
232 BLI_assert(equals_v3v3(knots[i].co, points[knots[i].point_index]));
233#endif
234 }
235
236 if (is_cyclic) {
237 knots[0].prev = &knots[bezt_array_last];
238 knots[bezt_array_last].next = &knots[0];
239 }
240 else {
241 knots[0].prev = nullptr;
242 knots[bezt_array_last].next = nullptr;
243
244 /* always keep end-points */
245 knots[0].can_remove = false;
246 knots[bezt_array_last].can_remove = false;
247 }
248
249 curve_decimate(points, points_len, knots, knots_len, error_sq_max, error_target_len);
250
251 MEM_freeN(points);
252
253 uint knots_len_decimated = knots_len;
254
255/* Update handle type on modifications. */
256#define HANDLE_UPDATE(a, b) \
257 { \
258 if (a == HD_VECT) { \
259 a = HD_FREE; \
260 } \
261 else if (ELEM(a, HD_AUTO, HD_AUTO_ANIM)) { \
262 a = HD_ALIGN; \
263 } \
264 /* opposite handle */ \
265 if (ELEM(b, HD_AUTO, HD_AUTO_ANIM)) { \
266 b = HD_ALIGN; \
267 } \
268 } \
269 ((void)0)
270
271 for (uint i = 0; i < bezt_array_len; i++) {
272 if (knots[i].is_removed) {
273 /* caller must remove */
274 bezt_array[i].f2 |= flag_set;
275 knots_len_decimated--;
276 }
277 else {
278 bezt_array[i].f2 &= char(~flag_set);
279 if (is_cyclic || i != 0) {
280 uint i_prev = (i != 0) ? i - 1 : bezt_array_last;
281 if (knots[i_prev].is_removed) {
283 bezt_array[i].vec[0], bezt_array[i].vec[1], knots[i].tan[0], knots[i].handles[0]);
284 HANDLE_UPDATE(bezt_array[i].h1, bezt_array[i].h2);
285 }
286 }
287 if (is_cyclic || i != bezt_array_last) {
288 uint i_next = (i != bezt_array_last) ? i + 1 : 0;
289 if (knots[i_next].is_removed) {
291 bezt_array[i].vec[2], bezt_array[i].vec[1], knots[i].tan[1], knots[i].handles[1]);
292 HANDLE_UPDATE(bezt_array[i].h2, bezt_array[i].h1);
293 }
294 }
295 }
296 }
297
298#undef HANDLE_UPDATE
299
300 MEM_freeN(knots);
301
302 return knots_len_decimated;
303}
304#define SELECT 1
305
307 const uint resolu,
308 const float error_sq_max,
309 const uint error_target_len)
310{
311 const char flag_test = BEZT_FLAG_TEMP_TAG;
312
313 const uint pntsu_dst = BKE_curve_decimate_bezt_array(nu->bezt,
314 uint(nu->pntsu),
315 resolu,
316 (nu->flagu & CU_NURB_CYCLIC) != 0,
317 SELECT,
318 flag_test,
319 error_sq_max,
320 error_target_len);
321
322 if (pntsu_dst == uint(nu->pntsu)) {
323 return;
324 }
325
326 BezTriple *bezt_src = nu->bezt;
327 BezTriple *bezt_dst = MEM_malloc_arrayN<BezTriple>(pntsu_dst, __func__);
328
329 int i_src = 0, i_dst = 0;
330
331 while (i_src < nu->pntsu) {
332 if ((bezt_src[i_src].f2 & flag_test) == 0) {
333 bezt_dst[i_dst] = bezt_src[i_src];
334 i_dst++;
335 }
336 i_src++;
337 }
338
339 MEM_freeN(bezt_src);
340
341 nu->bezt = bezt_dst;
342 nu->pntsu = i_dst;
343}
void BKE_curve_calc_coords_axis(const BezTriple *bezt_array, unsigned int bezt_array_len, unsigned int resolu, bool is_cyclic, bool use_cyclic_duplicate_endpoint, unsigned int axis, unsigned int stride, float *r_points)
Definition curve.cc:1613
unsigned int BKE_curve_calc_coords_axis_len(unsigned int bezt_array_len, unsigned int resolu, bool is_cyclic, bool use_cyclic_duplicate_endpoint)
Definition curve.cc:1603
#define BLI_assert(a)
Definition BLI_assert.h:46
A min-heap / priority queue ADT.
void BLI_heap_free(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition BLI_heap.cc:191
void BLI_heap_insert_or_update(Heap *heap, HeapNode **node_p, float value, void *ptr) ATTR_NONNULL(1
Heap * BLI_heap_new_ex(unsigned int reserve_num) ATTR_WARN_UNUSED_RESULT
Definition BLI_heap.cc:171
void void bool BLI_heap_is_empty(const Heap *heap) ATTR_NONNULL(1)
Definition BLI_heap.cc:269
void * BLI_heap_pop_min(Heap *heap) ATTR_NONNULL(1)
Definition BLI_heap.cc:291
void * BLI_heap_node_ptr(const HeapNode *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.cc:352
void void BLI_heap_remove(Heap *heap, HeapNode *node) ATTR_NONNULL(1
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE bool equals_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], float f)
MINLINE float normalize_v3(float n[3])
unsigned int uint
@ CU_NURB_CYCLIC
@ BEZT_FLAG_TEMP_TAG
Read Guarded memory(de)allocation.
nullptr float
#define HANDLE_UPDATE(a, b)
static void curve_decimate(const float(*points)[3], const uint points_len, Knot *knots, const uint knots_len, float error_sq_max, const uint error_target_len)
#define SELECT
void BKE_curve_decimate_nurb(Nurb *nu, const uint resolu, const float error_sq_max, const uint error_target_len)
uint BKE_curve_decimate_bezt_array(BezTriple *bezt_array, const uint bezt_array_len, const uint resolu, const bool is_cyclic, const char flag_test, const char flag_set, const float error_sq_max, const uint error_target_len)
static float knot_remove_error_value(const float tan_l[3], const float tan_r[3], const float(*points)[3], const uint points_len, float r_handle_factors[2])
static void knot_remove_error_recalculate(Heap *heap, const float(*points)[3], const uint points_len, Knot *k, const float error_sq_max)
static bool is_cyclic(const Nurb *nu)
#define tan
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define FLT_MAX
Definition stdcycles.h:14
float vec[3][3]
uint knot_index
const float * co
uint is_removed
Knot * next
Knot * prev
float handles[2]
uint can_remove
uint point_index
float tan[2][3]
HeapNode * heap_node
short flagu
BezTriple * bezt
float handles[2]
uint knot_index
i
Definition text_draw.cc:230
ParamHandle ** handles