Blender V4.3
curveprofile.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2019 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <algorithm>
10
11#include "MEM_guardedalloc.h"
12
13#include "DNA_curve_types.h"
15
16#include "BLI_math_geom.h"
17#include "BLI_math_vector.h"
18#include "BLI_rect.h"
19#include "BLI_utildefines.h"
20
21#include "BKE_curve.hh"
22#include "BKE_curveprofile.h"
23
24#include "BLO_read_write.hh"
25
27#define PROF_TABLE_MAX 512
28
29/* -------------------------------------------------------------------- */
34{
35 CurveProfile *profile = MEM_cnew<CurveProfile>(__func__);
36
38 profile->preset = preset;
40 BKE_curveprofile_update(profile, 0);
41
42 return profile;
43}
44
46{
47 MEM_SAFE_FREE(profile->path);
48 MEM_SAFE_FREE(profile->table);
49 MEM_SAFE_FREE(profile->segments);
50}
51
53{
54 if (profile) {
56 MEM_freeN(profile);
57 }
58}
59
61{
62 *target = *profile;
63
64 target->path = (CurveProfilePoint *)MEM_dupallocN(profile->path);
65 target->table = (CurveProfilePoint *)MEM_dupallocN(profile->table);
66 target->segments = (CurveProfilePoint *)MEM_dupallocN(profile->segments);
67
68 /* Update the reference the points have to the profile. */
69 for (int i = 0; i < target->path_len; i++) {
70 target->path[i].profile = target;
71 }
72}
73
75{
76 if (profile) {
77 CurveProfile *new_prdgt = (CurveProfile *)MEM_dupallocN(profile);
78 BKE_curveprofile_copy_data(new_prdgt, profile);
79 return new_prdgt;
80 }
81 return nullptr;
82}
83
85{
86 BLO_write_struct(writer, CurveProfile, profile);
87 BLO_write_struct_array(writer, CurveProfilePoint, profile->path_len, profile->path);
88}
89
91{
92 BLO_read_struct_array(reader, CurveProfilePoint, profile->path_len, &profile->path);
93 profile->table = nullptr;
94 profile->segments = nullptr;
95
96 /* Reset the points' pointers to the profile. */
97 for (int i = 0; i < profile->path_len; i++) {
98 profile->path[i].profile = profile;
99 }
100
101 BKE_curveprofile_init(profile, profile->segments_len);
102}
103
106/* -------------------------------------------------------------------- */
111 const bool handle_1,
112 const bool snap,
113 const float delta[2])
114{
115 short handle_type = (handle_1) ? point->h1 : point->h2;
116 float *handle_location = (handle_1) ? &point->h1_loc[0] : &point->h2_loc[0];
117
118 float start_position[2];
119 copy_v2_v2(start_position, handle_location);
120
121 /* Don't move the handle if it's not a free handle type. */
122 if (!ELEM(handle_type, HD_FREE, HD_ALIGN)) {
123 return false;
124 }
125
126 /* Move the handle. */
127 handle_location[0] += delta ? delta[0] : 0.0f;
128 handle_location[1] += delta ? delta[1] : 0.0f;
129 if (snap) {
130 handle_location[0] = 0.125f * roundf(8.0f * handle_location[0]);
131 handle_location[1] = 0.125f * roundf(8.0f * handle_location[1]);
132 }
133
134 /* Move the other handle if they are aligned. */
135 if (handle_type == HD_ALIGN) {
136 short other_handle_type = (handle_1) ? point->h2 : point->h1;
137 if (other_handle_type == HD_ALIGN) {
138 float *other_handle_location = (handle_1) ? &point->h2_loc[0] : &point->h1_loc[0];
139 other_handle_location[0] = 2.0f * point->x - handle_location[0];
140 other_handle_location[1] = 2.0f * point->y - handle_location[1];
141 }
142 }
143
144 if (!equals_v2v2(handle_location, start_position)) {
145 return true;
146 }
147 return false;
148}
149
151 CurveProfilePoint *point,
152 const bool snap,
153 const float delta[2])
154{
155 /* Don't move the final point. */
156 if (point == &profile->path[profile->path_len - 1]) {
157 return false;
158 }
159 /* Don't move the first point. */
160 if (point == profile->path) {
161 return false;
162 }
163 float origx = point->x;
164 float origy = point->y;
165
166 point->x += delta[0];
167 point->y += delta[1];
168 if (snap) {
169 point->x = 0.125f * roundf(8.0f * point->x);
170 point->y = 0.125f * roundf(8.0f * point->y);
171 }
172
173 /* Clip here instead to test clipping here to stop handles from moving too. */
174 if (profile->flag & PROF_USE_CLIP) {
175 point->x = max_ff(point->x, profile->clip_rect.xmin);
176 point->x = min_ff(point->x, profile->clip_rect.xmax);
177 point->y = max_ff(point->y, profile->clip_rect.ymin);
178 point->y = min_ff(point->y, profile->clip_rect.ymax);
179 }
180
181 /* Also move free handles even when they aren't selected. */
182 if (ELEM(point->h1, HD_FREE, HD_ALIGN)) {
183 point->h1_loc[0] += point->x - origx;
184 point->h1_loc[1] += point->y - origy;
185 }
186 if (ELEM(point->h2, HD_FREE, HD_ALIGN)) {
187 point->h2_loc[0] += point->x - origx;
188 point->h2_loc[1] += point->y - origy;
189 }
190
191 if (point->x != origx || point->y != origy) {
192 return true;
193 }
194 return false;
195}
196
198{
199 /* Must have 2 points minimum. */
200 if (profile->path_len <= 2) {
201 return false;
202 }
203
204 /* Input point must be within the array. */
205 if (!(point > profile->path && point < profile->path + profile->path_len)) {
206 return false;
207 }
208
210 sizeof(CurveProfilePoint) * profile->path_len, __func__);
211
212 int i_delete = int(point - profile->path);
213 BLI_assert(i_delete > 0);
214
215 /* Copy the before and after the deleted point. */
216 memcpy(new_path, profile->path, sizeof(CurveProfilePoint) * i_delete);
217 memcpy(new_path + i_delete,
218 profile->path + i_delete + 1,
219 sizeof(CurveProfilePoint) * (profile->path_len - i_delete - 1));
220
221 MEM_freeN(profile->path);
222 profile->path = new_path;
223 profile->path_len -= 1;
224 return true;
225}
226
228{
229 /* Copy every point without the flag into the new path. */
231 sizeof(CurveProfilePoint) * profile->path_len, __func__);
232
233 /* Build the new list without any of the points with the flag. Keep the first and last points. */
234 int i_new = 1;
235 int i_old = 1;
236 int n_removed = 0;
237 new_path[0] = profile->path[0];
238 for (; i_old < profile->path_len - 1; i_old++) {
239 if (!(profile->path[i_old].flag & flag)) {
240 new_path[i_new] = profile->path[i_old];
241 i_new++;
242 }
243 else {
244 n_removed++;
245 }
246 }
247 new_path[i_new] = profile->path[i_old];
248
249 MEM_freeN(profile->path);
250 profile->path = new_path;
251 profile->path_len -= n_removed;
252}
253
257static void point_init(CurveProfilePoint *point, float x, float y, short flag, char h1, char h2)
258{
259 point->x = x;
260 point->y = y;
261 point->flag = flag;
262 point->h1 = h1;
263 point->h2 = h2;
264}
265
267{
268 const float new_loc[2] = {x, y};
269
270 /* Don't add more control points than the maximum size of the higher resolution table. */
271 if (profile->path_len == PROF_TABLE_MAX - 1) {
272 return nullptr;
273 }
274
275 /* Find the index at the line segment that's closest to the new position. */
276 float min_distance = FLT_MAX;
277 int i_insert = 0;
278 for (int i = 0; i < profile->path_len - 1; i++) {
279 const float loc1[2] = {profile->path[i].x, profile->path[i].y};
280 const float loc2[2] = {profile->path[i + 1].x, profile->path[i + 1].y};
281
282 float distance = dist_squared_to_line_segment_v2(new_loc, loc1, loc2);
283 if (distance < min_distance) {
284 min_distance = distance;
285 i_insert = i + 1;
286 }
287 }
288
289 /* Insert the new point at the location we found and copy all of the old points in as well. */
290 profile->path_len++;
292 sizeof(CurveProfilePoint) * profile->path_len, __func__);
293 CurveProfilePoint *new_pt = nullptr;
294 for (int i_new = 0, i_old = 0; i_new < profile->path_len; i_new++) {
295 if (i_new != i_insert) {
296 /* Insert old points. */
297 new_path[i_new] = profile->path[i_old];
298 new_path[i_new].flag &= ~PROF_SELECT; /* Deselect old points. */
299 i_old++;
300 }
301 else {
302 /* Insert new point. */
303 /* Set handles of new point based on its neighbors. */
304 char new_handle_type = (new_path[i_new - 1].h2 == HD_VECT &&
305 profile->path[i_insert].h1 == HD_VECT) ?
306 HD_VECT :
307 HD_AUTO;
308 point_init(&new_path[i_new], x, y, PROF_SELECT, new_handle_type, new_handle_type);
309 new_pt = &new_path[i_new];
310 /* Give new point a reference to the profile. */
311 new_pt->profile = profile;
312 }
313 }
314
315 /* Free the old path and use the new one. */
316 MEM_freeN(profile->path);
317 profile->path = new_path;
318 return new_pt;
319}
320
321void BKE_curveprofile_selected_handle_set(CurveProfile *profile, int type_1, int type_2)
322{
323 for (int i = 0; i < profile->path_len; i++) {
324 if (ELEM(profile->path[i].flag, PROF_SELECT, PROF_H1_SELECT, PROF_H2_SELECT)) {
325 profile->path[i].h1 = type_1;
326 profile->path[i].h2 = type_2;
327
328 if (type_1 == HD_ALIGN && type_2 == HD_ALIGN) {
329 /* Align the handles. */
330 BKE_curveprofile_move_handle(&profile->path[i], true, false, nullptr);
331 }
332 }
333 }
334}
335
337{
338 CurveProfilePoint new_point = *point;
339 point_init(&new_point, point->y, point->x, point->flag, point->h2, point->h1);
340 return new_point;
341}
342
344{
345 /* When there are only two points, reversing shouldn't do anything. */
346 if (profile->path_len == 2) {
347 return;
348 }
350 sizeof(CurveProfilePoint) * profile->path_len, __func__);
351 /* Mirror the new points across the y = x line */
352 for (int i = 0; i < profile->path_len; i++) {
353 int i_reversed = profile->path_len - i - 1;
354 BLI_assert(i_reversed >= 0);
355 new_path[i_reversed] = mirror_point(&profile->path[i]);
356 new_path[i_reversed].profile = profile;
357
358 /* Mirror free handles, they can't be recalculated. */
359 if (ELEM(profile->path[i].h1, HD_FREE, HD_ALIGN)) {
360 new_path[i_reversed].h1_loc[0] = profile->path[i].h2_loc[1];
361 new_path[i_reversed].h1_loc[1] = profile->path[i].h2_loc[0];
362 }
363 if (ELEM(profile->path[i].h2, HD_FREE, HD_ALIGN)) {
364 new_path[i_reversed].h2_loc[0] = profile->path[i].h1_loc[1];
365 new_path[i_reversed].h2_loc[1] = profile->path[i].h1_loc[0];
366 }
367 }
368
369 /* Free the old points and use the new ones */
370 MEM_freeN(profile->path);
371 profile->path = new_path;
372}
373
378{
379 int n = profile->path_len;
380
381 point_init(&profile->path[0], 1.0f, 0.0f, 0, HD_VECT, HD_VECT);
382 point_init(&profile->path[1], 1.0f, 0.5f, 0, HD_VECT, HD_VECT);
383 for (int i = 1; i < n - 2; i++) {
384 const float x = 1.0f - (0.5f * (1.0f - cosf(float(i / float(n - 3)) * M_PI_2)));
385 const float y = 0.5f + 0.5f * sinf(float((i / float(n - 3)) * M_PI_2));
386 point_init(&profile->path[i], x, y, 0, HD_AUTO, HD_AUTO);
387 }
388 point_init(&profile->path[n - 2], 0.5f, 1.0f, 0, HD_VECT, HD_VECT);
389 point_init(&profile->path[n - 1], 0.0f, 1.0f, 0, HD_VECT, HD_VECT);
390}
391
397{
398 int n = profile->path_len;
399
400 /* Special case for two points to avoid dividing by zero later. */
401 if (n == 2) {
402 point_init(&profile->path[0], 1.0f, 0.0f, 0, HD_VECT, HD_VECT);
403 point_init(&profile->path[0], 0.0f, 1.0f, 0, HD_VECT, HD_VECT);
404 return;
405 }
406
407 float n_steps_x = (n % 2 == 0) ? n : (n - 1);
408 float n_steps_y = (n % 2 == 0) ? (n - 2) : (n - 1);
409
410 for (int i = 0; i < n; i++) {
411 int step_x = (i + 1) / 2;
412 int step_y = i / 2;
413 const float x = 1.0f - (float(2 * step_x) / n_steps_x);
414 const float y = float(2 * step_y) / n_steps_y;
415 point_init(&profile->path[i], x, y, 0, HD_VECT, HD_VECT);
416 }
417}
418
420{
421 profile->view_rect = profile->clip_rect;
422}
423
425{
426 MEM_SAFE_FREE(profile->path);
427
428 eCurveProfilePresets preset = static_cast<eCurveProfilePresets>(profile->preset);
429 switch (preset) {
430 case PROF_PRESET_LINE:
431 profile->path_len = 2;
432 break;
434 /* Use a dynamic number of control points for the widget's profile. */
435 if (profile->segments_len < 4) {
436 /* But always use enough points to at least build the support points. */
437 profile->path_len = 5;
438 }
439 else {
440 profile->path_len = profile->segments_len + 1;
441 }
442 break;
444 profile->path_len = 13;
445 break;
447 profile->path_len = 11;
448 break;
450 /* Also use dynamic number of control points based on the set number of segments. */
451 if (profile->segments_len == 0) {
452 /* totsegments hasn't been set-- use the number of control points for 8 steps. */
453 profile->path_len = 17;
454 }
455 else {
456 profile->path_len = profile->segments_len + 1;
457 }
458 break;
459 }
460
461 profile->path = (CurveProfilePoint *)MEM_callocN(sizeof(CurveProfilePoint) * profile->path_len,
462 __func__);
463
464 switch (preset) {
465 case PROF_PRESET_LINE:
466 point_init(&profile->path[0], 1.0f, 0.0f, 0, HD_AUTO, HD_AUTO);
467 point_init(&profile->path[1], 0.0f, 1.0f, 0, HD_AUTO, HD_AUTO);
468 break;
471 break;
473 point_init(&profile->path[0], 1.0f, 0.0f, 0, HD_VECT, HD_VECT);
474 point_init(&profile->path[1], 1.0f, 0.125f, 0, HD_VECT, HD_VECT);
475 point_init(&profile->path[2], 0.92f, 0.16f, 0, HD_AUTO, HD_AUTO);
476 point_init(&profile->path[3], 0.875f, 0.25f, 0, HD_VECT, HD_VECT);
477 point_init(&profile->path[4], 0.8f, 0.25f, 0, HD_VECT, HD_VECT);
478 point_init(&profile->path[5], 0.733f, 0.433f, 0, HD_AUTO, HD_AUTO);
479 point_init(&profile->path[6], 0.582f, 0.522f, 0, HD_AUTO, HD_AUTO);
480 point_init(&profile->path[7], 0.4f, 0.6f, 0, HD_AUTO, HD_AUTO);
481 point_init(&profile->path[8], 0.289f, 0.727f, 0, HD_AUTO, HD_AUTO);
482 point_init(&profile->path[9], 0.25f, 0.925f, 0, HD_VECT, HD_VECT);
483 point_init(&profile->path[10], 0.175f, 0.925f, 0, HD_VECT, HD_VECT);
484 point_init(&profile->path[11], 0.175f, 1.0f, 0, HD_VECT, HD_VECT);
485 point_init(&profile->path[12], 0.0f, 1.0f, 0, HD_VECT, HD_VECT);
486 break;
488 point_init(&profile->path[0], 1.0f, 0.0f, 0, HD_VECT, HD_VECT);
489 point_init(&profile->path[1], 1.0f, 0.25f, 0, HD_VECT, HD_VECT);
490 point_init(&profile->path[2], 0.75f, 0.25f, 0, HD_VECT, HD_VECT);
491 point_init(&profile->path[3], 0.75f, 0.325f, 0, HD_VECT, HD_VECT);
492 point_init(&profile->path[4], 0.925f, 0.4f, 0, HD_AUTO, HD_AUTO);
493 point_init(&profile->path[5], 0.975f, 0.5f, 0, HD_AUTO, HD_AUTO);
494 point_init(&profile->path[6], 0.94f, 0.65f, 0, HD_AUTO, HD_AUTO);
495 point_init(&profile->path[7], 0.85f, 0.75f, 0, HD_AUTO, HD_AUTO);
496 point_init(&profile->path[8], 0.75f, 0.875f, 0, HD_AUTO, HD_AUTO);
497 point_init(&profile->path[9], 0.7f, 1.0f, 0, HD_VECT, HD_VECT);
498 point_init(&profile->path[10], 0.0f, 1.0f, 0, HD_VECT, HD_VECT);
499 break;
502 break;
503 }
504
505 profile->flag &= ~PROF_DIRTY_PRESET;
506
507 /* Ensure each point has a reference to the profile. */
508 for (int i = 0; i < profile->path_len; i++) {
509 profile->path[i].profile = profile;
510 }
511
512 MEM_SAFE_FREE(profile->table);
513 profile->table = nullptr;
514}
515
518/* -------------------------------------------------------------------- */
523{
525 const int resolution = 16;
526
527 /* Make sure there is always one sample, even if there are no control points. */
528 return std::clamp((profile->path_len - 1) * resolution + 1, 1, PROF_TABLE_MAX);
529}
530
535static bool is_curved_edge(CurveProfilePoint *path, int i)
536{
537 return (path[i].h2 != HD_VECT || path[i + 1].h1 != HD_VECT);
538}
539
545 const CurveProfilePoint *prev,
546 const CurveProfilePoint *next)
547{
548 if (point->h1 == HD_FREE && point->h2 == HD_FREE) {
549 return;
550 }
551
552 float *point_loc = &point->x;
553
554 float pt[2];
555 const float *prev_loc, *next_loc;
556 if (prev == nullptr) {
557 next_loc = &next->x;
558 pt[0] = 2.0f * point_loc[0] - next_loc[0];
559 pt[1] = 2.0f * point_loc[1] - next_loc[1];
560 prev_loc = pt;
561 }
562 else {
563 prev_loc = &prev->x;
564 }
565
566 if (next == nullptr) {
567 prev_loc = &prev->x;
568 pt[0] = 2.0f * point_loc[0] - prev_loc[0];
569 pt[1] = 2.0f * point_loc[1] - prev_loc[1];
570 next_loc = pt;
571 }
572 else {
573 next_loc = &next->x;
574 }
575
576 float dvec_a[2], dvec_b[2];
577 sub_v2_v2v2(dvec_a, point_loc, prev_loc);
578 sub_v2_v2v2(dvec_b, next_loc, point_loc);
579
580 float len_a = len_v2(dvec_a);
581 float len_b = len_v2(dvec_b);
582 if (len_a == 0.0f) {
583 len_a = 1.0f;
584 }
585 if (len_b == 0.0f) {
586 len_b = 1.0f;
587 }
588
589 if (point->h1 == HD_AUTO || point->h2 == HD_AUTO) {
590 float tvec[2];
591 tvec[0] = dvec_b[0] / len_b + dvec_a[0] / len_a;
592 tvec[1] = dvec_b[1] / len_b + dvec_a[1] / len_a;
593
594 float len = len_v2(tvec) * 2.5614f;
595 if (len != 0.0f) {
596 if (point->h1 == HD_AUTO) {
597 len_a /= len;
598 madd_v2_v2v2fl(point->h1_loc, point_loc, tvec, -len_a);
599 }
600 if (point->h2 == HD_AUTO) {
601 len_b /= len;
602 madd_v2_v2v2fl(point->h2_loc, point_loc, tvec, len_b);
603 }
604 }
605 }
606
607 if (point->h1 == HD_VECT) {
608 madd_v2_v2v2fl(point->h1_loc, point_loc, dvec_a, -1.0f / 3.0f);
609 }
610 if (point->h2 == HD_VECT) {
611 madd_v2_v2v2fl(point->h2_loc, point_loc, dvec_b, 1.0f / 3.0f);
612 }
613}
614
615static void calculate_path_handles(CurveProfilePoint *path, int path_len)
616{
617 point_calculate_handle(&path[0], nullptr, &path[1]);
618 for (int i = 1; i < path_len - 1; i++) {
619 point_calculate_handle(&path[i], &path[i - 1], &path[i + 1]);
620 }
621 point_calculate_handle(&path[path_len - 1], &path[path_len - 2], nullptr);
622}
623
630static float bezt_edge_handle_angle(const CurveProfilePoint *path, int i_edge)
631{
632 /* Find the direction of the handles that define this edge along the direction of the path. */
633 float start_handle_direction[2], end_handle_direction[2];
634 /* Handle 2 - point location. */
635 sub_v2_v2v2(start_handle_direction, path[i_edge].h2_loc, &path[i_edge].x);
636 /* Point location - handle 1. */
637 sub_v2_v2v2(end_handle_direction, &path[i_edge + 1].x, path[i_edge + 1].h1_loc);
638
639 return angle_v2v2(start_handle_direction, end_handle_direction);
640}
641
649
653static int sort_points_curvature(const void *in_a, const void *in_b)
654{
655 const CurvatureSortPoint *a = (const CurvatureSortPoint *)in_a;
656 const CurvatureSortPoint *b = (const CurvatureSortPoint *)in_b;
657
658 if (a->point_curvature > b->point_curvature) {
659 return 0;
660 }
661
662 return 1;
663}
664
678static void create_samples(CurveProfile *profile,
679 int n_segments,
680 bool sample_straight_edges,
681 CurveProfilePoint *r_samples)
682{
683 CurveProfilePoint *path = profile->path;
684 int totpoints = profile->path_len;
685 BLI_assert(n_segments > 0);
686
687 int totedges = totpoints - 1;
688
689 calculate_path_handles(path, totpoints);
690
691 /* Create a list of edge indices with the most curved at the start, least curved at the end. */
693 sizeof(CurvatureSortPoint) * totedges, __func__);
694 for (int i = 0; i < totedges; i++) {
695 curve_sorted[i].point_index = i;
696 /* Calculate the curvature of each edge once for use when sorting for curvature. */
697 curve_sorted[i].point_curvature = bezt_edge_handle_angle(path, i);
698 }
699 qsort(curve_sorted, totedges, sizeof(CurvatureSortPoint), sort_points_curvature);
700
701 /* Assign the number of sampled points for each edge. */
702 int16_t *n_samples = (int16_t *)MEM_callocN(sizeof(int16_t) * totedges, "samples numbers");
703 int n_added = 0;
704 int n_left;
705 if (n_segments >= totedges) {
706 if (sample_straight_edges) {
707 /* Assign an even number to each edge if it's possible, then add the remainder of sampled
708 * points starting with the most curved edges. */
709 int n_common = n_segments / totedges;
710 n_left = n_segments % totedges;
711
712 /* Assign the points that fill fit evenly to the edges. */
713 if (n_common > 0) {
714 BLI_assert(n_common < INT16_MAX);
715 for (int i = 0; i < totedges; i++) {
716 n_samples[i] = n_common;
717 n_added += n_common;
718 }
719 }
720 }
721 else {
722 /* Count the number of curved edges */
723 int n_curved_edges = 0;
724 for (int i = 0; i < totedges; i++) {
725 if (is_curved_edge(path, i)) {
726 n_curved_edges++;
727 }
728 }
729 /* Just sample all of the edges if there are no curved edges. */
730 n_curved_edges = (n_curved_edges == 0) ? totedges : n_curved_edges;
731
732 /* Give all of the curved edges the same number of points and straight edges one point. */
733 n_left = n_segments - (totedges - n_curved_edges); /* Left after 1 for each straight edge. */
734 int n_common = n_left / n_curved_edges; /* Number assigned to all curved edges */
735 if (n_common > 0) {
736 for (int i = 0; i < totedges; i++) {
737 /* Add the common number if it's a curved edge or if edges are curved. */
738 if (is_curved_edge(path, i) || n_curved_edges == totedges) {
739 BLI_assert(n_common + n_samples[i] < INT16_MAX);
740 n_samples[i] += n_common;
741 n_added += n_common;
742 }
743 else {
744 n_samples[i] = 1;
745 n_added++;
746 }
747 }
748 }
749 n_left -= n_common * n_curved_edges;
750 }
751 }
752 else {
753 /* Not enough segments to give one to each edge, so just give them to the most curved edges. */
754 n_left = n_segments;
755 }
756 /* Assign the remainder of the points that couldn't be spread out evenly. */
757 BLI_assert(n_left < totedges);
758 for (int i = 0; i < n_left; i++) {
759 BLI_assert(n_samples[curve_sorted[i].point_index] < INT16_MAX);
760 n_samples[curve_sorted[i].point_index]++;
761 n_added++;
762 }
763
764 BLI_assert(n_added == n_segments); /* n_added is just used for this assert, could remove it. */
765 UNUSED_VARS_NDEBUG(n_added);
766
767 /* Sample the points and add them to the locations table. */
768 for (int i_sample = 0, i = 0; i < totedges; i++) {
769 if (n_samples[i] > 0) {
770 /* Carry over the handle types from the control point to its first corresponding sample. */
771 r_samples[i_sample].h1 = path[i].h1;
772 r_samples[i_sample].h2 = path[i].h2;
773 /* All extra sample points for this control point get "auto" handles. */
774 for (int j = i_sample + 1; j < i_sample + n_samples[i]; j++) {
775 r_samples[j].flag = 0;
776 r_samples[j].h1 = HD_AUTO;
777 r_samples[j].h2 = HD_AUTO;
778 BLI_assert(j < n_segments);
779 }
780
781 /* Sample from the bezier points. X then Y values. */
783 path[i].h2_loc[0],
784 path[i + 1].h1_loc[0],
785 path[i + 1].x,
786 &r_samples[i_sample].x,
787 n_samples[i],
788 sizeof(CurveProfilePoint));
790 path[i].h2_loc[1],
791 path[i + 1].h1_loc[1],
792 path[i + 1].y,
793 &r_samples[i_sample].y,
794 n_samples[i],
795 sizeof(CurveProfilePoint));
796 }
797 i_sample += n_samples[i]; /* Add the next set of points after the ones we just added. */
798 BLI_assert(i_sample <= n_segments);
799 }
800
801 MEM_freeN(curve_sorted);
802 MEM_freeN(n_samples);
803}
804
806{
807 profile->flag = PROF_USE_CLIP;
808
809 BLI_rctf_init(&profile->view_rect, 0.0f, 1.0f, 0.0f, 1.0f);
810 profile->clip_rect = profile->view_rect;
811
812 profile->path_len = 2;
813 profile->path = (CurveProfilePoint *)MEM_callocN(2 * sizeof(CurveProfilePoint), __func__);
814
815 profile->path[0].x = 1.0f;
816 profile->path[0].y = 0.0f;
817 profile->path[0].profile = profile;
818 profile->path[1].x = 1.0f;
819 profile->path[1].y = 1.0f;
820 profile->path[1].profile = profile;
821
822 profile->changed_timestamp = 0;
823}
824
825void BKE_curveprofile_init(CurveProfile *profile, short segments_len)
826{
827 if (segments_len != profile->segments_len) {
828 profile->flag |= PROF_DIRTY_PRESET;
829 }
830 profile->segments_len = segments_len;
831
832 /* Calculate the higher resolution / segments tables for display and evaluation. */
834}
835
843{
845
846 return len_v2v2(&profile->table[i].x, &profile->table[i + 1].x);
847}
848
854static float curveprofile_total_length(const CurveProfile *profile)
855{
856 float total_length = 0;
857 for (int i = 0; i < BKE_curveprofile_table_size(profile) - 1; i++) {
858 total_length += len_v2v2(&profile->table[i].x, &profile->table[i + 1].x);
859 }
860 return total_length;
861}
862
872 int n_segments,
873 CurveProfilePoint *r_samples)
874{
875 const float total_length = curveprofile_total_length(profile);
876 const float segment_length = total_length / n_segments;
877 float distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, 0);
878 float distance_to_previous_table_point = 0.0f;
879 int i_table = 0;
880
881 /* Set the location for the first point. */
882 r_samples[0].x = profile->table[0].x;
883 r_samples[0].y = profile->table[0].y;
884
885 /* Travel along the path, recording the locations of segments as we pass them. */
886 float segment_left = segment_length;
887 for (int i = 1; i < n_segments; i++) {
888 /* Travel over all of the points that fit inside this segment. */
889 while (distance_to_next_table_point < segment_left) {
890 segment_left -= distance_to_next_table_point;
891 i_table++;
892 distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, i_table);
893 distance_to_previous_table_point = 0.0f;
894 }
895 /* We're at the last table point that fits inside the current segment, use interpolation. */
896 float factor = (distance_to_previous_table_point + segment_left) /
897 (distance_to_previous_table_point + distance_to_next_table_point);
898 r_samples[i].x = interpf(profile->table[i_table + 1].x, profile->table[i_table].x, factor);
899 r_samples[i].y = interpf(profile->table[i_table + 1].y, profile->table[i_table].y, factor);
900 BLI_assert(factor <= 1.0f && factor >= 0.0f);
901#ifdef DEBUG_CURVEPROFILE_EVALUATE
902 printf("segment_left: %.3f\n", segment_left);
903 printf("i_table: %d\n", i_table);
904 printf("distance_to_previous_table_point: %.3f\n", distance_to_previous_table_point);
905 printf("distance_to_next_table_point: %.3f\n", distance_to_next_table_point);
906 printf("Interpolating with factor %.3f from (%.3f, %.3f) to (%.3f, %.3f)\n\n",
907 factor,
908 profile->table[i_table].x,
909 profile->table[i_table].y,
910 profile->table[i_table + 1].x,
911 profile->table[i_table + 1].y);
912#endif
913
914 /* We sampled in between this table point and the next, so the next travel step is smaller. */
915 distance_to_next_table_point -= segment_left;
916 distance_to_previous_table_point += segment_left;
917 segment_left = segment_length;
918 }
919}
920
926{
927 int n_samples = BKE_curveprofile_table_size(profile);
929 sizeof(CurveProfilePoint) * (n_samples + 1), __func__);
930
931 if (n_samples > 1) {
932 create_samples(profile, n_samples - 1, false, new_table);
933 }
934
935 /* Manually add last point at the end of the profile */
936 new_table[n_samples - 1].x = 0.0f;
937 new_table[n_samples - 1].y = 1.0f;
938
939 MEM_SAFE_FREE(profile->table);
940 profile->table = new_table;
941}
942
948{
949 int n_samples = profile->segments_len;
950 if (n_samples <= 0) {
951 return;
952 }
954 sizeof(CurveProfilePoint) * (n_samples + 1), __func__);
955
956 if (profile->flag & PROF_SAMPLE_EVEN_LENGTHS) {
957 /* Even length sampling incompatible with only straight edge sampling for now. */
958 create_samples_even_spacing(profile, n_samples, new_table);
959 }
960 else {
961 create_samples(profile, n_samples, profile->flag & PROF_SAMPLE_STRAIGHT_EDGES, new_table);
962 }
963
964 MEM_SAFE_FREE(profile->segments);
965 profile->segments = new_table;
966}
967
968void BKE_curveprofile_update(CurveProfile *profile, const int update_flags)
969{
970 CurveProfilePoint *points = profile->path;
971 const rctf *clipr = &profile->clip_rect;
972
973 profile->changed_timestamp++;
974
975 /* Clamp with the clipping rect in case something got past. */
976 if (profile->flag & PROF_USE_CLIP) {
977 /* Move points inside the clip rectangle. */
978 if (update_flags & PROF_UPDATE_CLIP) {
979 for (int i = 0; i < profile->path_len; i++) {
980 points[i].x = clamp_f(points[i].x, clipr->xmin, clipr->xmax);
981 points[i].y = clamp_f(points[i].y, clipr->ymin, clipr->ymax);
982
983 /* Extra sanity assert to make sure the points have the right profile pointer. */
984 BLI_assert(points[i].profile == profile);
985 }
986 }
987 /* Ensure zoom-level respects clipping. */
988 if (BLI_rctf_size_x(&profile->view_rect) > BLI_rctf_size_x(&profile->clip_rect)) {
989 profile->view_rect.xmin = profile->clip_rect.xmin;
990 profile->view_rect.xmax = profile->clip_rect.xmax;
991 }
992 if (BLI_rctf_size_y(&profile->view_rect) > BLI_rctf_size_y(&profile->clip_rect)) {
993 profile->view_rect.ymin = profile->clip_rect.ymin;
994 profile->view_rect.ymax = profile->clip_rect.ymax;
995 }
996 }
997
998 /* Remove doubles with a threshold set at 1% of default range. */
999 float thresh = pow2f(0.01f * BLI_rctf_size_x(clipr));
1000 if (update_flags & PROF_UPDATE_REMOVE_DOUBLES && profile->path_len > 2) {
1001 for (int i = 0; i < profile->path_len - 1; i++) {
1002 if (len_squared_v2v2(&points[i].x, &points[i + 1].x) < thresh) {
1003 if (i == 0) {
1004 BKE_curveprofile_remove_point(profile, &points[1]);
1005 }
1006 else {
1007 BKE_curveprofile_remove_point(profile, &points[i]);
1008 }
1009 break; /* Assumes 1 deletion per update call is ok. */
1010 }
1011 }
1012 }
1013
1014 /* Create the high resolution table for drawing and some evaluation functions. */
1015 curveprofile_make_table(profile);
1016
1017 /* Store a table of samples for the segment locations for a preview and the table's user. */
1018 if (profile->segments_len > 0) {
1020 }
1021}
1022
1024 float length_portion,
1025 float *x_out,
1026 float *y_out)
1027{
1028 const float total_length = curveprofile_total_length(profile);
1029 const float requested_length = length_portion * total_length;
1030
1031 /* Find the last point along the path with a lower length portion than the input. */
1032 int i = 0;
1033 float length_traveled = 0.0f;
1034 while (length_traveled < requested_length) {
1035 /* Check if we reached the last point before the final one. */
1036 if (i == BKE_curveprofile_table_size(profile) - 2) {
1037 break;
1038 }
1039 float new_length = curveprofile_distance_to_next_table_point(profile, i);
1040 if (length_traveled + new_length >= requested_length) {
1041 break;
1042 }
1043 length_traveled += new_length;
1044 i++;
1045 }
1046
1047 /* Now travel the remaining distance of length portion down the path to the next point and
1048 * find the location where we stop. */
1049 float distance_to_next_point = curveprofile_distance_to_next_table_point(profile, i);
1050 float lerp_factor = (requested_length - length_traveled) / distance_to_next_point;
1051
1052#ifdef DEBUG_CURVEPROFILE_EVALUATE
1053 printf("CURVEPROFILE EVALUATE\n");
1054 printf(" length portion input: %f\n", double(length_portion));
1055 printf(" requested path length: %f\n", double(requested_length));
1056 printf(" distance to next point: %f\n", double(distance_to_next_point));
1057 printf(" length traveled: %f\n", double(length_traveled));
1058 printf(" lerp-factor: %f\n", double(lerp_factor));
1059 printf(" ith point (%f, %f)\n", double(profile->path[i].x), double(profile->path[i].y));
1060 printf(" next point(%f, %f)\n", double(profile->path[i + 1].x), double(profile->path[i + 1].y));
1061#endif
1062
1063 *x_out = interpf(profile->table[i].x, profile->table[i + 1].x, lerp_factor);
1064 *y_out = interpf(profile->table[i].y, profile->table[i + 1].y, lerp_factor);
1065}
1066
void BKE_curve_forward_diff_bezier(float q0, float q1, float q2, float q3, float *p, int it, int stride)
Definition curve.cc:1663
@ PROF_UPDATE_CLIP
@ PROF_UPDATE_REMOVE_DOUBLES
@ PROF_UPDATE_NONE
#define BLI_assert(a)
Definition BLI_assert.h:50
MINLINE float max_ff(float a, float b)
MINLINE float pow2f(float x)
MINLINE float clamp_f(float value, float min, float max)
MINLINE float min_ff(float a, float b)
#define M_PI_2
MINLINE float interpf(float target, float origin, float t)
float dist_squared_to_line_segment_v2(const float p[2], const float l1[2], const float l2[2])
Definition math_geom.cc:289
MINLINE float len_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void madd_v2_v2v2fl(float r[2], const float a[2], const float b[2], float f)
float angle_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE bool equals_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
void BLI_rctf_init(struct rctf *rect, float xmin, float xmax, float ymin, float ymax)
Definition rct.c:408
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:197
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:201
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define BLO_write_struct(writer, struct_name, data_ptr)
#define BLO_write_struct_array(writer, struct_name, array_size, data_ptr)
#define BLO_read_struct_array(reader, struct_name, array_size, ptr_p)
@ HD_VECT
@ HD_FREE
@ HD_AUTO
@ HD_ALIGN
@ PROF_DIRTY_PRESET
@ PROF_SAMPLE_EVEN_LENGTHS
@ PROF_SAMPLE_STRAIGHT_EDGES
@ PROF_PRESET_CROWN
@ PROF_PRESET_LINE
@ PROF_PRESET_CORNICE
@ PROF_PRESET_SUPPORTS
@ PROF_PRESET_STEPS
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
local_group_size(16, 16) .push_constant(Type b
#define printf
static float bezt_edge_handle_angle(const CurveProfilePoint *path, int i_edge)
static float curveprofile_total_length(const CurveProfile *profile)
void BKE_curveprofile_init(CurveProfile *profile, short segments_len)
static void create_samples_even_spacing(CurveProfile *profile, int n_segments, CurveProfilePoint *r_samples)
void BKE_curveprofile_evaluate_length_portion(const CurveProfile *profile, float length_portion, float *x_out, float *y_out)
bool BKE_curveprofile_remove_point(CurveProfile *profile, CurveProfilePoint *point)
CurveProfilePoint * BKE_curveprofile_insert(CurveProfile *profile, float x, float y)
void BKE_curveprofile_copy_data(CurveProfile *target, const CurveProfile *profile)
void BKE_curveprofile_selected_handle_set(CurveProfile *profile, int type_1, int type_2)
#define PROF_TABLE_MAX
static void curveprofile_build_steps(CurveProfile *profile)
bool BKE_curveprofile_move_handle(CurveProfilePoint *point, const bool handle_1, const bool snap, const float delta[2])
bool BKE_curveprofile_move_point(CurveProfile *profile, CurveProfilePoint *point, const bool snap, const float delta[2])
static void curveprofile_build_supports(CurveProfile *profile)
static void curveprofile_make_table(CurveProfile *profile)
void BKE_curveprofile_reset(CurveProfile *profile)
static void calculate_path_handles(CurveProfilePoint *path, int path_len)
static CurveProfilePoint mirror_point(const CurveProfilePoint *point)
static bool is_curved_edge(CurveProfilePoint *path, int i)
int BKE_curveprofile_table_size(const CurveProfile *profile)
void BKE_curveprofile_free_data(CurveProfile *profile)
void BKE_curveprofile_blend_write(BlendWriter *writer, const CurveProfile *profile)
void BKE_curveprofile_blend_read(BlendDataReader *reader, CurveProfile *profile)
void BKE_curveprofile_remove_by_flag(CurveProfile *profile, const short flag)
void BKE_curveprofile_reset_view(CurveProfile *profile)
static void point_calculate_handle(CurveProfilePoint *point, const CurveProfilePoint *prev, const CurveProfilePoint *next)
CurveProfile * BKE_curveprofile_add(eCurveProfilePresets preset)
void BKE_curveprofile_reverse(CurveProfile *profile)
static void point_init(CurveProfilePoint *point, float x, float y, short flag, char h1, char h2)
CurveProfile * BKE_curveprofile_copy(const CurveProfile *profile)
static void create_samples(CurveProfile *profile, int n_segments, bool sample_straight_edges, CurveProfilePoint *r_samples)
void BKE_curveprofile_free(CurveProfile *profile)
static float curveprofile_distance_to_next_table_point(const CurveProfile *profile, int i)
void BKE_curveprofile_update(CurveProfile *profile, const int update_flags)
static int sort_points_curvature(const void *in_a, const void *in_b)
static void curveprofile_make_segments_table(CurveProfile *profile)
void BKE_curveprofile_set_defaults(CurveProfile *profile)
#define sinf(x)
#define cosf(x)
int len
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
static ulong * next
vector snap(vector a, vector b)
Definition node_math.h:65
float distance(float a, float b)
#define FLT_MAX
Definition stdcycles.h:14
signed short int16_t
Definition stdint.h:76
#define INT16_MAX
Definition stdint.h:135
struct CurveProfile * profile
float xmax
float xmin
float ymax
float ymin
uint8_t flag
Definition wm_window.cc:138