2 * Copyright (c) 2016, Campbell Barton.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
11 * * Neither the name of the <organization> nor the
12 * names of its contributors may be used to endorse or promote products
13 * derived from this software without specific prior written permission.
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 * Curve Re-fitting Method
29 * =======================
31 * This is a more processor intensive method of fitting,
32 * compared to #curve_fit_cubic_to_points_db, and works as follows:
34 * - First iteratively remove all points under the error threshold.
35 * - If corner calculation is enabled:
36 * - Find adjacent knots that exceed the angle limit
37 * - Find a 'split' point between the knots (could include the knots)
38 * - If copying the tangents to this split point doesn't exceed the error threshold:
39 * - Assign the tangents of the two knots to the split point, define it as a corner.
40 * (after this, we have many points which are too close).
41 * - Run a re-fit pass, where knots are re-positioned between their adjacent knots
42 * when their re-fit position has a lower 'error'.
43 * While re-fitting, remove knots that fall below the error threshold.
47 # define _USE_MATH_DEFINES
58 typedef unsigned int uint;
60 #include "curve_fit_inline.h"
61 #include "../curve_fit_nd.h"
63 #include "generic_heap.h"
66 # define alloca(size) _alloca(size)
69 #if !defined(_MSC_VER)
75 # pragma GCC diagnostic ignored "-Wvla"
79 # pragma GCC diagnostic error "-Wvla"
83 /* adjust the knots after simplifying */
84 #define USE_KNOT_REFIT
85 /* remove knots under the error threshold while re-fitting */
86 #define USE_KNOT_REFIT_REMOVE
87 /* detect corners over an angle threshold */
88 #define USE_CORNER_DETECT
89 /* avoid re-calculating lengths multiple times */
90 #define USE_LENGTH_CACHE
91 /* use pool allocator */
95 #define SPLIT_POINT_INVALID ((uint)-1)
97 #define MAX2(x, y) ((x) > (y) ? (x) : (y))
99 #define SQUARE(a) ((a) * (a))
102 # define UNLIKELY(x) __builtin_expect(!!(x), 0)
104 # define UNLIKELY(x) (x)
108 const double *points;
110 #ifdef USE_LENGTH_CACHE
111 const double *points_length_cache;
116 struct Knot *next, *prev;
121 * Currently the same, access as different for now
122 * since we may want to support different point/knot indices
132 * Store the error value, to see if we can improve on it
133 * (without having to re-calculate each time)
135 * This is the error between this knot and the next */
136 double error_sq_next;
138 /* Initially point to contiguous memory, however we may re-assign */
143 struct KnotRemoveState {
145 /* Handles for prev/next knots */
150 /* rstate_* pool allocator */
151 #define TPOOL_IMPL_PREFIX rstate
152 #define TPOOL_ALLOC_TYPE struct KnotRemoveState
153 #define TPOOL_STRUCT ElemPool_KnotRemoveState
154 #include "generic_alloc_impl.h"
155 #undef TPOOL_IMPL_PREFIX
156 #undef TPOOL_ALLOC_TYPE
158 #endif /* USE_TPOOL */
160 #ifdef USE_KNOT_REFIT
161 struct KnotRefitState {
163 /** When SPLIT_POINT_INVALID - remove this item */
165 /** Handles for prev/next knots */
166 double handles_prev[2], handles_next[2];
171 /* refit_* pool allocator */
172 #define TPOOL_IMPL_PREFIX refit
173 #define TPOOL_ALLOC_TYPE struct KnotRefitState
174 #define TPOOL_STRUCT ElemPool_KnotRefitState
175 #include "generic_alloc_impl.h"
176 #undef TPOOL_IMPL_PREFIX
177 #undef TPOOL_ALLOC_TYPE
179 #endif /* USE_TPOOL */
180 #endif /* USE_KNOT_REFIT */
183 #ifdef USE_CORNER_DETECT
184 /** Result of collapsing a corner. */
185 struct KnotCornerState {
187 /* Merge adjacent handles into this one (may be shared with the 'index') */
188 uint index_adjacent[2];
190 /* Handles for prev/next knots */
191 double handles_prev[2], handles_next[2];
195 /* refit_* pool allocator */
197 #define TPOOL_IMPL_PREFIX corner
198 #define TPOOL_ALLOC_TYPE struct KnotCornerState
199 #define TPOOL_STRUCT ElemPool_KnotCornerState
200 #include "generic_alloc_impl.h"
201 #undef TPOOL_IMPL_PREFIX
202 #undef TPOOL_ALLOC_TYPE
204 #endif /* USE_TPOOL */
205 #endif /* USE_CORNER_DETECT */
208 /* Utility functions */
210 #ifdef USE_KNOT_REFIT
212 * Find the most distant point between the 2 knots.
214 static uint knot_find_split_point(
215 const struct PointData *pd,
216 const struct Knot *knot_l, const struct Knot *knot_r,
217 const uint knots_len,
220 uint split_point = SPLIT_POINT_INVALID;
221 double split_point_dist_best = -DBL_MAX;
223 const double *offset = &pd->points[knot_l->index * dims];
226 double v_plane[dims];
228 double v_offset[dims];
230 double *v_plane = alloca(sizeof(double) * dims);
231 double *v_proj = alloca(sizeof(double) * dims);
232 double *v_offset = alloca(sizeof(double) * dims);
237 &pd->points[knot_l->index * dims],
238 &pd->points[knot_r->index * dims],
241 normalize_vn(v_plane, dims);
243 const uint knots_end = knots_len - 1;
244 const struct Knot *k_step = knot_l;
246 if (k_step->index != knots_end) {
251 k_step = k_step - knots_end;
254 if (k_step != knot_r) {
255 sub_vn_vnvn(v_offset, &pd->points[k_step->index * dims], offset, dims);
256 project_plane_vn_vnvn_normalized(v_proj, v_offset, v_plane, dims);
258 double split_point_dist_test = len_squared_vn(v_proj, dims);
259 if (split_point_dist_test > split_point_dist_best) {
260 split_point_dist_best = split_point_dist_test;
261 split_point = k_step->index;
272 #endif /* USE_KNOT_REFIT */
275 #ifdef USE_CORNER_DETECT
277 * Find the knot furthest from the line between \a knot_l & \a knot_r.
278 * This is to be used as a split point.
280 static uint knot_find_split_point_on_axis(
281 const struct PointData *pd,
282 const struct Knot *knot_l, const struct Knot *knot_r,
283 const uint knots_len,
284 const double *plane_no,
287 uint split_point = SPLIT_POINT_INVALID;
288 double split_point_dist_best = -DBL_MAX;
290 const uint knots_end = knots_len - 1;
291 const struct Knot *k_step = knot_l;
293 if (k_step->index != knots_end) {
298 k_step = k_step - knots_end;
301 if (k_step != knot_r) {
302 double split_point_dist_test = dot_vnvn(plane_no, &pd->points[k_step->index * dims], dims);
303 if (split_point_dist_test > split_point_dist_best) {
304 split_point_dist_best = split_point_dist_test;
305 split_point = k_step->index;
316 #endif /* USE_CORNER_DETECT */
319 static double knot_remove_error_value(
320 const double *tan_l, const double *tan_r,
321 const double *points_offset, const uint points_offset_len,
322 const double *points_offset_length_cache,
324 /* Avoid having to re-calculate again */
325 double r_handle_factors[2])
327 double error_sq = FLT_MAX;
330 double handle_factor_l[dims];
331 double handle_factor_r[dims];
333 double *handle_factor_l = alloca(sizeof(double) * dims);
334 double *handle_factor_r = alloca(sizeof(double) * dims);
337 curve_fit_cubic_to_points_single_db(
338 points_offset, points_offset_len, points_offset_length_cache, dims, 0.0,
340 handle_factor_l, handle_factor_r,
343 assert(error_sq != FLT_MAX);
345 isub_vnvn(handle_factor_l, points_offset, dims);
346 r_handle_factors[0] = dot_vnvn(tan_l, handle_factor_l, dims);
348 isub_vnvn(handle_factor_r, &points_offset[(points_offset_len - 1) * dims], dims);
349 r_handle_factors[1] = dot_vnvn(tan_r, handle_factor_r, dims);
354 static double knot_calc_curve_error_value(
355 const struct PointData *pd,
356 const struct Knot *knot_l, const struct Knot *knot_r,
357 const double *tan_l, const double *tan_r,
359 double r_handle_factors[2])
361 const uint points_offset_len = ((knot_l->index < knot_r->index) ?
362 (knot_r->index - knot_l->index) :
363 ((knot_r->index + pd->points_len) - knot_l->index)) + 1;
365 if (points_offset_len != 2) {
366 return knot_remove_error_value(
368 &pd->points[knot_l->index * dims], points_offset_len,
369 #ifdef USE_LENGTH_CACHE
370 &pd->points_length_cache[knot_l->index],
378 /* No points between, use 1/3 handle length with no error as a fallback. */
379 assert(points_offset_len == 2);
380 #ifdef USE_LENGTH_CACHE
381 r_handle_factors[0] = r_handle_factors[1] = pd->points_length_cache[knot_l->index] / 3.0;
383 r_handle_factors[0] = r_handle_factors[1] = len_vnvn(
384 &pd->points[(knot_l->index + 0) * dims],
385 &pd->points[(knot_l->index + 1) * dims], dims) / 3.0;
391 struct KnotRemove_Params {
393 const struct PointData *pd;
395 struct ElemPool_KnotRemoveState *epool;
399 static void knot_remove_error_recalculate(
400 struct KnotRemove_Params *p,
401 struct Knot *k, const double error_sq_max,
404 assert(k->can_remove);
407 const double cost_sq = knot_calc_curve_error_value(
408 p->pd, k->prev, k->next,
409 k->prev->tan[1], k->next->tan[0],
413 if (cost_sq < error_sq_max) {
414 struct KnotRemoveState *r;
416 r = HEAP_node_ptr(k->heap_node);
417 HEAP_remove(p->heap, k->heap_node);
421 r = rstate_pool_elem_alloc(p->epool);
423 r = malloc(sizeof(*r));
429 r->handles[0] = handles[0];
430 r->handles[1] = handles[1];
432 k->heap_node = HEAP_insert(p->heap, cost_sq, r);
436 struct KnotRemoveState *r;
437 r = HEAP_node_ptr(k->heap_node);
438 HEAP_remove(p->heap, k->heap_node);
441 rstate_pool_elem_free(p->epool, r);
452 * Return length after being reduced.
454 static uint curve_incremental_simplify(
455 const struct PointData *pd,
456 struct Knot *knots, const uint knots_len, uint knots_len_remaining,
457 double error_sq_max, const uint dims)
461 struct ElemPool_KnotRemoveState epool;
463 rstate_pool_create(&epool, 0);
466 Heap *heap = HEAP_new(knots_len);
468 struct KnotRemove_Params params = {
476 for (uint i = 0; i < knots_len; i++) {
477 struct Knot *k = &knots[i];
478 if (k->can_remove && (k->is_removed == false) && (k->is_corner == false)) {
479 knot_remove_error_recalculate(¶ms, k, error_sq_max, dims);
483 while (HEAP_is_empty(heap) == false) {
487 const double error_sq = HEAP_top_value(heap);
488 struct KnotRemoveState *r = HEAP_popmin(heap);
489 k = &knots[r->index];
491 k->prev->handles[1] = r->handles[0];
492 k->next->handles[0] = r->handles[1];
494 k->prev->error_sq_next = error_sq;
497 rstate_pool_elem_free(&epool, r);
503 if (UNLIKELY(knots_len_remaining <= 2)) {
507 struct Knot *k_prev = k->prev;
508 struct Knot *k_next = k->next;
510 /* Remove ourselves */
511 k_next->prev = k_prev;
512 k_prev->next = k_next;
516 k->is_removed = true;
518 if (k_prev->can_remove && (k_prev->is_corner == false) && (k_prev->prev && k_prev->next)) {
519 knot_remove_error_recalculate(¶ms, k_prev, error_sq_max, dims);
522 if (k_next->can_remove && (k_next->is_corner == false) && (k_next->prev && k_next->next)) {
523 knot_remove_error_recalculate(¶ms, k_next, error_sq_max, dims);
526 knots_len_remaining -= 1;
530 rstate_pool_destroy(&epool);
533 HEAP_free(heap, free);
535 return knots_len_remaining;
539 #ifdef USE_KNOT_REFIT
541 struct KnotRefit_Params {
543 const struct PointData *pd;
545 struct ElemPool_KnotRefitState *epool;
549 static void knot_refit_error_recalculate(
550 struct KnotRefit_Params *p,
551 struct Knot *knots, const uint knots_len,
553 const double error_sq_max,
556 assert(k->can_remove);
558 #ifdef USE_KNOT_REFIT_REMOVE
562 /* First check if we can remove, this allows to refit and remove as we go. */
563 const double cost_sq = knot_calc_curve_error_value(
564 p->pd, k->prev, k->next,
565 k->prev->tan[1], k->next->tan[0],
569 if (cost_sq < error_sq_max) {
570 struct KnotRefitState *r;
572 r = HEAP_node_ptr(k->heap_node);
573 HEAP_remove(p->heap, k->heap_node);
577 r = refit_pool_elem_alloc(p->epool);
579 r = malloc(sizeof(*r));
584 r->index_refit = SPLIT_POINT_INVALID;
586 r->handles_prev[0] = handles[0];
587 r->handles_prev[1] = 0.0; /* unused */
588 r->handles_next[0] = 0.0; /* unused */
589 r->handles_next[1] = handles[1];
591 r->error_sq[0] = r->error_sq[1] = cost_sq;
593 /* Always perform removal before refitting, (make a negative number) */
594 k->heap_node = HEAP_insert(p->heap, cost_sq - error_sq_max, r);
601 #endif /* USE_KNOT_REFIT_REMOVE */
603 const uint refit_index = knot_find_split_point(
604 p->pd, k->prev, k->next,
608 if ((refit_index == SPLIT_POINT_INVALID) ||
609 (refit_index == k->index))
614 struct Knot *k_refit = &knots[refit_index];
616 const double cost_sq_src_max = MAX2(k->prev->error_sq_next, k->error_sq_next);
617 assert(cost_sq_src_max <= error_sq_max);
619 double cost_sq_dst[2];
620 double handles_prev[2], handles_next[2];
622 if ((((cost_sq_dst[0] = knot_calc_curve_error_value(
623 p->pd, k->prev, k_refit,
624 k->prev->tan[1], k_refit->tan[0],
626 handles_prev)) < cost_sq_src_max) &&
627 ((cost_sq_dst[1] = knot_calc_curve_error_value(
628 p->pd, k_refit, k->next,
629 k_refit->tan[1], k->next->tan[0],
631 handles_next)) < cost_sq_src_max)))
634 struct KnotRefitState *r;
636 r = HEAP_node_ptr(k->heap_node);
637 HEAP_remove(p->heap, k->heap_node);
641 r = refit_pool_elem_alloc(p->epool);
643 r = malloc(sizeof(*r));
648 r->index_refit = refit_index;
650 r->handles_prev[0] = handles_prev[0];
651 r->handles_prev[1] = handles_prev[1];
653 r->handles_next[0] = handles_next[0];
654 r->handles_next[1] = handles_next[1];
656 r->error_sq[0] = cost_sq_dst[0];
657 r->error_sq[1] = cost_sq_dst[1];
659 const double cost_sq_dst_max = MAX2(cost_sq_dst[0], cost_sq_dst[1]);
661 assert(cost_sq_dst_max < cost_sq_src_max);
663 /* Weight for the greatest improvement */
664 k->heap_node = HEAP_insert(p->heap, cost_sq_src_max - cost_sq_dst_max, r);
670 struct KnotRefitState *r;
671 r = HEAP_node_ptr(k->heap_node);
672 HEAP_remove(p->heap, k->heap_node);
675 refit_pool_elem_free(p->epool, r);
686 * Re-adjust the curves by re-fitting points.
687 * test the error from moving using points between the adjacent.
689 static uint curve_incremental_simplify_refit(
690 const struct PointData *pd,
691 struct Knot *knots, const uint knots_len, uint knots_len_remaining,
692 const double error_sq_max,
696 struct ElemPool_KnotRefitState epool;
698 refit_pool_create(&epool, 0);
701 Heap *heap = HEAP_new(knots_len);
703 struct KnotRefit_Params params = {
711 for (uint i = 0; i < knots_len; i++) {
712 struct Knot *k = &knots[i];
714 (k->is_removed == false) &&
715 (k->is_corner == false) &&
716 (k->prev && k->next))
718 knot_refit_error_recalculate(¶ms, knots, knots_len, k, error_sq_max, dims);
722 while (HEAP_is_empty(heap) == false) {
723 struct Knot *k_old, *k_refit;
726 struct KnotRefitState *r = HEAP_popmin(heap);
727 k_old = &knots[r->index];
728 k_old->heap_node = NULL;
730 #ifdef USE_KNOT_REFIT_REMOVE
731 if (r->index_refit == SPLIT_POINT_INVALID) {
737 k_refit = &knots[r->index_refit];
738 k_refit->handles[0] = r->handles_prev[1];
739 k_refit->handles[1] = r->handles_next[0];
742 k_old->prev->handles[1] = r->handles_prev[0];
743 k_old->next->handles[0] = r->handles_next[1];
746 refit_pool_elem_free(&epool, r);
752 if (UNLIKELY(knots_len_remaining <= 2)) {
756 struct Knot *k_prev = k_old->prev;
757 struct Knot *k_next = k_old->next;
761 k_old->is_removed = true;
763 #ifdef USE_KNOT_REFIT_REMOVE
764 if (k_refit == NULL) {
765 k_next->prev = k_prev;
766 k_prev->next = k_next;
768 knots_len_remaining -= 1;
773 /* Remove ourselves */
774 k_next->prev = k_refit;
775 k_prev->next = k_refit;
777 k_refit->prev = k_prev;
778 k_refit->next = k_next;
779 k_refit->is_removed = false;
782 if (k_prev->can_remove && (k_prev->is_corner == false) && (k_prev->prev && k_prev->next)) {
783 knot_refit_error_recalculate(¶ms, knots, knots_len, k_prev, error_sq_max, dims);
786 if (k_next->can_remove && (k_next->is_corner == false) && (k_next->prev && k_next->next)) {
787 knot_refit_error_recalculate(¶ms, knots, knots_len, k_next, error_sq_max, dims);
792 refit_pool_destroy(&epool);
795 HEAP_free(heap, free);
797 return knots_len_remaining;
800 #endif /* USE_KNOT_REFIT */
803 #ifdef USE_CORNER_DETECT
805 struct KnotCorner_Params {
807 const struct PointData *pd;
809 struct ElemPool_KnotCornerState *epool;
814 * (Re)calculate the error incurred from turning this into a corner.
816 static void knot_corner_error_recalculate(
817 struct KnotCorner_Params *p,
818 struct Knot *k_split,
819 struct Knot *k_prev, struct Knot *k_next,
820 const double error_sq_max,
823 assert(k_prev->can_remove && k_next->can_remove);
825 double handles_prev[2], handles_next[2];
826 /* Test skipping 'k_prev' by using points (k_prev->prev to k_split) */
827 double cost_sq_dst[2];
829 if (((cost_sq_dst[0] = knot_calc_curve_error_value(
830 p->pd, k_prev, k_split,
831 k_prev->tan[1], k_prev->tan[1],
833 handles_prev)) < error_sq_max) &&
834 ((cost_sq_dst[1] = knot_calc_curve_error_value(
835 p->pd, k_split, k_next,
836 k_next->tan[0], k_next->tan[0],
838 handles_next)) < error_sq_max))
840 struct KnotCornerState *c;
841 if (k_split->heap_node) {
842 c = HEAP_node_ptr(k_split->heap_node);
843 HEAP_remove(p->heap, k_split->heap_node);
847 c = corner_pool_elem_alloc(p->epool);
849 c = malloc(sizeof(*c));
851 c->index = k_split->index;
854 c->index_adjacent[0] = k_prev->index;
855 c->index_adjacent[1] = k_next->index;
857 /* Need to store handle lengths for both sides */
858 c->handles_prev[0] = handles_prev[0];
859 c->handles_prev[1] = handles_prev[1];
861 c->handles_next[0] = handles_next[0];
862 c->handles_next[1] = handles_next[1];
864 c->error_sq[0] = cost_sq_dst[0];
865 c->error_sq[1] = cost_sq_dst[1];
867 const double cost_max_sq = MAX2(cost_sq_dst[0], cost_sq_dst[1]);
868 k_split->heap_node = HEAP_insert(p->heap, cost_max_sq, c);
871 if (k_split->heap_node) {
872 struct KnotCornerState *c;
873 c = HEAP_node_ptr(k_split->heap_node);
874 HEAP_remove(p->heap, k_split->heap_node);
876 corner_pool_elem_free(p->epool, c);
880 k_split->heap_node = NULL;
887 * Attempt to collapse close knots into corners,
888 * as long as they fall below the error threshold.
890 static uint curve_incremental_simplify_corners(
891 const struct PointData *pd,
892 struct Knot *knots, const uint knots_len, uint knots_len_remaining,
893 const double error_sq_max, const double error_sq_2x_max,
894 const double corner_angle,
896 uint *r_corner_index_len)
899 struct ElemPool_KnotCornerState epool;
901 corner_pool_create(&epool, 0);
904 Heap *heap = HEAP_new(0);
906 struct KnotCorner_Params params = {
915 double plane_no[dims];
916 double k_proj_ref[dims];
917 double k_proj_split[dims];
919 double *plane_no = alloca(sizeof(double) * dims);
920 double *k_proj_ref = alloca(sizeof(double) * dims);
921 double *k_proj_split = alloca(sizeof(double) * dims);
924 const double corner_angle_cos = cos(corner_angle);
926 uint corner_index_len = 0;
928 for (uint i = 0; i < knots_len; i++) {
929 if ((knots[i].is_removed == false) &&
930 (knots[i].can_remove == true) &&
931 (knots[i].next && knots[i].next->can_remove))
933 struct Knot *k_prev = &knots[i];
934 struct Knot *k_next = k_prev->next;
936 /* Angle outside threshold */
937 if (dot_vnvn(k_prev->tan[0], k_next->tan[1], dims) < corner_angle_cos) {
938 /* Measure distance projected onto a plane,
939 * since the points may be offset along their own tangents. */
940 sub_vn_vnvn(plane_no, k_next->tan[0], k_prev->tan[1], dims);
942 /* Compare 2x so as to allow both to be changed by maximum of error_sq_max */
943 const uint split_index = knot_find_split_point_on_axis(
949 if (split_index != SPLIT_POINT_INVALID) {
951 project_vn_vnvn(k_proj_ref, &pd->points[k_prev->index * dims], k_prev->tan[1], dims);
952 project_vn_vnvn(k_proj_split, &pd->points[split_index * dims], k_prev->tan[1], dims);
954 if (len_squared_vnvn(k_proj_ref, k_proj_split, dims) < error_sq_2x_max) {
956 project_vn_vnvn(k_proj_ref, &pd->points[k_next->index * dims], k_next->tan[0], dims);
957 project_vn_vnvn(k_proj_split, &pd->points[split_index * dims], k_next->tan[0], dims);
959 if (len_squared_vnvn(k_proj_ref, k_proj_split, dims) < error_sq_2x_max) {
961 struct Knot *k_split = &knots[split_index];
963 knot_corner_error_recalculate(
965 k_split, k_prev, k_next,
975 while (HEAP_is_empty(heap) == false) {
976 struct KnotCornerState *c = HEAP_popmin(heap);
978 struct Knot *k_split = &knots[c->index];
980 /* Remove while collapsing */
981 struct Knot *k_prev = &knots[c->index_adjacent[0]];
982 struct Knot *k_next = &knots[c->index_adjacent[1]];
985 k_split->is_removed = false;
986 k_split->prev = k_prev;
987 k_split->next = k_next;
988 k_prev->next = k_split;
989 k_next->prev = k_split;
991 /* Update tangents */
992 k_split->tan[0] = k_prev->tan[1];
993 k_split->tan[1] = k_next->tan[0];
996 k_prev->handles[1] = c->handles_prev[0];
997 k_split->handles[0] = c->handles_prev[1];
998 k_split->handles[1] = c->handles_next[0];
999 k_next->handles[0] = c->handles_next[1];
1001 k_prev->error_sq_next = c->error_sq[0];
1002 k_split->error_sq_next = c->error_sq[1];
1004 k_split->heap_node = NULL;
1007 corner_pool_elem_free(&epool, c);
1012 k_split->is_corner = true;
1014 knots_len_remaining++;
1020 corner_pool_destroy(&epool);
1023 HEAP_free(heap, free);
1025 *r_corner_index_len = corner_index_len;
1027 return knots_len_remaining;
1030 #endif /* USE_CORNER_DETECT */
1032 int curve_fit_cubic_to_points_refit_db(
1033 const double *points,
1034 const uint points_len,
1036 const double error_threshold,
1037 const uint calc_flag,
1038 const uint *corners,
1039 const uint corners_len,
1040 const double corner_angle,
1042 double **r_cubic_array, uint *r_cubic_array_len,
1043 uint **r_cubic_orig_index,
1044 uint **r_corner_index_array, uint *r_corner_index_len)
1046 const uint knots_len = points_len;
1047 struct Knot *knots = malloc(sizeof(Knot) * knots_len);
1048 knots[0].next = NULL;
1050 #ifndef USE_CORNER_DETECT
1051 (void)r_corner_index_array;
1052 (void)r_corner_index_len;
1058 const bool is_cyclic = (calc_flag & CURVE_FIT_CALC_CYCLIC) != 0 && (points_len > 2);
1059 #ifdef USE_CORNER_DETECT
1060 const bool use_corner = (corner_angle < M_PI);
1065 /* Over alloc the list x2 for cyclic curves,
1066 * so we can evaluate across the start/end */
1067 double *points_alloc = NULL;
1069 points_alloc = malloc((sizeof(double) * points_len * dims) * 2);
1070 memcpy(points_alloc, points, sizeof(double) * points_len * dims);
1071 memcpy(points_alloc + (points_len * dims), points_alloc, sizeof(double) * points_len * dims);
1072 points = points_alloc;
1075 double *tangents = malloc(sizeof(double) * knots_len * 2 * dims);
1078 double *t_step = tangents;
1079 for (uint i = 0; i < knots_len; i++) {
1080 knots[i].next = (knots + i) + 1;
1081 knots[i].prev = (knots + i) - 1;
1083 knots[i].heap_node = NULL;
1086 knots[i].can_remove = true;
1087 knots[i].is_removed = false;
1088 knots[i].is_corner = false;
1089 knots[i].error_sq_next = 0.0;
1090 knots[i].tan[0] = t_step; t_step += dims;
1091 knots[i].tan[1] = t_step; t_step += dims;
1093 assert(t_step == &tangents[knots_len * 2 * dims]);
1097 knots[0].prev = &knots[knots_len - 1];
1098 knots[knots_len - 1].next = &knots[0];
1101 knots[0].prev = NULL;
1102 knots[knots_len - 1].next = NULL;
1104 /* always keep end-points */
1105 knots[0].can_remove = false;
1106 knots[knots_len - 1].can_remove = false;
1109 #ifdef USE_LENGTH_CACHE
1110 double *points_length_cache = malloc(sizeof(double) * points_len * (is_cyclic ? 2 : 1));
1113 /* Initialize tangents,
1114 * also set the values for knot handles since some may not collapse. */
1117 double tan_prev[dims];
1118 double tan_next[dims];
1120 double *tan_prev = alloca(sizeof(double) * dims);
1121 double *tan_next = alloca(sizeof(double) * dims);
1123 double len_prev, len_next;
1126 /* 2x normalize calculations, but correct */
1128 for (uint i = 0; i < knots_len; i++) {
1129 Knot *k = &knots[i];
1132 sub_vn_vnvn(tan_prev, &points[k->prev->index * dims], &points[k->index * dims], dims);
1133 #ifdef USE_LENGTH_CACHE
1134 points_length_cache[i] =
1136 len_prev = normalize_vn(tan_prev, dims);
1139 zero_vn(tan_prev, dims);
1144 sub_vn_vnvn(tan_next, &points[k->index * dims], &points[k->next->index * dims], dims);
1145 len_next = normalize_vn(tan_next, dims);
1148 zero_vn(tan_next, dims);
1152 add_vn_vnvn(k->tan[0], tan_prev, tan_next, dims);
1153 normalize_vn(k->tan[0], dims);
1154 copy_vnvn(k->tan[1], k->tan[0], dims);
1155 k->handles[0] = len_prev / 3;
1156 k->handles[1] = len_next / 3;
1159 if (knots_len < 2) {
1160 /* NOP, set dummy values */
1161 for (uint i = 0; i < knots_len; i++) {
1162 struct Knot *k = &knots[i];
1163 zero_vn(k->tan[0], dims);
1164 zero_vn(k->tan[1], dims);
1165 k->handles[0] = 0.0;
1166 k->handles[1] = 0.0;
1167 #ifdef USE_LENGTH_CACHE
1168 points_length_cache[i] = 0.0;
1172 else if (is_cyclic) {
1173 len_prev = normalize_vn_vnvn(
1174 tan_prev, &points[(knots_len - 2) * dims], &points[(knots_len - 1) * dims], dims);
1175 for (uint i_curr = knots_len - 1, i_next = 0; i_next < knots_len; i_curr = i_next++) {
1176 struct Knot *k = &knots[i_curr];
1177 #ifdef USE_LENGTH_CACHE
1178 points_length_cache[i_next] =
1180 len_next = normalize_vn_vnvn(tan_next, &points[i_curr * dims], &points[i_next * dims], dims);
1182 add_vn_vnvn(k->tan[0], tan_prev, tan_next, dims);
1183 normalize_vn(k->tan[0], dims);
1184 copy_vnvn(k->tan[1], k->tan[0], dims);
1185 k->handles[0] = len_prev / 3;
1186 k->handles[1] = len_next / 3;
1188 copy_vnvn(tan_prev, tan_next, dims);
1189 len_prev = len_next;
1193 #ifdef USE_LENGTH_CACHE
1194 points_length_cache[0] = 0.0;
1195 points_length_cache[1] =
1197 len_prev = normalize_vn_vnvn(
1198 tan_prev, &points[0 * dims], &points[1 * dims], dims);
1199 copy_vnvn(knots[0].tan[0], tan_prev, dims);
1200 copy_vnvn(knots[0].tan[1], tan_prev, dims);
1201 knots[0].handles[0] = len_prev / 3;
1202 knots[0].handles[1] = len_prev / 3;
1204 for (uint i_curr = 1, i_next = 2; i_next < knots_len; i_curr = i_next++) {
1205 struct Knot *k = &knots[i_curr];
1207 #ifdef USE_LENGTH_CACHE
1208 points_length_cache[i_next] =
1210 len_next = normalize_vn_vnvn(tan_next, &points[i_curr * dims], &points[i_next * dims], dims);
1212 add_vn_vnvn(k->tan[0], tan_prev, tan_next, dims);
1213 normalize_vn(k->tan[0], dims);
1214 copy_vnvn(k->tan[1], k->tan[0], dims);
1215 k->handles[0] = len_prev / 3;
1216 k->handles[1] = len_next / 3;
1218 copy_vnvn(tan_prev, tan_next, dims);
1219 len_prev = len_next;
1221 copy_vnvn(knots[knots_len - 1].tan[0], tan_next, dims);
1222 copy_vnvn(knots[knots_len - 1].tan[1], tan_next, dims);
1224 knots[knots_len - 1].handles[0] = len_next / 3;
1225 knots[knots_len - 1].handles[1] = len_next / 3;
1230 #ifdef USE_LENGTH_CACHE
1232 memcpy(&points_length_cache[points_len], points_length_cache, sizeof(double) * points_len);
1238 for (uint i = 0; i < knots_len; i++) {
1239 Knot *k = &knots[i];
1240 printf("TAN %.8f %.8f %.8f %.8f\n", k->tan[0][0], k->tan[0][1], k->tan[1][0], k->tan[0][1]);
1244 const struct PointData pd = {
1246 .points_len = points_len,
1247 #ifdef USE_LENGTH_CACHE
1248 .points_length_cache = points_length_cache,
1252 uint knots_len_remaining = knots_len;
1254 /* 'curve_incremental_simplify_refit' can be called here, but its very slow
1255 * just remove all within the threshold first. */
1256 knots_len_remaining = curve_incremental_simplify(
1257 &pd, knots, knots_len, knots_len_remaining,
1258 SQUARE(error_threshold), dims);
1260 #ifdef USE_CORNER_DETECT
1262 for (uint i = 0; i < knots_len; i++) {
1263 assert(knots[i].heap_node == NULL);
1266 knots_len_remaining = curve_incremental_simplify_corners(
1267 &pd, knots, knots_len, knots_len_remaining,
1268 SQUARE(error_threshold), SQUARE(error_threshold * 3),
1271 r_corner_index_len);
1273 #endif /* USE_CORNER_DETECT */
1275 #ifdef USE_KNOT_REFIT
1276 knots_len_remaining = curve_incremental_simplify_refit(
1277 &pd, knots, knots_len, knots_len_remaining,
1278 SQUARE(error_threshold),
1280 #endif /* USE_KNOT_REFIT */
1283 #ifdef USE_CORNER_DETECT
1285 if (is_cyclic == false) {
1286 *r_corner_index_len += 2;
1289 uint *corner_index_array = malloc(sizeof(uint) * (*r_corner_index_len));
1290 uint k_index = 0, c_index = 0;
1293 if (is_cyclic == false) {
1294 corner_index_array[c_index++] = k_index;
1299 for (; i < knots_len; i++) {
1300 if (knots[i].is_removed == false) {
1301 if (knots[i].is_corner == true) {
1302 corner_index_array[c_index++] = k_index;
1308 if (is_cyclic == false) {
1309 corner_index_array[c_index++] = k_index;
1313 assert(c_index == *r_corner_index_len);
1314 *r_corner_index_array = corner_index_array;
1316 #endif /* USE_CORNER_DETECT */
1318 #ifdef USE_LENGTH_CACHE
1319 free(points_length_cache);
1322 uint *cubic_orig_index = NULL;
1324 if (r_cubic_orig_index) {
1325 cubic_orig_index = malloc(sizeof(uint) * knots_len_remaining);
1328 struct Knot *knots_first = NULL;
1331 for (uint i = 0; i < knots_len; i++) {
1332 if (knots[i].is_removed == false) {
1333 knots_first = &knots[i];
1338 if (cubic_orig_index) {
1340 for (uint i = 0; i < knots_len_remaining; i++, k = k->next) {
1341 cubic_orig_index[i] = k->index;
1346 /* Correct unused handle endpoints - not essential, but nice behavior */
1347 if (is_cyclic == false) {
1348 struct Knot *knots_last = knots_first;
1349 while (knots_last->next) {
1350 knots_last = knots_last->next;
1352 knots_first->handles[0] = -knots_first->handles[1];
1353 knots_last->handles[1] = -knots_last->handles[0];
1356 /* 3x for one knot and two handles */
1357 double *cubic_array = malloc(sizeof(double) * knots_len_remaining * 3 * dims);
1360 double *c_step = cubic_array;
1361 struct Knot *k = knots_first;
1362 for (uint i = 0; i < knots_len_remaining; i++, k = k->next) {
1363 const double *p = &points[k->index * dims];
1365 madd_vn_vnvn_fl(c_step, p, k->tan[0], k->handles[0], dims);
1367 copy_vnvn(c_step, p, dims);
1369 madd_vn_vnvn_fl(c_step, p, k->tan[1], k->handles[1], dims);
1372 assert(c_step == &cubic_array[knots_len_remaining * 3 * dims]);
1377 points_alloc = NULL;
1383 if (r_cubic_orig_index) {
1384 *r_cubic_orig_index = cubic_orig_index;
1387 *r_cubic_array = cubic_array;
1388 *r_cubic_array_len = knots_len_remaining;
1394 int curve_fit_cubic_to_points_refit_fl(
1395 const float *points,
1396 const unsigned int points_len,
1397 const unsigned int dims,
1398 const float error_threshold,
1399 const unsigned int calc_flag,
1400 const unsigned int *corners,
1401 unsigned int corners_len,
1402 const float corner_angle,
1404 float **r_cubic_array, unsigned int *r_cubic_array_len,
1405 unsigned int **r_cubic_orig_index,
1406 unsigned int **r_corner_index_array, unsigned int *r_corner_index_len)
1408 const uint points_flat_len = points_len * dims;
1409 double *points_db = malloc(sizeof(double) * points_flat_len);
1411 copy_vndb_vnfl(points_db, points, points_flat_len);
1413 double *cubic_array_db = NULL;
1414 float *cubic_array_fl = NULL;
1415 uint cubic_array_len = 0;
1417 int result = curve_fit_cubic_to_points_refit_db(
1418 points_db, points_len, dims, error_threshold, calc_flag, corners, corners_len,
1420 &cubic_array_db, &cubic_array_len,
1422 r_corner_index_array, r_corner_index_len);
1426 uint cubic_array_flat_len = cubic_array_len * 3 * dims;
1427 cubic_array_fl = malloc(sizeof(float) * cubic_array_flat_len);
1428 for (uint i = 0; i < cubic_array_flat_len; i++) {
1429 cubic_array_fl[i] = (float)cubic_array_db[i];
1431 free(cubic_array_db);
1434 *r_cubic_array = cubic_array_fl;
1435 *r_cubic_array_len = cubic_array_len;