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