Blender V4.3
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
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" /* 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 = static_cast<Removal *>(MEM_mallocN(sizeof(*r), __func__));
113 r->knot_index = k->knot_index;
114 }
115
116 copy_v2_v2(r->handles, handles);
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] = static_cast<float(*)[3]>(
200 MEM_mallocN((sizeof(float[3]) * points_len * (is_cyclic ? 2 : 1)), __func__));
201
203 bezt_array, bezt_array_len, resolu, is_cyclic, false, 0, sizeof(float[3]), &points[0][0]);
205 bezt_array, bezt_array_len, resolu, is_cyclic, false, 1, sizeof(float[3]), &points[0][1]);
207 bezt_array, bezt_array_len, resolu, is_cyclic, false, 2, sizeof(float[3]), &points[0][2]);
208
209 const uint knots_len = bezt_array_len;
210 Knot *knots = static_cast<Knot *>(MEM_mallocN((sizeof(*knots) * bezt_array_len), __func__));
211
212 if (is_cyclic) {
213 memcpy(points[points_len], points[0], sizeof(float[3]) * points_len);
214 }
215
216 for (uint i = 0; i < bezt_array_len; i++) {
217 knots[i].heap_node = nullptr;
218 knots[i].can_remove = (bezt_array[i].f2 & flag_test) != 0;
219 knots[i].is_removed = false;
220 knots[i].next = &knots[i + 1];
221 knots[i].prev = &knots[i - 1];
222 knots[i].point_index = i * resolu;
223 knots[i].knot_index = i;
224
225 sub_v3_v3v3(knots[i].tan[0], bezt_array[i].vec[0], bezt_array[i].vec[1]);
226 knots[i].handles[0] = normalize_v3(knots[i].tan[0]);
227
228 sub_v3_v3v3(knots[i].tan[1], bezt_array[i].vec[1], bezt_array[i].vec[2]);
229 knots[i].handles[1] = -normalize_v3(knots[i].tan[1]);
230
231#ifndef NDEBUG
232 knots[i].co = bezt_array[i].vec[1];
233 BLI_assert(equals_v3v3(knots[i].co, points[knots[i].point_index]));
234#endif
235 }
236
237 if (is_cyclic) {
238 knots[0].prev = &knots[bezt_array_last];
239 knots[bezt_array_last].next = &knots[0];
240 }
241 else {
242 knots[0].prev = nullptr;
243 knots[bezt_array_last].next = nullptr;
244
245 /* always keep end-points */
246 knots[0].can_remove = false;
247 knots[bezt_array_last].can_remove = false;
248 }
249
250 curve_decimate(points, points_len, knots, knots_len, error_sq_max, error_target_len);
251
252 MEM_freeN(points);
253
254 uint knots_len_decimated = knots_len;
255
256/* Update handle type on modifications. */
257#define HANDLE_UPDATE(a, b) \
258 { \
259 if (a == HD_VECT) { \
260 a = HD_FREE; \
261 } \
262 else if (ELEM(a, HD_AUTO, HD_AUTO_ANIM)) { \
263 a = HD_ALIGN; \
264 } \
265 /* opposite handle */ \
266 if (ELEM(b, HD_AUTO, HD_AUTO_ANIM)) { \
267 b = HD_ALIGN; \
268 } \
269 } \
270 ((void)0)
271
272 for (uint i = 0; i < bezt_array_len; i++) {
273 if (knots[i].is_removed) {
274 /* caller must remove */
275 bezt_array[i].f2 |= flag_set;
276 knots_len_decimated--;
277 }
278 else {
279 bezt_array[i].f2 &= char(~flag_set);
280 if (is_cyclic || i != 0) {
281 uint i_prev = (i != 0) ? i - 1 : bezt_array_last;
282 if (knots[i_prev].is_removed) {
284 bezt_array[i].vec[0], bezt_array[i].vec[1], knots[i].tan[0], knots[i].handles[0]);
285 HANDLE_UPDATE(bezt_array[i].h1, bezt_array[i].h2);
286 }
287 }
288 if (is_cyclic || i != bezt_array_last) {
289 uint i_next = (i != bezt_array_last) ? i + 1 : 0;
290 if (knots[i_next].is_removed) {
292 bezt_array[i].vec[2], bezt_array[i].vec[1], knots[i].tan[1], knots[i].handles[1]);
293 HANDLE_UPDATE(bezt_array[i].h2, bezt_array[i].h1);
294 }
295 }
296 }
297 }
298
299#undef HANDLE_UPDATE
300
301 MEM_freeN(knots);
302
303 return knots_len_decimated;
304}
305#define SELECT 1
306
308 const uint resolu,
309 const float error_sq_max,
310 const uint error_target_len)
311{
312 const char flag_test = BEZT_FLAG_TEMP_TAG;
313
314 const uint pntsu_dst = BKE_curve_decimate_bezt_array(nu->bezt,
315 uint(nu->pntsu),
316 resolu,
317 (nu->flagu & CU_NURB_CYCLIC) != 0,
318 SELECT,
319 flag_test,
320 error_sq_max,
321 error_target_len);
322
323 if (pntsu_dst == uint(nu->pntsu)) {
324 return;
325 }
326
327 BezTriple *bezt_src = nu->bezt;
328 BezTriple *bezt_dst = static_cast<BezTriple *>(
329 MEM_mallocN(sizeof(BezTriple) * pntsu_dst, __func__));
330
331 int i_src = 0, i_dst = 0;
332
333 while (i_src < nu->pntsu) {
334 if ((bezt_src[i_src].f2 & flag_test) == 0) {
335 bezt_dst[i_dst] = bezt_src[i_src];
336 i_dst++;
337 }
338 i_src++;
339 }
340
341 MEM_freeN(bezt_src);
342
343 nu->bezt = bezt_dst;
344 nu->pntsu = i_dst;
345}
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:1607
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:1597
#define BLI_assert(a)
Definition BLI_assert.h:50
A min-heap / priority queue ADT.
void BLI_heap_free(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition BLI_heap.c:192
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.c:172
void void bool BLI_heap_is_empty(const Heap *heap) ATTR_NONNULL(1)
Definition BLI_heap.c:269
void * BLI_heap_pop_min(Heap *heap) ATTR_NONNULL(1)
Definition BLI_heap.c:291
void * BLI_heap_node_ptr(const HeapNode *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.c: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.
#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)
draw_view in_light_buf[] float
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
#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