Blender  V2.93
curveprofile.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * Copyright (C) 2019 Blender Foundation.
17  * All rights reserved.
18  */
19 
24 #include <float.h>
25 #include <math.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include "MEM_guardedalloc.h"
30 
31 #include "DNA_curve_types.h"
32 #include "DNA_curveprofile_types.h"
33 
34 #include "BLI_blenlib.h"
35 #include "BLI_math.h"
36 #include "BLI_task.h"
37 #include "BLI_threads.h"
38 #include "BLI_utildefines.h"
39 
40 #include "BKE_curve.h"
41 #include "BKE_curveprofile.h"
42 #include "BKE_fcurve.h"
43 
44 #include "BLO_read_write.h"
45 
47 {
48  MEM_SAFE_FREE(profile->path);
49  MEM_SAFE_FREE(profile->table);
50  MEM_SAFE_FREE(profile->segments);
51 }
52 
54 {
55  if (profile) {
57  MEM_freeN(profile);
58  }
59 }
60 
62 {
63  *target = *profile;
64 
65  target->path = MEM_dupallocN(profile->path);
66  target->table = MEM_dupallocN(profile->table);
67  target->segments = MEM_dupallocN(profile->segments);
68 
69  /* Update the reference the points have to the profile. */
70  for (int i = 0; i < target->path_len; i++) {
71  target->path[i].profile = target;
72  }
73 }
74 
76 {
77  if (profile) {
78  CurveProfile *new_prdgt = MEM_dupallocN(profile);
79  BKE_curveprofile_copy_data(new_prdgt, profile);
80  return new_prdgt;
81  }
82  return NULL;
83 }
84 
94  const bool handle_1,
95  const bool snap,
96  const float delta[2])
97 {
98  short handle_type = (handle_1) ? point->h1 : point->h2;
99  float *handle_location = (handle_1) ? &point->h1_loc[0] : &point->h2_loc[0];
100 
101  float start_position[2];
102  copy_v2_v2(start_position, handle_location);
103 
104  /* Don't move the handle if it's not a free handle type. */
105  if (!ELEM(handle_type, HD_FREE, HD_ALIGN)) {
106  return false;
107  }
108 
109  /* Move the handle. */
110  handle_location[0] += delta ? delta[0] : 0.0f;
111  handle_location[1] += delta ? delta[1] : 0.0f;
112  if (snap) {
113  handle_location[0] = 0.125f * roundf(8.0f * handle_location[0]);
114  handle_location[1] = 0.125f * roundf(8.0f * handle_location[1]);
115  }
116 
117  /* Move the other handle if they are aligned. */
118  if (handle_type == HD_ALIGN) {
119  short other_handle_type = (handle_1) ? point->h2 : point->h1;
120  if (other_handle_type == HD_ALIGN) {
121  float *other_handle_location = (handle_1) ? &point->h2_loc[0] : &point->h1_loc[0];
122  other_handle_location[0] = 2.0f * point->x - handle_location[0];
123  other_handle_location[1] = 2.0f * point->y - handle_location[1];
124  }
125  }
126 
127  if (!equals_v2v2(handle_location, start_position)) {
128  return true;
129  }
130  return false;
131 }
132 
142  struct CurveProfilePoint *point,
143  const bool snap,
144  const float delta[2])
145 {
146  /* Don't move the final point. */
147  if (point == &profile->path[profile->path_len - 1]) {
148  return false;
149  }
150  /* Don't move the first point. */
151  if (point == profile->path) {
152  return false;
153  }
154  float origx = point->x;
155  float origy = point->y;
156 
157  point->x += delta[0];
158  point->y += delta[1];
159  if (snap) {
160  point->x = 0.125f * roundf(8.0f * point->x);
161  point->y = 0.125f * roundf(8.0f * point->y);
162  }
163 
164  /* Clip here instead to test clipping here to stop handles from moving too. */
165  if (profile->flag & PROF_USE_CLIP) {
166  point->x = max_ff(point->x, profile->clip_rect.xmin);
167  point->x = min_ff(point->x, profile->clip_rect.xmax);
168  point->y = max_ff(point->y, profile->clip_rect.ymin);
169  point->y = min_ff(point->y, profile->clip_rect.ymax);
170  }
171 
172  /* Also move free handles even when they aren't selected. */
173  if (ELEM(point->h1, HD_FREE, HD_ALIGN)) {
174  point->h1_loc[0] += point->x - origx;
175  point->h1_loc[1] += point->y - origy;
176  }
177  if (ELEM(point->h2, HD_FREE, HD_ALIGN)) {
178  point->h2_loc[0] += point->x - origx;
179  point->h2_loc[1] += point->y - origy;
180  }
181 
182  if (point->x != origx || point->y != origy) {
183  return true;
184  }
185  return false;
186 }
187 
193 {
194  /* Must have 2 points minimum. */
195  if (profile->path_len <= 2) {
196  return false;
197  }
198 
199  /* Input point must be within the array. */
200  if (!(point > profile->path && point < profile->path + profile->path_len)) {
201  return false;
202  }
203 
204  CurveProfilePoint *new_path = MEM_mallocN(sizeof(CurveProfilePoint) * profile->path_len,
205  "profile path");
206 
207  int i_delete = (int)(point - profile->path);
208  BLI_assert(i_delete > 0);
209 
210  /* Copy the before and after the deleted point. */
211  memcpy(new_path, profile->path, sizeof(CurveProfilePoint) * i_delete);
212  memcpy(new_path + i_delete,
213  profile->path + i_delete + 1,
214  sizeof(CurveProfilePoint) * (profile->path_len - i_delete - 1));
215 
216  MEM_freeN(profile->path);
217  profile->path = new_path;
218  profile->path_len -= 1;
219  return true;
220 }
221 
229 void BKE_curveprofile_remove_by_flag(CurveProfile *profile, const short flag)
230 {
231  /* Copy every point without the flag into the new path. */
232  CurveProfilePoint *new_path = MEM_mallocN(sizeof(CurveProfilePoint) * profile->path_len,
233  "profile path");
234 
235  /* Build the new list without any of the points with the flag. Keep the first and last points. */
236  int i_new = 1;
237  int i_old = 1;
238  int n_removed = 0;
239  new_path[0] = profile->path[0];
240  for (; i_old < profile->path_len - 1; i_old++) {
241  if (!(profile->path[i_old].flag & flag)) {
242  new_path[i_new] = profile->path[i_old];
243  i_new++;
244  }
245  else {
246  n_removed++;
247  }
248  }
249  new_path[i_new] = profile->path[i_old];
250 
251  MEM_freeN(profile->path);
252  profile->path = new_path;
253  profile->path_len -= n_removed;
254 }
255 
259 static void point_init(CurveProfilePoint *point, float x, float y, short flag, char h1, char h2)
260 {
261  point->x = x;
262  point->y = y;
263  point->flag = flag;
264  point->h1 = h1;
265  point->h2 = h2;
266 }
267 
276 {
277  const float new_loc[2] = {x, y};
278 
279  /* Don't add more control points than the maximum size of the higher resolution table. */
280  if (profile->path_len == PROF_TABLE_MAX - 1) {
281  return NULL;
282  }
283 
284  /* Find the index at the line segment that's closest to the new position. */
285  float min_distance = FLT_MAX;
286  int i_insert = 0;
287  for (int i = 0; i < profile->path_len - 1; i++) {
288  const float loc1[2] = {profile->path[i].x, profile->path[i].y};
289  const float loc2[2] = {profile->path[i + 1].x, profile->path[i + 1].y};
290 
291  float distance = dist_squared_to_line_segment_v2(new_loc, loc1, loc2);
292  if (distance < min_distance) {
293  min_distance = distance;
294  i_insert = i + 1;
295  }
296  }
297 
298  /* Insert the new point at the location we found and copy all of the old points in as well. */
299  profile->path_len++;
300  CurveProfilePoint *new_path = MEM_mallocN(sizeof(CurveProfilePoint) * profile->path_len,
301  "profile path");
302  CurveProfilePoint *new_pt = NULL;
303  for (int i_new = 0, i_old = 0; i_new < profile->path_len; i_new++) {
304  if (i_new != i_insert) {
305  /* Insert old points. */
306  new_path[i_new] = profile->path[i_old];
307  new_path[i_new].flag &= ~PROF_SELECT; /* Deselect old points. */
308  i_old++;
309  }
310  else {
311  /* Insert new point. */
312  /* Set handles of new point based on its neighbors. */
313  char new_handle_type = (new_path[i_new - 1].h2 == HD_VECT &&
314  profile->path[i_insert].h1 == HD_VECT) ?
315  HD_VECT :
316  HD_AUTO;
317  point_init(&new_path[i_new], x, y, PROF_SELECT, new_handle_type, new_handle_type);
318  new_pt = &new_path[i_new];
319  /* Give new point a reference to the profile. */
320  new_pt->profile = profile;
321  }
322  }
323 
324  /* Free the old path and use the new one. */
325  MEM_freeN(profile->path);
326  profile->path = new_path;
327  return new_pt;
328 }
329 
335 void BKE_curveprofile_selected_handle_set(CurveProfile *profile, int type_1, int type_2)
336 {
337  for (int i = 0; i < profile->path_len; i++) {
338  if (ELEM(profile->path[i].flag, PROF_SELECT, PROF_H1_SELECT, PROF_H2_SELECT)) {
339  profile->path[i].h1 = type_1;
340  profile->path[i].h2 = type_2;
341 
342  if (type_1 == HD_ALIGN && type_2 == HD_ALIGN) {
343  /* Align the handles. */
344  BKE_curveprofile_move_handle(&profile->path[i], true, false, NULL);
345  }
346  }
347  }
348 }
349 
351 {
352  CurveProfilePoint new_point = *point;
353  point_init(&new_point, point->y, point->x, point->flag, point->h2, point->h1);
354  return new_point;
355 }
356 
363 {
364  /* When there are only two points, reversing shouldn't do anything. */
365  if (profile->path_len == 2) {
366  return;
367  }
368  CurveProfilePoint *new_path = MEM_mallocN(sizeof(CurveProfilePoint) * profile->path_len,
369  "profile path");
370  /* Mirror the new points across the y = x line */
371  for (int i = 0; i < profile->path_len; i++) {
372  int i_reversed = profile->path_len - i - 1;
373  BLI_assert(i_reversed >= 0);
374  new_path[i_reversed] = mirror_point(&profile->path[i]);
375  new_path[i_reversed].profile = profile;
376 
377  /* Mirror free handles, they can't be recalculated. */
378  if (ELEM(profile->path[i].h1, HD_FREE, HD_ALIGN)) {
379  new_path[i_reversed].h1_loc[0] = profile->path[i].h2_loc[1];
380  new_path[i_reversed].h1_loc[1] = profile->path[i].h2_loc[0];
381  }
382  if (ELEM(profile->path[i].h2, HD_FREE, HD_ALIGN)) {
383  new_path[i_reversed].h2_loc[0] = profile->path[i].h1_loc[1];
384  new_path[i_reversed].h2_loc[1] = profile->path[i].h1_loc[0];
385  }
386  }
387 
388  /* Free the old points and use the new ones */
389  MEM_freeN(profile->path);
390  profile->path = new_path;
391 }
392 
397 {
398  int n = profile->path_len;
399 
400  point_init(&profile->path[0], 1.0f, 0.0f, 0, HD_VECT, HD_VECT);
401  point_init(&profile->path[1], 1.0f, 0.5f, 0, HD_VECT, HD_VECT);
402  for (int i = 1; i < n - 2; i++) {
403  const float x = 1.0f - (0.5f * (1.0f - cosf((float)((i / (float)(n - 3))) * M_PI_2)));
404  const float y = 0.5f + 0.5f * sinf((float)((i / (float)(n - 3)) * M_PI_2));
405  point_init(&profile->path[i], x, y, 0, HD_AUTO, HD_AUTO);
406  }
407  point_init(&profile->path[n - 2], 0.5f, 1.0f, 0, HD_VECT, HD_VECT);
408  point_init(&profile->path[n - 1], 0.0f, 1.0f, 0, HD_VECT, HD_VECT);
409 }
410 
416 {
417  int n = profile->path_len;
418 
419  /* Special case for two points to avoid dividing by zero later. */
420  if (n == 2) {
421  point_init(&profile->path[0], 1.0f, 0.0f, 0, HD_VECT, HD_VECT);
422  point_init(&profile->path[0], 0.0f, 1.0f, 0, HD_VECT, HD_VECT);
423  return;
424  }
425 
426  float n_steps_x = (n % 2 == 0) ? n : (n - 1);
427  float n_steps_y = (n % 2 == 0) ? (n - 2) : (n - 1);
428 
429  for (int i = 0; i < n; i++) {
430  int step_x = (i + 1) / 2;
431  int step_y = i / 2;
432  const float x = 1.0f - ((float)(2 * step_x) / n_steps_x);
433  const float y = (float)(2 * step_y) / n_steps_y;
434  point_init(&profile->path[i], x, y, 0, HD_VECT, HD_VECT);
435  }
436 }
437 
442 {
443  profile->view_rect = profile->clip_rect;
444 }
445 
452 {
453  MEM_SAFE_FREE(profile->path);
454 
455  eCurveProfilePresets preset = profile->preset;
456  switch (preset) {
457  case PROF_PRESET_LINE:
458  profile->path_len = 2;
459  break;
461  /* Use a dynamic number of control points for the widget's profile. */
462  if (profile->segments_len < 4) {
463  /* But always use enough points to at least build the support points. */
464  profile->path_len = 5;
465  }
466  else {
467  profile->path_len = profile->segments_len + 1;
468  }
469  break;
470  case PROF_PRESET_CORNICE:
471  profile->path_len = 13;
472  break;
473  case PROF_PRESET_CROWN:
474  profile->path_len = 11;
475  break;
476  case PROF_PRESET_STEPS:
477  /* Also use dynamic number of control points based on the set number of segments. */
478  if (profile->segments_len == 0) {
479  /* totsegments hasn't been set-- use the number of control points for 8 steps. */
480  profile->path_len = 17;
481  }
482  else {
483  profile->path_len = profile->segments_len + 1;
484  }
485  break;
486  }
487 
488  profile->path = MEM_callocN(sizeof(CurveProfilePoint) * profile->path_len, "profile path");
489 
490  switch (preset) {
491  case PROF_PRESET_LINE:
492  point_init(&profile->path[0], 1.0f, 0.0f, 0, HD_AUTO, HD_AUTO);
493  point_init(&profile->path[1], 0.0f, 1.0f, 0, HD_AUTO, HD_AUTO);
494  break;
497  break;
498  case PROF_PRESET_CORNICE:
499  point_init(&profile->path[0], 1.0f, 0.0f, 0, HD_VECT, HD_VECT);
500  point_init(&profile->path[1], 1.0f, 0.125f, 0, HD_VECT, HD_VECT);
501  point_init(&profile->path[2], 0.92f, 0.16f, 0, HD_AUTO, HD_AUTO);
502  point_init(&profile->path[3], 0.875f, 0.25f, 0, HD_VECT, HD_VECT);
503  point_init(&profile->path[4], 0.8f, 0.25f, 0, HD_VECT, HD_VECT);
504  point_init(&profile->path[5], 0.733f, 0.433f, 0, HD_AUTO, HD_AUTO);
505  point_init(&profile->path[6], 0.582f, 0.522f, 0, HD_AUTO, HD_AUTO);
506  point_init(&profile->path[7], 0.4f, 0.6f, 0, HD_AUTO, HD_AUTO);
507  point_init(&profile->path[8], 0.289f, 0.727f, 0, HD_AUTO, HD_AUTO);
508  point_init(&profile->path[9], 0.25f, 0.925f, 0, HD_VECT, HD_VECT);
509  point_init(&profile->path[10], 0.175f, 0.925f, 0, HD_VECT, HD_VECT);
510  point_init(&profile->path[11], 0.175f, 1.0f, 0, HD_VECT, HD_VECT);
511  point_init(&profile->path[12], 0.0f, 1.0f, 0, HD_VECT, HD_VECT);
512  break;
513  case PROF_PRESET_CROWN:
514  point_init(&profile->path[0], 1.0f, 0.0f, 0, HD_VECT, HD_VECT);
515  point_init(&profile->path[1], 1.0f, 0.25f, 0, HD_VECT, HD_VECT);
516  point_init(&profile->path[2], 0.75f, 0.25f, 0, HD_VECT, HD_VECT);
517  point_init(&profile->path[3], 0.75f, 0.325f, 0, HD_VECT, HD_VECT);
518  point_init(&profile->path[4], 0.925f, 0.4f, 0, HD_AUTO, HD_AUTO);
519  point_init(&profile->path[5], 0.975f, 0.5f, 0, HD_AUTO, HD_AUTO);
520  point_init(&profile->path[6], 0.94f, 0.65f, 0, HD_AUTO, HD_AUTO);
521  point_init(&profile->path[7], 0.85f, 0.75f, 0, HD_AUTO, HD_AUTO);
522  point_init(&profile->path[8], 0.75f, 0.875f, 0, HD_AUTO, HD_AUTO);
523  point_init(&profile->path[9], 0.7f, 1.0f, 0, HD_VECT, HD_VECT);
524  point_init(&profile->path[10], 0.0f, 1.0f, 0, HD_VECT, HD_VECT);
525  break;
526  case PROF_PRESET_STEPS:
527  curveprofile_build_steps(profile);
528  break;
529  }
530 
531  profile->flag &= ~PROF_DIRTY_PRESET;
532 
533  /* Ensure each point has a reference to the profile. */
534  for (int i = 0; i < profile->path_len; i++) {
535  profile->path[i].profile = profile;
536  }
537 
538  MEM_SAFE_FREE(profile->table);
539  profile->table = NULL;
540 }
541 
546 static bool is_curved_edge(CurveProfilePoint *path, int i)
547 {
548  return (path[i].h2 != HD_VECT || path[i + 1].h1 != HD_VECT);
549 }
550 
556  const CurveProfilePoint *prev,
557  const CurveProfilePoint *next)
558 {
559  if (point->h1 == HD_FREE && point->h2 == HD_FREE) {
560  return;
561  }
562 
563  float *point_loc = &point->x;
564 
565  float pt[2];
566  const float *prev_loc, *next_loc;
567  if (prev == NULL) {
568  next_loc = &next->x;
569  pt[0] = 2.0f * point_loc[0] - next_loc[0];
570  pt[1] = 2.0f * point_loc[1] - next_loc[1];
571  prev_loc = pt;
572  }
573  else {
574  prev_loc = &prev->x;
575  }
576 
577  if (next == NULL) {
578  prev_loc = &prev->x;
579  pt[0] = 2.0f * point_loc[0] - prev_loc[0];
580  pt[1] = 2.0f * point_loc[1] - prev_loc[1];
581  next_loc = pt;
582  }
583  else {
584  next_loc = &next->x;
585  }
586 
587  float dvec_a[2], dvec_b[2];
588  sub_v2_v2v2(dvec_a, point_loc, prev_loc);
589  sub_v2_v2v2(dvec_b, next_loc, point_loc);
590 
591  float len_a = len_v2(dvec_a);
592  float len_b = len_v2(dvec_b);
593  if (len_a == 0.0f) {
594  len_a = 1.0f;
595  }
596  if (len_b == 0.0f) {
597  len_b = 1.0f;
598  }
599 
600  if (point->h1 == HD_AUTO || point->h2 == HD_AUTO) {
601  float tvec[2];
602  tvec[0] = dvec_b[0] / len_b + dvec_a[0] / len_a;
603  tvec[1] = dvec_b[1] / len_b + dvec_a[1] / len_a;
604 
605  float len = len_v2(tvec) * 2.5614f;
606  if (len != 0.0f) {
607  if (point->h1 == HD_AUTO) {
608  len_a /= len;
609  madd_v2_v2v2fl(point->h1_loc, point_loc, tvec, -len_a);
610  }
611  if (point->h2 == HD_AUTO) {
612  len_b /= len;
613  madd_v2_v2v2fl(point->h2_loc, point_loc, tvec, len_b);
614  }
615  }
616  }
617 
618  if (point->h1 == HD_VECT) {
619  madd_v2_v2v2fl(point->h1_loc, point_loc, dvec_a, -1.0f / 3.0f);
620  }
621  if (point->h2 == HD_VECT) {
622  madd_v2_v2v2fl(point->h2_loc, point_loc, dvec_b, 1.0f / 3.0f);
623  }
624 }
625 
626 static void calculate_path_handles(CurveProfilePoint *path, int path_len)
627 {
628  point_calculate_handle(&path[0], NULL, &path[1]);
629  for (int i = 1; i < path_len - 1; i++) {
630  point_calculate_handle(&path[i], &path[i - 1], &path[i + 1]);
631  }
632  point_calculate_handle(&path[path_len - 1], &path[path_len - 2], NULL);
633 }
634 
641 static float bezt_edge_handle_angle(const CurveProfilePoint *path, int i_edge)
642 {
643  /* Find the direction of the handles that define this edge along the direction of the path. */
644  float start_handle_direction[2], end_handle_direction[2];
645  /* Handle 2 - point location. */
646  sub_v2_v2v2(start_handle_direction, path[i_edge].h2_loc, &path[i_edge].x);
647  /* Point location - handle 1. */
648  sub_v2_v2v2(end_handle_direction, &path[i_edge + 1].x, path[i_edge + 1].h1_loc);
649 
650  return angle_v2v2(start_handle_direction, end_handle_direction);
651 }
652 
654 typedef struct {
660 
664 static int sort_points_curvature(const void *in_a, const void *in_b)
665 {
666  const CurvatureSortPoint *a = (const CurvatureSortPoint *)in_a;
667  const CurvatureSortPoint *b = (const CurvatureSortPoint *)in_b;
668 
669  if (a->point_curvature > b->point_curvature) {
670  return 0;
671  }
672 
673  return 1;
674 }
675 
690  int n_segments,
691  bool sample_straight_edges,
692  CurveProfilePoint *r_samples)
693 {
694  CurveProfilePoint *path = profile->path;
695  int totpoints = profile->path_len;
696  BLI_assert(n_segments > 0);
697 
698  int totedges = totpoints - 1;
699 
700  calculate_path_handles(path, totpoints);
701 
702  /* Create a list of edge indices with the most curved at the start, least curved at the end. */
703  CurvatureSortPoint *curve_sorted = MEM_callocN(sizeof(CurvatureSortPoint) * totedges,
704  "curve sorted");
705  for (int i = 0; i < totedges; i++) {
706  curve_sorted[i].point_index = i;
707  /* Calculate the curvature of each edge once for use when sorting for curvature. */
708  curve_sorted[i].point_curvature = bezt_edge_handle_angle(path, i);
709  }
710  qsort(curve_sorted, totedges, sizeof(CurvatureSortPoint), sort_points_curvature);
711 
712  /* Assign the number of sampled points for each edge. */
713  int16_t *n_samples = MEM_callocN(sizeof(int16_t) * totedges, "samples numbers");
714  int n_added = 0;
715  int n_left;
716  if (n_segments >= totedges) {
717  if (sample_straight_edges) {
718  /* Assign an even number to each edge if it’s possible, then add the remainder of sampled
719  * points starting with the most curved edges. */
720  int n_common = n_segments / totedges;
721  n_left = n_segments % totedges;
722 
723  /* Assign the points that fill fit evenly to the edges. */
724  if (n_common > 0) {
725  BLI_assert(n_common < INT16_MAX);
726  for (int i = 0; i < totedges; i++) {
727  n_samples[i] = n_common;
728  n_added += n_common;
729  }
730  }
731  }
732  else {
733  /* Count the number of curved edges */
734  int n_curved_edges = 0;
735  for (int i = 0; i < totedges; i++) {
736  if (is_curved_edge(path, i)) {
737  n_curved_edges++;
738  }
739  }
740  /* Just sample all of the edges if there are no curved edges. */
741  n_curved_edges = (n_curved_edges == 0) ? totedges : n_curved_edges;
742 
743  /* Give all of the curved edges the same number of points and straight edges one point. */
744  n_left = n_segments - (totedges - n_curved_edges); /* Left after 1 for each straight edge. */
745  int n_common = n_left / n_curved_edges; /* Number assigned to all curved edges */
746  if (n_common > 0) {
747  for (int i = 0; i < totedges; i++) {
748  /* Add the common number if it's a curved edge or if edges are curved. */
749  if (is_curved_edge(path, i) || n_curved_edges == totedges) {
750  BLI_assert(n_common + n_samples[i] < INT16_MAX);
751  n_samples[i] += n_common;
752  n_added += n_common;
753  }
754  else {
755  n_samples[i] = 1;
756  n_added++;
757  }
758  }
759  }
760  n_left -= n_common * n_curved_edges;
761  }
762  }
763  else {
764  /* Not enough segments to give one to each edge, so just give them to the most curved edges. */
765  n_left = n_segments;
766  }
767  /* Assign the remainder of the points that couldn't be spread out evenly. */
768  BLI_assert(n_left < totedges);
769  for (int i = 0; i < n_left; i++) {
770  BLI_assert(n_samples[curve_sorted[i].point_index] < INT16_MAX);
771  n_samples[curve_sorted[i].point_index]++;
772  n_added++;
773  }
774 
775  BLI_assert(n_added == n_segments); /* n_added is just used for this assert, could remove it. */
776 
777  /* Sample the points and add them to the locations table. */
778  for (int i_sample = 0, i = 0; i < totedges; i++) {
779  if (n_samples[i] > 0) {
780  /* Carry over the handle types from the control point to its first corresponding sample. */
781  r_samples[i_sample].h1 = path[i].h1;
782  r_samples[i_sample].h2 = path[i].h2;
783  /* All extra sample points for this control point get "auto" handles. */
784  for (int j = i_sample + 1; j < i_sample + n_samples[i]; j++) {
785  r_samples[j].flag = 0;
786  r_samples[j].h1 = HD_AUTO;
787  r_samples[j].h2 = HD_AUTO;
788  BLI_assert(j < n_segments);
789  }
790 
791  /* Sample from the bezier points. X then Y values. */
793  path[i].h2_loc[0],
794  path[i + 1].h1_loc[0],
795  path[i + 1].x,
796  &r_samples[i_sample].x,
797  n_samples[i],
798  sizeof(CurveProfilePoint));
800  path[i].h2_loc[1],
801  path[i + 1].h1_loc[1],
802  path[i + 1].y,
803  &r_samples[i_sample].y,
804  n_samples[i],
805  sizeof(CurveProfilePoint));
806  }
807  i_sample += n_samples[i]; /* Add the next set of points after the ones we just added. */
808  BLI_assert(i_sample <= n_segments);
809  }
810 
811  MEM_freeN(curve_sorted);
812  MEM_freeN(n_samples);
813 }
814 
820 {
821  int n_samples = PROF_TABLE_LEN(profile->path_len);
822  CurveProfilePoint *new_table = MEM_callocN(sizeof(CurveProfilePoint) * (n_samples + 1),
823  __func__);
824 
825  BKE_curveprofile_create_samples(profile, n_samples - 1, false, new_table);
826  /* Manually add last point at the end of the profile */
827  new_table[n_samples - 1].x = 0.0f;
828  new_table[n_samples - 1].y = 1.0f;
829 
830  MEM_SAFE_FREE(profile->table);
831  profile->table = new_table;
832 }
833 
839 {
840  int n_samples = profile->segments_len;
841  if (n_samples <= 0) {
842  return;
843  }
844  CurveProfilePoint *new_table = MEM_callocN(sizeof(CurveProfilePoint) * (n_samples + 1),
845  __func__);
846 
847  if (profile->flag & PROF_SAMPLE_EVEN_LENGTHS) {
848  /* Even length sampling incompatible with only straight edge sampling for now. */
849  BKE_curveprofile_create_samples_even_spacing(profile, n_samples, new_table);
850  }
851  else {
853  profile, n_samples, profile->flag & PROF_SAMPLE_STRAIGHT_EDGES, new_table);
854  }
855 
856  MEM_SAFE_FREE(profile->segments);
857  profile->segments = new_table;
858 }
859 
865 {
866  profile->flag = PROF_USE_CLIP;
867 
868  BLI_rctf_init(&profile->view_rect, 0.0f, 1.0f, 0.0f, 1.0f);
869  profile->clip_rect = profile->view_rect;
870 
871  profile->path_len = 2;
872  profile->path = MEM_callocN(2 * sizeof(CurveProfilePoint), "path points");
873 
874  profile->path[0].x = 1.0f;
875  profile->path[0].y = 0.0f;
876  profile->path[0].profile = profile;
877  profile->path[1].x = 1.0f;
878  profile->path[1].y = 1.0f;
879  profile->path[1].profile = profile;
880 
881  profile->changed_timestamp = 0;
882 }
883 
888 {
889  CurveProfile *profile = MEM_callocN(sizeof(CurveProfile), "curve profile");
890 
892  profile->preset = preset;
893  BKE_curveprofile_reset(profile);
894  curveprofile_make_table(profile);
895 
896  return profile;
897 }
898 
905 void BKE_curveprofile_update(CurveProfile *profile, const int update_flags)
906 {
907  CurveProfilePoint *points = profile->path;
908  rctf *clipr = &profile->clip_rect;
909 
910  profile->changed_timestamp++;
911 
912  /* Clamp with the clipping rect in case something got past. */
913  if (profile->flag & PROF_USE_CLIP) {
914  /* Move points inside the clip rectangle. */
915  if (update_flags & PROF_UPDATE_CLIP) {
916  for (int i = 0; i < profile->path_len; i++) {
917  points[i].x = clamp_f(points[i].x, clipr->xmin, clipr->xmax);
918  points[i].y = clamp_f(points[i].y, clipr->ymin, clipr->ymax);
919 
920  /* Extra sanity assert to make sure the points have the right profile pointer. */
921  BLI_assert(points[i].profile == profile);
922  }
923  }
924  /* Ensure zoom-level respects clipping. */
925  if (BLI_rctf_size_x(&profile->view_rect) > BLI_rctf_size_x(&profile->clip_rect)) {
926  profile->view_rect.xmin = profile->clip_rect.xmin;
927  profile->view_rect.xmax = profile->clip_rect.xmax;
928  }
929  if (BLI_rctf_size_y(&profile->view_rect) > BLI_rctf_size_y(&profile->clip_rect)) {
930  profile->view_rect.ymin = profile->clip_rect.ymin;
931  profile->view_rect.ymax = profile->clip_rect.ymax;
932  }
933  }
934 
935  /* Remove doubles with a threshold set at 1% of default range. */
936  float thresh = pow2f(0.01f * BLI_rctf_size_x(clipr));
937  if (update_flags & PROF_UPDATE_REMOVE_DOUBLES && profile->path_len > 2) {
938  for (int i = 0; i < profile->path_len - 1; i++) {
939  if (len_squared_v2v2(&points[i].x, &points[i + 1].x) < thresh) {
940  if (i == 0) {
941  BKE_curveprofile_remove_point(profile, &points[1]);
942  }
943  else {
944  BKE_curveprofile_remove_point(profile, &points[i]);
945  }
946  break; /* Assumes 1 deletion per update call is ok. */
947  }
948  }
949  }
950 
951  /* Create the high resolution table for drawing and some evaluation functions. */
952  curveprofile_make_table(profile);
953 
954  /* Store a table of samples for the segment locations for a preview and the table's user. */
955  if (profile->segments_len > 0) {
957  }
958 }
959 
967 {
968  if (segments_len != profile->segments_len) {
969  profile->flag |= PROF_DIRTY_PRESET;
970  }
971  profile->segments_len = segments_len;
972 
973  /* Calculate the higher resolution / segments tables for display and evaluation. */
975 }
976 
983 static float curveprofile_distance_to_next_table_point(const CurveProfile *profile, int i)
984 {
985  BLI_assert(i < PROF_TABLE_LEN(profile->path_len));
986 
987  return len_v2v2(&profile->table[i].x, &profile->table[i + 1].x);
988 }
989 
996 {
997  float total_length = 0;
998  for (int i = 0; i < PROF_TABLE_LEN(profile->path_len) - 1; i++) {
999  total_length += len_v2v2(&profile->table[i].x, &profile->table[i + 1].x);
1000  }
1001  return total_length;
1002 }
1003 
1013  int n_segments,
1014  CurveProfilePoint *r_samples)
1015 {
1016  const float total_length = BKE_curveprofile_total_length(profile);
1017  const float segment_length = total_length / n_segments;
1018  float length_travelled = 0.0f;
1019  float distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, 0);
1020  float distance_to_previous_table_point = 0.0f;
1021  int i_table = 0;
1022 
1023  /* Set the location for the first point. */
1024  r_samples[0].x = profile->table[0].x;
1025  r_samples[0].y = profile->table[0].y;
1026 
1027  /* Travel along the path, recording the locations of segments as we pass them. */
1028  float segment_left = segment_length;
1029  for (int i = 1; i < n_segments; i++) {
1030  /* Travel over all of the points that fit inside this segment. */
1031  while (distance_to_next_table_point < segment_left) {
1032  length_travelled += distance_to_next_table_point;
1033  segment_left -= distance_to_next_table_point;
1034  i_table++;
1035  distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, i_table);
1036  distance_to_previous_table_point = 0.0f;
1037  }
1038  /* We're at the last table point that fits inside the current segment, use interpolation. */
1039  float factor = (distance_to_previous_table_point + segment_left) /
1040  (distance_to_previous_table_point + distance_to_next_table_point);
1041  r_samples[i].x = interpf(profile->table[i_table + 1].x, profile->table[i_table].x, factor);
1042  r_samples[i].y = interpf(profile->table[i_table + 1].y, profile->table[i_table].y, factor);
1043  BLI_assert(factor <= 1.0f && factor >= 0.0f);
1044 #ifdef DEBUG_CURVEPROFILE_EVALUATE
1045  printf("segment_left: %.3f\n", segment_left);
1046  printf("i_table: %d\n", i_table);
1047  printf("distance_to_previous_table_point: %.3f\n", distance_to_previous_table_point);
1048  printf("distance_to_next_table_point: %.3f\n", distance_to_next_table_point);
1049  printf("Interpolating with factor %.3f from (%.3f, %.3f) to (%.3f, %.3f)\n\n",
1050  factor,
1051  profile->table[i_table].x,
1052  profile->table[i_table].y,
1053  profile->table[i_table + 1].x,
1054  profile->table[i_table + 1].y);
1055 #endif
1056 
1057  /* We sampled in between this table point and the next, so the next travel step is smaller. */
1058  distance_to_next_table_point -= segment_left;
1059  distance_to_previous_table_point += segment_left;
1060  length_travelled += segment_left;
1061  segment_left = segment_length;
1062  }
1063 }
1064 
1073  float length_portion,
1074  float *x_out,
1075  float *y_out)
1076 {
1077  const float total_length = BKE_curveprofile_total_length(profile);
1078  const float requested_length = length_portion * total_length;
1079 
1080  /* Find the last point along the path with a lower length portion than the input. */
1081  int i = 0;
1082  float length_travelled = 0.0f;
1083  while (length_travelled < requested_length) {
1084  /* Check if we reached the last point before the final one. */
1085  if (i == PROF_TABLE_LEN(profile->path_len) - 2) {
1086  break;
1087  }
1088  float new_length = curveprofile_distance_to_next_table_point(profile, i);
1089  if (length_travelled + new_length >= requested_length) {
1090  break;
1091  }
1092  length_travelled += new_length;
1093  i++;
1094  }
1095 
1096  /* Now travel the remaining distance of length portion down the path to the next point and
1097  * find the location where we stop. */
1098  float distance_to_next_point = curveprofile_distance_to_next_table_point(profile, i);
1099  float lerp_factor = (requested_length - length_travelled) / distance_to_next_point;
1100 
1101 #ifdef DEBUG_CURVEPROFILE_EVALUATE
1102  printf("CURVEPROFILE EVALUATE\n");
1103  printf(" length portion input: %f\n", (double)length_portion);
1104  printf(" requested path length: %f\n", (double)requested_length);
1105  printf(" distance to next point: %f\n", (double)distance_to_next_point);
1106  printf(" length travelled: %f\n", (double)length_travelled);
1107  printf(" lerp-factor: %f\n", (double)lerp_factor);
1108  printf(" ith point (%f, %f)\n", (double)profile->path[i].x, (double)profile->path[i].y);
1109  printf(" next point(%f, %f)\n", (double)profile->path[i + 1].x, (double)profile->path[i + 1].y);
1110 #endif
1111 
1112  *x_out = interpf(profile->table[i].x, profile->table[i + 1].x, lerp_factor);
1113  *y_out = interpf(profile->table[i].y, profile->table[i + 1].y, lerp_factor);
1114 }
1115 
1116 void BKE_curveprofile_blend_write(struct BlendWriter *writer, const struct CurveProfile *profile)
1117 {
1118  BLO_write_struct(writer, CurveProfile, profile);
1119  BLO_write_struct_array(writer, CurveProfilePoint, profile->path_len, profile->path);
1120 }
1121 
1122 /* Expects that the curve profile itself has been read already. */
1123 void BKE_curveprofile_blend_read(struct BlendDataReader *reader, struct CurveProfile *profile)
1124 {
1125  BLO_read_data_address(reader, &profile->path);
1126  profile->table = NULL;
1127  profile->segments = NULL;
1128 
1129  /* Reset the points' pointers to the profile. */
1130  for (int i = 0; i < profile->path_len; i++) {
1131  profile->path[i].profile = profile;
1132  }
1133 
1134  BKE_curveprofile_init(profile, profile->segments_len);
1135 }
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.c:1804
@ PROF_UPDATE_CLIP
@ PROF_UPDATE_REMOVE_DOUBLES
@ PROF_UPDATE_NONE
#define BLI_assert(a)
Definition: BLI_assert.h:58
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:41
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:338
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:483
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:436
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition: BLI_rect.h:161
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition: BLI_rect.h:165
#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
#define PROF_TABLE_MAX
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
#define PROF_TABLE_LEN(n_pts)
@ PROF_H1_SELECT
@ PROF_H2_SELECT
_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)
static float bezt_edge_handle_angle(const CurveProfilePoint *path, int i_edge)
Definition: curveprofile.c:641
void BKE_curveprofile_init(CurveProfile *profile, short segments_len)
Definition: curveprofile.c:966
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)
Definition: curveprofile.c:192
void BKE_curveprofile_copy_data(CurveProfile *target, const CurveProfile *profile)
Definition: curveprofile.c:61
CurveProfile * BKE_curveprofile_copy(const CurveProfile *profile)
Definition: curveprofile.c:75
void BKE_curveprofile_selected_handle_set(CurveProfile *profile, int type_1, int type_2)
Definition: curveprofile.c:335
static void curveprofile_build_steps(CurveProfile *profile)
Definition: curveprofile.c:415
void BKE_curveprofile_blend_read(struct BlendDataReader *reader, struct CurveProfile *profile)
static void curveprofile_build_supports(CurveProfile *profile)
Definition: curveprofile.c:396
float BKE_curveprofile_total_length(const CurveProfile *profile)
Definition: curveprofile.c:995
void BKE_curveprofile_create_samples(CurveProfile *profile, int n_segments, bool sample_straight_edges, CurveProfilePoint *r_samples)
Definition: curveprofile.c:689
static void curveprofile_make_table(CurveProfile *profile)
Definition: curveprofile.c:819
void BKE_curveprofile_reset(CurveProfile *profile)
Definition: curveprofile.c:451
static void calculate_path_handles(CurveProfilePoint *path, int path_len)
Definition: curveprofile.c:626
void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile, int n_segments, CurveProfilePoint *r_samples)
static CurveProfilePoint mirror_point(const CurveProfilePoint *point)
Definition: curveprofile.c:350
static bool is_curved_edge(CurveProfilePoint *path, int i)
Definition: curveprofile.c:546
void BKE_curveprofile_free_data(CurveProfile *profile)
Definition: curveprofile.c:46
void BKE_curveprofile_remove_by_flag(CurveProfile *profile, const short flag)
Definition: curveprofile.c:229
CurveProfilePoint * BKE_curveprofile_insert(CurveProfile *profile, float x, float y)
Definition: curveprofile.c:275
void BKE_curveprofile_reset_view(CurveProfile *profile)
Definition: curveprofile.c:441
static void point_calculate_handle(CurveProfilePoint *point, const CurveProfilePoint *prev, const CurveProfilePoint *next)
Definition: curveprofile.c:555
void BKE_curveprofile_reverse(CurveProfile *profile)
Definition: curveprofile.c:362
bool BKE_curveprofile_move_point(struct CurveProfile *profile, struct CurveProfilePoint *point, const bool snap, const float delta[2])
Definition: curveprofile.c:141
static void point_init(CurveProfilePoint *point, float x, float y, short flag, char h1, char h2)
Definition: curveprofile.c:259
bool BKE_curveprofile_move_handle(struct CurveProfilePoint *point, const bool handle_1, const bool snap, const float delta[2])
Definition: curveprofile.c:93
void BKE_curveprofile_blend_write(struct BlendWriter *writer, const struct CurveProfile *profile)
struct CurveProfile * BKE_curveprofile_add(eCurveProfilePresets preset)
Definition: curveprofile.c:887
void BKE_curveprofile_free(CurveProfile *profile)
Definition: curveprofile.c:53
static float curveprofile_distance_to_next_table_point(const CurveProfile *profile, int i)
Definition: curveprofile.c:983
void BKE_curveprofile_update(CurveProfile *profile, const int update_flags)
Definition: curveprofile.c:905
static int sort_points_curvature(const void *in_a, const void *in_b)
Definition: curveprofile.c:664
static void curveprofile_make_segments_table(CurveProfile *profile)
Definition: curveprofile.c:838
void BKE_curveprofile_set_defaults(CurveProfile *profile)
Definition: curveprofile.c:864
#define sinf(x)
#define cosf(x)
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_dupallocN)(const void *vmemh)
Definition: mallocn.c:42
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
static ulong * next
static unsigned a[3]
Definition: RandGen.cpp:92
vector snap(vector a, vector b)
Definition: node_math.h:72
signed short int16_t
Definition: stdint.h:79
#define INT16_MAX
Definition: stdint.h:138
struct CurveProfile * profile
CurveProfilePoint * path
CurveProfilePoint * table
CurveProfilePoint * segments
float xmax
Definition: DNA_vec_types.h:85
float xmin
Definition: DNA_vec_types.h:85
float ymax
Definition: DNA_vec_types.h:86
float ymin
Definition: DNA_vec_types.h:86
ccl_device_inline float distance(const float2 &a, const float2 &b)
uint len